tclFCmd.c

Go to the documentation of this file.
00001 /*
00002  * tclFCmd.c
00003  *
00004  *      This file implements the generic portion of file manipulation
00005  *      subcommands of the "file" command.
00006  *
00007  * Copyright (c) 1996-1998 Sun Microsystems, Inc.
00008  *
00009  * See the file "license.terms" for information on usage and redistribution of
00010  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00011  *
00012  * RCS: @(#) $Id: tclFCmd.c,v 1.43 2007/12/13 15:23:17 dgp Exp $
00013  */
00014 
00015 #include "tclInt.h"
00016 
00017 /*
00018  * Declarations for local functions defined in this file:
00019  */
00020 
00021 static int              CopyRenameOneFile(Tcl_Interp *interp,
00022                             Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr,
00023                             int copyFlag, int force);
00024 static Tcl_Obj *        FileBasename(Tcl_Interp *interp, Tcl_Obj *pathPtr);
00025 static int              FileCopyRename(Tcl_Interp *interp,
00026                             int objc, Tcl_Obj *CONST objv[], int copyFlag);
00027 static int              FileForceOption(Tcl_Interp *interp,
00028                             int objc, Tcl_Obj *CONST objv[], int *forcePtr);
00029 
00030 /*
00031  *---------------------------------------------------------------------------
00032  *
00033  * TclFileRenameCmd
00034  *
00035  *      This function implements the "rename" subcommand of the "file"
00036  *      command. Filename arguments need to be translated to native format
00037  *      before being passed to platform-specific code that implements rename
00038  *      functionality.
00039  *
00040  * Results:
00041  *      A standard Tcl result.
00042  *
00043  * Side effects:
00044  *      See the user documentation.
00045  *
00046  *---------------------------------------------------------------------------
00047  */
00048 
00049 int
00050 TclFileRenameCmd(
00051     Tcl_Interp *interp,         /* Interp for error reporting or recursive
00052                                  * calls in the case of a tricky rename. */
00053     int objc,                   /* Number of arguments. */
00054     Tcl_Obj *CONST objv[])      /* Argument strings passed to Tcl_FileCmd. */
00055 {
00056     return FileCopyRename(interp, objc, objv, 0);
00057 }
00058 
00059 /*
00060  *---------------------------------------------------------------------------
00061  *
00062  * TclFileCopyCmd
00063  *
00064  *      This function implements the "copy" subcommand of the "file" command.
00065  *      Filename arguments need to be translated to native format before being
00066  *      passed to platform-specific code that implements copy functionality.
00067  *
00068  * Results:
00069  *      A standard Tcl result.
00070  *
00071  * Side effects:
00072  *      See the user documentation.
00073  *
00074  *---------------------------------------------------------------------------
00075  */
00076 
00077 int
00078 TclFileCopyCmd(
00079     Tcl_Interp *interp,         /* Used for error reporting or recursive calls
00080                                  * in the case of a tricky copy. */
00081     int objc,                   /* Number of arguments. */
00082     Tcl_Obj *CONST objv[])      /* Argument strings passed to Tcl_FileCmd. */
00083 {
00084     return FileCopyRename(interp, objc, objv, 1);
00085 }
00086 
00087 /*
00088  *---------------------------------------------------------------------------
00089  *
00090  * FileCopyRename --
00091  *
00092  *      Performs the work of TclFileRenameCmd and TclFileCopyCmd. See
00093  *      comments for those functions.
00094  *
00095  * Results:
00096  *      See above.
00097  *
00098  * Side effects:
00099  *      See above.
00100  *
00101  *---------------------------------------------------------------------------
00102  */
00103 
00104 static int
00105 FileCopyRename(
00106     Tcl_Interp *interp,         /* Used for error reporting. */
00107     int objc,                   /* Number of arguments. */
00108     Tcl_Obj *CONST objv[],      /* Argument strings passed to Tcl_FileCmd. */
00109     int copyFlag)               /* If non-zero, copy source(s). Otherwise,
00110                                  * rename them. */
00111 {
00112     int i, result, force;
00113     Tcl_StatBuf statBuf;
00114     Tcl_Obj *target;
00115 
00116     i = FileForceOption(interp, objc - 2, objv + 2, &force);
00117     if (i < 0) {
00118         return TCL_ERROR;
00119     }
00120     i += 2;
00121     if ((objc - i) < 2) {
00122         Tcl_AppendResult(interp, "wrong # args: should be \"",
00123                 TclGetString(objv[0]), " ", TclGetString(objv[1]),
00124                 " ?options? source ?source ...? target\"", NULL);
00125         return TCL_ERROR;
00126     }
00127 
00128     /*
00129      * If target doesn't exist or isn't a directory, try the copy/rename.
00130      * More than 2 arguments is only valid if the target is an existing
00131      * directory.
00132      */
00133 
00134     target = objv[objc - 1];
00135     if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
00136         return TCL_ERROR;
00137     }
00138 
00139     result = TCL_OK;
00140 
00141     /*
00142      * Call Tcl_FSStat() so that if target is a symlink that points to a
00143      * directory we will put the sources in that directory instead of
00144      * overwriting the symlink.
00145      */
00146 
00147     if ((Tcl_FSStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
00148         if ((objc - i) > 2) {
00149             errno = ENOTDIR;
00150             Tcl_PosixError(interp);
00151             Tcl_AppendResult(interp, "error ",
00152                     (copyFlag ? "copying" : "renaming"), ": target \"",
00153                     TclGetString(target), "\" is not a directory", NULL);
00154             result = TCL_ERROR;
00155         } else {
00156             /*
00157              * Even though already have target == translated(objv[i+1]), pass
00158              * the original argument down, so if there's an error, the error
00159              * message will reflect the original arguments.
00160              */
00161 
00162             result = CopyRenameOneFile(interp, objv[i], objv[i + 1], copyFlag,
00163                     force);
00164         }
00165         return result;
00166     }
00167 
00168     /*
00169      * Move each source file into target directory. Extract the basename from
00170      * each source, and append it to the end of the target path.
00171      */
00172 
00173     for ( ; i<objc-1 ; i++) {
00174         Tcl_Obj *jargv[2];
00175         Tcl_Obj *source, *newFileName;
00176         Tcl_Obj *temp;
00177 
00178         source = FileBasename(interp, objv[i]);
00179         if (source == NULL) {
00180             result = TCL_ERROR;
00181             break;
00182         }
00183         jargv[0] = objv[objc - 1];
00184         jargv[1] = source;
00185         temp = Tcl_NewListObj(2, jargv);
00186         newFileName = Tcl_FSJoinPath(temp, -1);
00187         Tcl_IncrRefCount(newFileName);
00188         result = CopyRenameOneFile(interp, objv[i], newFileName, copyFlag,
00189                 force);
00190         Tcl_DecrRefCount(newFileName);
00191         Tcl_DecrRefCount(temp);
00192         Tcl_DecrRefCount(source);
00193 
00194         if (result == TCL_ERROR) {
00195             break;
00196         }
00197     }
00198     return result;
00199 }
00200 
00201 /*
00202  *---------------------------------------------------------------------------
00203  *
00204  * TclFileMakeDirsCmd
00205  *
00206  *      This function implements the "mkdir" subcommand of the "file" command.
00207  *      Filename arguments need to be translated to native format before being
00208  *      passed to platform-specific code that implements mkdir functionality.
00209  *
00210  * Results:
00211  *      A standard Tcl result.
00212  *
00213  * Side effects:
00214  *      See the user documentation.
00215  *
00216  *----------------------------------------------------------------------
00217  */
00218 
00219 int
00220 TclFileMakeDirsCmd(
00221     Tcl_Interp *interp,         /* Used for error reporting. */
00222     int objc,                   /* Number of arguments */
00223     Tcl_Obj *CONST objv[])      /* Argument strings passed to Tcl_FileCmd. */
00224 {
00225     Tcl_Obj *errfile;
00226     int result, i, j, pobjc;
00227     Tcl_Obj *split = NULL;
00228     Tcl_Obj *target = NULL;
00229     Tcl_StatBuf statBuf;
00230 
00231     errfile = NULL;
00232 
00233     result = TCL_OK;
00234     for (i = 2; i < objc; i++) {
00235         if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
00236             result = TCL_ERROR;
00237             break;
00238         }
00239 
00240         split = Tcl_FSSplitPath(objv[i],&pobjc);
00241         Tcl_IncrRefCount(split);
00242         if (pobjc == 0) {
00243             errno = ENOENT;
00244             errfile = objv[i];
00245             break;
00246         }
00247         for (j = 0; j < pobjc; j++) {
00248             target = Tcl_FSJoinPath(split, j + 1);
00249             Tcl_IncrRefCount(target);
00250 
00251             /*
00252              * Call Tcl_FSStat() so that if target is a symlink that points to
00253              * a directory we will create subdirectories in that directory.
00254              */
00255 
00256             if (Tcl_FSStat(target, &statBuf) == 0) {
00257                 if (!S_ISDIR(statBuf.st_mode)) {
00258                     errno = EEXIST;
00259                     errfile = target;
00260                     goto done;
00261                 }
00262             } else if (errno != ENOENT) {
00263                 /*
00264                  * If Tcl_FSStat() failed and the error is anything other than
00265                  * non-existence of the target, throw the error.
00266                  */
00267 
00268                 errfile = target;
00269                 goto done;
00270             } else if (Tcl_FSCreateDirectory(target) != TCL_OK) {
00271                 /*
00272                  * Create might have failed because of being in a race
00273                  * condition with another process trying to create the same
00274                  * subdirectory.
00275                  */
00276 
00277                 if (errno == EEXIST) {
00278                     if ((Tcl_FSStat(target, &statBuf) == 0)
00279                             && S_ISDIR(statBuf.st_mode)) {
00280                         /*
00281                          * It is a directory that wasn't there before, so keep
00282                          * going without error.
00283                          */
00284 
00285                         Tcl_ResetResult(interp);
00286                     } else {
00287                         errfile = target;
00288                         goto done;
00289                     }
00290                 } else {
00291                     errfile = target;
00292                     goto done;
00293                 }
00294             }
00295 
00296             /*
00297              * Forget about this sub-path.
00298              */
00299 
00300             Tcl_DecrRefCount(target);
00301             target = NULL;
00302         }
00303         Tcl_DecrRefCount(split);
00304         split = NULL;
00305     }
00306 
00307   done:
00308     if (errfile != NULL) {
00309         Tcl_AppendResult(interp, "can't create directory \"",
00310                 TclGetString(errfile), "\": ", Tcl_PosixError(interp), NULL);
00311         result = TCL_ERROR;
00312     }
00313     if (split != NULL) {
00314         Tcl_DecrRefCount(split);
00315     }
00316     if (target != NULL) {
00317         Tcl_DecrRefCount(target);
00318     }
00319     return result;
00320 }
00321 
00322 /*
00323  *----------------------------------------------------------------------
00324  *
00325  * TclFileDeleteCmd
00326  *
00327  *      This function implements the "delete" subcommand of the "file"
00328  *      command.
00329  *
00330  * Results:
00331  *      A standard Tcl result.
00332  *
00333  * Side effects:
00334  *      See the user documentation.
00335  *
00336  *----------------------------------------------------------------------
00337  */
00338 
00339 int
00340 TclFileDeleteCmd(
00341     Tcl_Interp *interp,         /* Used for error reporting */
00342     int objc,                   /* Number of arguments */
00343     Tcl_Obj *CONST objv[])      /* Argument strings passed to Tcl_FileCmd. */
00344 {
00345     int i, force, result;
00346     Tcl_Obj *errfile;
00347     Tcl_Obj *errorBuffer = NULL;
00348 
00349     i = FileForceOption(interp, objc - 2, objv + 2, &force);
00350     if (i < 0) {
00351         return TCL_ERROR;
00352     }
00353     i += 2;
00354     if ((objc - i) < 1) {
00355         Tcl_AppendResult(interp, "wrong # args: should be \"",
00356                 TclGetString(objv[0]), " ", TclGetString(objv[1]),
00357                 " ?options? file ?file ...?\"", NULL);
00358         return TCL_ERROR;
00359     }
00360 
00361     errfile = NULL;
00362     result = TCL_OK;
00363 
00364     for ( ; i < objc; i++) {
00365         Tcl_StatBuf statBuf;
00366 
00367         errfile = objv[i];
00368         if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
00369             result = TCL_ERROR;
00370             goto done;
00371         }
00372 
00373         /*
00374          * Call lstat() to get info so can delete symbolic link itself.
00375          */
00376 
00377         if (Tcl_FSLstat(objv[i], &statBuf) != 0) {
00378             /*
00379              * Trying to delete a file that does not exist is not considered
00380              * an error, just a no-op
00381              */
00382 
00383             if (errno != ENOENT) {
00384                 result = TCL_ERROR;
00385             }
00386         } else if (S_ISDIR(statBuf.st_mode)) {
00387             /*
00388              * We own a reference count on errorBuffer, if it was set as a
00389              * result of this call.
00390              */
00391 
00392             result = Tcl_FSRemoveDirectory(objv[i], force, &errorBuffer);
00393             if (result != TCL_OK) {
00394                 if ((force == 0) && (errno == EEXIST)) {
00395                     Tcl_AppendResult(interp, "error deleting \"",
00396                             TclGetString(objv[i]), "\": directory not empty",
00397                             NULL);
00398                     Tcl_PosixError(interp);
00399                     goto done;
00400                 }
00401 
00402                 /*
00403                  * If possible, use the untranslated name for the file.
00404                  */
00405 
00406                 errfile = errorBuffer;
00407 
00408                 /*
00409                  * FS supposed to check between translated objv and errfile.
00410                  */
00411 
00412                 if (Tcl_FSEqualPaths(objv[i], errfile)) {
00413                     errfile = objv[i];
00414                 }
00415             }
00416         } else {
00417             result = Tcl_FSDeleteFile(objv[i]);
00418         }
00419 
00420         if (result != TCL_OK) {
00421             result = TCL_ERROR;
00422 
00423             /*
00424              * It is important that we break on error, otherwise we might end
00425              * up owning reference counts on numerous errorBuffers.
00426              */
00427 
00428             break;
00429         }
00430     }
00431     if (result != TCL_OK) {
00432         if (errfile == NULL) {
00433             /*
00434              * We try to accomodate poor error results from our Tcl_FS calls.
00435              */
00436 
00437             Tcl_AppendResult(interp, "error deleting unknown file: ",
00438                     Tcl_PosixError(interp), NULL);
00439         } else {
00440             Tcl_AppendResult(interp, "error deleting \"",
00441                     TclGetString(errfile), "\": ", Tcl_PosixError(interp),
00442                     NULL);
00443         }
00444     }
00445 
00446   done:
00447     if (errorBuffer != NULL) {
00448         Tcl_DecrRefCount(errorBuffer);
00449     }
00450     return result;
00451 }
00452 
00453 /*
00454  *---------------------------------------------------------------------------
00455  *
00456  * CopyRenameOneFile
00457  *
00458  *      Copies or renames specified source file or directory hierarchy to the
00459  *      specified target.
00460  *
00461  * Results:
00462  *      A standard Tcl result.
00463  *
00464  * Side effects:
00465  *      Target is overwritten if the force flag is set. Attempting to
00466  *      copy/rename a file onto a directory or a directory onto a file will
00467  *      always result in an error.
00468  *
00469  *----------------------------------------------------------------------
00470  */
00471 
00472 static int
00473 CopyRenameOneFile(
00474     Tcl_Interp *interp,         /* Used for error reporting. */
00475     Tcl_Obj *source,            /* Pathname of file to copy. May need to be
00476                                  * translated. */
00477     Tcl_Obj *target,            /* Pathname of file to create/overwrite. May
00478                                  * need to be translated. */
00479     int copyFlag,               /* If non-zero, copy files. Otherwise, rename
00480                                  * them. */
00481     int force)                  /* If non-zero, overwrite target file if it
00482                                  * exists. Otherwise, error if target already
00483                                  * exists. */
00484 {
00485     int result;
00486     Tcl_Obj *errfile, *errorBuffer;
00487     Tcl_Obj *actualSource=NULL; /* If source is a link, then this is the real
00488                                  * file/directory. */
00489     Tcl_StatBuf sourceStatBuf, targetStatBuf;
00490 
00491     if (Tcl_FSConvertToPathType(interp, source) != TCL_OK) {
00492         return TCL_ERROR;
00493     }
00494     if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
00495         return TCL_ERROR;
00496     }
00497 
00498     errfile = NULL;
00499     errorBuffer = NULL;
00500     result = TCL_ERROR;
00501 
00502     /*
00503      * We want to copy/rename links and not the files they point to, so we use
00504      * lstat(). If target is a link, we also want to replace the link and not
00505      * the file it points to, so we also use lstat() on the target.
00506      */
00507 
00508     if (Tcl_FSLstat(source, &sourceStatBuf) != 0) {
00509         errfile = source;
00510         goto done;
00511     }
00512     if (Tcl_FSLstat(target, &targetStatBuf) != 0) {
00513         if (errno != ENOENT) {
00514             errfile = target;
00515             goto done;
00516         }
00517     } else {
00518         if (force == 0) {
00519             errno = EEXIST;
00520             errfile = target;
00521             goto done;
00522         }
00523 
00524         /*
00525          * Prevent copying or renaming a file onto itself. Under Windows, stat
00526          * always returns 0 for st_ino. However, the Windows-specific code
00527          * knows how to deal with copying or renaming a file on top of itself.
00528          * It might be a good idea to write a stat that worked.
00529          */
00530 
00531         if ((sourceStatBuf.st_ino != 0) && (targetStatBuf.st_ino != 0)) {
00532             if ((sourceStatBuf.st_ino == targetStatBuf.st_ino) &&
00533                     (sourceStatBuf.st_dev == targetStatBuf.st_dev)) {
00534                 result = TCL_OK;
00535                 goto done;
00536             }
00537         }
00538 
00539         /*
00540          * Prevent copying/renaming a file onto a directory and vice-versa.
00541          * This is a policy decision based on the fact that existing
00542          * implementations of copy and rename on all platforms also prevent
00543          * this.
00544          */
00545 
00546         if (S_ISDIR(sourceStatBuf.st_mode)
00547                 && !S_ISDIR(targetStatBuf.st_mode)) {
00548             errno = EISDIR;
00549             Tcl_AppendResult(interp, "can't overwrite file \"",
00550                     TclGetString(target), "\" with directory \"",
00551                     TclGetString(source), "\"", NULL);
00552             goto done;
00553         }
00554         if (!S_ISDIR(sourceStatBuf.st_mode)
00555                 && S_ISDIR(targetStatBuf.st_mode)) {
00556             errno = EISDIR;
00557             Tcl_AppendResult(interp, "can't overwrite directory \"",
00558                     TclGetString(target), "\" with file \"",
00559                     TclGetString(source), "\"", NULL);
00560             goto done;
00561         }
00562 
00563         /*
00564          * The destination exists, but appears to be ok to over-write, and
00565          * -force is given. We now try to adjust permissions to ensure the
00566          * operation succeeds. If we can't adjust permissions, we'll let the
00567          * actual copy/rename return an error later.
00568          */
00569 
00570         {
00571             Tcl_Obj *perm;
00572             int index;
00573 
00574             TclNewLiteralStringObj(perm, "u+w");
00575             Tcl_IncrRefCount(perm);
00576             if (TclFSFileAttrIndex(target, "-permissions", &index) == TCL_OK) {
00577                 Tcl_FSFileAttrsSet(NULL, index, target, perm);
00578             }
00579             Tcl_DecrRefCount(perm);
00580         }
00581     }
00582 
00583     if (copyFlag == 0) {
00584         result = Tcl_FSRenameFile(source, target);
00585         if (result == TCL_OK) {
00586             goto done;
00587         }
00588 
00589         if (errno == EINVAL) {
00590             Tcl_AppendResult(interp, "error renaming \"",
00591                     TclGetString(source), "\" to \"", TclGetString(target),
00592                     "\": trying to rename a volume or "
00593                     "move a directory into itself", NULL);
00594             goto done;
00595         } else if (errno != EXDEV) {
00596             errfile = target;
00597             goto done;
00598         }
00599 
00600         /*
00601          * The rename failed because the move was across file systems. Fall
00602          * through to copy file and then remove original. Note that the
00603          * low-level Tcl_FSRenameFileProc in the filesystem is allowed to
00604          * implement cross-filesystem moves itself, if it desires.
00605          */
00606     }
00607 
00608     actualSource = source;
00609     Tcl_IncrRefCount(actualSource);
00610 
00611     /*
00612      * Activate the following block to copy files instead of links. However
00613      * Tcl's semantics currently say we should copy links, so any such change
00614      * should be the subject of careful study on the consequences.
00615      *
00616      * Perhaps there could be an optional flag to 'file copy' to dictate which
00617      * approach to use, with the default being _not_ to have this block
00618      * active.
00619      */
00620 
00621 #if 0
00622 #ifdef S_ISLNK
00623     if (copyFlag && S_ISLNK(sourceStatBuf.st_mode)) {
00624         /*
00625          * We want to copy files not links. Therefore we must follow the link.
00626          * There are two purposes to this 'stat' call here. First we want to
00627          * know if the linked-file/dir actually exists, and second, in the
00628          * block of code which follows, some 20 lines down, we want to check
00629          * if the thing is a file or directory.
00630          */
00631 
00632         if (Tcl_FSStat(source, &sourceStatBuf) != 0) {
00633             /*
00634              * Actual file doesn't exist.
00635              */
00636 
00637             Tcl_AppendResult(interp, "error copying \"", TclGetString(source),
00638                     "\": the target of this link doesn't exist", NULL);
00639             goto done;
00640         } else {
00641             int counter = 0;
00642 
00643             while (1) {
00644                 Tcl_Obj *path = Tcl_FSLink(actualSource, NULL, 0);
00645                 if (path == NULL) {
00646                     break;
00647                 }
00648 
00649                 /*
00650                  * Now we want to check if this is a relative path, and if so,
00651                  * to make it absolute.
00652                  */
00653 
00654                 if (Tcl_FSGetPathType(path) == TCL_PATH_RELATIVE) {
00655                     Tcl_Obj *abs = Tcl_FSJoinToPath(actualSource, 1, &path);
00656 
00657                     if (abs == NULL) {
00658                         break;
00659                     }
00660                     Tcl_IncrRefCount(abs);
00661                     Tcl_DecrRefCount(path);
00662                     path = abs;
00663                 }
00664                 Tcl_DecrRefCount(actualSource);
00665                 actualSource = path;
00666                 counter++;
00667 
00668                 /*
00669                  * Arbitrary limit of 20 links to follow.
00670                  */
00671 
00672                 if (counter > 20) {
00673                     /*
00674                      * Too many links.
00675                      */
00676 
00677                     Tcl_SetErrno(EMLINK);
00678                     errfile = source;
00679                     goto done;
00680                 }
00681             }
00682             /* Now 'actualSource' is the correct file */
00683         }
00684     }
00685 #endif /* S_ISLNK */
00686 #endif
00687 
00688     if (S_ISDIR(sourceStatBuf.st_mode)) {
00689         result = Tcl_FSCopyDirectory(actualSource, target, &errorBuffer);
00690         if (result != TCL_OK) {
00691             if (errno == EXDEV) {
00692                 /*
00693                  * The copy failed because we're trying to do a
00694                  * cross-filesystem copy. We do this through our Tcl library.
00695                  */
00696 
00697                 Tcl_Obj *copyCommand, *cmdObj, *opObj;
00698 
00699                 TclNewObj(copyCommand);
00700                 TclNewLiteralStringObj(cmdObj, "::tcl::CopyDirectory");
00701                 Tcl_ListObjAppendElement(interp, copyCommand, cmdObj);
00702                 if (copyFlag) {
00703                     TclNewLiteralStringObj(opObj, "copying");
00704                 } else {
00705                     TclNewLiteralStringObj(opObj, "renaming");
00706                 }
00707                 Tcl_ListObjAppendElement(interp, copyCommand, opObj);
00708                 Tcl_ListObjAppendElement(interp, copyCommand, source);
00709                 Tcl_ListObjAppendElement(interp, copyCommand, target);
00710                 Tcl_IncrRefCount(copyCommand);
00711                 result = Tcl_EvalObjEx(interp, copyCommand,
00712                         TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
00713                 Tcl_DecrRefCount(copyCommand);
00714                 if (result != TCL_OK) {
00715                     /*
00716                      * There was an error in the Tcl-level copy. We will pass
00717                      * on the Tcl error message and can ensure this by setting
00718                      * errfile to NULL
00719                      */
00720 
00721                     errfile = NULL;
00722                 }
00723             } else {
00724                 errfile = errorBuffer;
00725                 if (Tcl_FSEqualPaths(errfile, source)) {
00726                     errfile = source;
00727                 } else if (Tcl_FSEqualPaths(errfile, target)) {
00728                     errfile = target;
00729                 }
00730             }
00731         }
00732     } else {
00733         result = Tcl_FSCopyFile(actualSource, target);
00734         if ((result != TCL_OK) && (errno == EXDEV)) {
00735             result = TclCrossFilesystemCopy(interp, source, target);
00736         }
00737         if (result != TCL_OK) {
00738             /*
00739              * We could examine 'errno' to double-check if the problem was
00740              * with the target, but we checked the source above, so it should
00741              * be quite clear
00742              */
00743 
00744             errfile = target;
00745 
00746             /*
00747              * We now need to reset the result, because the above call, if it
00748              * failed, may have put an error message in place. (Ideally we
00749              * would prefer not to pass an interpreter in above, but the
00750              * channel IO code used by TclCrossFilesystemCopy currently
00751              * requires one).
00752              */
00753 
00754             Tcl_ResetResult(interp);
00755         }
00756     }
00757     if ((copyFlag == 0) && (result == TCL_OK)) {
00758         if (S_ISDIR(sourceStatBuf.st_mode)) {
00759             result = Tcl_FSRemoveDirectory(source, 1, &errorBuffer);
00760             if (result != TCL_OK) {
00761                 if (Tcl_FSEqualPaths(errfile, source) == 0) {
00762                     errfile = source;
00763                 }
00764             }
00765         } else {
00766             result = Tcl_FSDeleteFile(source);
00767             if (result != TCL_OK) {
00768                 errfile = source;
00769             }
00770         }
00771         if (result != TCL_OK) {
00772             Tcl_AppendResult(interp, "can't unlink \"", TclGetString(errfile),
00773                     "\": ", Tcl_PosixError(interp), NULL);
00774             errfile = NULL;
00775         }
00776     }
00777 
00778   done:
00779     if (errfile != NULL) {
00780         Tcl_AppendResult(interp, "error ", (copyFlag ? "copying" : "renaming"),
00781                  " \"", TclGetString(source), NULL);
00782         if (errfile != source) {
00783             Tcl_AppendResult(interp, "\" to \"", TclGetString(target), NULL);
00784             if (errfile != target) {
00785                 Tcl_AppendResult(interp, "\": \"", TclGetString(errfile),NULL);
00786             }
00787         }
00788         Tcl_AppendResult(interp, "\": ", Tcl_PosixError(interp), NULL);
00789     }
00790     if (errorBuffer != NULL) {
00791         Tcl_DecrRefCount(errorBuffer);
00792     }
00793     if (actualSource != NULL) {
00794         Tcl_DecrRefCount(actualSource);
00795     }
00796     return result;
00797 }
00798 
00799 /*
00800  *---------------------------------------------------------------------------
00801  *
00802  * FileForceOption --
00803  *
00804  *      Helps parse command line options for file commands that take the
00805  *      "-force" and "--" options.
00806  *
00807  * Results:
00808  *      The return value is how many arguments from argv were consumed by this
00809  *      function, or -1 if there was an error parsing the options. If an error
00810  *      occurred, an error message is left in the interp's result.
00811  *
00812  * Side effects:
00813  *      None.
00814  *
00815  *---------------------------------------------------------------------------
00816  */
00817 
00818 static int
00819 FileForceOption(
00820     Tcl_Interp *interp,         /* Interp, for error return. */
00821     int objc,                   /* Number of arguments. */
00822     Tcl_Obj *CONST objv[],      /* Argument strings.  First command line
00823                                  * option, if it exists, begins at 0. */
00824     int *forcePtr)              /* If the "-force" was specified, *forcePtr is
00825                                  * filled with 1, otherwise with 0. */
00826 {
00827     int force, i;
00828 
00829     force = 0;
00830     for (i = 0; i < objc; i++) {
00831         if (TclGetString(objv[i])[0] != '-') {
00832             break;
00833         }
00834         if (strcmp(TclGetString(objv[i]), "-force") == 0) {
00835             force = 1;
00836         } else if (strcmp(TclGetString(objv[i]), "--") == 0) {
00837             i++;
00838             break;
00839         } else {
00840             Tcl_AppendResult(interp, "bad option \"", TclGetString(objv[i]),
00841                     "\": should be -force or --", NULL);
00842             return -1;
00843         }
00844     }
00845     *forcePtr = force;
00846     return i;
00847 }
00848 /*
00849  *---------------------------------------------------------------------------
00850  *
00851  * FileBasename --
00852  *
00853  *      Given a path in either tcl format (with / separators), or in the
00854  *      platform-specific format for the current platform, return all the
00855  *      characters in the path after the last directory separator. But, if
00856  *      path is the root directory, returns no characters.
00857  *
00858  * Results:
00859  *      Returns the string object that represents the basename. If there is an
00860  *      error, an error message is left in interp, and NULL is returned.
00861  *
00862  * Side effects:
00863  *      None.
00864  *
00865  *---------------------------------------------------------------------------
00866  */
00867 
00868 static Tcl_Obj *
00869 FileBasename(
00870     Tcl_Interp *interp,         /* Interp, for error return. */
00871     Tcl_Obj *pathPtr)           /* Path whose basename to extract. */
00872 {
00873     int objc;
00874     Tcl_Obj *splitPtr;
00875     Tcl_Obj *resultPtr = NULL;
00876 
00877     splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
00878     Tcl_IncrRefCount(splitPtr);
00879 
00880     if (objc != 0) {
00881         if ((objc == 1) && (*TclGetString(pathPtr) == '~')) {
00882             Tcl_DecrRefCount(splitPtr);
00883             if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
00884                 return NULL;
00885             }
00886             splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
00887             Tcl_IncrRefCount(splitPtr);
00888         }
00889 
00890         /*
00891          * Return the last component, unless it is the only component, and it
00892          * is the root of an absolute path.
00893          */
00894 
00895         if (objc > 0) {
00896             Tcl_ListObjIndex(NULL, splitPtr, objc-1, &resultPtr);
00897             if ((objc == 1) &&
00898                     (Tcl_FSGetPathType(resultPtr) != TCL_PATH_RELATIVE)) {
00899                 resultPtr = NULL;
00900             }
00901         }
00902     }
00903     if (resultPtr == NULL) {
00904         resultPtr = Tcl_NewObj();
00905     }
00906     Tcl_IncrRefCount(resultPtr);
00907     Tcl_DecrRefCount(splitPtr);
00908     return resultPtr;
00909 }
00910 
00911 /*
00912  *----------------------------------------------------------------------
00913  *
00914  * TclFileAttrsCmd --
00915  *
00916  *      Sets or gets the platform-specific attributes of a file. The objc-objv
00917  *      points to the file name with the rest of the command line following.
00918  *      This routine uses platform-specific tables of option strings and
00919  *      callbacks. The callback to get the attributes take three parameters:
00920  *          Tcl_Interp *interp;     The interp to report errors with. Since
00921  *                                  this is an object-based API, the object
00922  *                                  form of the result should be used.
00923  *          CONST char *fileName;   This is extracted using
00924  *                                  Tcl_TranslateFileName.
00925  *          TclObj **attrObjPtrPtr; A new object to hold the attribute is
00926  *                                  allocated and put here.
00927  *      The first two parameters of the callback used to write out the
00928  *      attributes are the same. The third parameter is:
00929  *          CONST *attrObjPtr;      A pointer to the object that has the new
00930  *                                  attribute.
00931  *      They both return standard TCL errors; if the routine to get an
00932  *      attribute fails, no object is allocated and *attrObjPtrPtr is
00933  *      unchanged.
00934  *
00935  * Results:
00936  *      Standard TCL error.
00937  *
00938  * Side effects:
00939  *      May set file attributes for the file name.
00940  *
00941  *----------------------------------------------------------------------
00942  */
00943 
00944 int
00945 TclFileAttrsCmd(
00946     Tcl_Interp *interp,         /* The interpreter for error reporting. */
00947     int objc,                   /* Number of command line arguments. */
00948     Tcl_Obj *CONST objv[])      /* The command line objects. */
00949 {
00950     int result;
00951     CONST char ** attributeStrings;
00952     Tcl_Obj* objStrings = NULL;
00953     int numObjStrings = -1;
00954     Tcl_Obj *filePtr;
00955 
00956     if (objc < 3) {
00957         Tcl_WrongNumArgs(interp, 2, objv,
00958                 "name ?option? ?value? ?option value ...?");
00959         return TCL_ERROR;
00960     }
00961 
00962     filePtr = objv[2];
00963     if (Tcl_FSConvertToPathType(interp, filePtr) != TCL_OK) {
00964         return TCL_ERROR;
00965     }
00966 
00967     objc -= 3;
00968     objv += 3;
00969     result = TCL_ERROR;
00970     Tcl_SetErrno(0);
00971 
00972     attributeStrings = Tcl_FSFileAttrStrings(filePtr, &objStrings);
00973     if (attributeStrings == NULL) {
00974         int index;
00975         Tcl_Obj *objPtr;
00976 
00977         if (objStrings == NULL) {
00978             if (Tcl_GetErrno() != 0) {
00979                 /*
00980                  * There was an error, probably that the filePtr is not
00981                  * accepted by any filesystem
00982                  */
00983                 Tcl_AppendResult(interp, "could not read \"",
00984                         TclGetString(filePtr), "\": ", Tcl_PosixError(interp),
00985                         NULL);
00986                 return TCL_ERROR;
00987             }
00988             goto end;
00989         }
00990 
00991         /*
00992          * We own the object now.
00993          */
00994 
00995         Tcl_IncrRefCount(objStrings);
00996 
00997         /*
00998          * Use objStrings as a list object.
00999          */
01000 
01001         if (Tcl_ListObjLength(interp, objStrings, &numObjStrings) != TCL_OK) {
01002             goto end;
01003         }
01004         attributeStrings = (CONST char **) TclStackAlloc(interp,
01005                 (1+numObjStrings) * sizeof(char*));
01006         for (index = 0; index < numObjStrings; index++) {
01007             Tcl_ListObjIndex(interp, objStrings, index, &objPtr);
01008             attributeStrings[index] = TclGetString(objPtr);
01009         }
01010         attributeStrings[index] = NULL;
01011     }
01012     if (objc == 0) {
01013         /*
01014          * Get all attributes.
01015          */
01016 
01017         int index, res = TCL_OK, nbAtts = 0;
01018         Tcl_Obj *listPtr;
01019 
01020         listPtr = Tcl_NewListObj(0, NULL);
01021         for (index = 0; attributeStrings[index] != NULL; index++) {
01022             Tcl_Obj *objPtrAttr;
01023 
01024             if (res != TCL_OK) {
01025                 /*
01026                  * Clear the error from the last iteration.
01027                  */
01028 
01029                 Tcl_ResetResult(interp);
01030             }
01031 
01032             res = Tcl_FSFileAttrsGet(interp, index, filePtr, &objPtrAttr);
01033             if (res == TCL_OK) {
01034                 Tcl_Obj *objPtr =
01035                         Tcl_NewStringObj(attributeStrings[index], -1);
01036 
01037                 Tcl_ListObjAppendElement(interp, listPtr, objPtr);
01038                 Tcl_ListObjAppendElement(interp, listPtr, objPtrAttr);
01039                 nbAtts++;
01040             }
01041         }
01042 
01043         if (index > 0 && nbAtts == 0) {
01044             /*
01045              * Error: no valid attributes found.
01046              */
01047 
01048             Tcl_DecrRefCount(listPtr);
01049             goto end;
01050         }
01051 
01052         Tcl_SetObjResult(interp, listPtr);
01053     } else if (objc == 1) {
01054         /*
01055          * Get one attribute.
01056          */
01057 
01058         int index;
01059         Tcl_Obj *objPtr = NULL;
01060 
01061         if (numObjStrings == 0) {
01062             Tcl_AppendResult(interp, "bad option \"", TclGetString(objv[0]),
01063                     "\", there are no file attributes in this filesystem.",
01064                     NULL);
01065             goto end;
01066         }
01067 
01068         if (Tcl_GetIndexFromObj(interp, objv[0], attributeStrings,
01069                 "option", 0, &index) != TCL_OK) {
01070             goto end;
01071         }
01072         if (Tcl_FSFileAttrsGet(interp, index, filePtr,
01073                 &objPtr) != TCL_OK) {
01074             goto end;
01075         }
01076         Tcl_SetObjResult(interp, objPtr);
01077     } else {
01078         /*
01079          * Set option/value pairs.
01080          */
01081 
01082         int i, index;
01083 
01084         if (numObjStrings == 0) {
01085             Tcl_AppendResult(interp, "bad option \"", TclGetString(objv[0]),
01086                     "\", there are no file attributes in this filesystem.",
01087                     NULL);
01088             goto end;
01089         }
01090 
01091         for (i = 0; i < objc ; i += 2) {
01092             if (Tcl_GetIndexFromObj(interp, objv[i], attributeStrings,
01093                     "option", 0, &index) != TCL_OK) {
01094                 goto end;
01095             }
01096             if (i + 1 == objc) {
01097                 Tcl_AppendResult(interp, "value for \"",
01098                         TclGetString(objv[i]), "\" missing", NULL);
01099                 goto end;
01100             }
01101             if (Tcl_FSFileAttrsSet(interp, index, filePtr,
01102                     objv[i + 1]) != TCL_OK) {
01103                 goto end;
01104             }
01105         }
01106     }
01107     result = TCL_OK;
01108 
01109   end:
01110     if (numObjStrings != -1) {
01111         /*
01112          * Free up the array we allocated.
01113          */
01114 
01115         TclStackFree(interp, (void *)attributeStrings);
01116 
01117         /*
01118          * We don't need this object that was passed to us any more.
01119          */
01120 
01121         if (objStrings != NULL) {
01122             Tcl_DecrRefCount(objStrings);
01123         }
01124     }
01125     return result;
01126 }
01127 
01128 /*
01129  * Local Variables:
01130  * mode: c
01131  * c-basic-offset: 4
01132  * fill-column: 78
01133  * End:
01134  */



Generated on Wed Mar 12 12:18:16 2008 by  doxygen 1.5.1