static int TraversalDelete( char *src, /* Source pathname. */ char *ignore, /* Destination pathname (not used). */ DWORD srcAttr, /* File attributes for src (not used). */ int type, /* Reason for call - see TraverseWinTree(). */ Tcl_DString *errorPtr) /* If non-NULL, initialized DString for * error return. */ { switch (type) { case DOTREE_F: if (TclpDeleteFile(src) == TCL_OK) { return TCL_OK; } break; case DOTREE_PRED: return TCL_OK; case DOTREE_POSTD: if (TclpRemoveDirectory(src, 0, NULL) == TCL_OK) { return TCL_OK; } break; } if (errorPtr != NULL) { Tcl_DStringAppend(errorPtr, src, -1); } return TCL_ERROR; }
int TclpRenameFile( char *src, /* Pathname of file or dir to be renamed. */ char *dst) /* New pathname for file or directory. */ { FSSpec srcFileSpec, dstFileSpec, dstDirSpec; OSErr err; long srcID, dummy; Boolean srcIsDirectory, dstIsDirectory, dstExists, dstLocked; err = FSpLocationFromPath(strlen(src), src, &srcFileSpec); if (err == noErr) { FSpGetDirectoryID(&srcFileSpec, &srcID, &srcIsDirectory); } if (err == noErr) { err = GetFileSpecs(dst, &dstFileSpec, &dstDirSpec, &dstExists, &dstIsDirectory); } if (err == noErr) { if (dstExists == 0) { err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name); goto end; } err = FSpGetFLockCompat(&dstFileSpec, &dstLocked); if (dstLocked) { FSpRstFLockCompat(&dstFileSpec); } } if (err == noErr) { if (srcIsDirectory) { if (dstIsDirectory) { /* * The following call will remove an empty directory. If it * fails, it's because it wasn't empty. */ if (TclpRemoveDirectory(dst, 0, NULL) != TCL_OK) { return TCL_ERROR; } /* * Now that that empty directory is gone, we can try * renaming src. If that fails, we'll put this empty * directory back, for completeness. */ err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name); if (err != noErr) { FSpDirCreateCompat(&dstFileSpec, smSystemScript, &dummy); if (dstLocked) { FSpSetFLockCompat(&dstFileSpec); } } } else { errno = ENOTDIR; return TCL_ERROR; } } else { if (dstIsDirectory) { errno = EISDIR; return TCL_ERROR; } else { /* * Overwrite existing file by: * * 1. Rename existing file to temp name. * 2. Rename old file to new name. * 3. If success, delete temp file. If failure, * put temp file back to old name. */ Str31 tmpName; FSSpec tmpFileSpec; err = GenerateUniqueName(dstFileSpec.vRefNum, dstFileSpec.parID, dstFileSpec.parID, tmpName); if (err == noErr) { err = FSpRenameCompat(&dstFileSpec, tmpName); } if (err == noErr) { err = FSMakeFSSpecCompat(dstFileSpec.vRefNum, dstFileSpec.parID, tmpName, &tmpFileSpec); } if (err == noErr) { err = MoveRename(&srcFileSpec, &dstDirSpec, dstFileSpec.name); } if (err == noErr) { FSpDeleteCompat(&tmpFileSpec); } else { FSpDeleteCompat(&dstFileSpec); FSpRenameCompat(&tmpFileSpec, dstFileSpec.name); if (dstLocked) { FSpSetFLockCompat(&dstFileSpec); } } } } } end: if (err != noErr) { errno = TclMacOSErrorToPosixError(err); return TCL_ERROR; } return TCL_OK; }
int TclpRenameFile( char *src, /* Pathname of file or dir to be renamed. */ char *dst) /* New pathname for file or directory. */ { DWORD srcAttr, dstAttr; /* * Would throw an exception under NT if one of the arguments is a * char block device. */ try { if (MoveFile(src, dst) != FALSE) { return TCL_OK; } } except (-1) {} TclWinConvertError(GetLastError()); srcAttr = GetFileAttributes(src); dstAttr = GetFileAttributes(dst); if (srcAttr == (DWORD) -1) { srcAttr = 0; } if (dstAttr == (DWORD) -1) { dstAttr = 0; } if (errno == EBADF) { errno = EACCES; return TCL_ERROR; } if ((errno == EACCES) && (TclWinGetPlatformId() == VER_PLATFORM_WIN32s)) { if ((srcAttr != 0) && (dstAttr != 0)) { /* * Win32s reports trying to overwrite an existing file or directory * as EACCES. */ errno = EEXIST; } } if (errno == EACCES) { decode: if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { char srcPath[MAX_PATH], dstPath[MAX_PATH]; int srcArgc, dstArgc; char **srcArgv, **dstArgv; char *srcRest, *dstRest; int size; size = GetFullPathName(src, sizeof(srcPath), srcPath, &srcRest); if ((size == 0) || (size > sizeof(srcPath))) { return TCL_ERROR; } size = GetFullPathName(dst, sizeof(dstPath), dstPath, &dstRest); if ((size == 0) || (size > sizeof(dstPath))) { return TCL_ERROR; } if (srcRest == NULL) { srcRest = srcPath + strlen(srcPath); } if (strnicmp(srcPath, dstPath, srcRest - srcPath) == 0) { /* * Trying to move a directory into itself. */ errno = EINVAL; return TCL_ERROR; } Tcl_SplitPath(srcPath, &srcArgc, &srcArgv); Tcl_SplitPath(dstPath, &dstArgc, &dstArgv); if (srcArgc == 1) { /* * They are trying to move a root directory. Whether * or not it is across filesystems, this cannot be * done. */ errno = EINVAL; } else if ((srcArgc > 0) && (dstArgc > 0) && (stricmp(srcArgv[0], dstArgv[0]) != 0)) { /* * If src is a directory and dst filesystem != src * filesystem, errno should be EXDEV. It is very * important to get this behavior, so that the caller * can respond to a cross filesystem rename by * simulating it with copy and delete. The MoveFile * system call already handles the case of moving a * file between filesystems. */ errno = EXDEV; } ckfree((char *) srcArgv); ckfree((char *) dstArgv); } /* * Other types of access failure is that dst is a read-only * filesystem, that an open file referred to src or dest, or that * src or dest specified the current working directory on the * current filesystem. EACCES is returned for those cases. */ } else if (errno == EEXIST) { /* * Reports EEXIST any time the target already exists. If it makes * sense, remove the old file and try renaming again. */ if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) { if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) { /* * Overwrite empty dst directory with src directory. The * following call will remove an empty directory. If it * fails, it's because it wasn't empty. */ if (TclpRemoveDirectory(dst, 0, NULL) == TCL_OK) { /* * Now that that empty directory is gone, we can try * renaming again. If that fails, we'll put this empty * directory back, for completeness. */ if (MoveFile(src, dst) != FALSE) { return TCL_OK; } /* * Some new error has occurred. Don't know what it * could be, but report this one. */ TclWinConvertError(GetLastError()); CreateDirectory(dst, NULL); SetFileAttributes(dst, dstAttr); if (errno == EACCES) { /* * Decode the EACCES to a more meaningful error. */ goto decode; } } } else { /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */ errno = ENOTDIR; } } else { /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */ if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; } else { /* * Overwrite existing file by: * * 1. Rename existing file to temp name. * 2. Rename old file to new name. * 3. If success, delete temp file. If failure, * put temp file back to old name. */ char tempName[MAX_PATH]; int result, size; char *rest; size = GetFullPathName(dst, sizeof(tempName), tempName, &rest); if ((size == 0) || (size > sizeof(tempName)) || (rest == NULL)) { return TCL_ERROR; } *rest = '\0'; result = TCL_ERROR; if (GetTempFileName(tempName, "tclr", 0, tempName) != 0) { /* * Strictly speaking, need the following DeleteFile and * MoveFile to be joined as an atomic operation so no * other app comes along in the meantime and creates the * same temp file. */ DeleteFile(tempName); if (MoveFile(dst, tempName) != FALSE) { if (MoveFile(src, dst) != FALSE) { SetFileAttributes(tempName, FILE_ATTRIBUTE_NORMAL); DeleteFile(tempName); return TCL_OK; } else { DeleteFile(dst); MoveFile(tempName, dst); } } /* * Can't backup dst file or move src file. Return that * error. Could happen if an open file refers to dst. */ TclWinConvertError(GetLastError()); if (errno == EACCES) { /* * Decode the EACCES to a more meaningful error. */ goto decode; } } return result; } } } return TCL_ERROR; }