tclUnixFCmd.cGo to the documentation of this file.00001 /* 00002 * tclUnixFCmd.c 00003 * 00004 * This file implements the unix specific portion of file manipulation 00005 * subcommands of the "file" command. All filename arguments should 00006 * already be translated to native format. 00007 * 00008 * Copyright (c) 1996-1998 Sun Microsystems, Inc. 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclUnixFCmd.c,v 1.65 2007/12/13 15:28:42 dgp Exp $ 00014 * 00015 * Portions of this code were derived from NetBSD source code which has the 00016 * following copyright notice: 00017 * 00018 * Copyright (c) 1988, 1993, 1994 00019 * The Regents of the University of California. All rights reserved. 00020 * 00021 * Redistribution and use in source and binary forms, with or without 00022 * modification, are permitted provided that the following conditions are met: 00023 * 1. Redistributions of source code must retain the above copyright notice, 00024 * this list of conditions and the following disclaimer. 00025 * 2. Redistributions in binary form must reproduce the above copyright 00026 * notice, this list of conditions and the following disclaimer in the 00027 * documentation and/or other materials provided with the distribution. 00028 * 3. All advertising materials mentioning features or use of this software 00029 * must display the following acknowledgement: 00030 * This product includes software developed by the University of 00031 * California, Berkeley and its contributors. 00032 * 4. Neither the name of the University nor the names of its contributors may 00033 * be used to endorse or promote products derived from this software 00034 * without specific prior written permission. 00035 * 00036 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 00037 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00038 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00039 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 00040 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00041 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00042 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00043 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00044 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00045 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 00046 * DAMAGE. 00047 */ 00048 00049 #include "tclInt.h" 00050 #include <utime.h> 00051 #include <grp.h> 00052 #ifndef HAVE_ST_BLKSIZE 00053 #ifndef NO_FSTATFS 00054 #include <sys/statfs.h> 00055 #endif 00056 #endif 00057 #ifdef HAVE_FTS 00058 #include <fts.h> 00059 #endif 00060 00061 /* 00062 * The following constants specify the type of callback when 00063 * TraverseUnixTree() calls the traverseProc() 00064 */ 00065 00066 #define DOTREE_PRED 1 /* pre-order directory */ 00067 #define DOTREE_POSTD 2 /* post-order directory */ 00068 #define DOTREE_F 3 /* regular file */ 00069 00070 /* 00071 * Callbacks for file attributes code. 00072 */ 00073 00074 static int GetGroupAttribute(Tcl_Interp *interp, int objIndex, 00075 Tcl_Obj *fileName, Tcl_Obj **attributePtrPtr); 00076 static int GetOwnerAttribute(Tcl_Interp *interp, int objIndex, 00077 Tcl_Obj *fileName, Tcl_Obj **attributePtrPtr); 00078 static int GetPermissionsAttribute(Tcl_Interp *interp, 00079 int objIndex, Tcl_Obj *fileName, 00080 Tcl_Obj **attributePtrPtr); 00081 static int SetGroupAttribute(Tcl_Interp *interp, int objIndex, 00082 Tcl_Obj *fileName, Tcl_Obj *attributePtr); 00083 static int SetOwnerAttribute(Tcl_Interp *interp, int objIndex, 00084 Tcl_Obj *fileName, Tcl_Obj *attributePtr); 00085 static int SetPermissionsAttribute(Tcl_Interp *interp, 00086 int objIndex, Tcl_Obj *fileName, 00087 Tcl_Obj *attributePtr); 00088 static int GetModeFromPermString(Tcl_Interp *interp, 00089 char *modeStringPtr, mode_t *modePtr); 00090 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) 00091 static int GetReadOnlyAttribute(Tcl_Interp *interp, int objIndex, 00092 Tcl_Obj *fileName, Tcl_Obj **attributePtrPtr); 00093 static int SetReadOnlyAttribute(Tcl_Interp *interp, int objIndex, 00094 Tcl_Obj *fileName, Tcl_Obj *attributePtr); 00095 #endif 00096 00097 /* 00098 * Prototype for the TraverseUnixTree callback function. 00099 */ 00100 00101 typedef int (TraversalProc)(Tcl_DString *srcPtr, Tcl_DString *dstPtr, 00102 CONST Tcl_StatBuf *statBufPtr, int type, Tcl_DString *errorPtr); 00103 00104 /* 00105 * Constants and variables necessary for file attributes subcommand. 00106 * 00107 * IMPORTANT: The permissions attribute is assumed to be the third item (i.e. 00108 * to be indexed with '2' in arrays) in code in tclIOUtil.c and possibly 00109 * elsewhere in Tcl's core. 00110 */ 00111 00112 #ifdef DJGPP 00113 00114 /* 00115 * See contrib/djgpp/tclDjgppFCmd.c for definition. 00116 */ 00117 00118 extern TclFileAttrProcs tclpFileAttrProcs[]; 00119 extern char *tclpFileAttrStrings[]; 00120 00121 #else 00122 enum { 00123 UNIX_GROUP_ATTRIBUTE, UNIX_OWNER_ATTRIBUTE, UNIX_PERMISSIONS_ATTRIBUTE, 00124 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) 00125 UNIX_READONLY_ATTRIBUTE, 00126 #endif 00127 #ifdef MAC_OSX_TCL 00128 MACOSX_CREATOR_ATTRIBUTE, MACOSX_TYPE_ATTRIBUTE, MACOSX_HIDDEN_ATTRIBUTE, 00129 MACOSX_RSRCLENGTH_ATTRIBUTE, 00130 #endif 00131 UNIX_INVALID_ATTRIBUTE /* lint - last enum value needs no trailing , */ 00132 }; 00133 00134 MODULE_SCOPE CONST char *tclpFileAttrStrings[]; 00135 CONST char *tclpFileAttrStrings[] = { 00136 "-group", "-owner", "-permissions", 00137 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) 00138 "-readonly", 00139 #endif 00140 #ifdef MAC_OSX_TCL 00141 "-creator", "-type", "-hidden", "-rsrclength", 00142 #endif 00143 NULL 00144 }; 00145 00146 MODULE_SCOPE CONST TclFileAttrProcs tclpFileAttrProcs[]; 00147 CONST TclFileAttrProcs tclpFileAttrProcs[] = { 00148 {GetGroupAttribute, SetGroupAttribute}, 00149 {GetOwnerAttribute, SetOwnerAttribute}, 00150 {GetPermissionsAttribute, SetPermissionsAttribute}, 00151 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) 00152 {GetReadOnlyAttribute, SetReadOnlyAttribute}, 00153 #endif 00154 #ifdef MAC_OSX_TCL 00155 {TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute}, 00156 {TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute}, 00157 {TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute}, 00158 {TclMacOSXGetFileAttribute, TclMacOSXSetFileAttribute}, 00159 #endif 00160 }; 00161 #endif 00162 00163 /* 00164 * This is the maximum number of consecutive readdir/unlink calls that can be 00165 * made (with no intervening rewinddir or closedir/opendir) before triggering 00166 * a bug that makes readdir return NULL even though some directory entries 00167 * have not been processed. The bug afflicts SunOS's readdir when applied to 00168 * ufs file systems and Darwin 6.5's (and OSX v.10.3.8's) HFS+. JH found the 00169 * Darwin readdir to reset at 147, so 130 is chosen to be conservative. We 00170 * can't do a general rewind on failure as NFS can create special files that 00171 * recreate themselves when you try and delete them. 8.4.8 added a solution 00172 * that was affected by a single such NFS file, this solution should not be 00173 * affected by less than THRESHOLD such files. [Bug 1034337] 00174 */ 00175 00176 #define MAX_READDIR_UNLINK_THRESHOLD 130 00177 00178 /* 00179 * Declarations for local procedures defined in this file: 00180 */ 00181 00182 static int CopyFileAtts(CONST char *src, 00183 CONST char *dst, CONST Tcl_StatBuf *statBufPtr); 00184 static int DoCopyFile(CONST char *srcPtr, CONST char *dstPtr, 00185 CONST Tcl_StatBuf *statBufPtr); 00186 static int DoCreateDirectory(CONST char *pathPtr); 00187 static int DoRemoveDirectory(Tcl_DString *pathPtr, 00188 int recursive, Tcl_DString *errorPtr); 00189 static int DoRenameFile(CONST char *src, CONST char *dst); 00190 static int TraversalCopy(Tcl_DString *srcPtr, 00191 Tcl_DString *dstPtr, CONST Tcl_StatBuf *statBufPtr, 00192 int type, Tcl_DString *errorPtr); 00193 static int TraversalDelete(Tcl_DString *srcPtr, 00194 Tcl_DString *dstPtr, CONST Tcl_StatBuf *statBufPtr, 00195 int type, Tcl_DString *errorPtr); 00196 static int TraverseUnixTree(TraversalProc *traversalProc, 00197 Tcl_DString *sourcePtr, Tcl_DString *destPtr, 00198 Tcl_DString *errorPtr, int doRewind); 00199 00200 #ifdef PURIFY 00201 /* 00202 * realpath and purify don't mix happily. It has been noted that realpath 00203 * should not be used with purify because of bogus warnings, but just 00204 * memset'ing the resolved path will squelch those. This assumes we are 00205 * passing the standard MAXPATHLEN size resolved arg. 00206 */ 00207 00208 static char * Realpath(CONST char *path, char *resolved); 00209 00210 char * 00211 Realpath( 00212 CONST char *path, 00213 char *resolved) 00214 { 00215 memset(resolved, 0, MAXPATHLEN); 00216 return realpath(path, resolved); 00217 } 00218 #else 00219 #define Realpath realpath 00220 #endif 00221 00222 #ifndef NO_REALPATH 00223 #if defined(__APPLE__) && defined(TCL_THREADS) && \ 00224 defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ 00225 MAC_OS_X_VERSION_MIN_REQUIRED < 1030 00226 /* 00227 * Prior to Darwin 7, realpath is not thread-safe, c.f. Bug 711232; if we 00228 * might potentially be running on pre-10.3 OSX, check Darwin release at 00229 * runtime before using realpath. 00230 */ 00231 00232 MODULE_SCOPE long tclMacOSXDarwinRelease; 00233 #define haveRealpath (tclMacOSXDarwinRelease >= 7) 00234 #else 00235 #define haveRealpath 1 00236 #endif 00237 #endif /* NO_REALPATH */ 00238 00239 #ifdef HAVE_FTS 00240 #ifdef HAVE_STRUCT_STAT64 00241 /* fts doesn't do stat64 */ 00242 #define noFtsStat 1 00243 #elif defined(__APPLE__) && defined(__LP64__) && \ 00244 defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ 00245 MAC_OS_X_VERSION_MIN_REQUIRED < 1050 00246 /* 00247 * Prior to Darwin 9, 64bit fts_open() without FTS_NOSTAT may crash (due to a 00248 * 64bit-unsafe ALIGN macro); if we could be running on pre-10.5 OSX, check 00249 * Darwin release at runtime and do a separate stat() if necessary. 00250 */ 00251 00252 MODULE_SCOPE long tclMacOSXDarwinRelease; 00253 #define noFtsStat (tclMacOSXDarwinRelease < 9) 00254 #else 00255 #define noFtsStat 0 00256 #endif 00257 #endif /* HAVE_FTS */ 00258 00259 /* 00260 *--------------------------------------------------------------------------- 00261 * 00262 * TclpObjRenameFile, DoRenameFile -- 00263 * 00264 * Changes the name of an existing file or directory, from src to dst. If 00265 * src and dst refer to the same file or directory, does nothing and 00266 * returns success. Otherwise if dst already exists, it will be deleted 00267 * and replaced by src subject to the following conditions: 00268 * If src is a directory, dst may be an empty directory. 00269 * If src is a file, dst may be a file. 00270 * In any other situation where dst already exists, the rename will fail. 00271 * 00272 * Results: 00273 * If the directory was successfully created, returns TCL_OK. Otherwise 00274 * the return value is TCL_ERROR and errno is set to indicate the error. 00275 * Some possible values for errno are: 00276 * 00277 * EACCES: src or dst parent directory can't be read and/or written. 00278 * EEXIST: dst is a non-empty directory. 00279 * EINVAL: src is a root directory or dst is a subdirectory of src. 00280 * EISDIR: dst is a directory, but src is not. 00281 * ENOENT: src doesn't exist, or src or dst is "". 00282 * ENOTDIR: src is a directory, but dst is not. 00283 * EXDEV: src and dst are on different filesystems. 00284 * 00285 * Side effects: 00286 * The implementation of rename may allow cross-filesystem renames, but 00287 * the caller should be prepared to emulate it with copy and delete if 00288 * errno is EXDEV. 00289 * 00290 *--------------------------------------------------------------------------- 00291 */ 00292 00293 int 00294 TclpObjRenameFile( 00295 Tcl_Obj *srcPathPtr, 00296 Tcl_Obj *destPathPtr) 00297 { 00298 return DoRenameFile(Tcl_FSGetNativePath(srcPathPtr), 00299 Tcl_FSGetNativePath(destPathPtr)); 00300 } 00301 00302 static int 00303 DoRenameFile( 00304 CONST char *src, /* Pathname of file or dir to be renamed 00305 * (native). */ 00306 CONST char *dst) /* New pathname of file or directory 00307 * (native). */ 00308 { 00309 if (rename(src, dst) == 0) { /* INTL: Native. */ 00310 return TCL_OK; 00311 } 00312 if (errno == ENOTEMPTY) { 00313 errno = EEXIST; 00314 } 00315 00316 /* 00317 * IRIX returns EIO when you attept to move a directory into itself. We 00318 * just map EIO to EINVAL get the right message on SGI. Most platforms 00319 * don't return EIO except in really strange cases. 00320 */ 00321 00322 if (errno == EIO) { 00323 errno = EINVAL; 00324 } 00325 00326 #ifndef NO_REALPATH 00327 /* 00328 * SunOS 4.1.4 reports overwriting a non-empty directory with a directory 00329 * as EINVAL instead of EEXIST (first rule out the correct EINVAL result 00330 * code for moving a directory into itself). Must be conditionally 00331 * compiled because realpath() not defined on all systems. 00332 */ 00333 00334 if (errno == EINVAL && haveRealpath) { 00335 char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN]; 00336 DIR *dirPtr; 00337 Tcl_DirEntry *dirEntPtr; 00338 00339 if ((Realpath((char *) src, srcPath) != NULL) /* INTL: Native. */ 00340 && (Realpath((char *) dst, dstPath) != NULL) /* INTL: Native */ 00341 && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) { 00342 dirPtr = opendir(dst); /* INTL: Native. */ 00343 if (dirPtr != NULL) { 00344 while (1) { 00345 dirEntPtr = TclOSreaddir(dirPtr); /* INTL: Native. */ 00346 if (dirEntPtr == NULL) { 00347 break; 00348 } 00349 if ((strcmp(dirEntPtr->d_name, ".") != 0) && 00350 (strcmp(dirEntPtr->d_name, "..") != 0)) { 00351 errno = EEXIST; 00352 closedir(dirPtr); 00353 return TCL_ERROR; 00354 } 00355 } 00356 closedir(dirPtr); 00357 } 00358 } 00359 errno = EINVAL; 00360 } 00361 #endif /* !NO_REALPATH */ 00362 00363 if (strcmp(src, "/") == 0) { 00364 /* 00365 * Alpha reports renaming / as EBUSY and Linux reports it as EACCES, 00366 * instead of EINVAL. 00367 */ 00368 00369 errno = EINVAL; 00370 } 00371 00372 /* 00373 * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a file 00374 * across filesystems and the parent directory of that file is not 00375 * writable. Most other systems return EXDEV. Does nothing to correct this 00376 * behavior. 00377 */ 00378 00379 return TCL_ERROR; 00380 } 00381 00382 /* 00383 *--------------------------------------------------------------------------- 00384 * 00385 * TclpObjCopyFile, DoCopyFile -- 00386 * 00387 * Copy a single file (not a directory). If dst already exists and is not 00388 * a directory, it is removed. 00389 * 00390 * Results: 00391 * If the file was successfully copied, returns TCL_OK. Otherwise the 00392 * return value is TCL_ERROR and errno is set to indicate the error. Some 00393 * possible values for errno are: 00394 * 00395 * EACCES: src or dst parent directory can't be read and/or written. 00396 * EISDIR: src or dst is a directory. 00397 * ENOENT: src doesn't exist. src or dst is "". 00398 * 00399 * Side effects: 00400 * This procedure will also copy symbolic links, block, and character 00401 * devices, and fifos. For symbolic links, the links themselves will be 00402 * copied and not what they point to. For the other special file types, 00403 * the directory entry will be copied and not the contents of the device 00404 * that it refers to. 00405 * 00406 *--------------------------------------------------------------------------- 00407 */ 00408 00409 int 00410 TclpObjCopyFile( 00411 Tcl_Obj *srcPathPtr, 00412 Tcl_Obj *destPathPtr) 00413 { 00414 CONST char *src = Tcl_FSGetNativePath(srcPathPtr); 00415 Tcl_StatBuf srcStatBuf; 00416 00417 if (TclOSlstat(src, &srcStatBuf) != 0) { /* INTL: Native. */ 00418 return TCL_ERROR; 00419 } 00420 00421 return DoCopyFile(src, Tcl_FSGetNativePath(destPathPtr), &srcStatBuf); 00422 } 00423 00424 static int 00425 DoCopyFile( 00426 CONST char *src, /* Pathname of file to be copied (native). */ 00427 CONST char *dst, /* Pathname of file to copy to (native). */ 00428 CONST Tcl_StatBuf *statBufPtr) 00429 /* Used to determine filetype. */ 00430 { 00431 Tcl_StatBuf dstStatBuf; 00432 00433 if (S_ISDIR(statBufPtr->st_mode)) { 00434 errno = EISDIR; 00435 return TCL_ERROR; 00436 } 00437 00438 /* 00439 * Symlink, and some of the other calls will fail if the target exists, so 00440 * we remove it first. 00441 */ 00442 00443 if (TclOSlstat(dst, &dstStatBuf) == 0) { /* INTL: Native. */ 00444 if (S_ISDIR(dstStatBuf.st_mode)) { 00445 errno = EISDIR; 00446 return TCL_ERROR; 00447 } 00448 } 00449 if (unlink(dst) != 0) { /* INTL: Native. */ 00450 if (errno != ENOENT) { 00451 return TCL_ERROR; 00452 } 00453 } 00454 00455 switch ((int) (statBufPtr->st_mode & S_IFMT)) { 00456 #ifndef DJGPP 00457 case S_IFLNK: { 00458 char link[MAXPATHLEN]; 00459 int length; 00460 00461 length = readlink(src, link, sizeof(link)); /* INTL: Native. */ 00462 if (length == -1) { 00463 return TCL_ERROR; 00464 } 00465 link[length] = '\0'; 00466 if (symlink(link, dst) < 0) { /* INTL: Native. */ 00467 return TCL_ERROR; 00468 } 00469 #ifdef MAC_OSX_TCL 00470 TclMacOSXCopyFileAttributes(src, dst, statBufPtr); 00471 #endif 00472 break; 00473 } 00474 #endif 00475 case S_IFBLK: 00476 case S_IFCHR: 00477 if (mknod(dst, statBufPtr->st_mode, /* INTL: Native. */ 00478 statBufPtr->st_rdev) < 0) { 00479 return TCL_ERROR; 00480 } 00481 return CopyFileAtts(src, dst, statBufPtr); 00482 case S_IFIFO: 00483 if (mkfifo(dst, statBufPtr->st_mode) < 0) { /* INTL: Native. */ 00484 return TCL_ERROR; 00485 } 00486 return CopyFileAtts(src, dst, statBufPtr); 00487 default: 00488 return TclUnixCopyFile(src, dst, statBufPtr, 0); 00489 } 00490 return TCL_OK; 00491 } 00492 00493 /* 00494 *---------------------------------------------------------------------- 00495 * 00496 * TclUnixCopyFile - 00497 * 00498 * Helper function for TclpCopyFile. Copies one regular file, using 00499 * read() and write(). 00500 * 00501 * Results: 00502 * A standard Tcl result. 00503 * 00504 * Side effects: 00505 * A file is copied. Dst will be overwritten if it exists. 00506 * 00507 *---------------------------------------------------------------------- 00508 */ 00509 00510 int 00511 TclUnixCopyFile( 00512 CONST char *src, /* Pathname of file to copy (native). */ 00513 CONST char *dst, /* Pathname of file to create/overwrite 00514 * (native). */ 00515 CONST Tcl_StatBuf *statBufPtr, 00516 /* Used to determine mode and blocksize. */ 00517 int dontCopyAtts) /* If flag set, don't copy attributes. */ 00518 { 00519 int srcFd, dstFd; 00520 unsigned blockSize; /* Optimal I/O blocksize for filesystem */ 00521 char *buffer; /* Data buffer for copy */ 00522 size_t nread; 00523 00524 #ifdef DJGPP 00525 #define BINMODE |O_BINARY 00526 #else 00527 #define BINMODE 00528 #endif 00529 00530 if ((srcFd = TclOSopen(src, O_RDONLY BINMODE, 0)) < 0) { /* INTL: Native */ 00531 return TCL_ERROR; 00532 } 00533 00534 dstFd = TclOSopen(dst, O_CREAT|O_TRUNC|O_WRONLY BINMODE, /* INTL: Native */ 00535 statBufPtr->st_mode); 00536 if (dstFd < 0) { 00537 close(srcFd); 00538 return TCL_ERROR; 00539 } 00540 00541 /* 00542 * Try to work out the best size of buffer to use for copying. If we 00543 * can't, it's no big deal as we can just use a (32-bit) page, since 00544 * that's likely to be fairly efficient anyway. 00545 */ 00546 00547 #ifdef HAVE_ST_BLKSIZE 00548 blockSize = statBufPtr->st_blksize; 00549 #elif !defined(NO_FSTATFS) 00550 { 00551 struct statfs fs; 00552 00553 if (fstatfs(srcFd, &fs, sizeof(fs), 0) == 0) { 00554 blockSize = fs.f_bsize; 00555 } else { 00556 blockSize = 4096; 00557 } 00558 } 00559 #else 00560 blockSize = 4096; 00561 #endif /* HAVE_ST_BLKSIZE */ 00562 00563 /* 00564 * [SF Tcl Bug 1586470] Even if we HAVE_ST_BLKSIZE, there are filesystems 00565 * which report a bogus value for the blocksize. An example is the Andrew 00566 * Filesystem (afs), reporting a blocksize of 0. When detecting such a 00567 * situation we now simply fall back to a hardwired default size. 00568 */ 00569 00570 if (blockSize <= 0) { 00571 blockSize = 4096; 00572 } 00573 buffer = ckalloc(blockSize); 00574 while (1) { 00575 nread = (size_t) read(srcFd, buffer, blockSize); 00576 if ((nread == (size_t) -1) || (nread == 0)) { 00577 break; 00578 } 00579 if ((size_t) write(dstFd, buffer, nread) != nread) { 00580 nread = (size_t) -1; 00581 break; 00582 } 00583 } 00584 00585 ckfree(buffer); 00586 close(srcFd); 00587 if ((close(dstFd) != 0) || (nread == (size_t) -1)) { 00588 unlink(dst); /* INTL: Native. */ 00589 return TCL_ERROR; 00590 } 00591 if (!dontCopyAtts && CopyFileAtts(src, dst, statBufPtr) == TCL_ERROR) { 00592 /* 00593 * The copy succeeded, but setting the permissions failed, so be in a 00594 * consistent state, we remove the file that was created by the copy. 00595 */ 00596 00597 unlink(dst); /* INTL: Native. */ 00598 return TCL_ERROR; 00599 } 00600 return TCL_OK; 00601 } 00602 00603 /* 00604 *--------------------------------------------------------------------------- 00605 * 00606 * TclpObjDeleteFile, TclpDeleteFile -- 00607 * 00608 * Removes a single file (not a directory). 00609 * 00610 * Results: 00611 * If the file was successfully deleted, returns TCL_OK. Otherwise the 00612 * return value is TCL_ERROR and errno is set to indicate the error. Some 00613 * possible values for errno are: 00614 * 00615 * EACCES: a parent directory can't be read and/or written. 00616 * EISDIR: path is a directory. 00617 * ENOENT: path doesn't exist or is "". 00618 * 00619 * Side effects: 00620 * The file is deleted, even if it is read-only. 00621 * 00622 *--------------------------------------------------------------------------- 00623 */ 00624 00625 int 00626 TclpObjDeleteFile( 00627 Tcl_Obj *pathPtr) 00628 { 00629 return TclpDeleteFile(Tcl_FSGetNativePath(pathPtr)); 00630 } 00631 00632 int 00633 TclpDeleteFile( 00634 CONST char *path) /* Pathname of file to be removed (native). */ 00635 { 00636 if (unlink(path) != 0) { /* INTL: Native. */ 00637 return TCL_ERROR; 00638 } 00639 return TCL_OK; 00640 } 00641 00642 /* 00643 *--------------------------------------------------------------------------- 00644 * 00645 * TclpCreateDirectory, DoCreateDirectory -- 00646 * 00647 * Creates the specified directory. All parent directories of the 00648 * specified directory must already exist. The directory is automatically 00649 * created with permissions so that user can access the new directory and 00650 * create new files or subdirectories in it. 00651 * 00652 * Results: 00653 * If the directory was successfully created, returns TCL_OK. Otherwise 00654 * the return value is TCL_ERROR and errno is set to indicate the error. 00655 * Some possible values for errno are: 00656 * 00657 * EACCES: a parent directory can't be read and/or written. 00658 * EEXIST: path already exists. 00659 * ENOENT: a parent directory doesn't exist. 00660 * 00661 * Side effects: 00662 * A directory is created with the current umask, except that permission 00663 * for u+rwx will always be added. 00664 * 00665 *--------------------------------------------------------------------------- 00666 */ 00667 00668 int 00669 TclpObjCreateDirectory( 00670 Tcl_Obj *pathPtr) 00671 { 00672 return DoCreateDirectory(Tcl_FSGetNativePath(pathPtr)); 00673 } 00674 00675 static int 00676 DoCreateDirectory( 00677 CONST char *path) /* Pathname of directory to create (native). */ 00678 { 00679 mode_t mode; 00680 00681 mode = umask(0); 00682 umask(mode); 00683 00684 /* 00685 * umask return value is actually the inverse of the permissions. 00686 */ 00687 00688 mode = (0777 & ~mode) | S_IRUSR | S_IWUSR | S_IXUSR; 00689 00690 if (mkdir(path, mode) != 0) { /* INTL: Native. */ 00691 return TCL_ERROR; 00692 } 00693 return TCL_OK; 00694 } 00695 00696 /* 00697 *--------------------------------------------------------------------------- 00698 * 00699 * TclpObjCopyDirectory -- 00700 * 00701 * Recursively copies a directory. The target directory dst must not 00702 * already exist. Note that this function does not merge two directory 00703 * hierarchies, even if the target directory is an an empty directory. 00704 * 00705 * Results: 00706 * If the directory was successfully copied, returns TCL_OK. Otherwise 00707 * the return value is TCL_ERROR, errno is set to indicate the error, and 00708 * the pathname of the file that caused the error is stored in errorPtr. 00709 * See TclpObjCreateDirectory and TclpObjCopyFile for a description of 00710 * possible values for errno. 00711 * 00712 * Side effects: 00713 * An exact copy of the directory hierarchy src will be created with the 00714 * name dst. If an error occurs, the error will be returned immediately, 00715 * and remaining files will not be processed. 00716 * 00717 *--------------------------------------------------------------------------- 00718 */ 00719 00720 int 00721 TclpObjCopyDirectory( 00722 Tcl_Obj *srcPathPtr, 00723 Tcl_Obj *destPathPtr, 00724 Tcl_Obj **errorPtr) 00725 { 00726 Tcl_DString ds; 00727 Tcl_DString srcString, dstString; 00728 int ret; 00729 Tcl_Obj *transPtr; 00730 00731 transPtr = Tcl_FSGetTranslatedPath(NULL,srcPathPtr); 00732 Tcl_UtfToExternalDString(NULL, 00733 (transPtr != NULL ? TclGetString(transPtr) : NULL), 00734 -1, &srcString); 00735 if (transPtr != NULL) { 00736 Tcl_DecrRefCount(transPtr); 00737 } 00738 transPtr = Tcl_FSGetTranslatedPath(NULL,destPathPtr); 00739 Tcl_UtfToExternalDString(NULL, 00740 (transPtr != NULL ? TclGetString(transPtr) : NULL), 00741 -1, &dstString); 00742 if (transPtr != NULL) { 00743 Tcl_DecrRefCount(transPtr); 00744 } 00745 00746 ret = TraverseUnixTree(TraversalCopy, &srcString, &dstString, &ds, 0); 00747 00748 Tcl_DStringFree(&srcString); 00749 Tcl_DStringFree(&dstString); 00750 00751 if (ret != TCL_OK) { 00752 *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); 00753 Tcl_DStringFree(&ds); 00754 Tcl_IncrRefCount(*errorPtr); 00755 } 00756 return ret; 00757 } 00758 00759 /* 00760 *--------------------------------------------------------------------------- 00761 * 00762 * TclpRemoveDirectory, DoRemoveDirectory -- 00763 * 00764 * Removes directory (and its contents, if the recursive flag is set). 00765 * 00766 * Results: 00767 * If the directory was successfully removed, returns TCL_OK. Otherwise 00768 * the return value is TCL_ERROR, errno is set to indicate the error, and 00769 * the pathname of the file that caused the error is stored in errorPtr. 00770 * Some possible values for errno are: 00771 * 00772 * EACCES: path directory can't be read and/or written. 00773 * EEXIST: path is a non-empty directory. 00774 * EINVAL: path is a root directory. 00775 * ENOENT: path doesn't exist or is "". 00776 * ENOTDIR: path is not a directory. 00777 * 00778 * Side effects: 00779 * Directory removed. If an error occurs, the error will be returned 00780 * immediately, and remaining files will not be deleted. 00781 * 00782 *--------------------------------------------------------------------------- 00783 */ 00784 00785 int 00786 TclpObjRemoveDirectory( 00787 Tcl_Obj *pathPtr, 00788 int recursive, 00789 Tcl_Obj **errorPtr) 00790 { 00791 Tcl_DString ds; 00792 Tcl_DString pathString; 00793 int ret; 00794 Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); 00795 00796 Tcl_UtfToExternalDString(NULL, 00797 (transPtr != NULL ? TclGetString(transPtr) : NULL), 00798 -1, &pathString); 00799 if (transPtr != NULL) { 00800 Tcl_DecrRefCount(transPtr); 00801 } 00802 ret = DoRemoveDirectory(&pathString, recursive, &ds); 00803 Tcl_DStringFree(&pathString); 00804 00805 if (ret != TCL_OK) { 00806 *errorPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); 00807 Tcl_DStringFree(&ds); 00808 Tcl_IncrRefCount(*errorPtr); 00809 } 00810 return ret; 00811 } 00812 00813 static int 00814 DoRemoveDirectory( 00815 Tcl_DString *pathPtr, /* Pathname of directory to be removed 00816 * (native). */ 00817 int recursive, /* If non-zero, removes directories that are 00818 * nonempty. Otherwise, will only remove empty 00819 * directories. */ 00820 Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free DString 00821 * filled with UTF-8 name of file causing 00822 * error. */ 00823 { 00824 CONST char *path; 00825 mode_t oldPerm = 0; 00826 int result; 00827 00828 path = Tcl_DStringValue(pathPtr); 00829 00830 if (recursive != 0) { 00831 /* 00832 * We should try to change permissions so this can be deleted. 00833 */ 00834 00835 Tcl_StatBuf statBuf; 00836 int newPerm; 00837 00838 if (TclOSstat(path, &statBuf) == 0) { 00839 oldPerm = (mode_t) (statBuf.st_mode & 0x00007FFF); 00840 } 00841 00842 newPerm = oldPerm | (64+128+256); 00843 chmod(path, (mode_t) newPerm); 00844 } 00845 00846 if (rmdir(path) == 0) { /* INTL: Native. */ 00847 return TCL_OK; 00848 } 00849 if (errno == ENOTEMPTY) { 00850 errno = EEXIST; 00851 } 00852 00853 result = TCL_OK; 00854 if ((errno != EEXIST) || (recursive == 0)) { 00855 if (errorPtr != NULL) { 00856 Tcl_ExternalToUtfDString(NULL, path, -1, errorPtr); 00857 } 00858 result = TCL_ERROR; 00859 } 00860 00861 /* 00862 * The directory is nonempty, but the recursive flag has been specified, 00863 * so we recursively remove all the files in the directory. 00864 */ 00865 00866 if (result == TCL_OK) { 00867 result = TraverseUnixTree(TraversalDelete, pathPtr, NULL, errorPtr, 1); 00868 } 00869 00870 if ((result != TCL_OK) && (recursive != 0)) { 00871 /* 00872 * Try to restore permissions. 00873 */ 00874 00875 chmod(path, oldPerm); 00876 } 00877 return result; 00878 } 00879 00880 /* 00881 *--------------------------------------------------------------------------- 00882 * 00883 * TraverseUnixTree -- 00884 * 00885 * Traverse directory tree specified by sourcePtr, calling the function 00886 * traverseProc for each file and directory encountered. If destPtr is 00887 * non-null, each of name in the sourcePtr directory is appended to the 00888 * directory specified by destPtr and passed as the second argument to 00889 * traverseProc(). 00890 * 00891 * Results: 00892 * Standard Tcl result. 00893 * 00894 * Side effects: 00895 * None caused by TraverseUnixTree, however the user specified 00896 * traverseProc() may change state. If an error occurs, the error will be 00897 * returned immediately, and remaining files will not be processed. 00898 * 00899 *--------------------------------------------------------------------------- 00900 */ 00901 00902 static int 00903 TraverseUnixTree( 00904 TraversalProc *traverseProc,/* Function to call for every file and 00905 * directory in source hierarchy. */ 00906 Tcl_DString *sourcePtr, /* Pathname of source directory to be 00907 * traversed (native). */ 00908 Tcl_DString *targetPtr, /* Pathname of directory to traverse in 00909 * parallel with source directory (native). */ 00910 Tcl_DString *errorPtr, /* If non-NULL, uninitialized or free DString 00911 * filled with UTF-8 name of file causing 00912 * error. */ 00913 int doRewind) /* Flag indicating that to ensure complete 00914 * traversal of source hierarchy, the readdir 00915 * loop should be rewound whenever 00916 * traverseProc has returned TCL_OK; this is 00917 * required when traverseProc modifies the 00918 * source hierarchy, e.g. by deleting 00919 * files. */ 00920 { 00921 Tcl_StatBuf statBuf; 00922 CONST char *source, *errfile; 00923 int result, sourceLen; 00924 int targetLen; 00925 #ifndef HAVE_FTS 00926 int numProcessed = 0; 00927 Tcl_DirEntry *dirEntPtr; 00928 DIR *dirPtr; 00929 #else 00930 CONST char *paths[2] = {NULL, NULL}; 00931 FTS *fts = NULL; 00932 FTSENT *ent; 00933 #endif 00934 00935 errfile = NULL; 00936 result = TCL_OK; 00937 targetLen = 0; /* lint. */ 00938 00939 source = Tcl_DStringValue(sourcePtr); 00940 if (TclOSlstat(source, &statBuf) != 0) { /* INTL: Native. */ 00941 errfile = source; 00942 goto end; 00943 } 00944 if (!S_ISDIR(statBuf.st_mode)) { 00945 /* 00946 * Process the regular file 00947 */ 00948 00949 return (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_F, 00950 errorPtr); 00951 } 00952 #ifndef HAVE_FTS 00953 dirPtr = opendir(source); /* INTL: Native. */ 00954 if (dirPtr == NULL) { 00955 /* 00956 * Can't read directory 00957 */ 00958 00959 errfile = source; 00960 goto end; 00961 } 00962 result = (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_PRED, 00963 errorPtr); 00964 if (result != TCL_OK) { 00965 closedir(dirPtr); 00966 return result; 00967 } 00968 00969 Tcl_DStringAppend(sourcePtr, "/", 1); 00970 sourceLen = Tcl_DStringLength(sourcePtr); 00971 00972 if (targetPtr != NULL) { 00973 Tcl_DStringAppend(targetPtr, "/", 1); 00974 targetLen = Tcl_DStringLength(targetPtr); 00975 } 00976 00977 while ((dirEntPtr = TclOSreaddir(dirPtr)) != NULL) { /* INTL: Native. */ 00978 if ((dirEntPtr->d_name[0] == '.') 00979 && ((dirEntPtr->d_name[1] == '\0') 00980 || (strcmp(dirEntPtr->d_name, "..") == 0))) { 00981 continue; 00982 } 00983 00984 /* 00985 * Append name after slash, and recurse on the file. 00986 */ 00987 00988 Tcl_DStringAppend(sourcePtr, dirEntPtr->d_name, -1); 00989 if (targetPtr != NULL) { 00990 Tcl_DStringAppend(targetPtr, dirEntPtr->d_name, -1); 00991 } 00992 result = TraverseUnixTree(traverseProc, sourcePtr, targetPtr, 00993 errorPtr, doRewind); 00994 if (result != TCL_OK) { 00995 break; 00996 } else { 00997 numProcessed++; 00998 } 00999 01000 /* 01001 * Remove name after slash. 01002 */ 01003 01004 Tcl_DStringSetLength(sourcePtr, sourceLen); 01005 if (targetPtr != NULL) { 01006 Tcl_DStringSetLength(targetPtr, targetLen); 01007 } 01008 if (doRewind && (numProcessed > MAX_READDIR_UNLINK_THRESHOLD)) { 01009 /* 01010 * Call rewinddir if we've called unlink or rmdir so many times 01011 * (since the opendir or the previous rewinddir), to avoid a 01012 * NULL-return that may a symptom of a buggy readdir. 01013 */ 01014 01015 rewinddir(dirPtr); 01016 numProcessed = 0; 01017 } 01018 } 01019 closedir(dirPtr); 01020 01021 /* 01022 * Strip off the trailing slash we added 01023 */ 01024 01025 Tcl_DStringSetLength(sourcePtr, sourceLen - 1); 01026 if (targetPtr != NULL) { 01027 Tcl_DStringSetLength(targetPtr, targetLen - 1); 01028 } 01029 01030 if (result == TCL_OK) { 01031 /* 01032 * Call traverseProc() on a directory after visiting all the files in 01033 * that directory. 01034 */ 01035 01036 result = (*traverseProc)(sourcePtr, targetPtr, &statBuf, DOTREE_POSTD, 01037 errorPtr); 01038 } 01039 #else /* HAVE_FTS */ 01040 paths[0] = source; 01041 fts = fts_open((char**)paths, FTS_PHYSICAL | FTS_NOCHDIR | 01042 (noFtsStat || doRewind ? FTS_NOSTAT : 0), NULL); 01043 if (fts == NULL) { 01044 errfile = source; 01045 goto end; 01046 } 01047 01048 sourceLen = Tcl_DStringLength(sourcePtr); 01049 if (targetPtr != NULL) { 01050 targetLen = Tcl_DStringLength(targetPtr); 01051 } 01052 01053 while ((ent = fts_read(fts)) != NULL) { 01054 unsigned short info = ent->fts_info; 01055 char *path = ent->fts_path + sourceLen; 01056 unsigned short pathlen = ent->fts_pathlen - sourceLen; 01057 int type; 01058 Tcl_StatBuf *statBufPtr = NULL; 01059 01060 if (info == FTS_DNR || info == FTS_ERR || info == FTS_NS) { 01061 errfile = ent->fts_path; 01062 break; 01063 } 01064 Tcl_DStringAppend(sourcePtr, path, pathlen); 01065 if (targetPtr != NULL) { 01066 Tcl_DStringAppend(targetPtr, path, pathlen); 01067 } 01068 switch (info) { 01069 case FTS_D: 01070 type = DOTREE_PRED; 01071 break; 01072 case FTS_DP: 01073 type = DOTREE_POSTD; 01074 break; 01075 default: 01076 type = DOTREE_F; 01077 break; 01078 } 01079 if (!doRewind) { /* no need to stat for delete */ 01080 if (noFtsStat) { 01081 statBufPtr = &statBuf; 01082 if (TclOSlstat(ent->fts_path, statBufPtr) != 0) { 01083 errfile = ent->fts_path; 01084 break; 01085 } 01086 } else { 01087 statBufPtr = (Tcl_StatBuf *) ent->fts_statp; 01088 } 01089 } 01090 result = (*traverseProc)(sourcePtr, targetPtr, statBufPtr, type, 01091 errorPtr); 01092 if (result != TCL_OK) { 01093 break; 01094 } 01095 Tcl_DStringSetLength(sourcePtr, sourceLen); 01096 if (targetPtr != NULL) { 01097 Tcl_DStringSetLength(targetPtr, targetLen); 01098 } 01099 } 01100 #endif /* HAVE_FTS */ 01101 01102 end: 01103 if (errfile != NULL) { 01104 if (errorPtr != NULL) { 01105 Tcl_ExternalToUtfDString(NULL, errfile, -1, errorPtr); 01106 } 01107 result = TCL_ERROR; 01108 } 01109 #ifdef HAVE_FTS 01110 if (fts != NULL) { 01111 fts_close(fts); 01112 } 01113 #endif 01114 01115 return result; 01116 } 01117 01118 /* 01119 *---------------------------------------------------------------------- 01120 * 01121 * TraversalCopy 01122 * 01123 * Called from TraverseUnixTree in order to execute a recursive copy of a 01124 * directory. 01125 * 01126 * Results: 01127 * Standard Tcl result. 01128 * 01129 * Side effects: 01130 * The file or directory src may be copied to dst, depending on the value 01131 * of type. 01132 * 01133 *---------------------------------------------------------------------- 01134 */ 01135 01136 static int 01137 TraversalCopy( 01138 Tcl_DString *srcPtr, /* Source pathname to copy (native). */ 01139 Tcl_DString *dstPtr, /* Destination pathname of copy (native). */ 01140 CONST Tcl_StatBuf *statBufPtr, 01141 /* Stat info for file specified by srcPtr. */ 01142 int type, /* Reason for call - see TraverseUnixTree(). */ 01143 Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free DString 01144 * filled with UTF-8 name of file causing 01145 * error. */ 01146 { 01147 switch (type) { 01148 case DOTREE_F: 01149 if (DoCopyFile(Tcl_DStringValue(srcPtr), Tcl_DStringValue(dstPtr), 01150 statBufPtr) == TCL_OK) { 01151 return TCL_OK; 01152 } 01153 break; 01154 01155 case DOTREE_PRED: 01156 if (DoCreateDirectory(Tcl_DStringValue(dstPtr)) == TCL_OK) { 01157 return TCL_OK; 01158 } 01159 break; 01160 01161 case DOTREE_POSTD: 01162 if (CopyFileAtts(Tcl_DStringValue(srcPtr), 01163 Tcl_DStringValue(dstPtr), statBufPtr) == TCL_OK) { 01164 return TCL_OK; 01165 } 01166 break; 01167 } 01168 01169 /* 01170 * There shouldn't be a problem with src, because we already checked it to 01171 * get here. 01172 */ 01173 01174 if (errorPtr != NULL) { 01175 Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(dstPtr), 01176 Tcl_DStringLength(dstPtr), errorPtr); 01177 } 01178 return TCL_ERROR; 01179 } 01180 01181 /* 01182 *--------------------------------------------------------------------------- 01183 * 01184 * TraversalDelete -- 01185 * 01186 * Called by procedure TraverseUnixTree for every file and directory that 01187 * it encounters in a directory hierarchy. This procedure unlinks files, 01188 * and removes directories after all the containing files have been 01189 * processed. 01190 * 01191 * Results: 01192 * Standard Tcl result. 01193 * 01194 * Side effects: 01195 * Files or directory specified by src will be deleted. 01196 * 01197 *---------------------------------------------------------------------- 01198 */ 01199 01200 static int 01201 TraversalDelete( 01202 Tcl_DString *srcPtr, /* Source pathname (native). */ 01203 Tcl_DString *ignore, /* Destination pathname (not used). */ 01204 CONST Tcl_StatBuf *statBufPtr, 01205 /* Stat info for file specified by srcPtr. */ 01206 int type, /* Reason for call - see TraverseUnixTree(). */ 01207 Tcl_DString *errorPtr) /* If non-NULL, uninitialized or free DString 01208 * filled with UTF-8 name of file causing 01209 * error. */ 01210 { 01211 switch (type) { 01212 case DOTREE_F: 01213 if (TclpDeleteFile(Tcl_DStringValue(srcPtr)) == 0) { 01214 return TCL_OK; 01215 } 01216 break; 01217 case DOTREE_PRED: 01218 return TCL_OK; 01219 case DOTREE_POSTD: 01220 if (DoRemoveDirectory(srcPtr, 0, NULL) == 0) { 01221 return TCL_OK; 01222 } 01223 break; 01224 } 01225 if (errorPtr != NULL) { 01226 Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(srcPtr), 01227 Tcl_DStringLength(srcPtr), errorPtr); 01228 } 01229 return TCL_ERROR; 01230 } 01231 01232 /* 01233 *--------------------------------------------------------------------------- 01234 * 01235 * CopyFileAtts -- 01236 * 01237 * Copy the file attributes such as owner, group, permissions, and 01238 * modification date from one file to another. 01239 * 01240 * Results: 01241 * Standard Tcl result. 01242 * 01243 * Side effects: 01244 * User id, group id, permission bits, last modification time, and last 01245 * access time are updated in the new file to reflect the old file. 01246 * 01247 *--------------------------------------------------------------------------- 01248 */ 01249 01250 static int 01251 CopyFileAtts( 01252 CONST char *src, /* Path name of source file (native). */ 01253 CONST char *dst, /* Path name of target file (native). */ 01254 CONST Tcl_StatBuf *statBufPtr) 01255 /* Stat info for source file */ 01256 { 01257 struct utimbuf tval; 01258 mode_t newMode; 01259 01260 newMode = statBufPtr->st_mode 01261 & (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO); 01262 01263 /* 01264 * Note that if you copy a setuid file that is owned by someone else, and 01265 * you are not root, then the copy will be setuid to you. The most correct 01266 * implementation would probably be to have the copy not setuid to anyone 01267 * if the original file was owned by someone else, but this corner case 01268 * isn't currently handled. It would require another lstat(), or getuid(). 01269 */ 01270 01271 if (chmod(dst, newMode)) { /* INTL: Native. */ 01272 newMode &= ~(S_ISUID | S_ISGID); 01273 if (chmod(dst, newMode)) { /* INTL: Native. */ 01274 return TCL_ERROR; 01275 } 01276 } 01277 01278 tval.actime = statBufPtr->st_atime; 01279 tval.modtime = statBufPtr->st_mtime; 01280 01281 if (utime(dst, &tval)) { /* INTL: Native. */ 01282 return TCL_ERROR; 01283 } 01284 #ifdef MAC_OSX_TCL 01285 TclMacOSXCopyFileAttributes(src, dst, statBufPtr); 01286 #endif 01287 return TCL_OK; 01288 } 01289 01290 /* 01291 *---------------------------------------------------------------------- 01292 * 01293 * GetGroupAttribute 01294 * 01295 * Gets the group attribute of a file. 01296 * 01297 * Results: 01298 * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there 01299 * is no error. 01300 * 01301 * Side effects: 01302 * A new object is allocated. 01303 * 01304 *---------------------------------------------------------------------- 01305 */ 01306 01307 static int 01308 GetGroupAttribute( 01309 Tcl_Interp *interp, /* The interp we are using for errors. */ 01310 int objIndex, /* The index of the attribute. */ 01311 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01312 Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ 01313 { 01314 Tcl_StatBuf statBuf; 01315 struct group *groupPtr; 01316 int result; 01317 01318 result = TclpObjStat(fileName, &statBuf); 01319 01320 if (result != 0) { 01321 if (interp != NULL) { 01322 Tcl_AppendResult(interp, "could not read \"", 01323 TclGetString(fileName), "\": ", 01324 Tcl_PosixError(interp), NULL); 01325 } 01326 return TCL_ERROR; 01327 } 01328 01329 groupPtr = TclpGetGrGid(statBuf.st_gid); 01330 01331 if (groupPtr == NULL) { 01332 *attributePtrPtr = Tcl_NewIntObj((int) statBuf.st_gid); 01333 } else { 01334 Tcl_DString ds; 01335 CONST char *utf; 01336 01337 utf = Tcl_ExternalToUtfDString(NULL, groupPtr->gr_name, -1, &ds); 01338 *attributePtrPtr = Tcl_NewStringObj(utf, -1); 01339 Tcl_DStringFree(&ds); 01340 } 01341 endgrent(); 01342 return TCL_OK; 01343 } 01344 01345 /* 01346 *---------------------------------------------------------------------- 01347 * 01348 * GetOwnerAttribute 01349 * 01350 * Gets the owner attribute of a file. 01351 * 01352 * Results: 01353 * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there 01354 * is no error. 01355 * 01356 * Side effects: 01357 * A new object is allocated. 01358 * 01359 *---------------------------------------------------------------------- 01360 */ 01361 01362 static int 01363 GetOwnerAttribute( 01364 Tcl_Interp *interp, /* The interp we are using for errors. */ 01365 int objIndex, /* The index of the attribute. */ 01366 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01367 Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ 01368 { 01369 Tcl_StatBuf statBuf; 01370 struct passwd *pwPtr; 01371 int result; 01372 01373 result = TclpObjStat(fileName, &statBuf); 01374 01375 if (result != 0) { 01376 if (interp != NULL) { 01377 Tcl_AppendResult(interp, "could not read \"", 01378 TclGetString(fileName), "\": ", 01379 Tcl_PosixError(interp), NULL); 01380 } 01381 return TCL_ERROR; 01382 } 01383 01384 pwPtr = TclpGetPwUid(statBuf.st_uid); 01385 01386 if (pwPtr == NULL) { 01387 *attributePtrPtr = Tcl_NewIntObj((int) statBuf.st_uid); 01388 } else { 01389 Tcl_DString ds; 01390 CONST char *utf; 01391 01392 utf = Tcl_ExternalToUtfDString(NULL, pwPtr->pw_name, -1, &ds); 01393 *attributePtrPtr = Tcl_NewStringObj(utf, Tcl_DStringLength(&ds)); 01394 Tcl_DStringFree(&ds); 01395 } 01396 endpwent(); 01397 return TCL_OK; 01398 } 01399 01400 /* 01401 *---------------------------------------------------------------------- 01402 * 01403 * GetPermissionsAttribute 01404 * 01405 * Gets the group attribute of a file. 01406 * 01407 * Results: 01408 * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there 01409 * is no error. The object will have ref count 0. 01410 * 01411 * Side effects: 01412 * A new object is allocated. 01413 * 01414 *---------------------------------------------------------------------- 01415 */ 01416 01417 static int 01418 GetPermissionsAttribute( 01419 Tcl_Interp *interp, /* The interp we are using for errors. */ 01420 int objIndex, /* The index of the attribute. */ 01421 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01422 Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ 01423 { 01424 Tcl_StatBuf statBuf; 01425 int result; 01426 01427 result = TclpObjStat(fileName, &statBuf); 01428 01429 if (result != 0) { 01430 if (interp != NULL) { 01431 Tcl_AppendResult(interp, "could not read \"", 01432 TclGetString(fileName), "\": ", 01433 Tcl_PosixError(interp), NULL); 01434 } 01435 return TCL_ERROR; 01436 } 01437 01438 *attributePtrPtr = Tcl_ObjPrintf( 01439 "%0#5lo", (long) (statBuf.st_mode & 0x00007FFF)); 01440 return TCL_OK; 01441 } 01442 01443 /* 01444 *--------------------------------------------------------------------------- 01445 * 01446 * SetGroupAttribute -- 01447 * 01448 * Sets the group of the file to the specified group. 01449 * 01450 * Results: 01451 * Standard TCL result. 01452 * 01453 * Side effects: 01454 * As above. 01455 * 01456 *--------------------------------------------------------------------------- 01457 */ 01458 01459 static int 01460 SetGroupAttribute( 01461 Tcl_Interp *interp, /* The interp for error reporting. */ 01462 int objIndex, /* The index of the attribute. */ 01463 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01464 Tcl_Obj *attributePtr) /* New group for file. */ 01465 { 01466 long gid; 01467 int result; 01468 CONST char *native; 01469 01470 if (Tcl_GetLongFromObj(NULL, attributePtr, &gid) != TCL_OK) { 01471 Tcl_DString ds; 01472 struct group *groupPtr = NULL; 01473 CONST char *string; 01474 int length; 01475 01476 string = Tcl_GetStringFromObj(attributePtr, &length); 01477 01478 native = Tcl_UtfToExternalDString(NULL, string, length, &ds); 01479 groupPtr = TclpGetGrNam(native); /* INTL: Native. */ 01480 Tcl_DStringFree(&ds); 01481 01482 if (groupPtr == NULL) { 01483 endgrent(); 01484 if (interp != NULL) { 01485 Tcl_AppendResult(interp, "could not set group for file \"", 01486 TclGetString(fileName), "\": group \"", string, 01487 "\" does not exist", NULL); 01488 } 01489 return TCL_ERROR; 01490 } 01491 gid = groupPtr->gr_gid; 01492 } 01493 01494 native = Tcl_FSGetNativePath(fileName); 01495 result = chown(native, (uid_t) -1, (gid_t) gid); /* INTL: Native. */ 01496 01497 endgrent(); 01498 if (result != 0) { 01499 if (interp != NULL) { 01500 Tcl_AppendResult(interp, "could not set group for file \"", 01501 TclGetString(fileName), "\": ", Tcl_PosixError(interp), 01502 NULL); 01503 } 01504 return TCL_ERROR; 01505 } 01506 return TCL_OK; 01507 } 01508 01509 /* 01510 *--------------------------------------------------------------------------- 01511 * 01512 * SetOwnerAttribute -- 01513 * 01514 * Sets the owner of the file to the specified owner. 01515 * 01516 * Results: 01517 * Standard TCL result. 01518 * 01519 * Side effects: 01520 * As above. 01521 * 01522 *--------------------------------------------------------------------------- 01523 */ 01524 01525 static int 01526 SetOwnerAttribute( 01527 Tcl_Interp *interp, /* The interp for error reporting. */ 01528 int objIndex, /* The index of the attribute. */ 01529 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01530 Tcl_Obj *attributePtr) /* New owner for file. */ 01531 { 01532 long uid; 01533 int result; 01534 CONST char *native; 01535 01536 if (Tcl_GetLongFromObj(NULL, attributePtr, &uid) != TCL_OK) { 01537 Tcl_DString ds; 01538 struct passwd *pwPtr = NULL; 01539 CONST char *string; 01540 int length; 01541 01542 string = Tcl_GetStringFromObj(attributePtr, &length); 01543 01544 native = Tcl_UtfToExternalDString(NULL, string, length, &ds); 01545 pwPtr = TclpGetPwNam(native); /* INTL: Native. */ 01546 Tcl_DStringFree(&ds); 01547 01548 if (pwPtr == NULL) { 01549 if (interp != NULL) { 01550 Tcl_AppendResult(interp, "could not set owner for file \"", 01551 TclGetString(fileName), "\": user \"", string, 01552 "\" does not exist", NULL); 01553 } 01554 return TCL_ERROR; 01555 } 01556 uid = pwPtr->pw_uid; 01557 } 01558 01559 native = Tcl_FSGetNativePath(fileName); 01560 result = chown(native, (uid_t) uid, (gid_t) -1); /* INTL: Native. */ 01561 01562 if (result != 0) { 01563 if (interp != NULL) { 01564 Tcl_AppendResult(interp, "could not set owner for file \"", 01565 TclGetString(fileName), "\": ", Tcl_PosixError(interp), 01566 NULL); 01567 } 01568 return TCL_ERROR; 01569 } 01570 return TCL_OK; 01571 } 01572 01573 /* 01574 *--------------------------------------------------------------------------- 01575 * 01576 * SetPermissionsAttribute 01577 * 01578 * Sets the file to the given permission. 01579 * 01580 * Results: 01581 * Standard TCL result. 01582 * 01583 * Side effects: 01584 * The permission of the file is changed. 01585 * 01586 *--------------------------------------------------------------------------- 01587 */ 01588 01589 static int 01590 SetPermissionsAttribute( 01591 Tcl_Interp *interp, /* The interp we are using for errors. */ 01592 int objIndex, /* The index of the attribute. */ 01593 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 01594 Tcl_Obj *attributePtr) /* The attribute to set. */ 01595 { 01596 long mode; 01597 mode_t newMode; 01598 int result = TCL_ERROR; 01599 CONST char *native; 01600 char *modeStringPtr = TclGetString(attributePtr); 01601 int scanned = TclParseAllWhiteSpace(modeStringPtr, -1); 01602 01603 /* 01604 * First supply support for octal number format 01605 */ 01606 01607 if ((modeStringPtr[scanned] == '0') 01608 && (modeStringPtr[scanned+1] >= '0') 01609 && (modeStringPtr[scanned+1] <= '7')) { 01610 /* Leading zero - attempt octal interpretation */ 01611 Tcl_Obj *modeObj; 01612 01613 TclNewLiteralStringObj(modeObj, "0o"); 01614 Tcl_AppendToObj(modeObj, modeStringPtr+scanned+1, -1); 01615 result = Tcl_GetLongFromObj(NULL, modeObj, &mode); 01616 Tcl_DecrRefCount(modeObj); 01617 } 01618 if (result == TCL_OK 01619 || Tcl_GetLongFromObj(NULL, attributePtr, &mode) == TCL_OK) { 01620 newMode = (mode_t) (mode & 0x00007FFF); 01621 } else { 01622 Tcl_StatBuf buf; 01623 01624 /* 01625 * Try the forms "rwxrwxrwx" and "ugo=rwx" 01626 * 01627 * We get the current mode of the file, in order to allow for ug+-=rwx 01628 * style chmod strings. 01629 */ 01630 01631 result = TclpObjStat(fileName, &buf); 01632 if (result != 0) { 01633 if (interp != NULL) { 01634 Tcl_AppendResult(interp, "could not read \"", 01635 TclGetString(fileName), "\": ", 01636 Tcl_PosixError(interp), NULL); 01637 } 01638 return TCL_ERROR; 01639 } 01640 newMode = (mode_t) (buf.st_mode & 0x00007FFF); 01641 01642 if (GetModeFromPermString(NULL, modeStringPtr, &newMode) != TCL_OK) { 01643 if (interp != NULL) { 01644 Tcl_AppendResult(interp, "unknown permission string format \"", 01645 modeStringPtr, "\"", NULL); 01646 } 01647 return TCL_ERROR; 01648 } 01649 } 01650 01651 native = Tcl_FSGetNativePath(fileName); 01652 result = chmod(native, newMode); /* INTL: Native. */ 01653 if (result != 0) { 01654 if (interp != NULL) { 01655 Tcl_AppendResult(interp, "could not set permissions for file \"", 01656 TclGetString(fileName), "\": ", 01657 Tcl_PosixError(interp), NULL); 01658 } 01659 return TCL_ERROR; 01660 } 01661 return TCL_OK; 01662 } 01663 01664 #ifndef DJGPP 01665 /* 01666 *--------------------------------------------------------------------------- 01667 * 01668 * TclpObjListVolumes -- 01669 * 01670 * Lists the currently mounted volumes, which on UNIX is just /. 01671 * 01672 * Results: 01673 * The list of volumes. 01674 * 01675 * Side effects: 01676 * None. 01677 * 01678 *--------------------------------------------------------------------------- 01679 */ 01680 01681 Tcl_Obj * 01682 TclpObjListVolumes(void) 01683 { 01684 Tcl_Obj *resultPtr = Tcl_NewStringObj("/", 1); 01685 01686 Tcl_IncrRefCount(resultPtr); 01687 return resultPtr; 01688 } 01689 #endif 01690 01691 /* 01692 *---------------------------------------------------------------------- 01693 * 01694 * GetModeFromPermString -- 01695 * 01696 * This procedure is invoked to process the "file permissions" Tcl 01697 * command, to check for a "rwxrwxrwx" or "ugoa+-=rwxst" string. See the 01698 * user documentation for details on what it does. 01699 * 01700 * Results: 01701 * A standard Tcl result. 01702 * 01703 * Side effects: 01704 * See the user documentation. 01705 * 01706 *---------------------------------------------------------------------- 01707 */ 01708 01709 static int 01710 GetModeFromPermString( 01711 Tcl_Interp *interp, /* The interp we are using for errors. */ 01712 char *modeStringPtr, /* Permissions string */ 01713 mode_t *modePtr) /* pointer to the mode value */ 01714 { 01715 mode_t newMode; 01716 mode_t oldMode; /* Storage for the value of the old mode (that 01717 * is passed in), to allow for the chmod style 01718 * manipulation. */ 01719 int i,n, who, op, what, op_found, who_found; 01720 01721 /* 01722 * We start off checking for an "rwxrwxrwx" style permissions string 01723 */ 01724 01725 if (strlen(modeStringPtr) != 9) { 01726 goto chmodStyleCheck; 01727 } 01728 01729 newMode = 0; 01730 for (i = 0; i < 9; i++) { 01731 switch (*(modeStringPtr+i)) { 01732 case 'r': 01733 if ((i%3) != 0) { 01734 goto chmodStyleCheck; 01735 } 01736 newMode |= (1<<(8-i)); 01737 break; 01738 case 'w': 01739 if ((i%3) != 1) { 01740 goto chmodStyleCheck; 01741 } 01742 newMode |= (1<<(8-i)); 01743 break; 01744 case 'x': 01745 if ((i%3) != 2) { 01746 goto chmodStyleCheck; 01747 } 01748 newMode |= (1<<(8-i)); 01749 break; 01750 case 's': 01751 if (((i%3) != 2) || (i > 5)) { 01752 goto chmodStyleCheck; 01753 } 01754 newMode |= (1<<(8-i)); 01755 newMode |= (1<<(11-(i/3))); 01756 break; 01757 case 'S': 01758 if (((i%3) != 2) || (i > 5)) { 01759 goto chmodStyleCheck; 01760 } 01761 newMode |= (1<<(11-(i/3))); 01762 break; 01763 case 't': 01764 if (i != 8) { 01765 goto chmodStyleCheck; 01766 } 01767 newMode |= (1<<(8-i)); 01768 newMode |= (1<<9); 01769 break; 01770 case 'T': 01771 if (i != 8) { 01772 goto chmodStyleCheck; 01773 } 01774 newMode |= (1<<9); 01775 break; 01776 case '-': 01777 break; 01778 default: 01779 /* 01780 * Oops, not what we thought it was, so go on 01781 */ 01782 goto chmodStyleCheck; 01783 } 01784 } 01785 *modePtr = newMode; 01786 return TCL_OK; 01787 01788 chmodStyleCheck: 01789 /* 01790 * We now check for an "ugoa+-=rwxst" style permissions string 01791 */ 01792 01793 for (n = 0 ; *(modeStringPtr+n) != '\0' ; n = n + i) { 01794 oldMode = *modePtr; 01795 who = op = what = op_found = who_found = 0; 01796 for (i = 0 ; *(modeStringPtr+n+i) != '\0' ; i++ ) { 01797 if (!who_found) { 01798 /* who */ 01799 switch (*(modeStringPtr+n+i)) { 01800 case 'u': 01801 who |= 0x9c0; 01802 continue; 01803 case 'g': 01804 who |= 0x438; 01805 continue; 01806 case 'o': 01807 who |= 0x207; 01808 continue; 01809 case 'a': 01810 who |= 0xfff; 01811 continue; 01812 } 01813 } 01814 who_found = 1; 01815 if (who == 0) { 01816 who = 0xfff; 01817 } 01818 if (!op_found) { 01819 /* op */ 01820 switch (*(modeStringPtr+n+i)) { 01821 case '+': 01822 op = 1; 01823 op_found = 1; 01824 continue; 01825 case '-': 01826 op = 2; 01827 op_found = 1; 01828 continue; 01829 case '=': 01830 op = 3; 01831 op_found = 1; 01832 continue; 01833 default: 01834 return TCL_ERROR; 01835 } 01836 } 01837 /* what */ 01838 switch (*(modeStringPtr+n+i)) { 01839 case 'r': 01840 what |= 0x124; 01841 continue; 01842 case 'w': 01843 what |= 0x92; 01844 continue; 01845 case 'x': 01846 what |= 0x49; 01847 continue; 01848 case 's': 01849 what |= 0xc00; 01850 continue; 01851 case 't': 01852 what |= 0x200; 01853 continue; 01854 case ',': 01855 break; 01856 default: 01857 return TCL_ERROR; 01858 } 01859 if (*(modeStringPtr+n+i) == ',') { 01860 i++; 01861 break; 01862 } 01863 } 01864 switch (op) { 01865 case 1: 01866 *modePtr = oldMode | (who & what); 01867 continue; 01868 case 2: 01869 *modePtr = oldMode & ~(who & what); 01870 continue; 01871 case 3: 01872 *modePtr = (oldMode & ~who) | (who & what); 01873 continue; 01874 } 01875 } 01876 return TCL_OK; 01877 } 01878 01879 /* 01880 *--------------------------------------------------------------------------- 01881 * 01882 * TclpObjNormalizePath -- 01883 * 01884 * This function scans through a path specification and replaces it, in 01885 * place, with a normalized version. A normalized version is one in which 01886 * all symlinks in the path are replaced with their expanded form (except 01887 * a symlink at the very end of the path). 01888 * 01889 * Results: 01890 * The new 'nextCheckpoint' value, giving as far as we could understand 01891 * in the path. 01892 * 01893 * Side effects: 01894 * The pathPtr string, is modified. 01895 * 01896 *--------------------------------------------------------------------------- 01897 */ 01898 01899 int 01900 TclpObjNormalizePath( 01901 Tcl_Interp *interp, 01902 Tcl_Obj *pathPtr, 01903 int nextCheckpoint) 01904 { 01905 char *currentPathEndPosition; 01906 int pathLen; 01907 char cur; 01908 char *path = Tcl_GetStringFromObj(pathPtr, &pathLen); 01909 #ifndef NO_REALPATH 01910 char normPath[MAXPATHLEN]; 01911 Tcl_DString ds; 01912 CONST char *nativePath; 01913 #endif 01914 01915 /* 01916 * We add '1' here because if nextCheckpoint is zero we know that '/' 01917 * exists, and if it isn't zero, it must point at a directory separator 01918 * which we also know exists. 01919 */ 01920 01921 currentPathEndPosition = path + nextCheckpoint; 01922 if (*currentPathEndPosition == '/') { 01923 currentPathEndPosition++; 01924 } 01925 01926 #ifndef NO_REALPATH 01927 /* 01928 * For speed, try to get the entire path in one go. 01929 */ 01930 01931 if (nextCheckpoint == 0 && haveRealpath) { 01932 char *lastDir = strrchr(currentPathEndPosition, '/'); 01933 01934 if (lastDir != NULL) { 01935 nativePath = Tcl_UtfToExternalDString(NULL, path, 01936 lastDir-path, &ds); 01937 if (Realpath(nativePath, normPath) != NULL) { 01938 if (*nativePath != '/' && *normPath == '/') { 01939 /* 01940 * realpath has transformed a relative path into an 01941 * absolute path, we do not know how to handle this. 01942 */ 01943 } else { 01944 nextCheckpoint = lastDir - path; 01945 goto wholeStringOk; 01946 } 01947 } 01948 Tcl_DStringFree(&ds); 01949 } 01950 } 01951 01952 /* 01953 * Else do it the slow way. 01954 */ 01955 #endif 01956 01957 while (1) { 01958 cur = *currentPathEndPosition; 01959 if ((cur == '/') && (path != currentPathEndPosition)) { 01960 /* 01961 * Reached directory separator. 01962 */ 01963 01964 Tcl_DString ds; 01965 CONST char *nativePath; 01966 int accessOk; 01967 01968 nativePath = Tcl_UtfToExternalDString(NULL, path, 01969 currentPathEndPosition - path, &ds); 01970 accessOk = access(nativePath, F_OK); 01971 Tcl_DStringFree(&ds); 01972 01973 if (accessOk != 0) { 01974 /* 01975 * File doesn't exist. 01976 */ 01977 01978 break; 01979 } 01980 01981 /* 01982 * Update the acceptable point. 01983 */ 01984 01985 nextCheckpoint = currentPathEndPosition - path; 01986 } else if (cur == 0) { 01987 /* 01988 * Reached end of string. 01989 */ 01990 01991 break; 01992 } 01993 currentPathEndPosition++; 01994 } 01995 01996 /* 01997 * We should really now convert this to a canonical path. We do that with 01998 * 'realpath' if we have it available. Otherwise we could step through 01999 * every single path component, checking whether it is a symlink, but that 02000 * would be a lot of work, and most modern OSes have 'realpath'. 02001 */ 02002 02003 #ifndef NO_REALPATH 02004 if (haveRealpath) { 02005 /* 02006 * If we only had '/foo' or '/' then we never increment nextCheckpoint 02007 * and we don't need or want to go through 'Realpath'. Also, on some 02008 * platforms, passing an empty string to 'Realpath' will give us the 02009 * normalized pwd, which is not what we want at all! 02010 */ 02011 02012 if (nextCheckpoint == 0) { 02013 return 0; 02014 } 02015 02016 nativePath = Tcl_UtfToExternalDString(NULL, path, nextCheckpoint, &ds); 02017 if (Realpath(nativePath, normPath) != NULL) { 02018 int newNormLen; 02019 02020 wholeStringOk: 02021 newNormLen = strlen(normPath); 02022 if ((newNormLen == Tcl_DStringLength(&ds)) 02023 && (strcmp(normPath, nativePath) == 0)) { 02024 /* 02025 * String is unchanged. 02026 */ 02027 02028 Tcl_DStringFree(&ds); 02029 02030 /* 02031 * Enable this to have the native FS claim normalization of 02032 * the whole path for existing files. That would permit the 02033 * caller to declare normalization complete without calls to 02034 * additional filesystems. Saving lots of calls is probably 02035 * worth the extra access() time here. When no other FS's are 02036 * registered though, things are less clear. 02037 * 02038 if (0 == access(normPath, F_OK)) { 02039 return pathLen; 02040 } 02041 */ 02042 02043 return nextCheckpoint; 02044 } 02045 02046 /* 02047 * Free up the native path and put in its place the converted, 02048 * normalized path. 02049 */ 02050 02051 Tcl_DStringFree(&ds); 02052 Tcl_ExternalToUtfDString(NULL, normPath, (int) newNormLen, &ds); 02053 02054 if (path[nextCheckpoint] != '\0') { 02055 /* 02056 * Not at end, append remaining path. 02057 */ 02058 02059 int normLen = Tcl_DStringLength(&ds); 02060 02061 Tcl_DStringAppend(&ds, path + nextCheckpoint, 02062 pathLen - nextCheckpoint); 02063 02064 /* 02065 * We recognise up to and including the directory separator. 02066 */ 02067 02068 nextCheckpoint = normLen + 1; 02069 } else { 02070 /* 02071 * We recognise the whole string. 02072 */ 02073 02074 nextCheckpoint = Tcl_DStringLength(&ds); 02075 } 02076 02077 /* 02078 * Overwrite with the normalized path. 02079 */ 02080 02081 Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds), 02082 Tcl_DStringLength(&ds)); 02083 } 02084 Tcl_DStringFree(&ds); 02085 } 02086 #endif /* !NO_REALPATH */ 02087 02088 return nextCheckpoint; 02089 } 02090 02091 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) 02092 /* 02093 *---------------------------------------------------------------------- 02094 * 02095 * GetReadOnlyAttribute 02096 * 02097 * Gets the readonly attribute (user immutable flag) of a file. 02098 * 02099 * Results: 02100 * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there 02101 * is no error. The object will have ref count 0. 02102 * 02103 * Side effects: 02104 * A new object is allocated. 02105 * 02106 *---------------------------------------------------------------------- 02107 */ 02108 02109 static int 02110 GetReadOnlyAttribute( 02111 Tcl_Interp *interp, /* The interp we are using for errors. */ 02112 int objIndex, /* The index of the attribute. */ 02113 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 02114 Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */ 02115 { 02116 Tcl_StatBuf statBuf; 02117 int result; 02118 02119 result = TclpObjStat(fileName, &statBuf); 02120 02121 if (result != 0) { 02122 if (interp != NULL) { 02123 Tcl_AppendResult(interp, "could not read \"", 02124 TclGetString(fileName), "\": ", Tcl_PosixError(interp), 02125 NULL); 02126 } 02127 return TCL_ERROR; 02128 } 02129 02130 *attributePtrPtr = Tcl_NewBooleanObj((statBuf.st_flags&UF_IMMUTABLE) != 0); 02131 02132 return TCL_OK; 02133 } 02134 02135 /* 02136 *--------------------------------------------------------------------------- 02137 * 02138 * SetReadOnlyAttribute 02139 * 02140 * Sets the readonly attribute (user immutable flag) of a file. 02141 * 02142 * Results: 02143 * Standard TCL result. 02144 * 02145 * Side effects: 02146 * The readonly attribute of the file is changed. 02147 * 02148 *--------------------------------------------------------------------------- 02149 */ 02150 02151 static int 02152 SetReadOnlyAttribute( 02153 Tcl_Interp *interp, /* The interp we are using for errors. */ 02154 int objIndex, /* The index of the attribute. */ 02155 Tcl_Obj *fileName, /* The name of the file (UTF-8). */ 02156 Tcl_Obj *attributePtr) /* The attribute to set. */ 02157 { 02158 Tcl_StatBuf statBuf; 02159 int result; 02160 int readonly; 02161 CONST char *native; 02162 02163 if (Tcl_GetBooleanFromObj(interp, attributePtr, &readonly) != TCL_OK) { 02164 return TCL_ERROR; 02165 } 02166 02167 result = TclpObjStat(fileName, &statBuf); 02168 02169 if (result != 0) { 02170 if (interp != NULL) { 02171 Tcl_AppendResult(interp, "could not read \"", 02172 TclGetString(fileName), "\": ", Tcl_PosixError(interp), 02173 NULL); 02174 } 02175 return TCL_ERROR; 02176 } 02177 02178 if (readonly) { 02179 statBuf.st_flags |= UF_IMMUTABLE; 02180 } else { 02181 statBuf.st_flags &= ~UF_IMMUTABLE; 02182 } 02183 02184 native = Tcl_FSGetNativePath(fileName); 02185 result = chflags(native, statBuf.st_flags); /* INTL: Native. */ 02186 if (result != 0) { 02187 if (interp != NULL) { 02188 Tcl_AppendResult(interp, "could not set flags for file \"", 02189 TclGetString(fileName), "\": ", Tcl_PosixError(interp), 02190 NULL); 02191 } 02192 return TCL_ERROR; 02193 } 02194 return TCL_OK; 02195 } 02196 #endif /* defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE) */ 02197 02198 /* 02199 * Local Variables: 02200 * mode: c 02201 * c-basic-offset: 4 02202 * fill-column: 78 02203 * End: 02204 */
Generated on Wed Mar 12 12:18:26 2008 by 1.5.1 |