tclFCmd.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:28k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclFCmd.c
  3.  *
  4.  *      This file implements the generic portion of file manipulation 
  5.  *      subcommands of the "file" command. 
  6.  *
  7.  * Copyright (c) 1996-1998 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tclFCmd.c,v 1.20.2.2 2005/08/17 17:46:36 hobbs Exp $
  13.  */
  14. #include "tclInt.h"
  15. #include "tclPort.h"
  16. /*
  17.  * Declarations for local procedures defined in this file:
  18.  */
  19. static int CopyRenameOneFile _ANSI_ARGS_((Tcl_Interp *interp,
  20.     Tcl_Obj *srcPathPtr, Tcl_Obj *destPathPtr, 
  21.     int copyFlag, int force));
  22. static Tcl_Obj * FileBasename _ANSI_ARGS_((Tcl_Interp *interp,
  23.     Tcl_Obj *pathPtr));
  24. static int FileCopyRename _ANSI_ARGS_((Tcl_Interp *interp,
  25.     int objc, Tcl_Obj *CONST objv[], int copyFlag));
  26. static int FileForceOption _ANSI_ARGS_((Tcl_Interp *interp,
  27.     int objc, Tcl_Obj *CONST objv[], int *forcePtr));
  28. /*
  29.  *---------------------------------------------------------------------------
  30.  *
  31.  * TclFileRenameCmd
  32.  *
  33.  * This procedure implements the "rename" subcommand of the "file"
  34.  *      command.  Filename arguments need to be translated to native
  35.  * format before being passed to platform-specific code that
  36.  * implements rename functionality.
  37.  *
  38.  * Results:
  39.  * A standard Tcl result.
  40.  *
  41.  * Side effects:
  42.  * See the user documentation.
  43.  *
  44.  *---------------------------------------------------------------------------
  45.  */
  46. int
  47. TclFileRenameCmd(interp, objc, objv)
  48.     Tcl_Interp *interp; /* Interp for error reporting. */
  49.     int objc; /* Number of arguments. */
  50.     Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
  51. {
  52.     return FileCopyRename(interp, objc, objv, 0);
  53. }
  54. /*
  55.  *---------------------------------------------------------------------------
  56.  *
  57.  * TclFileCopyCmd
  58.  *
  59.  * This procedure implements the "copy" subcommand of the "file"
  60.  * command.  Filename arguments need to be translated to native
  61.  * format before being passed to platform-specific code that
  62.  * implements copy functionality.
  63.  *
  64.  * Results:
  65.  * A standard Tcl result.
  66.  *
  67.  * Side effects:
  68.  * See the user documentation.
  69.  *
  70.  *---------------------------------------------------------------------------
  71.  */
  72. int
  73. TclFileCopyCmd(interp, objc, objv)
  74.     Tcl_Interp *interp; /* Used for error reporting */
  75.     int objc; /* Number of arguments. */
  76.     Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
  77. {
  78.     return FileCopyRename(interp, objc, objv, 1);
  79. }
  80. /*
  81.  *---------------------------------------------------------------------------
  82.  *
  83.  * FileCopyRename --
  84.  *
  85.  * Performs the work of TclFileRenameCmd and TclFileCopyCmd.
  86.  * See comments for those procedures.
  87.  *
  88.  * Results:
  89.  * See above.
  90.  *
  91.  * Side effects:
  92.  * See above.
  93.  *
  94.  *---------------------------------------------------------------------------
  95.  */
  96. static int
  97. FileCopyRename(interp, objc, objv, copyFlag)
  98.     Tcl_Interp *interp; /* Used for error reporting. */
  99.     int objc; /* Number of arguments. */
  100.     Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
  101.     int copyFlag; /* If non-zero, copy source(s).  Otherwise,
  102.  * rename them. */
  103. {
  104.     int i, result, force;
  105.     Tcl_StatBuf statBuf; 
  106.     Tcl_Obj *target;
  107.     i = FileForceOption(interp, objc - 2, objv + 2, &force);
  108.     if (i < 0) {
  109. return TCL_ERROR;
  110.     }
  111.     i += 2;
  112.     if ((objc - i) < 2) {
  113. Tcl_AppendResult(interp, "wrong # args: should be "", 
  114. Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]), 
  115. " ?options? source ?source ...? target"", 
  116. (char *) NULL);
  117. return TCL_ERROR;
  118.     }
  119.     /*
  120.      * If target doesn't exist or isn't a directory, try the copy/rename.
  121.      * More than 2 arguments is only valid if the target is an existing
  122.      * directory.
  123.      */
  124.     target = objv[objc - 1];
  125.     if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
  126. return TCL_ERROR;
  127.     }
  128.     result = TCL_OK;
  129.     /*
  130.      * Call Tcl_FSStat() so that if target is a symlink that points to a
  131.      * directory we will put the sources in that directory instead of
  132.      * overwriting the symlink.
  133.      */
  134.     if ((Tcl_FSStat(target, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode)) {
  135. if ((objc - i) > 2) {
  136.     errno = ENOTDIR;
  137.     Tcl_PosixError(interp);
  138.     Tcl_AppendResult(interp, "error ",
  139.     ((copyFlag) ? "copying" : "renaming"), ": target "",
  140.     Tcl_GetString(target), "" is not a directory", 
  141.     (char *) NULL);
  142.     result = TCL_ERROR;
  143. } else {
  144.     /*
  145.      * Even though already have target == translated(objv[i+1]),
  146.      * pass the original argument down, so if there's an error, the
  147.      * error message will reflect the original arguments.
  148.      */
  149.     result = CopyRenameOneFile(interp, objv[i], objv[i + 1], copyFlag,
  150.     force);
  151. }
  152. return result;
  153.     }
  154.     
  155.     /*
  156.      * Move each source file into target directory.  Extract the basename
  157.      * from each source, and append it to the end of the target path.
  158.      */
  159.     for ( ; i < objc - 1; i++) {
  160. Tcl_Obj *jargv[2];
  161. Tcl_Obj *source, *newFileName;
  162. Tcl_Obj *temp;
  163. source = FileBasename(interp, objv[i]);
  164. if (source == NULL) {
  165.     result = TCL_ERROR;
  166.     break;
  167. }
  168. jargv[0] = objv[objc - 1];
  169. jargv[1] = source;
  170. temp = Tcl_NewListObj(2, jargv);
  171. newFileName = Tcl_FSJoinPath(temp, -1);
  172. Tcl_IncrRefCount(newFileName);
  173. result = CopyRenameOneFile(interp, objv[i], newFileName, copyFlag,
  174. force);
  175. Tcl_DecrRefCount(newFileName);
  176. Tcl_DecrRefCount(temp);
  177. Tcl_DecrRefCount(source);
  178. if (result == TCL_ERROR) {
  179.     break;
  180. }
  181.     }
  182.     return result;
  183. }
  184. /*
  185.  *---------------------------------------------------------------------------
  186.  *
  187.  * TclFileMakeDirsCmd
  188.  *
  189.  * This procedure implements the "mkdir" subcommand of the "file"
  190.  *      command.  Filename arguments need to be translated to native
  191.  * format before being passed to platform-specific code that
  192.  * implements mkdir functionality.
  193.  *
  194.  * Results:
  195.  * A standard Tcl result.
  196.  *
  197.  * Side effects:
  198.  * See the user documentation.
  199.  *
  200.  *----------------------------------------------------------------------
  201.  */
  202. int
  203. TclFileMakeDirsCmd(interp, objc, objv)
  204.     Tcl_Interp *interp; /* Used for error reporting. */
  205.     int objc; /* Number of arguments */
  206.     Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
  207. {
  208.     Tcl_Obj *errfile;
  209.     int result, i, j, pobjc;
  210.     Tcl_Obj *split = NULL;
  211.     Tcl_Obj *target = NULL;
  212.     Tcl_StatBuf statBuf;
  213.     errfile = NULL;
  214.     result = TCL_OK;
  215.     for (i = 2; i < objc; i++) {
  216. if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
  217.     result = TCL_ERROR;
  218.     break;
  219. }
  220. split = Tcl_FSSplitPath(objv[i],&pobjc);
  221. if (pobjc == 0) {
  222.     errno = ENOENT;
  223.     errfile = objv[i];
  224.     break;
  225. }
  226. for (j = 0; j < pobjc; j++) {
  227.     target = Tcl_FSJoinPath(split, j + 1);
  228.     Tcl_IncrRefCount(target);
  229.     /*
  230.      * Call Tcl_FSStat() so that if target is a symlink that
  231.      * points to a directory we will create subdirectories in
  232.      * that directory.
  233.      */
  234.     if (Tcl_FSStat(target, &statBuf) == 0) {
  235. if (!S_ISDIR(statBuf.st_mode)) {
  236.     errno = EEXIST;
  237.     errfile = target;
  238.     goto done;
  239. }
  240.     } else if (errno != ENOENT) {
  241. /*
  242.  * If Tcl_FSStat() failed and the error is anything
  243.  * other than non-existence of the target, throw the
  244.  * error.
  245.  */
  246. errfile = target;
  247. goto done;
  248.     } else if (Tcl_FSCreateDirectory(target) != TCL_OK) {
  249. /*
  250.  * Create might have failed because of being in a race
  251.  * condition with another process trying to create the
  252.  * same subdirectory.
  253.  */
  254. if (errno == EEXIST) {
  255.     if ((Tcl_FSStat(target, &statBuf) == 0)
  256.     && S_ISDIR(statBuf.st_mode)) {
  257. /*
  258.  * It is a directory that wasn't there before,
  259.  * so keep going without error.
  260.  */
  261. Tcl_ResetResult(interp);
  262.     } else {
  263. errfile = target;
  264. goto done;
  265.     }
  266. } else {
  267.     errfile = target;
  268.     goto done;
  269. }
  270.     }
  271.       /* Forget about this sub-path */
  272.     Tcl_DecrRefCount(target);
  273.     target = NULL;
  274. }
  275. Tcl_DecrRefCount(split);
  276. split = NULL;
  277.     }
  278.     done:
  279.     if (errfile != NULL) {
  280. Tcl_AppendResult(interp, "can't create directory "",
  281. Tcl_GetString(errfile), "": ", Tcl_PosixError(interp), 
  282. (char *) NULL);
  283. result = TCL_ERROR;
  284.     }
  285.     if (split != NULL) {
  286. Tcl_DecrRefCount(split);
  287.     }
  288.     if (target != NULL) {
  289. Tcl_DecrRefCount(target);
  290.     }
  291.     return result;
  292. }
  293. /*
  294.  *----------------------------------------------------------------------
  295.  *
  296.  * TclFileDeleteCmd
  297.  *
  298.  * This procedure implements the "delete" subcommand of the "file"
  299.  *      command.
  300.  *
  301.  * Results:
  302.  * A standard Tcl result.
  303.  *
  304.  * Side effects:
  305.  * See the user documentation.
  306.  *
  307.  *----------------------------------------------------------------------
  308.  */
  309. int
  310. TclFileDeleteCmd(interp, objc, objv)
  311.     Tcl_Interp *interp; /* Used for error reporting */
  312.     int objc; /* Number of arguments */
  313.     Tcl_Obj *CONST objv[]; /* Argument strings passed to Tcl_FileCmd. */
  314. {
  315.     int i, force, result;
  316.     Tcl_Obj *errfile;
  317.     Tcl_Obj *errorBuffer = NULL;
  318.     
  319.     i = FileForceOption(interp, objc - 2, objv + 2, &force);
  320.     if (i < 0) {
  321. return TCL_ERROR;
  322.     }
  323.     i += 2;
  324.     if ((objc - i) < 1) {
  325. Tcl_AppendResult(interp, "wrong # args: should be "", 
  326. Tcl_GetString(objv[0]), " ", Tcl_GetString(objv[1]), 
  327. " ?options? file ?file ...?"", (char *) NULL);
  328. return TCL_ERROR;
  329.     }
  330.     errfile = NULL;
  331.     result = TCL_OK;
  332.     for ( ; i < objc; i++) {
  333. Tcl_StatBuf statBuf;
  334. errfile = objv[i];
  335. if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
  336.     result = TCL_ERROR;
  337.     goto done;
  338. }
  339. /*
  340.  * Call lstat() to get info so can delete symbolic link itself.
  341.  */
  342. if (Tcl_FSLstat(objv[i], &statBuf) != 0) {
  343.     /*
  344.      * Trying to delete a file that does not exist is not
  345.      * considered an error, just a no-op
  346.      */
  347.     if (errno != ENOENT) {
  348. result = TCL_ERROR;
  349.     }
  350. } else if (S_ISDIR(statBuf.st_mode)) {
  351.     /* 
  352.      * We own a reference count on errorBuffer, if it was set
  353.      * as a result of this call. 
  354.      */
  355.     result = Tcl_FSRemoveDirectory(objv[i], force, &errorBuffer);
  356.     if (result != TCL_OK) {
  357. if ((force == 0) && (errno == EEXIST)) {
  358.     Tcl_AppendResult(interp, "error deleting "", 
  359.     Tcl_GetString(objv[i]),
  360.     "": directory not empty", (char *) NULL);
  361.     Tcl_PosixError(interp);
  362.     goto done;
  363. }
  364. /* 
  365.  * If possible, use the untranslated name for the file.
  366.  */
  367.  
  368. errfile = errorBuffer;
  369. /* FS supposed to check between translated objv and errfile */
  370. if (Tcl_FSEqualPaths(objv[i], errfile)) {
  371.     errfile = objv[i];
  372. }
  373.     }
  374. } else {
  375.     result = Tcl_FSDeleteFile(objv[i]);
  376. }
  377. if (result != TCL_OK) {
  378.     result = TCL_ERROR;
  379.     /* 
  380.      * It is important that we break on error, otherwise we
  381.      * might end up owning reference counts on numerous
  382.      * errorBuffers.
  383.      */
  384.     break;
  385. }
  386.     }
  387.     if (result != TCL_OK) {
  388. if (errfile == NULL) {
  389.     /* 
  390.      * We try to accomodate poor error results from our 
  391.      * Tcl_FS calls 
  392.      */
  393.     Tcl_AppendResult(interp, "error deleting unknown file: ", 
  394.     Tcl_PosixError(interp), (char *) NULL);
  395. } else {
  396.     Tcl_AppendResult(interp, "error deleting "", 
  397.     Tcl_GetString(errfile), "": ", 
  398.     Tcl_PosixError(interp), (char *) NULL);
  399. }
  400.     } 
  401.     done:
  402.     if (errorBuffer != NULL) {
  403. Tcl_DecrRefCount(errorBuffer);
  404.     }
  405.     return result;
  406. }
  407. /*
  408.  *---------------------------------------------------------------------------
  409.  *
  410.  * CopyRenameOneFile
  411.  *
  412.  * Copies or renames specified source file or directory hierarchy
  413.  * to the specified target.  
  414.  *
  415.  * Results:
  416.  * A standard Tcl result.
  417.  *
  418.  * Side effects:
  419.  * Target is overwritten if the force flag is set.  Attempting to
  420.  * copy/rename a file onto a directory or a directory onto a file
  421.  * will always result in an error.  
  422.  *
  423.  *----------------------------------------------------------------------
  424.  */
  425. static int
  426. CopyRenameOneFile(interp, source, target, copyFlag, force) 
  427.     Tcl_Interp *interp; /* Used for error reporting. */
  428.     Tcl_Obj *source; /* Pathname of file to copy.  May need to
  429.  * be translated. */
  430.     Tcl_Obj *target; /* Pathname of file to create/overwrite.
  431.  * May need to be translated. */
  432.     int copyFlag; /* If non-zero, copy files.  Otherwise,
  433.  * rename them. */
  434.     int force; /* If non-zero, overwrite target file if it
  435.  * exists.  Otherwise, error if target already
  436.  * exists. */
  437. {
  438.     int result;
  439.     Tcl_Obj *errfile, *errorBuffer;
  440.     /* If source is a link, then this is the real file/directory */
  441.     Tcl_Obj *actualSource = NULL;
  442.     Tcl_StatBuf sourceStatBuf, targetStatBuf;
  443.     if (Tcl_FSConvertToPathType(interp, source) != TCL_OK) {
  444. return TCL_ERROR;
  445.     }
  446.     if (Tcl_FSConvertToPathType(interp, target) != TCL_OK) {
  447. return TCL_ERROR;
  448.     }
  449.     
  450.     errfile = NULL;
  451.     errorBuffer = NULL;
  452.     result = TCL_ERROR;
  453.     
  454.     /*
  455.      * We want to copy/rename links and not the files they point to, so we
  456.      * use lstat(). If target is a link, we also want to replace the 
  457.      * link and not the file it points to, so we also use lstat() on the
  458.      * target.
  459.      */
  460.     if (Tcl_FSLstat(source, &sourceStatBuf) != 0) {
  461. errfile = source;
  462. goto done;
  463.     }
  464.     if (Tcl_FSLstat(target, &targetStatBuf) != 0) {
  465. if (errno != ENOENT) {
  466.     errfile = target;
  467.     goto done;
  468. }
  469.     } else {
  470. if (force == 0) {
  471.     errno = EEXIST;
  472.     errfile = target;
  473.     goto done;
  474. }
  475.         /* 
  476.          * Prevent copying or renaming a file onto itself.  Under Windows, 
  477.          * stat always returns 0 for st_ino.  However, the Windows-specific 
  478.          * code knows how to deal with copying or renaming a file on top of
  479.          * itself.  It might be a good idea to write a stat that worked.
  480.          */
  481.      
  482.         if ((sourceStatBuf.st_ino != 0) && (targetStatBuf.st_ino != 0)) {
  483.             if ((sourceStatBuf.st_ino == targetStatBuf.st_ino) &&
  484.                  (sourceStatBuf.st_dev == targetStatBuf.st_dev)) {
  485.              result = TCL_OK;
  486.              goto done;
  487.             }
  488.         }
  489. /*
  490.  * Prevent copying/renaming a file onto a directory and
  491.  * vice-versa.  This is a policy decision based on the fact that
  492.  * existing implementations of copy and rename on all platforms
  493.  * also prevent this.
  494.  */
  495. if (S_ISDIR(sourceStatBuf.st_mode)
  496.                 && !S_ISDIR(targetStatBuf.st_mode)) {
  497.     errno = EISDIR;
  498.     Tcl_AppendResult(interp, "can't overwrite file "", 
  499.     Tcl_GetString(target), "" with directory "", 
  500.     Tcl_GetString(source), """, (char *) NULL);
  501.     goto done;
  502. }
  503. if (!S_ISDIR(sourceStatBuf.st_mode)
  504.         && S_ISDIR(targetStatBuf.st_mode)) {
  505.     errno = EISDIR;
  506.     Tcl_AppendResult(interp, "can't overwrite directory "", 
  507.     Tcl_GetString(target), "" with file "", 
  508.     Tcl_GetString(source), """, (char *) NULL);
  509.     goto done;
  510. }
  511.     }
  512.     if (copyFlag == 0) {
  513. result = Tcl_FSRenameFile(source, target);
  514. if (result == TCL_OK) {
  515.     goto done;
  516. }
  517.     
  518. if (errno == EINVAL) {
  519.     Tcl_AppendResult(interp, "error renaming "", 
  520.     Tcl_GetString(source), "" to "",
  521.     Tcl_GetString(target), "": trying to rename a volume or ",
  522.     "move a directory into itself", (char *) NULL);
  523.     goto done;
  524. } else if (errno != EXDEV) {
  525.     errfile = target;
  526.     goto done;
  527. }
  528. /*
  529.  * The rename failed because the move was across file systems.
  530.  * Fall through to copy file and then remove original.  Note that
  531.  * the low-level Tcl_FSRenameFileProc in the filesystem is allowed 
  532.  * to implement cross-filesystem moves itself, if it desires.
  533.  */
  534.     }
  535.     actualSource = source;
  536.     Tcl_IncrRefCount(actualSource);
  537. #if 0
  538. #ifdef S_ISLNK
  539.     /* 
  540.      * To add a flag to make 'copy' copy links instead of files, we could
  541.      * add a condition to ignore this 'if' here.
  542.      */
  543.     if (copyFlag && S_ISLNK(sourceStatBuf.st_mode)) {
  544. /* 
  545.  * We want to copy files not links.  Therefore we must follow the
  546.  * link.  There are two purposes to this 'stat' call here.  First
  547.  * we want to know if the linked-file/dir actually exists, and
  548.  * second, in the block of code which follows, some 20 lines
  549.  * down, we want to check if the thing is a file or directory.
  550.  */
  551. if (Tcl_FSStat(source, &sourceStatBuf) != 0) {
  552.     /* Actual file doesn't exist */
  553.     Tcl_AppendResult(interp, 
  554.     "error copying "", Tcl_GetString(source), 
  555.     "": the target of this link doesn't exist",
  556.     (char *) NULL);
  557.     goto done;
  558. } else {
  559.     int counter = 0;
  560.     while (1) {
  561. Tcl_Obj *path = Tcl_FSLink(actualSource, NULL, 0);
  562. if (path == NULL) {
  563.     break;
  564. }
  565. Tcl_DecrRefCount(actualSource);
  566. actualSource = path;
  567. counter++;
  568. /* Arbitrary limit of 20 links to follow */
  569. if (counter > 20) {
  570.     /* Too many links */
  571.     Tcl_SetErrno(EMLINK);
  572.     errfile = source;
  573.     goto done;
  574. }
  575.     }
  576.     /* Now 'actualSource' is the correct file */
  577. }
  578.     }
  579. #endif
  580. #endif
  581.     if (S_ISDIR(sourceStatBuf.st_mode)) {
  582. result = Tcl_FSCopyDirectory(actualSource, target, &errorBuffer);
  583. if (result != TCL_OK) {
  584.     if (errno == EXDEV) {
  585. /* 
  586.  * The copy failed because we're trying to do a
  587.  * cross-filesystem copy.  We do this through our Tcl
  588.  * library.
  589.  */
  590. Tcl_SavedResult savedResult;
  591. Tcl_Obj *copyCommand = Tcl_NewListObj(0,NULL);
  592. Tcl_IncrRefCount(copyCommand);
  593. Tcl_ListObjAppendElement(interp, copyCommand, 
  594. Tcl_NewStringObj("::tcl::CopyDirectory",-1));
  595. if (copyFlag) {
  596.     Tcl_ListObjAppendElement(interp, copyCommand, 
  597.      Tcl_NewStringObj("copying",-1));
  598. } else {
  599.     Tcl_ListObjAppendElement(interp, copyCommand, 
  600.      Tcl_NewStringObj("renaming",-1));
  601. }
  602. Tcl_ListObjAppendElement(interp, copyCommand, source);
  603. Tcl_ListObjAppendElement(interp, copyCommand, target);
  604. Tcl_SaveResult(interp, &savedResult);
  605. result = Tcl_EvalObjEx(interp, copyCommand, 
  606.        TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
  607. Tcl_DecrRefCount(copyCommand);
  608. if (result != TCL_OK) {
  609.     /* 
  610.      * There was an error in the Tcl-level copy.
  611.      * We will pass on the Tcl error message and
  612.      * can ensure this by setting errfile to NULL
  613.      */
  614.     Tcl_DiscardResult(&savedResult);
  615.     errfile = NULL;
  616. } else {
  617.     /* The copy was successful */
  618.     Tcl_RestoreResult(interp, &savedResult);
  619. }
  620.     } else {
  621. errfile = errorBuffer;
  622. if (Tcl_FSEqualPaths(errfile, source)) {
  623.     errfile = source;
  624. } else if (Tcl_FSEqualPaths(errfile, target)) {
  625.     errfile = target;
  626. }
  627.     }
  628. }
  629.     } else {
  630. result = Tcl_FSCopyFile(actualSource, target);
  631. if ((result != TCL_OK) && (errno == EXDEV)) {
  632.     result = TclCrossFilesystemCopy(interp, source, target);
  633. }
  634. if (result != TCL_OK) {
  635.     /* 
  636.      * We could examine 'errno' to double-check if the problem
  637.      * was with the target, but we checked the source above,
  638.      * so it should be quite clear 
  639.      */
  640.     errfile = target;
  641.     /* 
  642.      * We now need to reset the result, because the above call,
  643.      * if it failed, may have put an error message in place.
  644.      * (Ideally we would prefer not to pass an interpreter in
  645.      * above, but the channel IO code used by
  646.      * TclCrossFilesystemCopy currently requires one)
  647.      */
  648.     Tcl_ResetResult(interp);
  649. }
  650.     }
  651.     if ((copyFlag == 0) && (result == TCL_OK)) {
  652. if (S_ISDIR(sourceStatBuf.st_mode)) {
  653.     result = Tcl_FSRemoveDirectory(source, 1, &errorBuffer);
  654.     if (result != TCL_OK) {
  655. if (Tcl_FSEqualPaths(errfile, source) == 0) {
  656.     errfile = source;
  657. }
  658.     }
  659. } else {
  660.     result = Tcl_FSDeleteFile(source);
  661.     if (result != TCL_OK) {
  662. errfile = source;
  663.     }
  664. }
  665. if (result != TCL_OK) {
  666.     Tcl_AppendResult(interp, "can't unlink "", 
  667. Tcl_GetString(errfile), "": ",
  668. Tcl_PosixError(interp), (char *) NULL);
  669.     errfile = NULL;
  670. }
  671.     }
  672.     
  673.     done:
  674.     if (errfile != NULL) {
  675. Tcl_AppendResult(interp, 
  676. ((copyFlag) ? "error copying "" : "error renaming ""),
  677.  Tcl_GetString(source), (char *) NULL);
  678. if (errfile != source) {
  679.     Tcl_AppendResult(interp, "" to "", Tcl_GetString(target), 
  680.      (char *) NULL);
  681.     if (errfile != target) {
  682. Tcl_AppendResult(interp, "": "", Tcl_GetString(errfile), 
  683.  (char *) NULL);
  684.     }
  685. }
  686. Tcl_AppendResult(interp, "": ", Tcl_PosixError(interp),
  687. (char *) NULL);
  688.     }
  689.     if (errorBuffer != NULL) {
  690.         Tcl_DecrRefCount(errorBuffer);
  691.     }
  692.     if (actualSource != NULL) {
  693. Tcl_DecrRefCount(actualSource);
  694.     }
  695.     return result;
  696. }
  697. /*
  698.  *---------------------------------------------------------------------------
  699.  *
  700.  * FileForceOption --
  701.  *
  702.  * Helps parse command line options for file commands that take
  703.  * the "-force" and "--" options.
  704.  *
  705.  * Results:
  706.  * The return value is how many arguments from argv were consumed
  707.  * by this function, or -1 if there was an error parsing the
  708.  * options.  If an error occurred, an error message is left in the
  709.  * interp's result.
  710.  *
  711.  * Side effects:
  712.  * None.
  713.  *
  714.  *---------------------------------------------------------------------------
  715.  */
  716. static int
  717. FileForceOption(interp, objc, objv, forcePtr)
  718.     Tcl_Interp *interp; /* Interp, for error return. */
  719.     int objc; /* Number of arguments. */
  720.     Tcl_Obj *CONST objv[]; /* Argument strings.  First command line
  721.  * option, if it exists, begins at 0. */
  722.     int *forcePtr; /* If the "-force" was specified, *forcePtr
  723.  * is filled with 1, otherwise with 0. */
  724. {
  725.     int force, i;
  726.     
  727.     force = 0;
  728.     for (i = 0; i < objc; i++) {
  729. if (Tcl_GetString(objv[i])[0] != '-') {
  730.     break;
  731. }
  732. if (strcmp(Tcl_GetString(objv[i]), "-force") == 0) {
  733.     force = 1;
  734. } else if (strcmp(Tcl_GetString(objv[i]), "--") == 0) {
  735.     i++;
  736.     break;
  737. } else {
  738.     Tcl_AppendResult(interp, "bad option "", Tcl_GetString(objv[i]), 
  739.     "": should be -force or --", (char *)NULL);
  740.     return -1;
  741. }
  742.     }
  743.     *forcePtr = force;
  744.     return i;
  745. }
  746. /*
  747.  *---------------------------------------------------------------------------
  748.  *
  749.  * FileBasename --
  750.  *
  751.  * Given a path in either tcl format (with / separators), or in the
  752.  * platform-specific format for the current platform, return all the
  753.  * characters in the path after the last directory separator.  But,
  754.  * if path is the root directory, returns no characters.
  755.  *
  756.  * Results:
  757.  * Returns the string object that represents the basename.  If there 
  758.  * is an error, an error message is left in interp, and NULL is 
  759.  * returned.
  760.  *
  761.  * Side effects:
  762.  * None.
  763.  *
  764.  *---------------------------------------------------------------------------
  765.  */
  766. static Tcl_Obj *
  767. FileBasename(interp, pathPtr)
  768.     Tcl_Interp *interp; /* Interp, for error return. */
  769.     Tcl_Obj *pathPtr; /* Path whose basename to extract. */
  770. {
  771.     int objc;
  772.     Tcl_Obj *splitPtr;
  773.     Tcl_Obj *resultPtr = NULL;
  774.     
  775.     splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
  776.     if (objc != 0) {
  777. if ((objc == 1) && (*Tcl_GetString(pathPtr) == '~')) {
  778.     Tcl_DecrRefCount(splitPtr);
  779.     if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
  780. return NULL;
  781.     }
  782.     splitPtr = Tcl_FSSplitPath(pathPtr, &objc);
  783. }
  784. /*
  785.  * Return the last component, unless it is the only component, and it
  786.  * is the root of an absolute path.
  787.  */
  788. if (objc > 0) {
  789.     Tcl_ListObjIndex(NULL, splitPtr, objc-1, &resultPtr);
  790.     if ((objc == 1) &&
  791.       (Tcl_FSGetPathType(resultPtr) != TCL_PATH_RELATIVE)) {
  792. resultPtr = NULL;
  793.     }
  794. }
  795.     }
  796.     if (resultPtr == NULL) {
  797. resultPtr = Tcl_NewObj();
  798.     }
  799.     Tcl_IncrRefCount(resultPtr);
  800.     Tcl_DecrRefCount(splitPtr);
  801.     return resultPtr;
  802. }
  803. /*
  804.  *----------------------------------------------------------------------
  805.  *
  806.  * TclFileAttrsCmd --
  807.  *
  808.  *      Sets or gets the platform-specific attributes of a file.  The
  809.  *      objc-objv points to the file name with the rest of the command
  810.  *      line following.  This routine uses platform-specific tables of
  811.  *      option strings and callbacks.  The callback to get the
  812.  *      attributes take three parameters:
  813.  *     Tcl_Interp *interp;     The interp to report errors with.
  814.  *     Since this is an object-based API,
  815.  *     the object form of the result should 
  816.  *     be used.
  817.  *     CONST char *fileName;   This is extracted using
  818.  *     Tcl_TranslateFileName.
  819.  *     TclObj **attrObjPtrPtr; A new object to hold the attribute
  820.  *     is allocated and put here.
  821.  * The first two parameters of the callback used to write out the
  822.  * attributes are the same. The third parameter is:
  823.  *     CONST *attrObjPtr;     A pointer to the object that has
  824.  *     the new attribute.
  825.  * They both return standard TCL errors; if the routine to get
  826.  * an attribute fails, no object is allocated and *attrObjPtrPtr
  827.  * is unchanged.
  828.  *
  829.  * Results:
  830.  *      Standard TCL error.
  831.  *
  832.  * Side effects:
  833.  *      May set file attributes for the file name.
  834.  *      
  835.  *----------------------------------------------------------------------
  836.  */
  837. int
  838. TclFileAttrsCmd(interp, objc, objv)
  839.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  840.     int objc; /* Number of command line arguments. */
  841.     Tcl_Obj *CONST objv[]; /* The command line objects. */
  842. {
  843.     int result;
  844.     CONST char ** attributeStrings;
  845.     Tcl_Obj* objStrings = NULL;
  846.     int numObjStrings = -1;
  847.     Tcl_Obj *filePtr;
  848.     
  849.     if (objc < 3) {
  850. Tcl_WrongNumArgs(interp, 2, objv,
  851. "name ?option? ?value? ?option value ...?");
  852. return TCL_ERROR;
  853.     }
  854.     filePtr = objv[2];
  855.     if (Tcl_FSConvertToPathType(interp, filePtr) != TCL_OK) {
  856.      return TCL_ERROR;
  857.     }
  858.     
  859.     objc -= 3;
  860.     objv += 3;
  861.     result = TCL_ERROR;
  862.     Tcl_SetErrno(0);
  863.     attributeStrings = Tcl_FSFileAttrStrings(filePtr, &objStrings);
  864.     if (attributeStrings == NULL) {
  865. int index;
  866. Tcl_Obj *objPtr;
  867. if (objStrings == NULL) {
  868.     if (Tcl_GetErrno() != 0) {
  869. /* 
  870.  * There was an error, probably that the filePtr is
  871.  * not accepted by any filesystem
  872.  */
  873. Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), 
  874. "could not read "", Tcl_GetString(filePtr), 
  875. "": ", Tcl_PosixError(interp), 
  876. (char *) NULL);
  877. return TCL_ERROR;
  878.     }
  879.     goto end;
  880. }
  881. /* We own the object now */
  882. Tcl_IncrRefCount(objStrings);
  883.         /* Use objStrings as a list object */
  884. if (Tcl_ListObjLength(interp, objStrings, &numObjStrings) != TCL_OK) {
  885.     goto end;
  886. }
  887. attributeStrings = (CONST char **)
  888. ckalloc ((1+numObjStrings) * sizeof(char*));
  889. for (index = 0; index < numObjStrings; index++) {
  890.     Tcl_ListObjIndex(interp, objStrings, index, &objPtr);
  891.     attributeStrings[index] = Tcl_GetString(objPtr);
  892. }
  893. attributeStrings[index] = NULL;
  894.     }
  895.     if (objc == 0) {
  896. /*
  897.  * Get all attributes.
  898.  */
  899. int index;
  900. Tcl_Obj *listPtr;
  901.  
  902. listPtr = Tcl_NewListObj(0, NULL);
  903. for (index = 0; attributeStrings[index] != NULL; index++) {
  904.     Tcl_Obj *objPtr = Tcl_NewStringObj(attributeStrings[index], -1);
  905.     Tcl_ListObjAppendElement(interp, listPtr, objPtr);
  906.     /* We now forget about objPtr, it is in the list */
  907.     objPtr = NULL;
  908.     if (Tcl_FSFileAttrsGet(interp, index, filePtr,
  909.     &objPtr) != TCL_OK) {
  910. Tcl_DecrRefCount(listPtr);
  911. goto end;
  912.     }
  913.     Tcl_ListObjAppendElement(interp, listPtr, objPtr);
  914. }
  915.      Tcl_SetObjResult(interp, listPtr);
  916.     } else if (objc == 1) {
  917. /*
  918.  * Get one attribute.
  919.  */
  920. int index;
  921. Tcl_Obj *objPtr = NULL;
  922. if (numObjStrings == 0) {
  923.     Tcl_AppendResult(interp, "bad option "",
  924.     Tcl_GetString(objv[0]), "", there are no file attributes"
  925.      " in this filesystem.", (char *) NULL);
  926.     goto end;
  927. }
  928. if (Tcl_GetIndexFromObj(interp, objv[0], attributeStrings,
  929. "option", 0, &index) != TCL_OK) {
  930.     goto end;
  931. }
  932. if (Tcl_FSFileAttrsGet(interp, index, filePtr,
  933. &objPtr) != TCL_OK) {
  934.     goto end;
  935. }
  936. Tcl_SetObjResult(interp, objPtr);
  937.     } else {
  938. /*
  939.  * Set option/value pairs.
  940.  */
  941. int i, index;
  942.         
  943. if (numObjStrings == 0) {
  944.     Tcl_AppendResult(interp, "bad option "",
  945.     Tcl_GetString(objv[0]), "", there are no file attributes"
  946.      " in this filesystem.", (char *) NULL);
  947.     goto end;
  948. }
  949.      for (i = 0; i < objc ; i += 2) {
  950.          if (Tcl_GetIndexFromObj(interp, objv[i], attributeStrings,
  951.     "option", 0, &index) != TCL_OK) {
  952. goto end;
  953.          }
  954.     if (i + 1 == objc) {
  955. Tcl_AppendResult(interp, "value for "",
  956. Tcl_GetString(objv[i]), "" missing",
  957. (char *) NULL);
  958. goto end;
  959.     }
  960.          if (Tcl_FSFileAttrsSet(interp, index, filePtr,
  961.               objv[i + 1]) != TCL_OK) {
  962. goto end;
  963.          }
  964.      }
  965.     }
  966.     result = TCL_OK;
  967.     end:
  968.     if (numObjStrings != -1) {
  969. /* Free up the array we allocated */
  970. ckfree((char*)attributeStrings);
  971. /* 
  972.  * We don't need this object that was passed to us
  973.  * any more.
  974.  */
  975. if (objStrings != NULL) {
  976.     Tcl_DecrRefCount(objStrings);
  977. }
  978.     }
  979.     return result;
  980. }