tclFCmd.cGo 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 1.5.1 |