tclIOUtil.cGo to the documentation of this file.00001 /* 00002 * tclIOUtil.c -- 00003 * 00004 * This file contains the implementation of Tcl's generic filesystem 00005 * code, which supports a pluggable filesystem architecture allowing both 00006 * platform specific filesystems and 'virtual filesystems'. All 00007 * filesystem access should go through the functions defined in this 00008 * file. Most of this code was contributed by Vince Darley. 00009 * 00010 * Parts of this file are based on code contributed by Karl Lehenbauer, 00011 * Mark Diekhans and Peter da Silva. 00012 * 00013 * Copyright (c) 1991-1994 The Regents of the University of California. 00014 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 00015 * Copyright (c) 2001-2004 Vincent Darley. 00016 * 00017 * See the file "license.terms" for information on usage and redistribution of 00018 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00019 * 00020 * RCS: @(#) $Id: tclIOUtil.c,v 1.150 2008/01/23 21:21:31 dgp Exp $ 00021 */ 00022 00023 #include "tclInt.h" 00024 #ifdef __WIN32__ 00025 # include "tclWinInt.h" 00026 #endif 00027 #include "tclFileSystem.h" 00028 00029 /* 00030 * Prototypes for functions defined later in this file. 00031 */ 00032 00033 static FilesystemRecord*FsGetFirstFilesystem(void); 00034 static void FsThrExitProc(ClientData cd); 00035 static Tcl_Obj * FsListMounts(Tcl_Obj *pathPtr, const char *pattern); 00036 static void FsAddMountsToGlobResult(Tcl_Obj *resultPtr, 00037 Tcl_Obj *pathPtr, const char *pattern, 00038 Tcl_GlobTypeData *types); 00039 static void FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData); 00040 00041 #ifdef TCL_THREADS 00042 static void FsRecacheFilesystemList(void); 00043 #endif 00044 00045 /* 00046 * These form part of the native filesystem support. They are needed here 00047 * because we have a few native filesystem functions (which are the same for 00048 * win/unix) in this file. There is no need to place them in tclInt.h, because 00049 * they are not (and should not be) used anywhere else. 00050 */ 00051 00052 MODULE_SCOPE const char * tclpFileAttrStrings[]; 00053 MODULE_SCOPE const TclFileAttrProcs tclpFileAttrProcs[]; 00054 00055 /* 00056 * The following functions are obsolete string based APIs, and should be 00057 * removed in a future release (Tcl 9 would be a good time). 00058 */ 00059 00060 00061 /* Obsolete */ 00062 int 00063 Tcl_Stat( 00064 const char *path, /* Path of file to stat (in current CP). */ 00065 struct stat *oldStyleBuf) /* Filled with results of stat call. */ 00066 { 00067 int ret; 00068 Tcl_StatBuf buf; 00069 Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); 00070 00071 #ifndef TCL_WIDE_INT_IS_LONG 00072 Tcl_WideInt tmp1, tmp2; 00073 #ifdef HAVE_ST_BLOCKS 00074 Tcl_WideInt tmp3; 00075 #endif 00076 #endif 00077 00078 Tcl_IncrRefCount(pathPtr); 00079 ret = Tcl_FSStat(pathPtr, &buf); 00080 Tcl_DecrRefCount(pathPtr); 00081 if (ret != -1) { 00082 #ifndef TCL_WIDE_INT_IS_LONG 00083 # define OUT_OF_RANGE(x) \ 00084 (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || \ 00085 ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX)) 00086 # define OUT_OF_URANGE(x) \ 00087 (((Tcl_WideUInt)(x)) > ((Tcl_WideUInt)ULONG_MAX)) 00088 00089 /* 00090 * Perform the result-buffer overflow check manually. 00091 * 00092 * Note that ino_t/ino64_t is unsigned... 00093 * 00094 * Workaround gcc warning of "comparison is always false due to limited range of 00095 * data type" by assigning to tmp var of type Tcl_WideInt. 00096 */ 00097 00098 tmp1 = (Tcl_WideInt) buf.st_ino; 00099 tmp2 = (Tcl_WideInt) buf.st_size; 00100 #ifdef HAVE_ST_BLOCKS 00101 tmp3 = (Tcl_WideInt) buf.st_blocks; 00102 #endif 00103 00104 if (OUT_OF_URANGE(tmp1) || OUT_OF_RANGE(tmp2) 00105 #ifdef HAVE_ST_BLOCKS 00106 || OUT_OF_RANGE(tmp3) 00107 #endif 00108 ) { 00109 #ifdef EFBIG 00110 errno = EFBIG; 00111 #else 00112 # ifdef EOVERFLOW 00113 errno = EOVERFLOW; 00114 # else 00115 # error "What status should be returned for file size out of range?" 00116 # endif 00117 #endif 00118 return -1; 00119 } 00120 00121 # undef OUT_OF_RANGE 00122 # undef OUT_OF_URANGE 00123 #endif /* !TCL_WIDE_INT_IS_LONG */ 00124 00125 /* 00126 * Copy across all supported fields, with possible type coercions on 00127 * those fields that change between the normal and lf64 versions of 00128 * the stat structure (on Solaris at least). This is slow when the 00129 * structure sizes coincide, but that's what you get for using an 00130 * obsolete interface. 00131 */ 00132 00133 oldStyleBuf->st_mode = buf.st_mode; 00134 oldStyleBuf->st_ino = (ino_t) buf.st_ino; 00135 oldStyleBuf->st_dev = buf.st_dev; 00136 oldStyleBuf->st_rdev = buf.st_rdev; 00137 oldStyleBuf->st_nlink = buf.st_nlink; 00138 oldStyleBuf->st_uid = buf.st_uid; 00139 oldStyleBuf->st_gid = buf.st_gid; 00140 oldStyleBuf->st_size = (off_t) buf.st_size; 00141 oldStyleBuf->st_atime = buf.st_atime; 00142 oldStyleBuf->st_mtime = buf.st_mtime; 00143 oldStyleBuf->st_ctime = buf.st_ctime; 00144 #ifdef HAVE_ST_BLOCKS 00145 oldStyleBuf->st_blksize = buf.st_blksize; 00146 oldStyleBuf->st_blocks = (blkcnt_t) buf.st_blocks; 00147 #endif 00148 } 00149 return ret; 00150 } 00151 00152 /* Obsolete */ 00153 int 00154 Tcl_Access( 00155 const char *path, /* Path of file to access (in current CP). */ 00156 int mode) /* Permission setting. */ 00157 { 00158 int ret; 00159 Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); 00160 00161 Tcl_IncrRefCount(pathPtr); 00162 ret = Tcl_FSAccess(pathPtr,mode); 00163 Tcl_DecrRefCount(pathPtr); 00164 00165 return ret; 00166 } 00167 00168 /* Obsolete */ 00169 Tcl_Channel 00170 Tcl_OpenFileChannel( 00171 Tcl_Interp *interp, /* Interpreter for error reporting; can be 00172 * NULL. */ 00173 const char *path, /* Name of file to open. */ 00174 const char *modeString, /* A list of POSIX open modes or a string such 00175 * as "rw". */ 00176 int permissions) /* If the open involves creating a file, with 00177 * what modes to create it? */ 00178 { 00179 Tcl_Channel ret; 00180 Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1); 00181 00182 Tcl_IncrRefCount(pathPtr); 00183 ret = Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions); 00184 Tcl_DecrRefCount(pathPtr); 00185 00186 return ret; 00187 } 00188 00189 /* Obsolete */ 00190 int 00191 Tcl_Chdir( 00192 const char *dirName) 00193 { 00194 int ret; 00195 Tcl_Obj *pathPtr = Tcl_NewStringObj(dirName,-1); 00196 Tcl_IncrRefCount(pathPtr); 00197 ret = Tcl_FSChdir(pathPtr); 00198 Tcl_DecrRefCount(pathPtr); 00199 return ret; 00200 } 00201 00202 /* Obsolete */ 00203 char * 00204 Tcl_GetCwd( 00205 Tcl_Interp *interp, 00206 Tcl_DString *cwdPtr) 00207 { 00208 Tcl_Obj *cwd; 00209 cwd = Tcl_FSGetCwd(interp); 00210 if (cwd == NULL) { 00211 return NULL; 00212 } else { 00213 Tcl_DStringInit(cwdPtr); 00214 Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1); 00215 Tcl_DecrRefCount(cwd); 00216 return Tcl_DStringValue(cwdPtr); 00217 } 00218 } 00219 00220 /* Obsolete */ 00221 int 00222 Tcl_EvalFile( 00223 Tcl_Interp *interp, /* Interpreter in which to process file. */ 00224 const char *fileName) /* Name of file to process. Tilde-substitution 00225 * will be performed on this name. */ 00226 { 00227 int ret; 00228 Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1); 00229 Tcl_IncrRefCount(pathPtr); 00230 ret = Tcl_FSEvalFile(interp, pathPtr); 00231 Tcl_DecrRefCount(pathPtr); 00232 return ret; 00233 } 00234 00235 /* 00236 * The 3 hooks for Stat, Access and OpenFileChannel are obsolete. The 00237 * complete, general hooked filesystem APIs should be used instead. This 00238 * define decides whether to include the obsolete hooks and related code. If 00239 * these are removed, we'll also want to remove them from stubs/tclInt. The 00240 * only known users of these APIs are prowrap and mktclapp. New 00241 * code/extensions should not use them, since they do not provide as full 00242 * support as the full filesystem API. 00243 * 00244 * As soon as prowrap and mktclapp are updated to use the full filesystem 00245 * support, I suggest all these hooks are removed. 00246 */ 00247 00248 #undef USE_OBSOLETE_FS_HOOKS 00249 00250 #ifdef USE_OBSOLETE_FS_HOOKS 00251 00252 /* 00253 * The following typedef declarations allow for hooking into the chain of 00254 * functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' & 00255 * 'Tcl_OpenFileChannel(...)'. Basically for each hookable function a linked 00256 * list is defined. 00257 */ 00258 00259 typedef struct StatProc { 00260 TclStatProc_ *proc; /* Function to process a 'stat()' call */ 00261 struct StatProc *nextPtr; /* The next 'stat()' function to call */ 00262 } StatProc; 00263 00264 typedef struct AccessProc { 00265 TclAccessProc_ *proc; /* Function to process a 'access()' call */ 00266 struct AccessProc *nextPtr; /* The next 'access()' function to call */ 00267 } AccessProc; 00268 00269 typedef struct OpenFileChannelProc { 00270 TclOpenFileChannelProc_ *proc; 00271 /* Function to process a 00272 * 'Tcl_OpenFileChannel()' call */ 00273 struct OpenFileChannelProc *nextPtr; 00274 /* The next 'Tcl_OpenFileChannel()' function 00275 * to call */ 00276 } OpenFileChannelProc; 00277 00278 /* 00279 * For each type of (obsolete) hookable function, a static node is declared to 00280 * hold the function pointer for the "built-in" routine (e.g. 'TclpStat(...)') 00281 * and the respective list is initialized as a pointer to that node. 00282 * 00283 * The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that these 00284 * statically declared list entry cannot be inadvertently removed. 00285 * 00286 * This method avoids the need to call any sort of "initialization" function. 00287 * 00288 * All three lists are protected by a global obsoleteFsHookMutex. 00289 */ 00290 00291 static StatProc *statProcList = NULL; 00292 static AccessProc *accessProcList = NULL; 00293 static OpenFileChannelProc *openFileChannelProcList = NULL; 00294 00295 TCL_DECLARE_MUTEX(obsoleteFsHookMutex) 00296 00297 #endif /* USE_OBSOLETE_FS_HOOKS */ 00298 00299 /* 00300 * Declare the native filesystem support. These functions should be considered 00301 * private to Tcl, and should really not be called directly by any code other 00302 * than this file (i.e. neither by Tcl's core nor by extensions). Similarly, 00303 * the old string-based Tclp... native filesystem functions should not be 00304 * called. 00305 * 00306 * The correct API to use now is the Tcl_FS... set of functions, which ensure 00307 * correct and complete virtual filesystem support. 00308 * 00309 * We cannot make all of these static, since some of them are implemented in 00310 * the platform-specific directories. 00311 */ 00312 00313 static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator; 00314 static Tcl_FSFreeInternalRepProc NativeFreeInternalRep; 00315 static Tcl_FSFileAttrStringsProc NativeFileAttrStrings; 00316 static Tcl_FSFileAttrsGetProc NativeFileAttrsGet; 00317 static Tcl_FSFileAttrsSetProc NativeFileAttrsSet; 00318 00319 /* 00320 * The only reason these functions are not static is that they are either 00321 * called by code in the native (win/unix) directories or they are actually 00322 * implemented in those directories. They should simply not be called by code 00323 * outside Tcl's native filesystem core i.e. they should be considered 00324 * 'static' to Tcl's filesystem code (if we ever built the native filesystem 00325 * support into a separate code library, this could actually be enforced). 00326 */ 00327 00328 Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType; 00329 Tcl_FSInternalToNormalizedProc TclpNativeToNormalized; 00330 Tcl_FSStatProc TclpObjStat; 00331 Tcl_FSAccessProc TclpObjAccess; 00332 Tcl_FSMatchInDirectoryProc TclpMatchInDirectory; 00333 Tcl_FSChdirProc TclpObjChdir; 00334 Tcl_FSLstatProc TclpObjLstat; 00335 Tcl_FSCopyFileProc TclpObjCopyFile; 00336 Tcl_FSDeleteFileProc TclpObjDeleteFile; 00337 Tcl_FSRenameFileProc TclpObjRenameFile; 00338 Tcl_FSCreateDirectoryProc TclpObjCreateDirectory; 00339 Tcl_FSCopyDirectoryProc TclpObjCopyDirectory; 00340 Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory; 00341 Tcl_FSUnloadFileProc TclpUnloadFile; 00342 Tcl_FSLinkProc TclpObjLink; 00343 Tcl_FSListVolumesProc TclpObjListVolumes; 00344 00345 /* 00346 * Define the native filesystem dispatch table. If necessary, it is ok to make 00347 * this non-static, but it should only be accessed by the functions actually 00348 * listed within it (or perhaps other helper functions of them). Anything 00349 * which is not part of this 'native filesystem implementation' should not be 00350 * delving inside here! 00351 */ 00352 00353 Tcl_Filesystem tclNativeFilesystem = { 00354 "native", 00355 sizeof(Tcl_Filesystem), 00356 TCL_FILESYSTEM_VERSION_2, 00357 &TclNativePathInFilesystem, 00358 &TclNativeDupInternalRep, 00359 &NativeFreeInternalRep, 00360 &TclpNativeToNormalized, 00361 &TclNativeCreateNativeRep, 00362 &TclpObjNormalizePath, 00363 &TclpFilesystemPathType, 00364 &NativeFilesystemSeparator, 00365 &TclpObjStat, 00366 &TclpObjAccess, 00367 &TclpOpenFileChannel, 00368 &TclpMatchInDirectory, 00369 &TclpUtime, 00370 #ifndef S_IFLNK 00371 NULL, 00372 #else 00373 &TclpObjLink, 00374 #endif /* S_IFLNK */ 00375 &TclpObjListVolumes, 00376 &NativeFileAttrStrings, 00377 &NativeFileAttrsGet, 00378 &NativeFileAttrsSet, 00379 &TclpObjCreateDirectory, 00380 &TclpObjRemoveDirectory, 00381 &TclpObjDeleteFile, 00382 &TclpObjCopyFile, 00383 &TclpObjRenameFile, 00384 &TclpObjCopyDirectory, 00385 &TclpObjLstat, 00386 &TclpDlopen, 00387 /* Needs a cast since we're using version_2 */ 00388 (Tcl_FSGetCwdProc *) &TclpGetNativeCwd, 00389 &TclpObjChdir 00390 }; 00391 00392 /* 00393 * Define the tail of the linked list. Note that for unconventional uses of 00394 * Tcl without a native filesystem, we may in the future wish to modify the 00395 * current approach of hard-coding the native filesystem in the lookup list 00396 * 'filesystemList' below. 00397 * 00398 * We initialize the record so that it thinks one file uses it. This means it 00399 * will never be freed. 00400 */ 00401 00402 static FilesystemRecord nativeFilesystemRecord = { 00403 NULL, 00404 &tclNativeFilesystem, 00405 1, 00406 NULL 00407 }; 00408 00409 /* 00410 * This is incremented each time we modify the linked list of filesystems. Any 00411 * time it changes, all cached filesystem representations are suspect and must 00412 * be freed. For multithreading builds, change of the filesystem epoch will 00413 * trigger cache cleanup in all threads. 00414 */ 00415 00416 static int theFilesystemEpoch = 0; 00417 00418 /* 00419 * Stores the linked list of filesystems. A 1:1 copy of this list is also 00420 * maintained in the TSD for each thread. This is to avoid synchronization 00421 * issues. 00422 */ 00423 00424 static FilesystemRecord *filesystemList = &nativeFilesystemRecord; 00425 TCL_DECLARE_MUTEX(filesystemMutex) 00426 00427 /* 00428 * Used to implement Tcl_FSGetCwd in a file-system independent way. 00429 */ 00430 00431 static Tcl_Obj* cwdPathPtr = NULL; 00432 static int cwdPathEpoch = 0; 00433 static ClientData cwdClientData = NULL; 00434 TCL_DECLARE_MUTEX(cwdMutex) 00435 00436 Tcl_ThreadDataKey tclFsDataKey; 00437 00438 /* 00439 * Declare fallback support function and information for Tcl_FSLoadFile 00440 */ 00441 00442 static Tcl_FSUnloadFileProc FSUnloadTempFile; 00443 00444 /* 00445 * One of these structures is used each time we successfully load a file from 00446 * a file system by way of making a temporary copy of the file on the native 00447 * filesystem. We need to store both the actual unloadProc/clientData 00448 * combination which was used, and the original and modified filenames, so 00449 * that we can correctly undo the entire operation when we want to unload the 00450 * code. 00451 */ 00452 00453 typedef struct FsDivertLoad { 00454 Tcl_LoadHandle loadHandle; 00455 Tcl_FSUnloadFileProc *unloadProcPtr; 00456 Tcl_Obj *divertedFile; 00457 const Tcl_Filesystem *divertedFilesystem; 00458 ClientData divertedFileNativeRep; 00459 } FsDivertLoad; 00460 00461 /* 00462 * Now move on to the basic filesystem implementation 00463 */ 00464 00465 static void 00466 FsThrExitProc( 00467 ClientData cd) 00468 { 00469 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) cd; 00470 FilesystemRecord *fsRecPtr = NULL, *tmpFsRecPtr = NULL; 00471 00472 /* 00473 * Trash the cwd copy. 00474 */ 00475 00476 if (tsdPtr->cwdPathPtr != NULL) { 00477 Tcl_DecrRefCount(tsdPtr->cwdPathPtr); 00478 tsdPtr->cwdPathPtr = NULL; 00479 } 00480 if (tsdPtr->cwdClientData != NULL) { 00481 NativeFreeInternalRep(tsdPtr->cwdClientData); 00482 } 00483 00484 /* 00485 * Trash the filesystems cache. 00486 */ 00487 00488 fsRecPtr = tsdPtr->filesystemList; 00489 while (fsRecPtr != NULL) { 00490 tmpFsRecPtr = fsRecPtr->nextPtr; 00491 if (--fsRecPtr->fileRefCount <= 0) { 00492 ckfree((char *)fsRecPtr); 00493 } 00494 fsRecPtr = tmpFsRecPtr; 00495 } 00496 tsdPtr->initialized = 0; 00497 } 00498 00499 int 00500 TclFSCwdIsNative(void) 00501 { 00502 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00503 00504 if (tsdPtr->cwdClientData != NULL) { 00505 return 1; 00506 } else { 00507 return 0; 00508 } 00509 } 00510 00511 /* 00512 *---------------------------------------------------------------------- 00513 * 00514 * TclFSCwdPointerEquals -- 00515 * 00516 * Check whether the current working directory is equal to the path 00517 * given. 00518 * 00519 * Results: 00520 * 1 (equal) or 0 (un-equal) as appropriate. 00521 * 00522 * Side effects: 00523 * If the paths are equal, but are not the same object, this method will 00524 * modify the given pathPtrPtr to refer to the same object. In this case 00525 * the object pointed to by pathPtrPtr will have its refCount 00526 * decremented, and it will be adjusted to point to the cwd (with a new 00527 * refCount). 00528 * 00529 *---------------------------------------------------------------------- 00530 */ 00531 00532 int 00533 TclFSCwdPointerEquals( 00534 Tcl_Obj** pathPtrPtr) 00535 { 00536 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00537 00538 Tcl_MutexLock(&cwdMutex); 00539 if (tsdPtr->cwdPathPtr == NULL 00540 || tsdPtr->cwdPathEpoch != cwdPathEpoch) { 00541 if (tsdPtr->cwdPathPtr != NULL) { 00542 Tcl_DecrRefCount(tsdPtr->cwdPathPtr); 00543 } 00544 if (tsdPtr->cwdClientData != NULL) { 00545 NativeFreeInternalRep(tsdPtr->cwdClientData); 00546 } 00547 if (cwdPathPtr == NULL) { 00548 tsdPtr->cwdPathPtr = NULL; 00549 } else { 00550 tsdPtr->cwdPathPtr = Tcl_DuplicateObj(cwdPathPtr); 00551 Tcl_IncrRefCount(tsdPtr->cwdPathPtr); 00552 } 00553 if (cwdClientData == NULL) { 00554 tsdPtr->cwdClientData = NULL; 00555 } else { 00556 tsdPtr->cwdClientData = TclNativeDupInternalRep(cwdClientData); 00557 } 00558 tsdPtr->cwdPathEpoch = cwdPathEpoch; 00559 } 00560 Tcl_MutexUnlock(&cwdMutex); 00561 00562 if (tsdPtr->initialized == 0) { 00563 Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); 00564 tsdPtr->initialized = 1; 00565 } 00566 00567 if (pathPtrPtr == NULL) { 00568 return (tsdPtr->cwdPathPtr == NULL); 00569 } 00570 00571 if (tsdPtr->cwdPathPtr == *pathPtrPtr) { 00572 return 1; 00573 } else { 00574 int len1, len2; 00575 const char *str1, *str2; 00576 00577 str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1); 00578 str2 = Tcl_GetStringFromObj(*pathPtrPtr, &len2); 00579 if (len1 == len2 && !strcmp(str1,str2)) { 00580 /* 00581 * They are equal, but different objects. Update so they will be 00582 * the same object in the future. 00583 */ 00584 00585 Tcl_DecrRefCount(*pathPtrPtr); 00586 *pathPtrPtr = tsdPtr->cwdPathPtr; 00587 Tcl_IncrRefCount(*pathPtrPtr); 00588 return 1; 00589 } else { 00590 return 0; 00591 } 00592 } 00593 } 00594 00595 #ifdef TCL_THREADS 00596 static void 00597 FsRecacheFilesystemList(void) 00598 { 00599 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00600 FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL; 00601 00602 /* 00603 * Trash the current cache. 00604 */ 00605 00606 fsRecPtr = tsdPtr->filesystemList; 00607 while (fsRecPtr != NULL) { 00608 tmpFsRecPtr = fsRecPtr->nextPtr; 00609 if (--fsRecPtr->fileRefCount <= 0) { 00610 ckfree((char *)fsRecPtr); 00611 } 00612 fsRecPtr = tmpFsRecPtr; 00613 } 00614 tsdPtr->filesystemList = NULL; 00615 00616 /* 00617 * Code below operates on shared data. We are already called under mutex 00618 * lock so we can safely proceed. 00619 * 00620 * Locate tail of the global filesystem list. 00621 */ 00622 00623 fsRecPtr = filesystemList; 00624 while (fsRecPtr != NULL) { 00625 tmpFsRecPtr = fsRecPtr; 00626 fsRecPtr = fsRecPtr->nextPtr; 00627 } 00628 00629 /* 00630 * Refill the cache honouring the order. 00631 */ 00632 00633 fsRecPtr = tmpFsRecPtr; 00634 while (fsRecPtr != NULL) { 00635 tmpFsRecPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); 00636 *tmpFsRecPtr = *fsRecPtr; 00637 tmpFsRecPtr->nextPtr = tsdPtr->filesystemList; 00638 tmpFsRecPtr->prevPtr = NULL; 00639 if (tsdPtr->filesystemList) { 00640 tsdPtr->filesystemList->prevPtr = tmpFsRecPtr; 00641 } 00642 tsdPtr->filesystemList = tmpFsRecPtr; 00643 fsRecPtr = fsRecPtr->prevPtr; 00644 } 00645 00646 /* 00647 * Make sure the above gets released on thread exit. 00648 */ 00649 00650 if (tsdPtr->initialized == 0) { 00651 Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData) tsdPtr); 00652 tsdPtr->initialized = 1; 00653 } 00654 } 00655 #endif /* TCL_THREADS */ 00656 00657 static FilesystemRecord * 00658 FsGetFirstFilesystem(void) 00659 { 00660 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00661 FilesystemRecord *fsRecPtr; 00662 #ifndef TCL_THREADS 00663 tsdPtr->filesystemEpoch = theFilesystemEpoch; 00664 fsRecPtr = filesystemList; 00665 #else 00666 Tcl_MutexLock(&filesystemMutex); 00667 if (tsdPtr->filesystemList == NULL 00668 || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) { 00669 FsRecacheFilesystemList(); 00670 tsdPtr->filesystemEpoch = theFilesystemEpoch; 00671 } 00672 Tcl_MutexUnlock(&filesystemMutex); 00673 fsRecPtr = tsdPtr->filesystemList; 00674 #endif 00675 return fsRecPtr; 00676 } 00677 00678 /* 00679 * The epoch can be changed both by filesystems being added or removed and by 00680 * env(HOME) changing. 00681 */ 00682 00683 int 00684 TclFSEpochOk( 00685 int filesystemEpoch) 00686 { 00687 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00688 (void) FsGetFirstFilesystem(); 00689 return (filesystemEpoch == tsdPtr->filesystemEpoch); 00690 } 00691 00692 /* 00693 * If non-NULL, clientData is owned by us and must be freed later. 00694 */ 00695 00696 static void 00697 FsUpdateCwd( 00698 Tcl_Obj *cwdObj, 00699 ClientData clientData) 00700 { 00701 int len; 00702 char *str = NULL; 00703 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 00704 00705 if (cwdObj != NULL) { 00706 str = Tcl_GetStringFromObj(cwdObj, &len); 00707 } 00708 00709 Tcl_MutexLock(&cwdMutex); 00710 if (cwdPathPtr != NULL) { 00711 Tcl_DecrRefCount(cwdPathPtr); 00712 } 00713 if (cwdClientData != NULL) { 00714 NativeFreeInternalRep(cwdClientData); 00715 } 00716 00717 if (cwdObj == NULL) { 00718 cwdPathPtr = NULL; 00719 cwdClientData = NULL; 00720 } else { 00721 /* 00722 * This must be stored as string obj! 00723 */ 00724 00725 cwdPathPtr = Tcl_NewStringObj(str, len); 00726 Tcl_IncrRefCount(cwdPathPtr); 00727 cwdClientData = TclNativeDupInternalRep(clientData); 00728 } 00729 00730 cwdPathEpoch++; 00731 tsdPtr->cwdPathEpoch = cwdPathEpoch; 00732 Tcl_MutexUnlock(&cwdMutex); 00733 00734 if (tsdPtr->cwdPathPtr) { 00735 Tcl_DecrRefCount(tsdPtr->cwdPathPtr); 00736 } 00737 if (tsdPtr->cwdClientData) { 00738 NativeFreeInternalRep(tsdPtr->cwdClientData); 00739 } 00740 00741 if (cwdObj == NULL) { 00742 tsdPtr->cwdPathPtr = NULL; 00743 tsdPtr->cwdClientData = NULL; 00744 } else { 00745 tsdPtr->cwdPathPtr = Tcl_NewStringObj(str, len); 00746 tsdPtr->cwdClientData = clientData; 00747 Tcl_IncrRefCount(tsdPtr->cwdPathPtr); 00748 } 00749 } 00750 00751 /* 00752 *---------------------------------------------------------------------- 00753 * 00754 * TclFinalizeFilesystem -- 00755 * 00756 * Clean up the filesystem. After this, calls to all Tcl_FS... functions 00757 * will fail. 00758 * 00759 * We will later call TclResetFilesystem to restore the FS to a pristine 00760 * state. 00761 * 00762 * Results: 00763 * None. 00764 * 00765 * Side effects: 00766 * Frees any memory allocated by the filesystem. 00767 * 00768 *---------------------------------------------------------------------- 00769 */ 00770 00771 void 00772 TclFinalizeFilesystem(void) 00773 { 00774 FilesystemRecord *fsRecPtr; 00775 00776 /* 00777 * Assumption that only one thread is active now. Otherwise we would need 00778 * to put various mutexes around this code. 00779 */ 00780 00781 if (cwdPathPtr != NULL) { 00782 Tcl_DecrRefCount(cwdPathPtr); 00783 cwdPathPtr = NULL; 00784 cwdPathEpoch = 0; 00785 } 00786 if (cwdClientData != NULL) { 00787 NativeFreeInternalRep(cwdClientData); 00788 cwdClientData = NULL; 00789 } 00790 00791 /* 00792 * Remove all filesystems, freeing any allocated memory that is no longer 00793 * needed 00794 */ 00795 00796 fsRecPtr = filesystemList; 00797 while (fsRecPtr != NULL) { 00798 FilesystemRecord *tmpFsRecPtr = fsRecPtr->nextPtr; 00799 if (fsRecPtr->fileRefCount <= 0) { 00800 /* 00801 * The native filesystem is static, so we don't free it. 00802 */ 00803 00804 if (fsRecPtr->fsPtr != &tclNativeFilesystem) { 00805 ckfree((char *)fsRecPtr); 00806 } 00807 } 00808 fsRecPtr = tmpFsRecPtr; 00809 } 00810 filesystemList = NULL; 00811 00812 /* 00813 * Now filesystemList is NULL. This means that any attempt to use the 00814 * filesystem is likely to fail. 00815 */ 00816 00817 #ifdef USE_OBSOLETE_FS_HOOKS 00818 statProcList = NULL; 00819 accessProcList = NULL; 00820 openFileChannelProcList = NULL; 00821 #endif 00822 #ifdef __WIN32__ 00823 TclWinEncodingsCleanup(); 00824 #endif 00825 } 00826 00827 /* 00828 *---------------------------------------------------------------------- 00829 * 00830 * TclResetFilesystem -- 00831 * 00832 * Restore the filesystem to a pristine state. 00833 * 00834 * Results: 00835 * None. 00836 * 00837 * Side effects: 00838 * None. 00839 * 00840 *---------------------------------------------------------------------- 00841 */ 00842 00843 void 00844 TclResetFilesystem(void) 00845 { 00846 filesystemList = &nativeFilesystemRecord; 00847 00848 /* 00849 * Note, at this point, I believe nativeFilesystemRecord -> fileRefCount 00850 * should equal 1 and if not, we should try to track down the cause. 00851 */ 00852 00853 #ifdef __WIN32__ 00854 /* 00855 * Cleans up the win32 API filesystem proc lookup table. This must happen 00856 * very late in finalization so that deleting of copied dlls can occur. 00857 */ 00858 00859 TclWinResetInterfaces(); 00860 #endif 00861 } 00862 00863 /* 00864 *---------------------------------------------------------------------- 00865 * 00866 * Tcl_FSRegister -- 00867 * 00868 * Insert the filesystem function table at the head of the list of 00869 * functions which are used during calls to all file-system operations. 00870 * The filesystem will be added even if it is already in the list. (You 00871 * can use Tcl_FSData to check if it is in the list, provided the 00872 * ClientData used was not NULL). 00873 * 00874 * Note that the filesystem handling is head-to-tail of the list. Each 00875 * filesystem is asked in turn whether it can handle a particular 00876 * request, until one of them says 'yes'. At that point no further 00877 * filesystems are asked. 00878 * 00879 * In particular this means if you want to add a diagnostic filesystem 00880 * (which simply reports all fs activity), it must be at the head of the 00881 * list: i.e. it must be the last registered. 00882 * 00883 * Results: 00884 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could 00885 * not be allocated. 00886 * 00887 * Side effects: 00888 * Memory allocated and modifies the link list for filesystems. 00889 * 00890 *---------------------------------------------------------------------- 00891 */ 00892 00893 int 00894 Tcl_FSRegister( 00895 ClientData clientData, /* Client specific data for this fs */ 00896 Tcl_Filesystem *fsPtr) /* The filesystem record for the new fs. */ 00897 { 00898 FilesystemRecord *newFilesystemPtr; 00899 00900 if (fsPtr == NULL) { 00901 return TCL_ERROR; 00902 } 00903 00904 newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord)); 00905 00906 newFilesystemPtr->clientData = clientData; 00907 newFilesystemPtr->fsPtr = fsPtr; 00908 00909 /* 00910 * We start with a refCount of 1. If this drops to zero, then anyone is 00911 * welcome to ckfree us. 00912 */ 00913 00914 newFilesystemPtr->fileRefCount = 1; 00915 00916 /* 00917 * Is this lock and wait strictly speaking necessary? Since any iterators 00918 * out there will have grabbed a copy of the head of the list and be 00919 * iterating away from that, if we add a new element to the head of the 00920 * list, it can't possibly have any effect on any of their loops. In fact 00921 * it could be better not to wait, since we are adjusting the filesystem 00922 * epoch, any cached representations calculated by existing iterators are 00923 * going to have to be thrown away anyway. 00924 * 00925 * However, since registering and unregistering filesystems is a very rare 00926 * action, this is not a very important point. 00927 */ 00928 00929 Tcl_MutexLock(&filesystemMutex); 00930 00931 newFilesystemPtr->nextPtr = filesystemList; 00932 newFilesystemPtr->prevPtr = NULL; 00933 if (filesystemList) { 00934 filesystemList->prevPtr = newFilesystemPtr; 00935 } 00936 filesystemList = newFilesystemPtr; 00937 00938 /* 00939 * Increment the filesystem epoch counter, since existing paths might 00940 * conceivably now belong to different filesystems. 00941 */ 00942 00943 theFilesystemEpoch++; 00944 Tcl_MutexUnlock(&filesystemMutex); 00945 00946 return TCL_OK; 00947 } 00948 00949 /* 00950 *---------------------------------------------------------------------- 00951 * 00952 * Tcl_FSUnregister -- 00953 * 00954 * Remove the passed filesystem from the list of filesystem function 00955 * tables. It also ensures that the built-in (native) filesystem is not 00956 * removable, although we may wish to change that decision in the future 00957 * to allow a smaller Tcl core, in which the native filesystem is not 00958 * used at all (we could, say, initialise Tcl completely over a network 00959 * connection). 00960 * 00961 * Results: 00962 * TCL_OK if the function pointer was successfully removed, TCL_ERROR 00963 * otherwise. 00964 * 00965 * Side effects: 00966 * Memory may be deallocated (or will be later, once no "path" objects 00967 * refer to this filesystem), but the list of registered filesystems is 00968 * updated immediately. 00969 * 00970 *---------------------------------------------------------------------- 00971 */ 00972 00973 int 00974 Tcl_FSUnregister( 00975 Tcl_Filesystem *fsPtr) /* The filesystem record to remove. */ 00976 { 00977 int retVal = TCL_ERROR; 00978 FilesystemRecord *fsRecPtr; 00979 00980 Tcl_MutexLock(&filesystemMutex); 00981 00982 /* 00983 * Traverse the 'filesystemList' looking for the particular node whose 00984 * 'fsPtr' member matches 'fsPtr' and remove that one from the list. 00985 * Ensure that the "default" node cannot be removed. 00986 */ 00987 00988 fsRecPtr = filesystemList; 00989 while ((retVal == TCL_ERROR) && (fsRecPtr->fsPtr != &tclNativeFilesystem)) { 00990 if (fsRecPtr->fsPtr == fsPtr) { 00991 if (fsRecPtr->prevPtr) { 00992 fsRecPtr->prevPtr->nextPtr = fsRecPtr->nextPtr; 00993 } else { 00994 filesystemList = fsRecPtr->nextPtr; 00995 } 00996 if (fsRecPtr->nextPtr) { 00997 fsRecPtr->nextPtr->prevPtr = fsRecPtr->prevPtr; 00998 } 00999 01000 /* 01001 * Increment the filesystem epoch counter, since existing paths 01002 * might conceivably now belong to different filesystems. This 01003 * should also ensure that paths which have cached the filesystem 01004 * which is about to be deleted do not reference that filesystem 01005 * (which would of course lead to memory exceptions). 01006 */ 01007 01008 theFilesystemEpoch++; 01009 01010 fsRecPtr->fileRefCount--; 01011 if (fsRecPtr->fileRefCount <= 0) { 01012 ckfree((char *)fsRecPtr); 01013 } 01014 01015 retVal = TCL_OK; 01016 } else { 01017 fsRecPtr = fsRecPtr->nextPtr; 01018 } 01019 } 01020 01021 Tcl_MutexUnlock(&filesystemMutex); 01022 return retVal; 01023 } 01024 01025 /* 01026 *---------------------------------------------------------------------- 01027 * 01028 * Tcl_FSMatchInDirectory -- 01029 * 01030 * This routine is used by the globbing code to search a directory for 01031 * all files which match a given pattern. The appropriate function for 01032 * the filesystem to which pathPtr belongs will be called. If pathPtr 01033 * does not belong to any filesystem and if it is NULL or the empty 01034 * string, then we assume the pattern is to be matched in the current 01035 * working directory. To avoid have the Tcl_FSMatchInDirectoryProc for 01036 * each filesystem from having to deal with this issue, we create a 01037 * pathPtr on the fly (equal to the cwd), and then remove it from the 01038 * results returned. This makes filesystems easy to write, since they can 01039 * assume the pathPtr passed to them is an ordinary path. In fact this 01040 * means we could remove such special case handling from Tcl's native 01041 * filesystems. 01042 * 01043 * If 'pattern' is NULL, then pathPtr is assumed to be a fully specified 01044 * path of a single file/directory which must be checked for existence 01045 * and correct type. 01046 * 01047 * Results: 01048 * 01049 * The return value is a standard Tcl result indicating whether an error 01050 * occurred in globbing. Error messages are placed in interp, but good 01051 * results are placed in the resultPtr given. 01052 * 01053 * Recursive searches, e.g. 01054 * glob -dir $dir -join * pkgIndex.tcl 01055 * which must recurse through each directory matching '*' are handled 01056 * internally by Tcl, by passing specific flags in a modified 'types' 01057 * parameter. This means the actual filesystem only ever sees patterns 01058 * which match in a single directory. 01059 * 01060 * Side effects: 01061 * The interpreter may have an error message inserted into it. 01062 * 01063 *---------------------------------------------------------------------- 01064 */ 01065 01066 int 01067 Tcl_FSMatchInDirectory( 01068 Tcl_Interp *interp, /* Interpreter to receive error messages, but 01069 * may be NULL. */ 01070 Tcl_Obj *resultPtr, /* List object to receive results. */ 01071 Tcl_Obj *pathPtr, /* Contains path to directory to search. */ 01072 const char *pattern, /* Pattern to match against. */ 01073 Tcl_GlobTypeData *types) /* Object containing list of acceptable types. 01074 * May be NULL. In particular the directory 01075 * flag is very important. */ 01076 { 01077 const Tcl_Filesystem *fsPtr; 01078 Tcl_Obj *cwd, *tmpResultPtr, **elemsPtr; 01079 int resLength, i, ret = -1; 01080 01081 if (types != NULL && types->type & TCL_GLOB_TYPE_MOUNT) { 01082 /* 01083 * We don't currently allow querying of mounts by external code (a 01084 * valuable future step), so since we're the only function that 01085 * actually knows about mounts, this means we're being called 01086 * recursively by ourself. Return no matches. 01087 */ 01088 01089 return TCL_OK; 01090 } 01091 01092 if (pathPtr != NULL) { 01093 fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 01094 } else { 01095 fsPtr = NULL; 01096 } 01097 01098 /* 01099 * Check if we've successfully mapped the path to a filesystem within 01100 * which to search. 01101 */ 01102 01103 if (fsPtr != NULL) { 01104 if (fsPtr->matchInDirectoryProc == NULL) { 01105 Tcl_SetErrno(ENOENT); 01106 return -1; 01107 } 01108 ret = (*fsPtr->matchInDirectoryProc)(interp, resultPtr, pathPtr, 01109 pattern, types); 01110 if (ret == TCL_OK && pattern != NULL) { 01111 FsAddMountsToGlobResult(resultPtr, pathPtr, pattern, types); 01112 } 01113 return ret; 01114 } 01115 01116 /* 01117 * If the path isn't empty, we have no idea how to match files in a 01118 * directory which belongs to no known filesystem 01119 */ 01120 01121 if (pathPtr != NULL && TclGetString(pathPtr)[0] != '\0') { 01122 Tcl_SetErrno(ENOENT); 01123 return -1; 01124 } 01125 01126 /* 01127 * We have an empty or NULL path. This is defined to mean we must search 01128 * for files within the current 'cwd'. We therefore use that, but then 01129 * since the proc we call will return results which include the cwd we 01130 * must then trim it off the front of each path in the result. We choose 01131 * to deal with this here (in the generic code), since if we don't, every 01132 * single filesystem's implementation of Tcl_FSMatchInDirectory will have 01133 * to deal with it for us. 01134 */ 01135 01136 cwd = Tcl_FSGetCwd(NULL); 01137 if (cwd == NULL) { 01138 if (interp != NULL) { 01139 Tcl_SetResult(interp, "glob couldn't determine " 01140 "the current working directory", TCL_STATIC); 01141 } 01142 return TCL_ERROR; 01143 } 01144 01145 fsPtr = Tcl_FSGetFileSystemForPath(cwd); 01146 if (fsPtr != NULL && fsPtr->matchInDirectoryProc != NULL) { 01147 TclNewObj(tmpResultPtr); 01148 Tcl_IncrRefCount(tmpResultPtr); 01149 ret = (*fsPtr->matchInDirectoryProc)(interp, tmpResultPtr, cwd, 01150 pattern, types); 01151 if (ret == TCL_OK) { 01152 FsAddMountsToGlobResult(tmpResultPtr, cwd, pattern, types); 01153 01154 /* 01155 * Note that we know resultPtr and tmpResultPtr are distinct. 01156 */ 01157 01158 ret = Tcl_ListObjGetElements(interp, tmpResultPtr, 01159 &resLength, &elemsPtr); 01160 for (i=0 ; ret==TCL_OK && i<resLength ; i++) { 01161 ret = Tcl_ListObjAppendElement(interp, resultPtr, 01162 TclFSMakePathRelative(interp, elemsPtr[i], cwd)); 01163 } 01164 } 01165 TclDecrRefCount(tmpResultPtr); 01166 } 01167 Tcl_DecrRefCount(cwd); 01168 return ret; 01169 } 01170 01171 /* 01172 *---------------------------------------------------------------------- 01173 * 01174 * FsAddMountsToGlobResult -- 01175 * 01176 * This routine is used by the globbing code to take the results of a 01177 * directory listing and add any mounted paths to that listing. This is 01178 * required so that simple things like 'glob *' merge mounts and listings 01179 * correctly. 01180 * 01181 * Results: 01182 * None. 01183 * 01184 * Side effects: 01185 * Modifies the resultPtr. 01186 * 01187 *---------------------------------------------------------------------- 01188 */ 01189 01190 static void 01191 FsAddMountsToGlobResult( 01192 Tcl_Obj *resultPtr, /* The current list of matching paths; must 01193 * not be shared! */ 01194 Tcl_Obj *pathPtr, /* The directory in question */ 01195 const char *pattern, /* Pattern to match against. */ 01196 Tcl_GlobTypeData *types) /* Object containing list of acceptable types. 01197 * May be NULL. In particular the directory 01198 * flag is very important. */ 01199 { 01200 int mLength, gLength, i; 01201 int dir = (types == NULL || (types->type & TCL_GLOB_TYPE_DIR)); 01202 Tcl_Obj *mounts = FsListMounts(pathPtr, pattern); 01203 01204 if (mounts == NULL) { 01205 return; 01206 } 01207 01208 if (Tcl_ListObjLength(NULL, mounts, &mLength) != TCL_OK || mLength == 0) { 01209 goto endOfMounts; 01210 } 01211 if (Tcl_ListObjLength(NULL, resultPtr, &gLength) != TCL_OK) { 01212 goto endOfMounts; 01213 } 01214 for (i=0 ; i<mLength ; i++) { 01215 Tcl_Obj *mElt; 01216 int j; 01217 int found = 0; 01218 01219 Tcl_ListObjIndex(NULL, mounts, i, &mElt); 01220 01221 for (j=0 ; j<gLength ; j++) { 01222 Tcl_Obj *gElt; 01223 01224 Tcl_ListObjIndex(NULL, resultPtr, j, &gElt); 01225 if (Tcl_FSEqualPaths(mElt, gElt)) { 01226 found = 1; 01227 if (!dir) { 01228 /* 01229 * We don't want to list this. 01230 */ 01231 01232 Tcl_ListObjReplace(NULL, resultPtr, j, 1, 0, NULL); 01233 gLength--; 01234 } 01235 break; /* Break out of for loop */ 01236 } 01237 } 01238 if (!found && dir) { 01239 Tcl_Obj *norm; 01240 int len, mlen; 01241 01242 /* 01243 * We know mElt is absolute normalized and lies inside pathPtr, so 01244 * now we must add to the result the right representation of mElt, 01245 * i.e. the representation which is relative to pathPtr. 01246 */ 01247 01248 norm = Tcl_FSGetNormalizedPath(NULL, pathPtr); 01249 if (norm != NULL) { 01250 const char *path, *mount; 01251 01252 mount = Tcl_GetStringFromObj(mElt, &mlen); 01253 path = Tcl_GetStringFromObj(norm, &len); 01254 if (path[len-1] == '/') { 01255 /* 01256 * Deal with the root of the volume. 01257 */ 01258 01259 len--; 01260 } 01261 len++; /* account for '/' in the mElt [Bug 1602539] */ 01262 mElt = TclNewFSPathObj(pathPtr, mount + len, mlen - len); 01263 Tcl_ListObjAppendElement(NULL, resultPtr, mElt); 01264 } 01265 /* 01266 * No need to increment gLength, since we don't want to compare 01267 * mounts against mounts. 01268 */ 01269 } 01270 } 01271 01272 endOfMounts: 01273 Tcl_DecrRefCount(mounts); 01274 } 01275 01276 /* 01277 *---------------------------------------------------------------------- 01278 * 01279 * Tcl_FSMountsChanged -- 01280 * 01281 * Notify the filesystem that the available mounted filesystems (or 01282 * within any one filesystem type, the number or location of mount 01283 * points) have changed. 01284 * 01285 * Results: 01286 * None. 01287 * 01288 * Side effects: 01289 * The global filesystem variable 'theFilesystemEpoch' is incremented. 01290 * The effect of this is to make all cached path representations invalid. 01291 * Clearly it should only therefore be called when it is really required! 01292 * There are a few circumstances when it should be called: 01293 * 01294 * (1) when a new filesystem is registered or unregistered. Strictly 01295 * speaking this is only necessary if the new filesystem accepts file 01296 * paths as is (normally the filesystem itself is really a shell which 01297 * hasn't yet had any mount points established and so its 01298 * 'pathInFilesystem' proc will always fail). However, for safety, Tcl 01299 * always calls this for you in these circumstances. 01300 * 01301 * (2) when additional mount points are established inside any existing 01302 * filesystem (except the native fs) 01303 * 01304 * (3) when any filesystem (except the native fs) changes the list of 01305 * available volumes. 01306 * 01307 * (4) when the mapping from a string representation of a file to a full, 01308 * normalized path changes. For example, if 'env(HOME)' is modified, then 01309 * any path containing '~' will map to a different filesystem location. 01310 * Therefore all such paths need to have their internal representation 01311 * invalidated. 01312 * 01313 * Tcl has no control over (2) and (3), so any registered filesystem must 01314 * make sure it calls this function when those situations occur. 01315 * 01316 * (Note: the reason for the exception in 2,3 for the native filesystem 01317 * is that the native filesystem by default claims all unknown files even 01318 * if it really doesn't understand them or if they don't exist). 01319 * 01320 *---------------------------------------------------------------------- 01321 */ 01322 01323 void 01324 Tcl_FSMountsChanged( 01325 Tcl_Filesystem *fsPtr) 01326 { 01327 /* 01328 * We currently don't do anything with this parameter. We could in the 01329 * future only invalidate files for this filesystem or otherwise take more 01330 * advanced action. 01331 */ 01332 01333 (void)fsPtr; 01334 01335 /* 01336 * Increment the filesystem epoch counter, since existing paths might now 01337 * belong to different filesystems. 01338 */ 01339 01340 Tcl_MutexLock(&filesystemMutex); 01341 theFilesystemEpoch++; 01342 Tcl_MutexUnlock(&filesystemMutex); 01343 } 01344 01345 /* 01346 *---------------------------------------------------------------------- 01347 * 01348 * Tcl_FSData -- 01349 * 01350 * Retrieve the clientData field for the filesystem given, or NULL if 01351 * that filesystem is not registered. 01352 * 01353 * Results: 01354 * A clientData value, or NULL. Note that if the filesystem was 01355 * registered with a NULL clientData field, this function will return 01356 * that NULL value. 01357 * 01358 * Side effects: 01359 * None. 01360 * 01361 *---------------------------------------------------------------------- 01362 */ 01363 01364 ClientData 01365 Tcl_FSData( 01366 Tcl_Filesystem *fsPtr) /* The filesystem record to query. */ 01367 { 01368 ClientData retVal = NULL; 01369 FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); 01370 01371 /* 01372 * Traverse the list of filesystems look for a particular one. If found, 01373 * return that filesystem's clientData (originally provided when calling 01374 * Tcl_FSRegister). 01375 */ 01376 01377 while ((retVal == NULL) && (fsRecPtr != NULL)) { 01378 if (fsRecPtr->fsPtr == fsPtr) { 01379 retVal = fsRecPtr->clientData; 01380 } 01381 fsRecPtr = fsRecPtr->nextPtr; 01382 } 01383 01384 return retVal; 01385 } 01386 01387 /* 01388 *--------------------------------------------------------------------------- 01389 * 01390 * TclFSNormalizeToUniquePath -- 01391 * 01392 * Takes a path specification containing no ../, ./ sequences, and 01393 * converts it into a unique path for the given platform. On Unix, this 01394 * means the path must be free of symbolic links/aliases, and on Windows 01395 * it means we want the long form, with that long form's case-dependence 01396 * (which gives us a unique, case-dependent path). 01397 * 01398 * Results: 01399 * The pathPtr is modified in place. The return value is the last byte 01400 * offset which was recognised in the path string. 01401 * 01402 * Side effects: 01403 * None (beyond the memory allocation for the result). 01404 * 01405 * Special notes: 01406 * If the filesystem-specific normalizePathProcs can re-introduce ../, ./ 01407 * sequences into the path, then this function will not return the 01408 * correct result. This may be possible with symbolic links on unix. 01409 * 01410 * Important assumption: if startAt is non-zero, it must point to a 01411 * directory separator that we know exists and is already normalized (so 01412 * it is important not to point to the char just after the separator). 01413 * 01414 *--------------------------------------------------------------------------- 01415 */ 01416 01417 int 01418 TclFSNormalizeToUniquePath( 01419 Tcl_Interp *interp, /* Used for error messages. */ 01420 Tcl_Obj *pathPtr, /* The path to normalize in place */ 01421 int startAt, /* Start at this char-offset */ 01422 ClientData *clientDataPtr) /* If we generated a complete normalized path 01423 * for a given filesystem, we can optionally 01424 * return an fs-specific clientdata here. */ 01425 { 01426 FilesystemRecord *fsRecPtr, *firstFsRecPtr; 01427 /* Ignore this variable */ 01428 (void) clientDataPtr; 01429 01430 /* 01431 * Call each of the "normalise path" functions in succession. This is a 01432 * special case, in which if we have a native filesystem handler, we call 01433 * it first. This is because the root of Tcl's filesystem is always a 01434 * native filesystem (i.e. '/' on unix is native). 01435 */ 01436 01437 firstFsRecPtr = FsGetFirstFilesystem(); 01438 01439 fsRecPtr = firstFsRecPtr; 01440 while (fsRecPtr != NULL) { 01441 if (fsRecPtr->fsPtr == &tclNativeFilesystem) { 01442 Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; 01443 if (proc != NULL) { 01444 startAt = (*proc)(interp, pathPtr, startAt); 01445 } 01446 break; 01447 } 01448 fsRecPtr = fsRecPtr->nextPtr; 01449 } 01450 01451 fsRecPtr = firstFsRecPtr; 01452 while (fsRecPtr != NULL) { 01453 /* 01454 * Skip the native system next time through. 01455 */ 01456 01457 if (fsRecPtr->fsPtr != &tclNativeFilesystem) { 01458 Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc; 01459 if (proc != NULL) { 01460 startAt = (*proc)(interp, pathPtr, startAt); 01461 } 01462 01463 /* 01464 * We could add an efficiency check like this: 01465 * if (retVal == length-of(pathPtr)) {break;} 01466 * but there's not much benefit. 01467 */ 01468 } 01469 fsRecPtr = fsRecPtr->nextPtr; 01470 } 01471 01472 return startAt; 01473 } 01474 01475 /* 01476 *--------------------------------------------------------------------------- 01477 * 01478 * TclGetOpenMode -- 01479 * 01480 * This routine is an obsolete, limited version of TclGetOpenModeEx() 01481 * below. It exists only to satisfy any extensions imprudently using it 01482 * via Tcl's internal stubs table. 01483 * 01484 * Results: 01485 * Same as TclGetOpenModeEx(). 01486 * 01487 * Side effects: 01488 * Same as TclGetOpenModeEx(). 01489 * 01490 *--------------------------------------------------------------------------- 01491 */ 01492 01493 int 01494 TclGetOpenMode( 01495 Tcl_Interp *interp, /* Interpreter to use for error reporting - 01496 * may be NULL. */ 01497 const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */ 01498 int *seekFlagPtr) /* Set this to 1 if the caller should seek to 01499 * EOF during the opening of the file. */ 01500 { 01501 int binary = 0; 01502 return TclGetOpenModeEx(interp, modeString, seekFlagPtr, &binary); 01503 } 01504 01505 /* 01506 *--------------------------------------------------------------------------- 01507 * 01508 * TclGetOpenModeEx -- 01509 * 01510 * Computes a POSIX mode mask for opening a file, from a given string, 01511 * and also sets flags to indicate whether the caller should seek to EOF 01512 * after opening the file, and whether the caller should configure the 01513 * channel for binary data. 01514 * 01515 * Results: 01516 * On success, returns mode to pass to "open". If an error occurs, the 01517 * return value is -1 and if interp is not NULL, sets interp's result 01518 * object to an error message. 01519 * 01520 * Side effects: 01521 * Sets the integer referenced by seekFlagPtr to 1 to tell the caller to 01522 * seek to EOF after opening the file, or to 0 otherwise. Sets the 01523 * integer referenced by binaryPtr to 1 to tell the caller to seek to 01524 * configure the channel for binary data, or to 0 otherwise. 01525 * 01526 * Special note: 01527 * This code is based on a prototype implementation contributed by Mark 01528 * Diekhans. 01529 * 01530 *--------------------------------------------------------------------------- 01531 */ 01532 01533 int 01534 TclGetOpenModeEx( 01535 Tcl_Interp *interp, /* Interpreter to use for error reporting - 01536 * may be NULL. */ 01537 const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */ 01538 int *seekFlagPtr, /* Set this to 1 if the caller should seek to 01539 * EOF during the opening of the file. */ 01540 int *binaryPtr) /* Set this to 1 if the caller should 01541 * configure the opened channel for binary 01542 * operations */ 01543 { 01544 int mode, modeArgc, c, i, gotRW; 01545 const char **modeArgv, *flag; 01546 #define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR) 01547 01548 /* 01549 * Check for the simpler fopen-like access modes (e.g. "r"). They are 01550 * distinguished from the POSIX access modes by the presence of a 01551 * lower-case first letter. 01552 */ 01553 01554 *seekFlagPtr = 0; 01555 *binaryPtr = 0; 01556 mode = 0; 01557 01558 /* 01559 * Guard against international characters before using byte oriented 01560 * routines. 01561 */ 01562 01563 if (!(modeString[0] & 0x80) 01564 && islower(UCHAR(modeString[0]))) { /* INTL: ISO only. */ 01565 switch (modeString[0]) { 01566 case 'r': 01567 mode = O_RDONLY; 01568 break; 01569 case 'w': 01570 mode = O_WRONLY|O_CREAT|O_TRUNC; 01571 break; 01572 case 'a': 01573 /* 01574 * Added O_APPEND for proper automatic seek-to-end-on-write by the 01575 * OS. [Bug 680143] 01576 */ 01577 01578 mode = O_WRONLY|O_CREAT|O_APPEND; 01579 *seekFlagPtr = 1; 01580 break; 01581 default: 01582 goto error; 01583 } 01584 i=1; 01585 while (i<3 && modeString[i]) { 01586 if (modeString[i] == modeString[i-1]) { 01587 goto error; 01588 } 01589 switch (modeString[i++]) { 01590 case '+': 01591 /* 01592 * Must remove the O_APPEND flag so that the seek command 01593 * works. [Bug 1773127] 01594 */ 01595 01596 mode &= ~(O_RDONLY|O_WRONLY|O_APPEND); 01597 mode |= O_RDWR; 01598 break; 01599 case 'b': 01600 *binaryPtr = 1; 01601 break; 01602 default: 01603 goto error; 01604 } 01605 } 01606 if (modeString[i] != 0) { 01607 goto error; 01608 } 01609 return mode; 01610 01611 error: 01612 *seekFlagPtr = 0; 01613 *binaryPtr = 0; 01614 if (interp != NULL) { 01615 Tcl_AppendResult(interp, "illegal access mode \"", modeString, 01616 "\"", NULL); 01617 } 01618 return -1; 01619 } 01620 01621 /* 01622 * The access modes are specified using a list of POSIX modes such as 01623 * O_CREAT. 01624 * 01625 * IMPORTANT NOTE: We rely on Tcl_SplitList working correctly when a NULL 01626 * interpreter is passed in. 01627 */ 01628 01629 if (Tcl_SplitList(interp, modeString, &modeArgc, &modeArgv) != TCL_OK) { 01630 if (interp != NULL) { 01631 Tcl_AddErrorInfo(interp, 01632 "\n while processing open access modes \""); 01633 Tcl_AddErrorInfo(interp, modeString); 01634 Tcl_AddErrorInfo(interp, "\""); 01635 } 01636 return -1; 01637 } 01638 01639 gotRW = 0; 01640 for (i = 0; i < modeArgc; i++) { 01641 flag = modeArgv[i]; 01642 c = flag[0]; 01643 if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) { 01644 mode = (mode & ~RW_MODES) | O_RDONLY; 01645 gotRW = 1; 01646 } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) { 01647 mode = (mode & ~RW_MODES) | O_WRONLY; 01648 gotRW = 1; 01649 } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) { 01650 mode = (mode & ~RW_MODES) | O_RDWR; 01651 gotRW = 1; 01652 } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) { 01653 mode |= O_APPEND; 01654 *seekFlagPtr = 1; 01655 } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) { 01656 mode |= O_CREAT; 01657 } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) { 01658 mode |= O_EXCL; 01659 01660 } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) { 01661 #ifdef O_NOCTTY 01662 mode |= O_NOCTTY; 01663 #else 01664 if (interp != NULL) { 01665 Tcl_AppendResult(interp, "access mode \"", flag, 01666 "\" not supported by this system", NULL); 01667 } 01668 ckfree((char *) modeArgv); 01669 return -1; 01670 #endif 01671 01672 } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) { 01673 #if defined(O_NDELAY) || defined(O_NONBLOCK) 01674 # ifdef O_NONBLOCK 01675 mode |= O_NONBLOCK; 01676 # else 01677 mode |= O_NDELAY; 01678 # endif 01679 01680 #else 01681 if (interp != NULL) { 01682 Tcl_AppendResult(interp, "access mode \"", flag, 01683 "\" not supported by this system", NULL); 01684 } 01685 ckfree((char *) modeArgv); 01686 return -1; 01687 #endif 01688 01689 } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) { 01690 mode |= O_TRUNC; 01691 } else if ((c == 'B') && (strcmp(flag, "BINARY") == 0)) { 01692 *binaryPtr = 1; 01693 } else { 01694 01695 if (interp != NULL) { 01696 Tcl_AppendResult(interp, "invalid access mode \"", flag, 01697 "\": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, " 01698 "CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC", NULL); 01699 } 01700 ckfree((char *) modeArgv); 01701 return -1; 01702 } 01703 } 01704 01705 ckfree((char *) modeArgv); 01706 01707 if (!gotRW) { 01708 if (interp != NULL) { 01709 Tcl_AppendResult(interp, "access mode must include either" 01710 " RDONLY, WRONLY, or RDWR", NULL); 01711 } 01712 return -1; 01713 } 01714 return mode; 01715 } 01716 01717 /* 01718 * Tcl_FSEvalFile is Tcl_FSEvalFileEx without encoding argument. 01719 */ 01720 01721 int 01722 Tcl_FSEvalFile( 01723 Tcl_Interp *interp, /* Interpreter in which to process file. */ 01724 Tcl_Obj *pathPtr) /* Path of file to process. Tilde-substitution 01725 * will be performed on this name. */ 01726 { 01727 return Tcl_FSEvalFileEx(interp, pathPtr, NULL); 01728 } 01729 01730 /* 01731 *---------------------------------------------------------------------- 01732 * 01733 * Tcl_FSEvalFileEx -- 01734 * 01735 * Read in a file and process the entire file as one gigantic Tcl 01736 * command. 01737 * 01738 * Results: 01739 * A standard Tcl result, which is either the result of executing the 01740 * file or an error indicating why the file couldn't be read. 01741 * 01742 * Side effects: 01743 * Depends on the commands in the file. During the evaluation of the 01744 * contents of the file, iPtr->scriptFile is made to point to pathPtr 01745 * (the old value is cached and replaced when this function returns). 01746 * 01747 *---------------------------------------------------------------------- 01748 */ 01749 01750 int 01751 Tcl_FSEvalFileEx( 01752 Tcl_Interp *interp, /* Interpreter in which to process file. */ 01753 Tcl_Obj *pathPtr, /* Path of file to process. Tilde-substitution 01754 * will be performed on this name. */ 01755 const char *encodingName) /* If non-NULL, then use this encoding for the 01756 * file. NULL means use the system encoding. */ 01757 { 01758 int length, result = TCL_ERROR; 01759 Tcl_StatBuf statBuf; 01760 Tcl_Obj *oldScriptFile; 01761 Interp *iPtr; 01762 char *string; 01763 Tcl_Channel chan; 01764 Tcl_Obj *objPtr; 01765 01766 if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) { 01767 return result; 01768 } 01769 01770 if (Tcl_FSStat(pathPtr, &statBuf) == -1) { 01771 Tcl_SetErrno(errno); 01772 Tcl_AppendResult(interp, "couldn't read file \"", 01773 Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); 01774 return result; 01775 } 01776 chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644); 01777 if (chan == (Tcl_Channel) NULL) { 01778 Tcl_ResetResult(interp); 01779 Tcl_AppendResult(interp, "couldn't read file \"", 01780 Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); 01781 return result; 01782 } 01783 01784 /* 01785 * The eofchar is \32 (^Z). This is the usual on Windows, but we effect 01786 * this cross-platform to allow for scripted documents. [Bug: 2040] 01787 */ 01788 01789 Tcl_SetChannelOption(interp, chan, "-eofchar", "\32"); 01790 01791 /* 01792 * If the encoding is specified, set it for the channel. Else don't touch 01793 * it (and use the system encoding) Report error on unknown encoding. 01794 */ 01795 01796 if (encodingName != NULL) { 01797 if (Tcl_SetChannelOption(interp, chan, "-encoding", encodingName) 01798 != TCL_OK) { 01799 Tcl_Close(interp,chan); 01800 return result; 01801 } 01802 } 01803 01804 objPtr = Tcl_NewObj(); 01805 Tcl_IncrRefCount(objPtr); 01806 if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) { 01807 Tcl_Close(interp, chan); 01808 Tcl_AppendResult(interp, "couldn't read file \"", 01809 Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); 01810 goto end; 01811 } 01812 01813 if (Tcl_Close(interp, chan) != TCL_OK) { 01814 goto end; 01815 } 01816 01817 iPtr = (Interp *) interp; 01818 oldScriptFile = iPtr->scriptFile; 01819 iPtr->scriptFile = pathPtr; 01820 Tcl_IncrRefCount(iPtr->scriptFile); 01821 string = Tcl_GetStringFromObj(objPtr, &length); 01822 /* TIP #280 Force the evaluator to open a frame for a sourced 01823 * file. */ 01824 iPtr->evalFlags |= TCL_EVAL_FILE; 01825 result = Tcl_EvalEx(interp, string, length, 0); 01826 01827 /* 01828 * Now we have to be careful; the script may have changed the 01829 * iPtr->scriptFile value, so we must reset it without assuming it still 01830 * points to 'pathPtr'. 01831 */ 01832 01833 if (iPtr->scriptFile != NULL) { 01834 Tcl_DecrRefCount(iPtr->scriptFile); 01835 } 01836 iPtr->scriptFile = oldScriptFile; 01837 01838 if (result == TCL_RETURN) { 01839 result = TclUpdateReturnInfo(iPtr); 01840 } else if (result == TCL_ERROR) { 01841 /* 01842 * Record information telling where the error occurred. 01843 */ 01844 01845 const char *pathString = Tcl_GetStringFromObj(pathPtr, &length); 01846 int limit = 150; 01847 int overflow = (length > limit); 01848 01849 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( 01850 "\n (file \"%.*s%s\" line %d)", 01851 (overflow ? limit : length), pathString, 01852 (overflow ? "..." : ""), interp->errorLine)); 01853 } 01854 01855 end: 01856 Tcl_DecrRefCount(objPtr); 01857 return result; 01858 } 01859 01860 /* 01861 *---------------------------------------------------------------------- 01862 * 01863 * Tcl_GetErrno -- 01864 * 01865 * Gets the current value of the Tcl error code variable. This is 01866 * currently the global variable "errno" but could in the future change 01867 * to something else. 01868 * 01869 * Results: 01870 * The value of the Tcl error code variable. 01871 * 01872 * Side effects: 01873 * None. Note that the value of the Tcl error code variable is UNDEFINED 01874 * if a call to Tcl_SetErrno did not precede this call. 01875 * 01876 *---------------------------------------------------------------------- 01877 */ 01878 01879 int 01880 Tcl_GetErrno(void) 01881 { 01882 return errno; 01883 } 01884 01885 /* 01886 *---------------------------------------------------------------------- 01887 * 01888 * Tcl_SetErrno -- 01889 * 01890 * Sets the Tcl error code variable to the supplied value. 01891 * 01892 * Results: 01893 * None. 01894 * 01895 * Side effects: 01896 * Modifies the value of the Tcl error code variable. 01897 * 01898 *---------------------------------------------------------------------- 01899 */ 01900 01901 void 01902 Tcl_SetErrno( 01903 int err) /* The new value. */ 01904 { 01905 errno = err; 01906 } 01907 01908 /* 01909 *---------------------------------------------------------------------- 01910 * 01911 * Tcl_PosixError -- 01912 * 01913 * This function is typically called after UNIX kernel calls return 01914 * errors. It stores machine-readable information about the error in 01915 * errorCode field of interp and returns an information string for the 01916 * caller's use. 01917 * 01918 * Results: 01919 * The return value is a human-readable string describing the error. 01920 * 01921 * Side effects: 01922 * The errorCode field of the interp is set. 01923 * 01924 *---------------------------------------------------------------------- 01925 */ 01926 01927 const char * 01928 Tcl_PosixError( 01929 Tcl_Interp *interp) /* Interpreter whose errorCode field is to be 01930 * set. */ 01931 { 01932 const char *id, *msg; 01933 01934 msg = Tcl_ErrnoMsg(errno); 01935 id = Tcl_ErrnoId(); 01936 if (interp) { 01937 Tcl_SetErrorCode(interp, "POSIX", id, msg, NULL); 01938 } 01939 return msg; 01940 } 01941 01942 /* 01943 *---------------------------------------------------------------------- 01944 * 01945 * Tcl_FSStat -- 01946 * 01947 * This function replaces the library version of stat and lsat. 01948 * 01949 * The appropriate function for the filesystem to which pathPtr belongs 01950 * will be called. 01951 * 01952 * Results: 01953 * See stat documentation. 01954 * 01955 * Side effects: 01956 * See stat documentation. 01957 * 01958 *---------------------------------------------------------------------- 01959 */ 01960 01961 int 01962 Tcl_FSStat( 01963 Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */ 01964 Tcl_StatBuf *buf) /* Filled with results of stat call. */ 01965 { 01966 const Tcl_Filesystem *fsPtr; 01967 #ifdef USE_OBSOLETE_FS_HOOKS 01968 struct stat oldStyleStatBuffer; 01969 int retVal = -1; 01970 01971 /* 01972 * Call each of the "stat" function in succession. A non-return value of 01973 * -1 indicates the particular function has succeeded. 01974 */ 01975 01976 Tcl_MutexLock(&obsoleteFsHookMutex); 01977 01978 if (statProcList != NULL) { 01979 StatProc *statProcPtr; 01980 char *path; 01981 Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); 01982 if (transPtr == NULL) { 01983 path = NULL; 01984 } else { 01985 path = Tcl_GetString(transPtr); 01986 } 01987 01988 statProcPtr = statProcList; 01989 while ((retVal == -1) && (statProcPtr != NULL)) { 01990 retVal = (*statProcPtr->proc)(path, &oldStyleStatBuffer); 01991 statProcPtr = statProcPtr->nextPtr; 01992 } 01993 if (transPtr != NULL) { 01994 Tcl_DecrRefCount(transPtr); 01995 } 01996 } 01997 01998 Tcl_MutexUnlock(&obsoleteFsHookMutex); 01999 if (retVal != -1) { 02000 /* 02001 * Note that EOVERFLOW is not a problem here, and these assignments 02002 * should all be widening (if not identity.) 02003 */ 02004 02005 buf->st_mode = oldStyleStatBuffer.st_mode; 02006 buf->st_ino = oldStyleStatBuffer.st_ino; 02007 buf->st_dev = oldStyleStatBuffer.st_dev; 02008 buf->st_rdev = oldStyleStatBuffer.st_rdev; 02009 buf->st_nlink = oldStyleStatBuffer.st_nlink; 02010 buf->st_uid = oldStyleStatBuffer.st_uid; 02011 buf->st_gid = oldStyleStatBuffer.st_gid; 02012 buf->st_size = Tcl_LongAsWide(oldStyleStatBuffer.st_size); 02013 buf->st_atime = oldStyleStatBuffer.st_atime; 02014 buf->st_mtime = oldStyleStatBuffer.st_mtime; 02015 buf->st_ctime = oldStyleStatBuffer.st_ctime; 02016 #ifdef HAVE_ST_BLOCKS 02017 buf->st_blksize = oldStyleStatBuffer.st_blksize; 02018 buf->st_blocks = Tcl_LongAsWide(oldStyleStatBuffer.st_blocks); 02019 #endif 02020 return retVal; 02021 } 02022 #endif /* USE_OBSOLETE_FS_HOOKS */ 02023 02024 fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02025 if (fsPtr != NULL) { 02026 Tcl_FSStatProc *proc = fsPtr->statProc; 02027 if (proc != NULL) { 02028 return (*proc)(pathPtr, buf); 02029 } 02030 } 02031 Tcl_SetErrno(ENOENT); 02032 return -1; 02033 } 02034 02035 /* 02036 *---------------------------------------------------------------------- 02037 * 02038 * Tcl_FSLstat -- 02039 * 02040 * This function replaces the library version of lstat. The appropriate 02041 * function for the filesystem to which pathPtr belongs will be called. 02042 * If no 'lstat' function is listed, but a 'stat' function is, then Tcl 02043 * will fall back on the stat function. 02044 * 02045 * Results: 02046 * See lstat documentation. 02047 * 02048 * Side effects: 02049 * See lstat documentation. 02050 * 02051 *---------------------------------------------------------------------- 02052 */ 02053 02054 int 02055 Tcl_FSLstat( 02056 Tcl_Obj *pathPtr, /* Path of file to stat (in current CP). */ 02057 Tcl_StatBuf *buf) /* Filled with results of stat call. */ 02058 { 02059 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02060 if (fsPtr != NULL) { 02061 Tcl_FSLstatProc *proc = fsPtr->lstatProc; 02062 if (proc != NULL) { 02063 return (*proc)(pathPtr, buf); 02064 } else { 02065 Tcl_FSStatProc *sproc = fsPtr->statProc; 02066 if (sproc != NULL) { 02067 return (*sproc)(pathPtr, buf); 02068 } 02069 } 02070 } 02071 Tcl_SetErrno(ENOENT); 02072 return -1; 02073 } 02074 02075 /* 02076 *---------------------------------------------------------------------- 02077 * 02078 * Tcl_FSAccess -- 02079 * 02080 * This function replaces the library version of access. The appropriate 02081 * function for the filesystem to which pathPtr belongs will be called. 02082 * 02083 * Results: 02084 * See access documentation. 02085 * 02086 * Side effects: 02087 * See access documentation. 02088 * 02089 *---------------------------------------------------------------------- 02090 */ 02091 02092 int 02093 Tcl_FSAccess( 02094 Tcl_Obj *pathPtr, /* Path of file to access (in current CP). */ 02095 int mode) /* Permission setting. */ 02096 { 02097 const Tcl_Filesystem *fsPtr; 02098 #ifdef USE_OBSOLETE_FS_HOOKS 02099 int retVal = -1; 02100 02101 /* 02102 * Call each of the "access" function in succession. A non-return value of 02103 * -1 indicates the particular function has succeeded. 02104 */ 02105 02106 Tcl_MutexLock(&obsoleteFsHookMutex); 02107 02108 if (accessProcList != NULL) { 02109 AccessProc *accessProcPtr; 02110 char *path; 02111 Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); 02112 if (transPtr == NULL) { 02113 path = NULL; 02114 } else { 02115 path = Tcl_GetString(transPtr); 02116 } 02117 02118 accessProcPtr = accessProcList; 02119 while ((retVal == -1) && (accessProcPtr != NULL)) { 02120 retVal = (*accessProcPtr->proc)(path, mode); 02121 accessProcPtr = accessProcPtr->nextPtr; 02122 } 02123 if (transPtr != NULL) { 02124 Tcl_DecrRefCount(transPtr); 02125 } 02126 } 02127 02128 Tcl_MutexUnlock(&obsoleteFsHookMutex); 02129 if (retVal != -1) { 02130 return retVal; 02131 } 02132 #endif /* USE_OBSOLETE_FS_HOOKS */ 02133 02134 fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02135 if (fsPtr != NULL) { 02136 Tcl_FSAccessProc *proc = fsPtr->accessProc; 02137 if (proc != NULL) { 02138 return (*proc)(pathPtr, mode); 02139 } 02140 } 02141 02142 Tcl_SetErrno(ENOENT); 02143 return -1; 02144 } 02145 02146 /* 02147 *---------------------------------------------------------------------- 02148 * 02149 * Tcl_FSOpenFileChannel -- 02150 * 02151 * The appropriate function for the filesystem to which pathPtr belongs 02152 * will be called. 02153 * 02154 * Results: 02155 * The new channel or NULL, if the named file could not be opened. 02156 * 02157 * Side effects: 02158 * May open the channel and may cause creation of a file on the file 02159 * system. 02160 * 02161 *---------------------------------------------------------------------- 02162 */ 02163 02164 Tcl_Channel 02165 Tcl_FSOpenFileChannel( 02166 Tcl_Interp *interp, /* Interpreter for error reporting; can be 02167 * NULL. */ 02168 Tcl_Obj *pathPtr, /* Name of file to open. */ 02169 const char *modeString, /* A list of POSIX open modes or a string such 02170 * as "rw". */ 02171 int permissions) /* If the open involves creating a file, with 02172 * what modes to create it? */ 02173 { 02174 const Tcl_Filesystem *fsPtr; 02175 Tcl_Channel retVal = NULL; 02176 02177 #ifdef USE_OBSOLETE_FS_HOOKS 02178 /* 02179 * Call each of the "Tcl_OpenFileChannel" functions in succession. A 02180 * non-NULL return value indicates the particular function has succeeded. 02181 */ 02182 02183 Tcl_MutexLock(&obsoleteFsHookMutex); 02184 if (openFileChannelProcList != NULL) { 02185 OpenFileChannelProc *openFileChannelProcPtr; 02186 char *path; 02187 Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr); 02188 02189 if (transPtr == NULL) { 02190 path = NULL; 02191 } else { 02192 path = Tcl_GetString(transPtr); 02193 } 02194 02195 openFileChannelProcPtr = openFileChannelProcList; 02196 02197 while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) { 02198 retVal = (*openFileChannelProcPtr->proc)(interp, path, 02199 modeString, permissions); 02200 openFileChannelProcPtr = openFileChannelProcPtr->nextPtr; 02201 } 02202 if (transPtr != NULL) { 02203 Tcl_DecrRefCount(transPtr); 02204 } 02205 } 02206 Tcl_MutexUnlock(&obsoleteFsHookMutex); 02207 if (retVal != NULL) { 02208 return retVal; 02209 } 02210 #endif /* USE_OBSOLETE_FS_HOOKS */ 02211 02212 /* 02213 * We need this just to ensure we return the correct error messages under 02214 * some circumstances. 02215 */ 02216 02217 if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) { 02218 return NULL; 02219 } 02220 02221 fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02222 if (fsPtr != NULL) { 02223 Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc; 02224 if (proc != NULL) { 02225 int mode, seekFlag, binary; 02226 02227 /* 02228 * Parse the mode, picking up whether we want to seek to start 02229 * with and/or set the channel automatically into binary mode. 02230 */ 02231 02232 mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); 02233 if (mode == -1) { 02234 return NULL; 02235 } 02236 02237 /* 02238 * Do the actual open() call. 02239 */ 02240 02241 retVal = (*proc)(interp, pathPtr, mode, permissions); 02242 if (retVal == NULL) { 02243 return NULL; 02244 } 02245 02246 /* 02247 * Apply appropriate flags parsed out above. 02248 */ 02249 02250 if (seekFlag && Tcl_Seek(retVal, (Tcl_WideInt)0, 02251 SEEK_END) < (Tcl_WideInt)0) { 02252 if (interp != NULL) { 02253 Tcl_AppendResult(interp, "could not seek to end " 02254 "of file while opening \"", Tcl_GetString(pathPtr), 02255 "\": ", Tcl_PosixError(interp), NULL); 02256 } 02257 Tcl_Close(NULL, retVal); 02258 return NULL; 02259 } 02260 if (binary) { 02261 Tcl_SetChannelOption(interp, retVal, "-translation", "binary"); 02262 } 02263 return retVal; 02264 } 02265 } 02266 02267 /* 02268 * File doesn't belong to any filesystem that can open it. 02269 */ 02270 02271 Tcl_SetErrno(ENOENT); 02272 if (interp != NULL) { 02273 Tcl_AppendResult(interp, "couldn't open \"", Tcl_GetString(pathPtr), 02274 "\": ", Tcl_PosixError(interp), NULL); 02275 } 02276 return NULL; 02277 } 02278 02279 /* 02280 *---------------------------------------------------------------------- 02281 * 02282 * Tcl_FSUtime -- 02283 * 02284 * This function replaces the library version of utime. The appropriate 02285 * function for the filesystem to which pathPtr belongs will be called. 02286 * 02287 * Results: 02288 * See utime documentation. 02289 * 02290 * Side effects: 02291 * See utime documentation. 02292 * 02293 *---------------------------------------------------------------------- 02294 */ 02295 02296 int 02297 Tcl_FSUtime( 02298 Tcl_Obj *pathPtr, /* File to change access/modification times */ 02299 struct utimbuf *tval) /* Structure containing access/modification 02300 * times to use. Should not be modified. */ 02301 { 02302 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02303 if (fsPtr != NULL) { 02304 Tcl_FSUtimeProc *proc = fsPtr->utimeProc; 02305 if (proc != NULL) { 02306 return (*proc)(pathPtr, tval); 02307 } 02308 } 02309 return -1; 02310 } 02311 02312 /* 02313 *---------------------------------------------------------------------- 02314 * 02315 * NativeFileAttrStrings -- 02316 * 02317 * This function implements the platform dependent 'file attributes' 02318 * subcommand, for the native filesystem, for listing the set of possible 02319 * attribute strings. This function is part of Tcl's native filesystem 02320 * support, and is placed here because it is shared by Unix and Windows 02321 * code. 02322 * 02323 * Results: 02324 * An array of strings 02325 * 02326 * Side effects: 02327 * None. 02328 * 02329 *---------------------------------------------------------------------- 02330 */ 02331 02332 static const char ** 02333 NativeFileAttrStrings( 02334 Tcl_Obj *pathPtr, 02335 Tcl_Obj **objPtrRef) 02336 { 02337 return tclpFileAttrStrings; 02338 } 02339 02340 /* 02341 *---------------------------------------------------------------------- 02342 * 02343 * NativeFileAttrsGet -- 02344 * 02345 * This function implements the platform dependent 'file attributes' 02346 * subcommand, for the native filesystem, for 'get' operations. This 02347 * function is part of Tcl's native filesystem support, and is placed 02348 * here because it is shared by Unix and Windows code. 02349 * 02350 * Results: 02351 * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK 02352 * was returned) is likely to have a refCount of zero. Either way we must 02353 * either store it somewhere (e.g. the Tcl result), or Incr/Decr its 02354 * refCount to ensure it is properly freed. 02355 * 02356 * Side effects: 02357 * None. 02358 * 02359 *---------------------------------------------------------------------- 02360 */ 02361 02362 static int 02363 NativeFileAttrsGet( 02364 Tcl_Interp *interp, /* The interpreter for error reporting. */ 02365 int index, /* index of the attribute command. */ 02366 Tcl_Obj *pathPtr, /* path of file we are operating on. */ 02367 Tcl_Obj **objPtrRef) /* for output. */ 02368 { 02369 return (*tclpFileAttrProcs[index].getProc)(interp, index, pathPtr, 02370 objPtrRef); 02371 } 02372 02373 /* 02374 *---------------------------------------------------------------------- 02375 * 02376 * NativeFileAttrsSet -- 02377 * 02378 * This function implements the platform dependent 'file attributes' 02379 * subcommand, for the native filesystem, for 'set' operations. This 02380 * function is part of Tcl's native filesystem support, and is placed 02381 * here because it is shared by Unix and Windows code. 02382 * 02383 * Results: 02384 * Standard Tcl return code. 02385 * 02386 * Side effects: 02387 * None. 02388 * 02389 *---------------------------------------------------------------------- 02390 */ 02391 02392 static int 02393 NativeFileAttrsSet( 02394 Tcl_Interp *interp, /* The interpreter for error reporting. */ 02395 int index, /* index of the attribute command. */ 02396 Tcl_Obj *pathPtr, /* path of file we are operating on. */ 02397 Tcl_Obj *objPtr) /* set to this value. */ 02398 { 02399 return (*tclpFileAttrProcs[index].setProc)(interp, index, pathPtr, objPtr); 02400 } 02401 02402 /* 02403 *---------------------------------------------------------------------- 02404 * 02405 * Tcl_FSFileAttrStrings -- 02406 * 02407 * This function implements part of the hookable 'file attributes' 02408 * subcommand. The appropriate function for the filesystem to which 02409 * pathPtr belongs will be called. 02410 * 02411 * Results: 02412 * The called function may either return an array of strings, or may 02413 * instead return NULL and place a Tcl list into the given objPtrRef. 02414 * Tcl will take that list and first increment its refCount before using 02415 * it. On completion of that use, Tcl will decrement its refCount. Hence 02416 * if the list should be disposed of by Tcl when done, it should have a 02417 * refCount of zero, and if the list should not be disposed of, the 02418 * filesystem should ensure it retains a refCount on the object. 02419 * 02420 * Side effects: 02421 * None. 02422 * 02423 *---------------------------------------------------------------------- 02424 */ 02425 02426 const char ** 02427 Tcl_FSFileAttrStrings( 02428 Tcl_Obj *pathPtr, 02429 Tcl_Obj **objPtrRef) 02430 { 02431 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02432 02433 if (fsPtr != NULL) { 02434 Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc; 02435 if (proc != NULL) { 02436 return (*proc)(pathPtr, objPtrRef); 02437 } 02438 } 02439 Tcl_SetErrno(ENOENT); 02440 return NULL; 02441 } 02442 02443 /* 02444 *---------------------------------------------------------------------- 02445 * 02446 * TclFSFileAttrIndex -- 02447 * 02448 * Helper function for converting an attribute name to an index into the 02449 * attribute table. 02450 * 02451 * Results: 02452 * Tcl result code, index written to *indexPtr on result==TCL_OK 02453 * 02454 * Side effects: 02455 * None. 02456 * 02457 *---------------------------------------------------------------------- 02458 */ 02459 02460 int 02461 TclFSFileAttrIndex( 02462 Tcl_Obj *pathPtr, /* File whose attributes are to be indexed 02463 * into. */ 02464 const char *attributeName, /* The attribute being looked for. */ 02465 int *indexPtr) /* Where to write the found index. */ 02466 { 02467 Tcl_Obj *listObj = NULL; 02468 const char **attrTable; 02469 02470 /* 02471 * Get the attribute table for the file. 02472 */ 02473 02474 attrTable = Tcl_FSFileAttrStrings(pathPtr, &listObj); 02475 if (listObj != NULL) { 02476 Tcl_IncrRefCount(listObj); 02477 } 02478 02479 if (attrTable != NULL) { 02480 /* 02481 * It's a constant attribute table, so use T_GIFO. 02482 */ 02483 02484 Tcl_Obj *tmpObj = Tcl_NewStringObj(attributeName, -1); 02485 int result; 02486 02487 result = Tcl_GetIndexFromObj(NULL, tmpObj, attrTable, NULL, TCL_EXACT, 02488 indexPtr); 02489 TclDecrRefCount(tmpObj); 02490 if (listObj != NULL) { 02491 TclDecrRefCount(listObj); 02492 } 02493 return result; 02494 } else if (listObj != NULL) { 02495 /* 02496 * It's a non-constant attribute list, so do a literal search. 02497 */ 02498 02499 int i, objc; 02500 Tcl_Obj **objv; 02501 02502 if (Tcl_ListObjGetElements(NULL, listObj, &objc, &objv) != TCL_OK) { 02503 TclDecrRefCount(listObj); 02504 return TCL_ERROR; 02505 } 02506 for (i=0 ; i<objc ; i++) { 02507 if (!strcmp(attributeName, TclGetString(objv[i]))) { 02508 TclDecrRefCount(listObj); 02509 *indexPtr = i; 02510 return TCL_OK; 02511 } 02512 } 02513 TclDecrRefCount(listObj); 02514 return TCL_ERROR; 02515 } else { 02516 return TCL_ERROR; 02517 } 02518 } 02519 02520 /* 02521 *---------------------------------------------------------------------- 02522 * 02523 * Tcl_FSFileAttrsGet -- 02524 * 02525 * This function implements read access for the hookable 'file 02526 * attributes' subcommand. The appropriate function for the filesystem to 02527 * which pathPtr belongs will be called. 02528 * 02529 * Results: 02530 * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK 02531 * was returned) is likely to have a refCount of zero. Either way we must 02532 * either store it somewhere (e.g. the Tcl result), or Incr/Decr its 02533 * refCount to ensure it is properly freed. 02534 * 02535 * Side effects: 02536 * None. 02537 * 02538 *---------------------------------------------------------------------- 02539 */ 02540 02541 int 02542 Tcl_FSFileAttrsGet( 02543 Tcl_Interp *interp, /* The interpreter for error reporting. */ 02544 int index, /* index of the attribute command. */ 02545 Tcl_Obj *pathPtr, /* filename we are operating on. */ 02546 Tcl_Obj **objPtrRef) /* for output. */ 02547 { 02548 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02549 02550 if (fsPtr != NULL) { 02551 Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc; 02552 if (proc != NULL) { 02553 return (*proc)(interp, index, pathPtr, objPtrRef); 02554 } 02555 } 02556 Tcl_SetErrno(ENOENT); 02557 return -1; 02558 } 02559 02560 /* 02561 *---------------------------------------------------------------------- 02562 * 02563 * Tcl_FSFileAttrsSet -- 02564 * 02565 * This function implements write access for the hookable 'file 02566 * attributes' subcommand. The appropriate function for the filesystem to 02567 * which pathPtr belongs will be called. 02568 * 02569 * Results: 02570 * Standard Tcl return code. 02571 * 02572 * Side effects: 02573 * None. 02574 * 02575 *---------------------------------------------------------------------- 02576 */ 02577 02578 int 02579 Tcl_FSFileAttrsSet( 02580 Tcl_Interp *interp, /* The interpreter for error reporting. */ 02581 int index, /* index of the attribute command. */ 02582 Tcl_Obj *pathPtr, /* filename we are operating on. */ 02583 Tcl_Obj *objPtr) /* Input value. */ 02584 { 02585 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02586 02587 if (fsPtr != NULL) { 02588 Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc; 02589 if (proc != NULL) { 02590 return (*proc)(interp, index, pathPtr, objPtr); 02591 } 02592 } 02593 Tcl_SetErrno(ENOENT); 02594 return -1; 02595 } 02596 02597 /* 02598 *---------------------------------------------------------------------- 02599 * 02600 * Tcl_FSGetCwd -- 02601 * 02602 * This function replaces the library version of getcwd(). 02603 * 02604 * Most VFS's will *not* implement a 'cwdProc'. Tcl now maintains its own 02605 * record (in a Tcl_Obj) of the cwd, and an attempt is made to synch this 02606 * with the cwd's containing filesystem, if that filesystem provides a 02607 * cwdProc (e.g. the native filesystem). 02608 * 02609 * Note that if Tcl's cwd is not in the native filesystem, then of course 02610 * Tcl's cwd and the native cwd are different: extensions should 02611 * therefore ensure they only access the cwd through this function to 02612 * avoid confusion. 02613 * 02614 * If a global cwdPathPtr already exists, it is cached in the thread's 02615 * private data structures and reference to the cached copy is returned, 02616 * subject to a synchronisation attempt in that cwdPathPtr's fs. 02617 * 02618 * Otherwise, the chain of functions that have been "inserted" into the 02619 * filesystem will be called in succession until either a value other 02620 * than NULL is returned, or the entire list is visited. 02621 * 02622 * Results: 02623 * The result is a pointer to a Tcl_Obj specifying the current directory, 02624 * or NULL if the current directory could not be determined. If NULL is 02625 * returned, an error message is left in the interp's result. 02626 * 02627 * The result already has its refCount incremented for the caller. When 02628 * it is no longer needed, that refCount should be decremented. 02629 * 02630 * Side effects: 02631 * Various objects may be freed and allocated. 02632 * 02633 *---------------------------------------------------------------------- 02634 */ 02635 02636 Tcl_Obj * 02637 Tcl_FSGetCwd( 02638 Tcl_Interp *interp) 02639 { 02640 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 02641 02642 if (TclFSCwdPointerEquals(NULL)) { 02643 FilesystemRecord *fsRecPtr; 02644 Tcl_Obj *retVal = NULL; 02645 02646 /* 02647 * We've never been called before, try to find a cwd. Call each of the 02648 * "Tcl_GetCwd" function in succession. A non-NULL return value 02649 * indicates the particular function has succeeded. 02650 */ 02651 02652 fsRecPtr = FsGetFirstFilesystem(); 02653 while ((retVal == NULL) && (fsRecPtr != NULL)) { 02654 Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc; 02655 if (proc != NULL) { 02656 if (fsRecPtr->fsPtr->version != TCL_FILESYSTEM_VERSION_1) { 02657 ClientData retCd; 02658 TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc; 02659 02660 retCd = (*proc2)(NULL); 02661 if (retCd != NULL) { 02662 Tcl_Obj *norm; 02663 /* Looks like a new current directory */ 02664 retVal = (*fsRecPtr->fsPtr->internalToNormalizedProc)( 02665 retCd); 02666 Tcl_IncrRefCount(retVal); 02667 norm = TclFSNormalizeAbsolutePath(interp,retVal,NULL); 02668 if (norm != NULL) { 02669 /* 02670 * We found a cwd, which is now in our global 02671 * storage. We must make a copy. Norm already has 02672 * a refCount of 1. 02673 * 02674 * Threading issue: note that multiple threads at 02675 * system startup could in principle call this 02676 * function simultaneously. They will therefore 02677 * each set the cwdPathPtr independently. That 02678 * behaviour is a bit peculiar, but should be 02679 * fine. Once we have a cwd, we'll always be in 02680 * the 'else' branch below which is simpler. 02681 */ 02682 02683 FsUpdateCwd(norm, retCd); 02684 Tcl_DecrRefCount(norm); 02685 } else { 02686 (*fsRecPtr->fsPtr->freeInternalRepProc)(retCd); 02687 } 02688 Tcl_DecrRefCount(retVal); 02689 retVal = NULL; 02690 goto cdDidNotChange; 02691 } else if (interp != NULL) { 02692 Tcl_AppendResult(interp, 02693 "error getting working directory name: ", 02694 Tcl_PosixError(interp), NULL); 02695 } 02696 } else { 02697 retVal = (*proc)(interp); 02698 } 02699 } 02700 fsRecPtr = fsRecPtr->nextPtr; 02701 } 02702 02703 /* 02704 * Now the 'cwd' may NOT be normalized, at least on some platforms. 02705 * For the sake of efficiency, we want a completely normalized cwd at 02706 * all times. 02707 * 02708 * Finally, if retVal is NULL, we do not have a cwd, which could be 02709 * problematic. 02710 */ 02711 02712 if (retVal != NULL) { 02713 Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal, NULL); 02714 if (norm != NULL) { 02715 /* 02716 * We found a cwd, which is now in our global storage. We must 02717 * make a copy. Norm already has a refCount of 1. 02718 * 02719 * Threading issue: note that multiple threads at system 02720 * startup could in principle call this function 02721 * simultaneously. They will therefore each set the cwdPathPtr 02722 * independently. That behaviour is a bit peculiar, but should 02723 * be fine. Once we have a cwd, we'll always be in the 'else' 02724 * branch below which is simpler. 02725 */ 02726 02727 ClientData cd = (ClientData) Tcl_FSGetNativePath(norm); 02728 FsUpdateCwd(norm, TclNativeDupInternalRep(cd)); 02729 Tcl_DecrRefCount(norm); 02730 } 02731 Tcl_DecrRefCount(retVal); 02732 } 02733 } else { 02734 /* 02735 * We already have a cwd cached, but we want to give the filesystem it 02736 * is in a chance to check whether that cwd has changed, or is perhaps 02737 * no longer accessible. This allows an error to be thrown if, say, 02738 * the permissions on that directory have changed. 02739 */ 02740 02741 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr); 02742 02743 /* 02744 * If the filesystem couldn't be found, or if no cwd function exists 02745 * for this filesystem, then we simply assume the cached cwd is ok. 02746 * If we do call a cwd, we must watch for errors (if the cwd returns 02747 * NULL). This ensures that, say, on Unix if the permissions of the 02748 * cwd change, 'pwd' does actually throw the correct error in Tcl. 02749 * (This is tested for in the test suite on unix). 02750 */ 02751 02752 if (fsPtr != NULL) { 02753 Tcl_FSGetCwdProc *proc = fsPtr->getCwdProc; 02754 ClientData retCd = NULL; 02755 if (proc != NULL) { 02756 Tcl_Obj *retVal; 02757 if (fsPtr->version != TCL_FILESYSTEM_VERSION_1) { 02758 TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)proc; 02759 02760 retCd = (*proc2)(tsdPtr->cwdClientData); 02761 if (retCd == NULL && interp != NULL) { 02762 Tcl_AppendResult(interp, 02763 "error getting working directory name: ", 02764 Tcl_PosixError(interp), NULL); 02765 } 02766 02767 if (retCd == tsdPtr->cwdClientData) { 02768 goto cdDidNotChange; 02769 } 02770 02771 /* 02772 * Looks like a new current directory. 02773 */ 02774 02775 retVal = (*fsPtr->internalToNormalizedProc)(retCd); 02776 Tcl_IncrRefCount(retVal); 02777 } else { 02778 retVal = (*proc)(interp); 02779 } 02780 if (retVal != NULL) { 02781 Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, 02782 retVal, NULL); 02783 02784 /* 02785 * Check whether cwd has changed from the value previously 02786 * stored in cwdPathPtr. Really 'norm' shouldn't be NULL, 02787 * but we are careful. 02788 */ 02789 02790 if (norm == NULL) { 02791 /* Do nothing */ 02792 if (retCd != NULL) { 02793 (*fsPtr->freeInternalRepProc)(retCd); 02794 } 02795 } else if (norm == tsdPtr->cwdPathPtr) { 02796 goto cdEqual; 02797 } else { 02798 /* 02799 * Note that both 'norm' and 'tsdPtr->cwdPathPtr' are 02800 * normalized paths. Therefore we can be more 02801 * efficient than calling 'Tcl_FSEqualPaths', and in 02802 * addition avoid a nasty infinite loop bug when 02803 * trying to normalize tsdPtr->cwdPathPtr. 02804 */ 02805 02806 int len1, len2; 02807 char *str1, *str2; 02808 02809 str1 = Tcl_GetStringFromObj(tsdPtr->cwdPathPtr, &len1); 02810 str2 = Tcl_GetStringFromObj(norm, &len2); 02811 if ((len1 == len2) && (strcmp(str1, str2) == 0)) { 02812 /* 02813 * If the paths were equal, we can be more 02814 * efficient and retain the old path object which 02815 * will probably already be shared. In this case 02816 * we can simply free the normalized path we just 02817 * calculated. 02818 */ 02819 02820 cdEqual: 02821 Tcl_DecrRefCount(norm); 02822 if (retCd != NULL) { 02823 (*fsPtr->freeInternalRepProc)(retCd); 02824 } 02825 } else { 02826 FsUpdateCwd(norm, retCd); 02827 Tcl_DecrRefCount(norm); 02828 } 02829 } 02830 Tcl_DecrRefCount(retVal); 02831 } else { 02832 /* 02833 * The 'cwd' function returned an error; reset the cwd. 02834 */ 02835 02836 FsUpdateCwd(NULL, NULL); 02837 } 02838 } 02839 } 02840 } 02841 02842 cdDidNotChange: 02843 if (tsdPtr->cwdPathPtr != NULL) { 02844 Tcl_IncrRefCount(tsdPtr->cwdPathPtr); 02845 } 02846 02847 return tsdPtr->cwdPathPtr; 02848 } 02849 02850 /* 02851 *---------------------------------------------------------------------- 02852 * 02853 * Tcl_FSChdir -- 02854 * 02855 * This function replaces the library version of chdir(). 02856 * 02857 * The path is normalized and then passed to the filesystem which claims 02858 * it. 02859 * 02860 * Results: 02861 * See chdir() documentation. If successful, we keep a record of the 02862 * successful path in cwdPathPtr for subsequent calls to getcwd. 02863 * 02864 * Side effects: 02865 * See chdir() documentation. The global cwdPathPtr may change value. 02866 * 02867 *---------------------------------------------------------------------- 02868 */ 02869 02870 int 02871 Tcl_FSChdir( 02872 Tcl_Obj *pathPtr) 02873 { 02874 const Tcl_Filesystem *fsPtr; 02875 int retVal = -1; 02876 02877 if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) { 02878 Tcl_SetErrno(ENOENT); 02879 return retVal; 02880 } 02881 02882 fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 02883 if (fsPtr != NULL) { 02884 Tcl_FSChdirProc *proc = fsPtr->chdirProc; 02885 if (proc != NULL) { 02886 /* 02887 * If this fails, an appropriate errno will have been stored using 02888 * 'Tcl_SetErrno()'. 02889 */ 02890 02891 retVal = (*proc)(pathPtr); 02892 } else { 02893 /* 02894 * Fallback on stat-based implementation. 02895 */ 02896 02897 Tcl_StatBuf buf; 02898 02899 /* 02900 * If the file can be stat'ed and is a directory and is readable, 02901 * then we can chdir. If any of these actions fail, then 02902 * 'Tcl_SetErrno()' should automatically have been called to set 02903 * an appropriate error code 02904 */ 02905 02906 if ((Tcl_FSStat(pathPtr, &buf) == 0) && (S_ISDIR(buf.st_mode)) 02907 && (Tcl_FSAccess(pathPtr, R_OK) == 0)) { 02908 /* 02909 * We allow the chdir. 02910 */ 02911 02912 retVal = 0; 02913 } 02914 } 02915 } else { 02916 Tcl_SetErrno(ENOENT); 02917 } 02918 02919 /* 02920 * The cwd changed, or an error was thrown. If an error was thrown, we can 02921 * just continue (and that will report the error to the user). If there 02922 * was no error we must assume that the cwd was actually changed to the 02923 * normalized value we calculated above, and we must therefore cache that 02924 * information. 02925 */ 02926 02927 /* 02928 * If the filesystem in question has a getCwdProc, then the correct logic 02929 * which performs the part below is already part of the Tcl_FSGetCwd() 02930 * call, so no need to replicate it again. This will have a side effect 02931 * though. The private authoritative representation of the current working 02932 * directory stored in cwdPathPtr in static memory will be out-of-sync 02933 * with the real OS-maintained value. The first call to Tcl_FSGetCwd will 02934 * however recalculate the private copy to match the OS-value so 02935 * everything will work right. 02936 * 02937 * However, if there is no getCwdProc, then we _must_ update our private 02938 * storage of the cwd, since this is the only opportunity to do that! 02939 * 02940 * Note: We currently call this block of code irrespective of whether 02941 * there was a getCwdProc or not, but the code should all in principle 02942 * work if we only call this block if fsPtr->getCwdProc == NULL. 02943 */ 02944 02945 if (retVal == 0) { 02946 /* 02947 * Note that this normalized path may be different to what we found 02948 * above (or at least a different object), if the filesystem epoch 02949 * changed recently. This can actually happen with scripted documents 02950 * very easily. Therefore we ask for the normalized path again (the 02951 * correct value will have been cached as a result of the 02952 * Tcl_FSGetFileSystemForPath call above anyway). 02953 */ 02954 02955 Tcl_Obj *normDirName = Tcl_FSGetNormalizedPath(NULL, pathPtr); 02956 02957 if (normDirName == NULL) { 02958 /* Not really true, but what else to do? */ 02959 Tcl_SetErrno(ENOENT); 02960 return -1; 02961 } 02962 02963 if (fsPtr == &tclNativeFilesystem) { 02964 /* 02965 * For the native filesystem, we keep a cache of the native 02966 * representation of the cwd. But, we want to do that for the 02967 * exact format that is returned by 'getcwd' (so that we can later 02968 * compare the two representations for equality), which might not 02969 * be exactly the same char-string as the native representation of 02970 * the fully normalized path (e.g. on Windows there's a 02971 * forward-slash vs backslash difference). Hence we ask for this 02972 * again here. On Unix it might actually be true that we always 02973 * have the correct form in the native rep in which case we could 02974 * simply use: 02975 * cd = Tcl_FSGetNativePath(pathPtr); 02976 * instead. This should be examined by someone on Unix. 02977 */ 02978 02979 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); 02980 ClientData cd; 02981 ClientData oldcd = tsdPtr->cwdClientData; 02982 02983 /* 02984 * Assumption we are using a filesystem version 2. 02985 */ 02986 02987 TclFSGetCwdProc2 *proc2 = (TclFSGetCwdProc2*)fsPtr->getCwdProc; 02988 cd = (*proc2)(oldcd); 02989 if (cd != oldcd) { 02990 FsUpdateCwd(normDirName, cd); 02991 } 02992 } else { 02993 FsUpdateCwd(normDirName, NULL); 02994 } 02995 } 02996 02997 return retVal; 02998 } 02999 03000 /* 03001 *---------------------------------------------------------------------- 03002 * 03003 * Tcl_FSLoadFile -- 03004 * 03005 * Dynamically loads a binary code file into memory and returns the 03006 * addresses of two functions within that file, if they are defined. The 03007 * appropriate function for the filesystem to which pathPtr belongs will 03008 * be called. 03009 * 03010 * Note that the native filesystem doesn't actually assume 'pathPtr' is a 03011 * path. Rather it assumes pathPtr is either a path or just the name 03012 * (tail) of a file which can be found somewhere in the environment's 03013 * loadable path. This behaviour is not very compatible with virtual 03014 * filesystems (and has other problems documented in the load man-page), 03015 * so it is advised that full paths are always used. 03016 * 03017 * Results: 03018 * A standard Tcl completion code. If an error occurs, an error message 03019 * is left in the interp's result. 03020 * 03021 * Side effects: 03022 * New code suddenly appears in memory. This may later be unloaded by 03023 * passing the clientData to the unloadProc. 03024 * 03025 *---------------------------------------------------------------------- 03026 */ 03027 03028 int 03029 Tcl_FSLoadFile( 03030 Tcl_Interp *interp, /* Used for error reporting. */ 03031 Tcl_Obj *pathPtr, /* Name of the file containing the desired 03032 * code. */ 03033 const char *sym1, const char *sym2, 03034 /* Names of two functions to look up in the 03035 * file's symbol table. */ 03036 Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, 03037 /* Where to return the addresses corresponding 03038 * to sym1 and sym2. */ 03039 Tcl_LoadHandle *handlePtr, /* Filled with token for dynamically loaded 03040 * file which will be passed back to 03041 * (*unloadProcPtr)() to unload the file. */ 03042 Tcl_FSUnloadFileProc **unloadProcPtr) 03043 /* Filled with address of Tcl_FSUnloadFileProc 03044 * function which should be used for this 03045 * file. */ 03046 { 03047 const char *symbols[2]; 03048 Tcl_PackageInitProc **procPtrs[2]; 03049 ClientData clientData; 03050 int res; 03051 03052 /* 03053 * Initialize the arrays. 03054 */ 03055 03056 symbols[0] = sym1; 03057 symbols[1] = sym2; 03058 procPtrs[0] = proc1Ptr; 03059 procPtrs[1] = proc2Ptr; 03060 03061 /* 03062 * Perform the load. 03063 */ 03064 03065 res = TclLoadFile(interp, pathPtr, 2, symbols, procPtrs, handlePtr, 03066 &clientData, unloadProcPtr); 03067 03068 /* 03069 * Due to an unfortunate mis-design in Tcl 8.4 fs, when loading a shared 03070 * library, we don't keep the loadHandle (for TclpFindSymbol) and the 03071 * clientData (for the unloadProc) separately. In fact we effectively 03072 * throw away the loadHandle and only use the clientData. It just so 03073 * happens, for the native filesystem only, that these two are identical. 03074 * 03075 * This also means that the signatures Tcl_FSUnloadFileProc and 03076 * Tcl_FSLoadFileProc are both misleading. 03077 */ 03078 03079 *handlePtr = (Tcl_LoadHandle) clientData; 03080 return res; 03081 } 03082 03083 /* 03084 *---------------------------------------------------------------------- 03085 * 03086 * TclLoadFile -- 03087 * 03088 * Dynamically loads a binary code file into memory and returns the 03089 * addresses of a number of given functions within that file, if they are 03090 * defined. The appropriate function for the filesystem to which pathPtr 03091 * belongs will be called. 03092 * 03093 * Note that the native filesystem doesn't actually assume 'pathPtr' is a 03094 * path. Rather it assumes pathPtr is either a path or just the name 03095 * (tail) of a file which can be found somewhere in the environment's 03096 * loadable path. This behaviour is not very compatible with virtual 03097 * filesystems (and has other problems documented in the load man-page), 03098 * so it is advised that full paths are always used. 03099 * 03100 * This function is currently private to Tcl. It may be exported in the 03101 * future and its interface fixed (but we should clean up the 03102 * loadHandle/clientData confusion at that time -- see the above comments 03103 * in Tcl_FSLoadFile for details). For a public function, see 03104 * Tcl_FSLoadFile. 03105 * 03106 * Results: 03107 * A standard Tcl completion code. If an error occurs, an error message 03108 * is left in the interp's result. 03109 * 03110 * Side effects: 03111 * New code suddenly appears in memory. This may later be unloaded by 03112 * passing the clientData to the unloadProc. 03113 * 03114 *---------------------------------------------------------------------- 03115 */ 03116 03117 int 03118 TclLoadFile( 03119 Tcl_Interp *interp, /* Used for error reporting. */ 03120 Tcl_Obj *pathPtr, /* Name of the file containing the desired 03121 * code. */ 03122 int symc, /* Number of symbols/procPtrs in the next two 03123 * arrays. */ 03124 const char *symbols[], /* Names of functions to look up in the file's 03125 * symbol table. */ 03126 Tcl_PackageInitProc **procPtrs[], 03127 /* Where to return the addresses corresponding 03128 * to symbols[]. */ 03129 Tcl_LoadHandle *handlePtr, /* Filled with token for shared library 03130 * information which can be used in 03131 * TclpFindSymbol. */ 03132 ClientData *clientDataPtr, /* Filled with token for dynamically loaded 03133 * file which will be passed back to 03134 * (*unloadProcPtr)() to unload the file. */ 03135 Tcl_FSUnloadFileProc **unloadProcPtr) 03136 /* Filled with address of Tcl_FSUnloadFileProc 03137 * function which should be used for this 03138 * file. */ 03139 { 03140 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 03141 Tcl_FSLoadFileProc *proc; 03142 Tcl_Filesystem *copyFsPtr; 03143 Tcl_Obj *copyToPtr; 03144 Tcl_LoadHandle newLoadHandle = NULL; 03145 ClientData newClientData = NULL; 03146 Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL; 03147 FsDivertLoad *tvdlPtr; 03148 int retVal; 03149 03150 if (fsPtr == NULL) { 03151 Tcl_SetErrno(ENOENT); 03152 return TCL_ERROR; 03153 } 03154 03155 proc = fsPtr->loadFileProc; 03156 if (proc != NULL) { 03157 int retVal = (*proc)(interp, pathPtr, handlePtr, unloadProcPtr); 03158 if (retVal == TCL_OK) { 03159 if (*handlePtr == NULL) { 03160 return TCL_ERROR; 03161 } 03162 03163 /* 03164 * Copy this across, since both are equal for the native fs. 03165 */ 03166 03167 *clientDataPtr = (ClientData)*handlePtr; 03168 Tcl_ResetResult(interp); 03169 goto resolveSymbols; 03170 } 03171 if (Tcl_GetErrno() != EXDEV) { 03172 return retVal; 03173 } 03174 } 03175 03176 /* 03177 * The filesystem doesn't support 'load', so we fall back on the following 03178 * technique: 03179 * 03180 * First check if it is readable -- and exists! 03181 */ 03182 03183 if (Tcl_FSAccess(pathPtr, R_OK) != 0) { 03184 Tcl_AppendResult(interp, "couldn't load library \"", 03185 Tcl_GetString(pathPtr), "\": ", Tcl_PosixError(interp), NULL); 03186 return TCL_ERROR; 03187 } 03188 03189 #ifdef TCL_LOAD_FROM_MEMORY 03190 /* 03191 * The platform supports loading code from memory, so ask for a buffer of 03192 * the appropriate size, read the file into it and load the code from the 03193 * buffer: 03194 */ 03195 03196 { 03197 int ret, size; 03198 void *buffer; 03199 Tcl_StatBuf statBuf; 03200 Tcl_Channel data; 03201 03202 ret = Tcl_FSStat(pathPtr, &statBuf); 03203 if (ret < 0) { 03204 goto mustCopyToTempAnyway; 03205 } 03206 size = (int) statBuf.st_size; 03207 03208 /* 03209 * Tcl_Read takes an int: check that file size isn't wide. 03210 */ 03211 03212 if (size != (Tcl_WideInt) statBuf.st_size) { 03213 goto mustCopyToTempAnyway; 03214 } 03215 data = Tcl_FSOpenFileChannel(interp, pathPtr, "rb", 0666); 03216 if (!data) { 03217 goto mustCopyToTempAnyway; 03218 } 03219 buffer = TclpLoadMemoryGetBuffer(interp, size); 03220 if (!buffer) { 03221 Tcl_Close(interp, data); 03222 goto mustCopyToTempAnyway; 03223 } 03224 ret = Tcl_Read(data, buffer, size); 03225 Tcl_Close(interp, data); 03226 ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr, 03227 unloadProcPtr); 03228 if (ret == TCL_OK && *handlePtr != NULL) { 03229 *clientDataPtr = (ClientData) *handlePtr; 03230 goto resolveSymbols; 03231 } 03232 } 03233 03234 mustCopyToTempAnyway: 03235 Tcl_ResetResult(interp); 03236 #endif 03237 03238 /* 03239 * Get a temporary filename to use, first to copy the file into, and then 03240 * to load. 03241 */ 03242 03243 copyToPtr = TclpTempFileName(); 03244 if (copyToPtr == NULL) { 03245 Tcl_AppendResult(interp, "couldn't create temporary file: ", 03246 Tcl_PosixError(interp), NULL); 03247 return TCL_ERROR; 03248 } 03249 Tcl_IncrRefCount(copyToPtr); 03250 03251 copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr); 03252 if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) { 03253 /* 03254 * We already know we can't use Tcl_FSLoadFile from this filesystem, 03255 * and we must avoid a possible infinite loop. Try to delete the file 03256 * we probably created, and then exit. 03257 */ 03258 03259 Tcl_FSDeleteFile(copyToPtr); 03260 Tcl_DecrRefCount(copyToPtr); 03261 Tcl_AppendResult(interp, "couldn't load from current filesystem",NULL); 03262 return TCL_ERROR; 03263 } 03264 03265 if (TclCrossFilesystemCopy(interp, pathPtr, copyToPtr) != TCL_OK) { 03266 /* 03267 * Cross-platform copy failed. 03268 */ 03269 03270 Tcl_FSDeleteFile(copyToPtr); 03271 Tcl_DecrRefCount(copyToPtr); 03272 return TCL_ERROR; 03273 } 03274 03275 #if !defined(__WIN32__) 03276 /* 03277 * Do we need to set appropriate permissions on the file? This may be 03278 * required on some systems. On Unix we could loop over the file 03279 * attributes, and set any that are called "-permissions" to 0700. However 03280 * we just do this directly, like this: 03281 */ 03282 03283 { 03284 int index; 03285 Tcl_Obj *perm; 03286 03287 TclNewLiteralStringObj(perm, "0700"); 03288 Tcl_IncrRefCount(perm); 03289 if (TclFSFileAttrIndex(copyToPtr, "-permissions", &index) == TCL_OK) { 03290 Tcl_FSFileAttrsSet(NULL, index, copyToPtr, perm); 03291 } 03292 Tcl_DecrRefCount(perm); 03293 } 03294 #endif 03295 03296 /* 03297 * We need to reset the result now, because the cross-filesystem copy may 03298 * have stored the number of bytes in the result. 03299 */ 03300 03301 Tcl_ResetResult(interp); 03302 03303 retVal = TclLoadFile(interp, copyToPtr, symc, symbols, procPtrs, 03304 &newLoadHandle, &newClientData, &newUnloadProcPtr); 03305 if (retVal != TCL_OK) { 03306 /* 03307 * The file didn't load successfully. 03308 */ 03309 03310 Tcl_FSDeleteFile(copyToPtr); 03311 Tcl_DecrRefCount(copyToPtr); 03312 return retVal; 03313 } 03314 03315 /* 03316 * Try to delete the file immediately - this is possible in some OSes, and 03317 * avoids any worries about leaving the copy laying around on exit. 03318 */ 03319 03320 if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) { 03321 Tcl_DecrRefCount(copyToPtr); 03322 03323 /* 03324 * We tell our caller about the real shared library which was loaded. 03325 * Note that this does mean that the package list maintained by 'load' 03326 * will store the original (vfs) path alongside the temporary load 03327 * handle and unload proc ptr. 03328 */ 03329 03330 (*handlePtr) = newLoadHandle; 03331 (*clientDataPtr) = newClientData; 03332 (*unloadProcPtr) = newUnloadProcPtr; 03333 Tcl_ResetResult(interp); 03334 return TCL_OK; 03335 } 03336 03337 /* 03338 * When we unload this file, we need to divert the unloading so we can 03339 * unload and cleanup the temporary file correctly. 03340 */ 03341 03342 tvdlPtr = (FsDivertLoad *) ckalloc(sizeof(FsDivertLoad)); 03343 03344 /* 03345 * Remember three pieces of information. This allows us to cleanup the 03346 * diverted load completely, on platforms which allow proper unloading of 03347 * code. 03348 */ 03349 03350 tvdlPtr->loadHandle = newLoadHandle; 03351 tvdlPtr->unloadProcPtr = newUnloadProcPtr; 03352 03353 if (copyFsPtr != &tclNativeFilesystem) { 03354 /* 03355 * copyToPtr is already incremented for this reference. 03356 */ 03357 03358 tvdlPtr->divertedFile = copyToPtr; 03359 03360 /* 03361 * This is the filesystem we loaded it into. Since we have a reference 03362 * to 'copyToPtr', we already have a refCount on this filesystem, so 03363 * we don't need to worry about it disappearing on us. 03364 */ 03365 03366 tvdlPtr->divertedFilesystem = copyFsPtr; 03367 tvdlPtr->divertedFileNativeRep = NULL; 03368 } else { 03369 /* 03370 * We need the native rep. 03371 */ 03372 03373 tvdlPtr->divertedFileNativeRep = TclNativeDupInternalRep( 03374 Tcl_FSGetInternalRep(copyToPtr, copyFsPtr)); 03375 03376 /* 03377 * We don't need or want references to the copied Tcl_Obj or the 03378 * filesystem if it is the native one. 03379 */ 03380 03381 tvdlPtr->divertedFile = NULL; 03382 tvdlPtr->divertedFilesystem = NULL; 03383 Tcl_DecrRefCount(copyToPtr); 03384 } 03385 03386 copyToPtr = NULL; 03387 (*handlePtr) = newLoadHandle; 03388 (*clientDataPtr) = (ClientData) tvdlPtr; 03389 (*unloadProcPtr) = &FSUnloadTempFile; 03390 03391 Tcl_ResetResult(interp); 03392 return retVal; 03393 03394 resolveSymbols: 03395 { 03396 int i; 03397 03398 for (i=0 ; i<symc ; i++) { 03399 if (symbols[i] != NULL) { 03400 *procPtrs[i] = TclpFindSymbol(interp, *handlePtr, symbols[i]); 03401 } 03402 } 03403 } 03404 return TCL_OK; 03405 } 03406 /* 03407 * This function used to be in the platform specific directories, but it has 03408 * now been made to work cross-platform 03409 */ 03410 03411 int 03412 TclpLoadFile( 03413 Tcl_Interp *interp, /* Used for error reporting. */ 03414 Tcl_Obj *pathPtr, /* Name of the file containing the desired 03415 * code (UTF-8). */ 03416 const char *sym1, CONST char *sym2, 03417 /* Names of two functions to look up in the 03418 * file's symbol table. */ 03419 Tcl_PackageInitProc **proc1Ptr, Tcl_PackageInitProc **proc2Ptr, 03420 /* Where to return the addresses corresponding 03421 * to sym1 and sym2. */ 03422 ClientData *clientDataPtr, /* Filled with token for dynamically loaded 03423 * file which will be passed back to 03424 * (*unloadProcPtr)() to unload the file. */ 03425 Tcl_FSUnloadFileProc **unloadProcPtr) 03426 /* Filled with address of Tcl_FSUnloadFileProc 03427 * function which should be used for this 03428 * file. */ 03429 { 03430 Tcl_LoadHandle handle = NULL; 03431 int res; 03432 03433 res = TclpDlopen(interp, pathPtr, &handle, unloadProcPtr); 03434 03435 if (res != TCL_OK) { 03436 return res; 03437 } 03438 03439 if (handle == NULL) { 03440 return TCL_ERROR; 03441 } 03442 03443 *clientDataPtr = (ClientData) handle; 03444 03445 *proc1Ptr = TclpFindSymbol(interp, handle, sym1); 03446 *proc2Ptr = TclpFindSymbol(interp, handle, sym2); 03447 return TCL_OK; 03448 } 03449 03450 /* 03451 *--------------------------------------------------------------------------- 03452 * 03453 * FSUnloadTempFile -- 03454 * 03455 * This function is called when we loaded a library of code via an 03456 * intermediate temporary file. This function ensures the library is 03457 * correctly unloaded and the temporary file is correctly deleted. 03458 * 03459 * Results: 03460 * None. 03461 * 03462 * Side effects: 03463 * The effects of the 'unload' function called, and of course the 03464 * temporary file will be deleted. 03465 * 03466 *--------------------------------------------------------------------------- 03467 */ 03468 03469 static void 03470 FSUnloadTempFile( 03471 Tcl_LoadHandle loadHandle) /* loadHandle returned by a previous call to 03472 * Tcl_FSLoadFile(). The loadHandle is a token 03473 * that represents the loaded file. */ 03474 { 03475 FsDivertLoad *tvdlPtr = (FsDivertLoad *) loadHandle; 03476 03477 /* 03478 * This test should never trigger, since we give the client data in the 03479 * function above. 03480 */ 03481 03482 if (tvdlPtr == NULL) { 03483 return; 03484 } 03485 03486 /* 03487 * Call the real 'unloadfile' proc we actually used. It is very important 03488 * that we call this first, so that the shared library is actually 03489 * unloaded by the OS. Otherwise, the following 'delete' may well fail 03490 * because the shared library is still in use. 03491 */ 03492 03493 if (tvdlPtr->unloadProcPtr != NULL) { 03494 (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle); 03495 } 03496 03497 if (tvdlPtr->divertedFilesystem == NULL) { 03498 /* 03499 * It was the native filesystem, and we have a special function 03500 * available just for this purpose, which we know works even at this 03501 * late stage. 03502 */ 03503 03504 TclpDeleteFile(tvdlPtr->divertedFileNativeRep); 03505 NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep); 03506 03507 } else { 03508 /* 03509 * Remove the temporary file we created. Note, we may crash here 03510 * because encodings have been taken down already. 03511 */ 03512 03513 if (tvdlPtr->divertedFilesystem->deleteFileProc(tvdlPtr->divertedFile) 03514 != TCL_OK) { 03515 /* 03516 * The above may have failed because the filesystem, or something 03517 * it depends upon (e.g. encodings) have been taken down because 03518 * Tcl is exiting. 03519 * 03520 * We may need to work out how to delete this file more robustly 03521 * (or give the filesystem the information it needs to delete the 03522 * file more robustly). 03523 * 03524 * In particular, one problem might be that the filesystem cannot 03525 * extract the information it needs from the above path object 03526 * because Tcl's entire filesystem apparatus (the code in this 03527 * file) has been finalized, and it refuses to pass the internal 03528 * representation to the filesystem. 03529 */ 03530 } 03531 03532 /* 03533 * And free up the allocations. This will also of course remove a 03534 * refCount from the Tcl_Filesystem to which this file belongs, which 03535 * could then free up the filesystem if we are exiting. 03536 */ 03537 03538 Tcl_DecrRefCount(tvdlPtr->divertedFile); 03539 } 03540 03541 ckfree((char*)tvdlPtr); 03542 } 03543 03544 /* 03545 *--------------------------------------------------------------------------- 03546 * 03547 * Tcl_FSLink -- 03548 * 03549 * This function replaces the library version of readlink() and can also 03550 * be used to make links. The appropriate function for the filesystem to 03551 * which pathPtr belongs will be called. 03552 * 03553 * Results: 03554 * If toPtr is NULL, then the result is a Tcl_Obj specifying the contents 03555 * of the symbolic link given by 'pathPtr', or NULL if the symbolic link 03556 * could not be read. The result is owned by the caller, which should 03557 * call Tcl_DecrRefCount when the result is no longer needed. 03558 * 03559 * If toPtr is non-NULL, then the result is toPtr if the link action was 03560 * successful, or NULL if not. In this case the result has no additional 03561 * reference count, and need not be freed. The actual action to perform 03562 * is given by the 'linkAction' flags, which is an or'd combination of: 03563 * 03564 * TCL_CREATE_SYMBOLIC_LINK 03565 * TCL_CREATE_HARD_LINK 03566 * 03567 * Note that most filesystems will not support linking across to 03568 * different filesystems, so this function will usually fail unless toPtr 03569 * is in the same FS as pathPtr. 03570 * 03571 * Side effects: 03572 * See readlink() documentation. A new filesystem link object may appear. 03573 * 03574 *--------------------------------------------------------------------------- 03575 */ 03576 03577 Tcl_Obj * 03578 Tcl_FSLink( 03579 Tcl_Obj *pathPtr, /* Path of file to readlink or link */ 03580 Tcl_Obj *toPtr, /* NULL or path to be linked to */ 03581 int linkAction) /* Action to perform */ 03582 { 03583 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 03584 03585 if (fsPtr != NULL) { 03586 Tcl_FSLinkProc *proc = fsPtr->linkProc; 03587 03588 if (proc != NULL) { 03589 return (*proc)(pathPtr, toPtr, linkAction); 03590 } 03591 } 03592 03593 /* 03594 * If S_IFLNK isn't defined it means that the machine doesn't support 03595 * symbolic links, so the file can't possibly be a symbolic link. Generate 03596 * an EINVAL error, which is what happens on machines that do support 03597 * symbolic links when you invoke readlink on a file that isn't a symbolic 03598 * link. 03599 */ 03600 03601 #ifndef S_IFLNK 03602 errno = EINVAL; 03603 #else 03604 Tcl_SetErrno(ENOENT); 03605 #endif /* S_IFLNK */ 03606 return NULL; 03607 } 03608 03609 /* 03610 *--------------------------------------------------------------------------- 03611 * 03612 * Tcl_FSListVolumes -- 03613 * 03614 * Lists the currently mounted volumes. The chain of functions that have 03615 * been "inserted" into the filesystem will be called in succession; each 03616 * may return a list of volumes, all of which are added to the result 03617 * until all mounted file systems are listed. 03618 * 03619 * Notice that we assume the lists returned by each filesystem (if non 03620 * NULL) have been given a refCount for us already. However, we are NOT 03621 * allowed to hang on to the list itself (it belongs to the filesystem we 03622 * called). Therefore we quite naturally add its contents to the result 03623 * we are building, and then decrement the refCount. 03624 * 03625 * Results: 03626 * The list of volumes, in an object which has refCount 0. 03627 * 03628 * Side effects: 03629 * None 03630 * 03631 *--------------------------------------------------------------------------- 03632 */ 03633 03634 Tcl_Obj* 03635 Tcl_FSListVolumes(void) 03636 { 03637 FilesystemRecord *fsRecPtr; 03638 Tcl_Obj *resultPtr = Tcl_NewObj(); 03639 03640 /* 03641 * Call each of the "listVolumes" function in succession. A non-NULL 03642 * return value indicates the particular function has succeeded. We call 03643 * all the functions registered, since we want a list of all drives from 03644 * all filesystems. 03645 */ 03646 03647 fsRecPtr = FsGetFirstFilesystem(); 03648 while (fsRecPtr != NULL) { 03649 Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; 03650 if (proc != NULL) { 03651 Tcl_Obj *thisFsVolumes = (*proc)(); 03652 if (thisFsVolumes != NULL) { 03653 Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes); 03654 Tcl_DecrRefCount(thisFsVolumes); 03655 } 03656 } 03657 fsRecPtr = fsRecPtr->nextPtr; 03658 } 03659 03660 return resultPtr; 03661 } 03662 03663 /* 03664 *--------------------------------------------------------------------------- 03665 * 03666 * FsListMounts -- 03667 * 03668 * List all mounts within the given directory, which match the given 03669 * pattern. 03670 * 03671 * Results: 03672 * The list of mounts, in a list object which has refCount 0, or NULL if 03673 * we didn't even find any filesystems to try to list mounts. 03674 * 03675 * Side effects: 03676 * None 03677 * 03678 *--------------------------------------------------------------------------- 03679 */ 03680 03681 static Tcl_Obj * 03682 FsListMounts( 03683 Tcl_Obj *pathPtr, /* Contains path to directory to search. */ 03684 const char *pattern) /* Pattern to match against. */ 03685 { 03686 FilesystemRecord *fsRecPtr; 03687 Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL }; 03688 Tcl_Obj *resultPtr = NULL; 03689 03690 /* 03691 * Call each of the "matchInDirectory" functions in succession, with the 03692 * specific type information 'mountsOnly'. A non-NULL return value 03693 * indicates the particular function has succeeded. We call all the 03694 * functions registered, since we want a list from each filesystems. 03695 */ 03696 03697 fsRecPtr = FsGetFirstFilesystem(); 03698 while (fsRecPtr != NULL) { 03699 if (fsRecPtr->fsPtr != &tclNativeFilesystem) { 03700 Tcl_FSMatchInDirectoryProc *proc = 03701 fsRecPtr->fsPtr->matchInDirectoryProc; 03702 if (proc != NULL) { 03703 if (resultPtr == NULL) { 03704 resultPtr = Tcl_NewObj(); 03705 } 03706 (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly); 03707 } 03708 } 03709 fsRecPtr = fsRecPtr->nextPtr; 03710 } 03711 03712 return resultPtr; 03713 } 03714 03715 /* 03716 *--------------------------------------------------------------------------- 03717 * 03718 * Tcl_FSSplitPath -- 03719 * 03720 * This function takes the given Tcl_Obj, which should be a valid path, 03721 * and returns a Tcl List object containing each segment of that path as 03722 * an element. 03723 * 03724 * Results: 03725 * Returns list object with refCount of zero. If the passed in lenPtr is 03726 * non-NULL, we use it to return the number of elements in the returned 03727 * list. 03728 * 03729 * Side effects: 03730 * None. 03731 * 03732 *--------------------------------------------------------------------------- 03733 */ 03734 03735 Tcl_Obj * 03736 Tcl_FSSplitPath( 03737 Tcl_Obj *pathPtr, /* Path to split. */ 03738 int *lenPtr) /* int to store number of path elements. */ 03739 { 03740 Tcl_Obj *result = NULL; /* Needed only to prevent gcc warnings. */ 03741 Tcl_Filesystem *fsPtr; 03742 char separator = '/'; 03743 int driveNameLength; 03744 char *p; 03745 03746 /* 03747 * Perform platform specific splitting. 03748 */ 03749 03750 if (TclFSGetPathType(pathPtr, &fsPtr, 03751 &driveNameLength) == TCL_PATH_ABSOLUTE) { 03752 if (fsPtr == &tclNativeFilesystem) { 03753 return TclpNativeSplitPath(pathPtr, lenPtr); 03754 } 03755 } else { 03756 return TclpNativeSplitPath(pathPtr, lenPtr); 03757 } 03758 03759 /* 03760 * We assume separators are single characters. 03761 */ 03762 03763 if (fsPtr->filesystemSeparatorProc != NULL) { 03764 Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr); 03765 if (sep != NULL) { 03766 Tcl_IncrRefCount(sep); 03767 separator = Tcl_GetString(sep)[0]; 03768 Tcl_DecrRefCount(sep); 03769 } 03770 } 03771 03772 /* 03773 * Place the drive name as first element of the result list. The drive 03774 * name may contain strange characters, like colons and multiple forward 03775 * slashes (for example 'ftp://' is a valid vfs drive name) 03776 */ 03777 03778 result = Tcl_NewObj(); 03779 p = Tcl_GetString(pathPtr); 03780 Tcl_ListObjAppendElement(NULL, result, 03781 Tcl_NewStringObj(p, driveNameLength)); 03782 p += driveNameLength; 03783 03784 /* 03785 * Add the remaining path elements to the list. 03786 */ 03787 03788 for (;;) { 03789 char *elementStart = p; 03790 int length; 03791 while ((*p != '\0') && (*p != separator)) { 03792 p++; 03793 } 03794 length = p - elementStart; 03795 if (length > 0) { 03796 Tcl_Obj *nextElt; 03797 if (elementStart[0] == '~') { 03798 TclNewLiteralStringObj(nextElt, "./"); 03799 Tcl_AppendToObj(nextElt, elementStart, length); 03800 } else { 03801 nextElt = Tcl_NewStringObj(elementStart, length); 03802 } 03803 Tcl_ListObjAppendElement(NULL, result, nextElt); 03804 } 03805 if (*p++ == '\0') { 03806 break; 03807 } 03808 } 03809 03810 /* 03811 * Compute the number of elements in the result. 03812 */ 03813 03814 if (lenPtr != NULL) { 03815 TclListObjLength(NULL, result, lenPtr); 03816 } 03817 return result; 03818 } 03819 03820 /* Simple helper function */ 03821 Tcl_Obj * 03822 TclFSInternalToNormalized( 03823 Tcl_Filesystem *fromFilesystem, 03824 ClientData clientData, 03825 FilesystemRecord **fsRecPtrPtr) 03826 { 03827 FilesystemRecord *fsRecPtr = FsGetFirstFilesystem(); 03828 03829 while (fsRecPtr != NULL) { 03830 if (fsRecPtr->fsPtr == fromFilesystem) { 03831 *fsRecPtrPtr = fsRecPtr; 03832 break; 03833 } 03834 fsRecPtr = fsRecPtr->nextPtr; 03835 } 03836 03837 if ((fsRecPtr != NULL) 03838 && (fromFilesystem->internalToNormalizedProc != NULL)) { 03839 return (*fromFilesystem->internalToNormalizedProc)(clientData); 03840 } else { 03841 return NULL; 03842 } 03843 } 03844 03845 /* 03846 *---------------------------------------------------------------------- 03847 * 03848 * TclGetPathType -- 03849 * 03850 * Helper function used by FSGetPathType. 03851 * 03852 * Results: 03853 * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or 03854 * TCL_PATH_VOLUME_RELATIVE. The filesystem reference will be set if and 03855 * only if it is non-NULL and the function's return value is 03856 * TCL_PATH_ABSOLUTE. 03857 * 03858 * Side effects: 03859 * None. 03860 * 03861 *---------------------------------------------------------------------- 03862 */ 03863 03864 Tcl_PathType 03865 TclGetPathType( 03866 Tcl_Obj *pathPtr, /* Path to determine type for */ 03867 Tcl_Filesystem **filesystemPtrPtr, 03868 /* If absolute path and this is not NULL, then 03869 * set to the filesystem which claims this 03870 * path. */ 03871 int *driveNameLengthPtr, /* If the path is absolute, and this is 03872 * non-NULL, then set to the length of the 03873 * driveName. */ 03874 Tcl_Obj **driveNameRef) /* If the path is absolute, and this is 03875 * non-NULL, then set to the name of the 03876 * drive, network-volume which contains the 03877 * path, already with a refCount for the 03878 * caller. */ 03879 { 03880 int pathLen; 03881 char *path; 03882 Tcl_PathType type; 03883 03884 path = Tcl_GetStringFromObj(pathPtr, &pathLen); 03885 03886 type = TclFSNonnativePathType(path, pathLen, filesystemPtrPtr, 03887 driveNameLengthPtr, driveNameRef); 03888 03889 if (type != TCL_PATH_ABSOLUTE) { 03890 type = TclpGetNativePathType(pathPtr, driveNameLengthPtr, 03891 driveNameRef); 03892 if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) { 03893 *filesystemPtrPtr = &tclNativeFilesystem; 03894 } 03895 } 03896 return type; 03897 } 03898 03899 /* 03900 *---------------------------------------------------------------------- 03901 * 03902 * TclFSNonnativePathType -- 03903 * 03904 * Helper function used by TclGetPathType. Its purpose is to check 03905 * whether the given path starts with a string which corresponds to a 03906 * file volume in any registered filesystem except the native one. For 03907 * speed and historical reasons the native filesystem has special 03908 * hard-coded checks dotted here and there in the filesystem code. 03909 * 03910 * Results: 03911 * Returns one of TCL_PATH_ABSOLUTE or TCL_PATH_RELATIVE. The filesystem 03912 * reference will be set if and only if it is non-NULL and the function's 03913 * return value is TCL_PATH_ABSOLUTE. 03914 * 03915 * Side effects: 03916 * None. 03917 * 03918 *---------------------------------------------------------------------- 03919 */ 03920 03921 Tcl_PathType 03922 TclFSNonnativePathType( 03923 const char *path, /* Path to determine type for */ 03924 int pathLen, /* Length of the path */ 03925 Tcl_Filesystem **filesystemPtrPtr, 03926 /* If absolute path and this is not NULL, then 03927 * set to the filesystem which claims this 03928 * path. */ 03929 int *driveNameLengthPtr, /* If the path is absolute, and this is 03930 * non-NULL, then set to the length of the 03931 * driveName. */ 03932 Tcl_Obj **driveNameRef) /* If the path is absolute, and this is 03933 * non-NULL, then set to the name of the 03934 * drive, network-volume which contains the 03935 * path, already with a refCount for the 03936 * caller. */ 03937 { 03938 FilesystemRecord *fsRecPtr; 03939 Tcl_PathType type = TCL_PATH_RELATIVE; 03940 03941 /* 03942 * Call each of the "listVolumes" function in succession, checking whether 03943 * the given path is an absolute path on any of the volumes returned (this 03944 * is done by checking whether the path's prefix matches). 03945 */ 03946 03947 fsRecPtr = FsGetFirstFilesystem(); 03948 while (fsRecPtr != NULL) { 03949 Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc; 03950 03951 /* 03952 * We want to skip the native filesystem in this loop because 03953 * otherwise we won't necessarily pass all the Tcl testsuite -- this 03954 * is because some of the tests artificially change the current 03955 * platform (between win, unix) but the list of volumes we get by 03956 * calling (*proc) will reflect the current (real) platform only and 03957 * this may cause some tests to fail. In particular, on unix '/' will 03958 * match the beginning of certain absolute Windows paths starting '//' 03959 * and those tests will go wrong. 03960 * 03961 * Besides these test-suite issues, there is one other reason to skip 03962 * the native filesystem --- since the tclFilename.c code has nice 03963 * fast 'absolute path' checkers, we don't want to waste time 03964 * repeating that effort here, and this function is actually called 03965 * quite often, so if we can save the overhead of the native 03966 * filesystem returning us a list of volumes all the time, it is 03967 * better. 03968 */ 03969 03970 if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) { 03971 int numVolumes; 03972 Tcl_Obj *thisFsVolumes = (*proc)(); 03973 03974 if (thisFsVolumes != NULL) { 03975 if (Tcl_ListObjLength(NULL, thisFsVolumes, &numVolumes) 03976 != TCL_OK) { 03977 /* 03978 * This is VERY bad; the Tcl_FSListVolumesProc didn't 03979 * return a valid list. Set numVolumes to -1 so that we 03980 * skip the while loop below and just return with the 03981 * current value of 'type'. 03982 * 03983 * It would be better if we could signal an error here 03984 * (but Tcl_Panic seems a bit excessive). 03985 */ 03986 03987 numVolumes = -1; 03988 } 03989 while (numVolumes > 0) { 03990 Tcl_Obj *vol; 03991 int len; 03992 char *strVol; 03993 03994 numVolumes--; 03995 Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol); 03996 strVol = Tcl_GetStringFromObj(vol,&len); 03997 if (pathLen < len) { 03998 continue; 03999 } 04000 if (strncmp(strVol, path, (size_t) len) == 0) { 04001 type = TCL_PATH_ABSOLUTE; 04002 if (filesystemPtrPtr != NULL) { 04003 *filesystemPtrPtr = fsRecPtr->fsPtr; 04004 } 04005 if (driveNameLengthPtr != NULL) { 04006 *driveNameLengthPtr = len; 04007 } 04008 if (driveNameRef != NULL) { 04009 *driveNameRef = vol; 04010 Tcl_IncrRefCount(vol); 04011 } 04012 break; 04013 } 04014 } 04015 Tcl_DecrRefCount(thisFsVolumes); 04016 if (type == TCL_PATH_ABSOLUTE) { 04017 /* 04018 * We don't need to examine any more filesystems. 04019 */ 04020 break; 04021 } 04022 } 04023 } 04024 fsRecPtr = fsRecPtr->nextPtr; 04025 } 04026 return type; 04027 } 04028 04029 /* 04030 *--------------------------------------------------------------------------- 04031 * 04032 * Tcl_FSRenameFile -- 04033 * 04034 * If the two paths given belong to the same filesystem, we call that 04035 * filesystems rename function. Otherwise we simply return the POSIX 04036 * error 'EXDEV', and -1. 04037 * 04038 * Results: 04039 * Standard Tcl error code if a function was called. 04040 * 04041 * Side effects: 04042 * A file may be renamed. 04043 * 04044 *--------------------------------------------------------------------------- 04045 */ 04046 04047 int 04048 Tcl_FSRenameFile( 04049 Tcl_Obj* srcPathPtr, /* Pathname of file or dir to be renamed 04050 * (UTF-8). */ 04051 Tcl_Obj *destPathPtr) /* New pathname of file or directory 04052 * (UTF-8). */ 04053 { 04054 int retVal = -1; 04055 const Tcl_Filesystem *fsPtr, *fsPtr2; 04056 fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); 04057 fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); 04058 04059 if ((fsPtr == fsPtr2) && (fsPtr != NULL)) { 04060 Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc; 04061 if (proc != NULL) { 04062 retVal = (*proc)(srcPathPtr, destPathPtr); 04063 } 04064 } 04065 if (retVal == -1) { 04066 Tcl_SetErrno(EXDEV); 04067 } 04068 return retVal; 04069 } 04070 04071 /* 04072 *--------------------------------------------------------------------------- 04073 * 04074 * Tcl_FSCopyFile -- 04075 * 04076 * If the two paths given belong to the same filesystem, we call that 04077 * filesystem's copy function. Otherwise we simply return the POSIX error 04078 * 'EXDEV', and -1. 04079 * 04080 * Note that in the native filesystems, 'copyFileProc' is defined to copy 04081 * soft links (i.e. it copies the links themselves, not the things they 04082 * point to). 04083 * 04084 * Results: 04085 * Standard Tcl error code if a function was called. 04086 * 04087 * Side effects: 04088 * A file may be copied. 04089 * 04090 *--------------------------------------------------------------------------- 04091 */ 04092 04093 int 04094 Tcl_FSCopyFile( 04095 Tcl_Obj *srcPathPtr, /* Pathname of file to be copied (UTF-8). */ 04096 Tcl_Obj *destPathPtr) /* Pathname of file to copy to (UTF-8). */ 04097 { 04098 int retVal = -1; 04099 const Tcl_Filesystem *fsPtr, *fsPtr2; 04100 fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); 04101 fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); 04102 04103 if (fsPtr == fsPtr2 && fsPtr != NULL) { 04104 Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc; 04105 if (proc != NULL) { 04106 retVal = (*proc)(srcPathPtr, destPathPtr); 04107 } 04108 } 04109 if (retVal == -1) { 04110 Tcl_SetErrno(EXDEV); 04111 } 04112 return retVal; 04113 } 04114 04115 /* 04116 *--------------------------------------------------------------------------- 04117 * 04118 * TclCrossFilesystemCopy -- 04119 * 04120 * Helper for above function, and for Tcl_FSLoadFile, to copy files from 04121 * one filesystem to another. This function will overwrite the target 04122 * file if it already exists. 04123 * 04124 * Results: 04125 * Standard Tcl error code. 04126 * 04127 * Side effects: 04128 * A file may be created. 04129 * 04130 *--------------------------------------------------------------------------- 04131 */ 04132 int 04133 TclCrossFilesystemCopy( 04134 Tcl_Interp *interp, /* For error messages */ 04135 Tcl_Obj *source, /* Pathname of file to be copied (UTF-8). */ 04136 Tcl_Obj *target) /* Pathname of file to copy to (UTF-8). */ 04137 { 04138 int result = TCL_ERROR; 04139 int prot = 0666; 04140 Tcl_Channel in, out; 04141 Tcl_StatBuf sourceStatBuf; 04142 struct utimbuf tval; 04143 04144 out = Tcl_FSOpenFileChannel(interp, target, "wb", prot); 04145 if (out == NULL) { 04146 /* 04147 * It looks like we cannot copy it over. Bail out... 04148 */ 04149 goto done; 04150 } 04151 04152 in = Tcl_FSOpenFileChannel(interp, source, "rb", prot); 04153 if (in == NULL) { 04154 /* 04155 * This is very strange, caller should have checked this... 04156 */ 04157 04158 Tcl_Close(interp, out); 04159 goto done; 04160 } 04161 04162 /* 04163 * Copy it synchronously. We might wish to add an asynchronous option to 04164 * support vfs's which are slow (e.g. network sockets). 04165 */ 04166 04167 if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) { 04168 result = TCL_OK; 04169 } 04170 04171 /* 04172 * If the copy failed, assume that copy channel left a good error message. 04173 */ 04174 04175 Tcl_Close(interp, in); 04176 Tcl_Close(interp, out); 04177 04178 /* 04179 * Set modification date of copied file. 04180 */ 04181 04182 if (Tcl_FSLstat(source, &sourceStatBuf) == 0) { 04183 tval.actime = sourceStatBuf.st_atime; 04184 tval.modtime = sourceStatBuf.st_mtime; 04185 Tcl_FSUtime(target, &tval); 04186 } 04187 04188 done: 04189 return result; 04190 } 04191 04192 /* 04193 *--------------------------------------------------------------------------- 04194 * 04195 * Tcl_FSDeleteFile -- 04196 * 04197 * The appropriate function for the filesystem to which pathPtr belongs 04198 * will be called. 04199 * 04200 * Results: 04201 * Standard Tcl error code. 04202 * 04203 * Side effects: 04204 * A file may be deleted. 04205 * 04206 *--------------------------------------------------------------------------- 04207 */ 04208 04209 int 04210 Tcl_FSDeleteFile( 04211 Tcl_Obj *pathPtr) /* Pathname of file to be removed (UTF-8). */ 04212 { 04213 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 04214 if (fsPtr != NULL) { 04215 Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc; 04216 if (proc != NULL) { 04217 return (*proc)(pathPtr); 04218 } 04219 } 04220 Tcl_SetErrno(ENOENT); 04221 return -1; 04222 } 04223 04224 /* 04225 *--------------------------------------------------------------------------- 04226 * 04227 * Tcl_FSCreateDirectory -- 04228 * 04229 * The appropriate function for the filesystem to which pathPtr belongs 04230 * will be called. 04231 * 04232 * Results: 04233 * Standard Tcl error code. 04234 * 04235 * Side effects: 04236 * A directory may be created. 04237 * 04238 *--------------------------------------------------------------------------- 04239 */ 04240 04241 int 04242 Tcl_FSCreateDirectory( 04243 Tcl_Obj *pathPtr) /* Pathname of directory to create (UTF-8). */ 04244 { 04245 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 04246 if (fsPtr != NULL) { 04247 Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc; 04248 if (proc != NULL) { 04249 return (*proc)(pathPtr); 04250 } 04251 } 04252 Tcl_SetErrno(ENOENT); 04253 return -1; 04254 } 04255 04256 /* 04257 *--------------------------------------------------------------------------- 04258 * 04259 * Tcl_FSCopyDirectory -- 04260 * 04261 * If the two paths given belong to the same filesystem, we call that 04262 * filesystems copy-directory function. Otherwise we simply return the 04263 * POSIX error 'EXDEV', and -1. 04264 * 04265 * Results: 04266 * Standard Tcl error code if a function was called. 04267 * 04268 * Side effects: 04269 * A directory may be copied. 04270 * 04271 *--------------------------------------------------------------------------- 04272 */ 04273 04274 int 04275 Tcl_FSCopyDirectory( 04276 Tcl_Obj* srcPathPtr, /* Pathname of directory to be copied 04277 * (UTF-8). */ 04278 Tcl_Obj *destPathPtr, /* Pathname of target directory (UTF-8). */ 04279 Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new 04280 * object containing name of file causing 04281 * error, with refCount 1. */ 04282 { 04283 int retVal = -1; 04284 const Tcl_Filesystem *fsPtr, *fsPtr2; 04285 fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr); 04286 fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr); 04287 04288 if (fsPtr == fsPtr2 && fsPtr != NULL) { 04289 Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc; 04290 if (proc != NULL) { 04291 retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr); 04292 } 04293 } 04294 if (retVal == -1) { 04295 Tcl_SetErrno(EXDEV); 04296 } 04297 return retVal; 04298 } 04299 04300 /* 04301 *--------------------------------------------------------------------------- 04302 * 04303 * Tcl_FSRemoveDirectory -- 04304 * 04305 * The appropriate function for the filesystem to which pathPtr belongs 04306 * will be called. 04307 * 04308 * Results: 04309 * Standard Tcl error code. 04310 * 04311 * Side effects: 04312 * A directory may be deleted. 04313 * 04314 *--------------------------------------------------------------------------- 04315 */ 04316 04317 int 04318 Tcl_FSRemoveDirectory( 04319 Tcl_Obj *pathPtr, /* Pathname of directory to be removed 04320 * (UTF-8). */ 04321 int recursive, /* If non-zero, removes directories that are 04322 * nonempty. Otherwise, will only remove empty 04323 * directories. */ 04324 Tcl_Obj **errorPtr) /* If non-NULL, then will be set to a new 04325 * object containing name of file causing 04326 * error, with refCount 1. */ 04327 { 04328 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 04329 if (fsPtr != NULL && fsPtr->removeDirectoryProc != NULL) { 04330 Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc; 04331 if (recursive) { 04332 /* 04333 * We check whether the cwd lies inside this directory and move it 04334 * if it does. 04335 */ 04336 04337 Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL); 04338 04339 if (cwdPtr != NULL) { 04340 char *cwdStr, *normPathStr; 04341 int cwdLen, normLen; 04342 Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); 04343 04344 if (normPath != NULL) { 04345 normPathStr = Tcl_GetStringFromObj(normPath, &normLen); 04346 cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen); 04347 if ((cwdLen >= normLen) && (strncmp(normPathStr, cwdStr, 04348 (size_t) normLen) == 0)) { 04349 /* 04350 * The cwd is inside the directory, so we perform a 04351 * 'cd [file dirname $path]'. 04352 */ 04353 04354 Tcl_Obj *dirPtr = TclPathPart(NULL, pathPtr, 04355 TCL_PATH_DIRNAME); 04356 04357 Tcl_FSChdir(dirPtr); 04358 Tcl_DecrRefCount(dirPtr); 04359 } 04360 } 04361 Tcl_DecrRefCount(cwdPtr); 04362 } 04363 } 04364 return (*proc)(pathPtr, recursive, errorPtr); 04365 } 04366 Tcl_SetErrno(ENOENT); 04367 return -1; 04368 } 04369 04370 /* 04371 *--------------------------------------------------------------------------- 04372 * 04373 * Tcl_FSGetFileSystemForPath -- 04374 * 04375 * This function determines which filesystem to use for a particular path 04376 * object, and returns the filesystem which accepts this file. If no 04377 * filesystem will accept this object as a valid file path, then NULL is 04378 * returned. 04379 * 04380 * Results: 04381 * NULL or a filesystem which will accept this path. 04382 * 04383 * Side effects: 04384 * The object may be converted to a path type. 04385 * 04386 *--------------------------------------------------------------------------- 04387 */ 04388 04389 Tcl_Filesystem * 04390 Tcl_FSGetFileSystemForPath( 04391 Tcl_Obj* pathPtr) 04392 { 04393 FilesystemRecord *fsRecPtr; 04394 Tcl_Filesystem* retVal = NULL; 04395 04396 if (pathPtr == NULL) { 04397 Tcl_Panic("Tcl_FSGetFileSystemForPath called with NULL object"); 04398 return NULL; 04399 } 04400 04401 /* 04402 * If the object has a refCount of zero, we reject it. This is to avoid 04403 * possible segfaults or nondeterministic memory leaks (i.e. the user 04404 * doesn't know if they should decrement the ref count on return or not). 04405 */ 04406 04407 if (pathPtr->refCount == 0) { 04408 Tcl_Panic("Tcl_FSGetFileSystemForPath called with object with refCount == 0"); 04409 return NULL; 04410 } 04411 04412 /* 04413 * Check if the filesystem has changed in some way since this object's 04414 * internal representation was calculated. Before doing that, assure we 04415 * have the most up-to-date copy of the master filesystem. This is 04416 * accomplished by the FsGetFirstFilesystem() call. 04417 */ 04418 04419 fsRecPtr = FsGetFirstFilesystem(); 04420 04421 if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) { 04422 return NULL; 04423 } 04424 04425 /* 04426 * Call each of the "pathInFilesystem" functions in succession. A 04427 * non-return value of -1 indicates the particular function has succeeded. 04428 */ 04429 04430 while ((retVal == NULL) && (fsRecPtr != NULL)) { 04431 Tcl_FSPathInFilesystemProc *proc = 04432 fsRecPtr->fsPtr->pathInFilesystemProc; 04433 04434 if (proc != NULL) { 04435 ClientData clientData = NULL; 04436 if ((*proc)(pathPtr, &clientData) != -1) { 04437 /* 04438 * We assume the type of pathPtr hasn't been changed by the 04439 * above call to the pathInFilesystemProc. 04440 */ 04441 04442 TclFSSetPathDetails(pathPtr, fsRecPtr, clientData); 04443 retVal = fsRecPtr->fsPtr; 04444 } 04445 } 04446 fsRecPtr = fsRecPtr->nextPtr; 04447 } 04448 04449 return retVal; 04450 } 04451 04452 /* 04453 *--------------------------------------------------------------------------- 04454 * 04455 * Tcl_FSGetNativePath -- 04456 * 04457 * This function is for use by the Win/Unix native filesystems, so that 04458 * they can easily retrieve the native (char* or TCHAR*) representation 04459 * of a path. Other filesystems will probably want to implement similar 04460 * functions. They basically act as a safety net around 04461 * Tcl_FSGetInternalRep. Normally your file-system functions will always 04462 * be called with path objects already converted to the correct 04463 * filesystem, but if for some reason they are called directly (i.e. by 04464 * functions not in this file), then one cannot necessarily guarantee 04465 * that the path object pointer is from the correct filesystem. 04466 * 04467 * Note: in the future it might be desireable to have separate versions 04468 * of this function with different signatures, for example 04469 * Tcl_FSGetNativeWinPath, Tcl_FSGetNativeUnixPath etc. Right now, since 04470 * native paths are all string based, we use just one function. 04471 * 04472 * Results: 04473 * NULL or a valid native path. 04474 * 04475 * Side effects: 04476 * See Tcl_FSGetInternalRep. 04477 * 04478 *--------------------------------------------------------------------------- 04479 */ 04480 04481 const char * 04482 Tcl_FSGetNativePath( 04483 Tcl_Obj *pathPtr) 04484 { 04485 return (const char *) Tcl_FSGetInternalRep(pathPtr, &tclNativeFilesystem); 04486 } 04487 04488 /* 04489 *--------------------------------------------------------------------------- 04490 * 04491 * NativeFreeInternalRep -- 04492 * 04493 * Free a native internal representation, which will be non-NULL. 04494 * 04495 * Results: 04496 * None. 04497 * 04498 * Side effects: 04499 * Memory is released. 04500 * 04501 *--------------------------------------------------------------------------- 04502 */ 04503 04504 static void 04505 NativeFreeInternalRep( 04506 ClientData clientData) 04507 { 04508 ckfree((char *) clientData); 04509 } 04510 04511 /* 04512 *--------------------------------------------------------------------------- 04513 * 04514 * Tcl_FSFileSystemInfo -- 04515 * 04516 * This function returns a list of two elements. The first element is the 04517 * name of the filesystem (e.g. "native" or "vfs"), and the second is the 04518 * particular type of the given path within that filesystem. 04519 * 04520 * Results: 04521 * A list of two elements. 04522 * 04523 * Side effects: 04524 * The object may be converted to a path type. 04525 * 04526 *--------------------------------------------------------------------------- 04527 */ 04528 04529 Tcl_Obj * 04530 Tcl_FSFileSystemInfo( 04531 Tcl_Obj *pathPtr) 04532 { 04533 Tcl_Obj *resPtr; 04534 Tcl_FSFilesystemPathTypeProc *proc; 04535 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 04536 04537 if (fsPtr == NULL) { 04538 return NULL; 04539 } 04540 04541 resPtr = Tcl_NewListObj(0, NULL); 04542 Tcl_ListObjAppendElement(NULL,resPtr,Tcl_NewStringObj(fsPtr->typeName,-1)); 04543 04544 proc = fsPtr->filesystemPathTypeProc; 04545 if (proc != NULL) { 04546 Tcl_Obj *typePtr = (*proc)(pathPtr); 04547 if (typePtr != NULL) { 04548 Tcl_ListObjAppendElement(NULL, resPtr, typePtr); 04549 } 04550 } 04551 04552 return resPtr; 04553 } 04554 04555 /* 04556 *--------------------------------------------------------------------------- 04557 * 04558 * Tcl_FSPathSeparator -- 04559 * 04560 * This function returns the separator to be used for a given path. The 04561 * object returned should have a refCount of zero 04562 * 04563 * Results: 04564 * A Tcl object, with a refCount of zero. If the caller needs to retain a 04565 * reference to the object, it should call Tcl_IncrRefCount, and should 04566 * otherwise free the object. 04567 * 04568 * Side effects: 04569 * The path object may be converted to a path type. 04570 * 04571 *--------------------------------------------------------------------------- 04572 */ 04573 04574 Tcl_Obj * 04575 Tcl_FSPathSeparator( 04576 Tcl_Obj *pathPtr) 04577 { 04578 const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); 04579 04580 if (fsPtr == NULL) { 04581 return NULL; 04582 } 04583 if (fsPtr->filesystemSeparatorProc != NULL) { 04584 return (*fsPtr->filesystemSeparatorProc)(pathPtr); 04585 } else { 04586 Tcl_Obj *resultObj; 04587 04588 /* 04589 * Allow filesystems not to provide a filesystemSeparatorProc if they 04590 * wish to use the standard forward slash. 04591 */ 04592 04593 TclNewLiteralStringObj(resultObj, "/"); 04594 return resultObj; 04595 } 04596 } 04597 04598 /* 04599 *--------------------------------------------------------------------------- 04600 * 04601 * NativeFilesystemSeparator -- 04602 * 04603 * This function is part of the native filesystem support, and returns 04604 * the separator for the given path. 04605 * 04606 * Results: 04607 * String object containing the separator character. 04608 * 04609 * Side effects: 04610 * None. 04611 * 04612 *--------------------------------------------------------------------------- 04613 */ 04614 04615 static Tcl_Obj * 04616 NativeFilesystemSeparator( 04617 Tcl_Obj *pathPtr) 04618 { 04619 const char *separator = NULL; /* lint */ 04620 switch (tclPlatform) { 04621 case TCL_PLATFORM_UNIX: 04622 separator = "/"; 04623 break; 04624 case TCL_PLATFORM_WINDOWS: 04625 separator = "\\"; 04626 break; 04627 } 04628 return Tcl_NewStringObj(separator,1); 04629 } 04630 04631 /* Everything from here on is contained in this obsolete ifdef */ 04632 #ifdef USE_OBSOLETE_FS_HOOKS 04633 04634 /* 04635 *---------------------------------------------------------------------- 04636 * 04637 * TclStatInsertProc -- 04638 * 04639 * Insert the passed function pointer at the head of the list of 04640 * functions which are used during a call to 'TclStat(...)'. The passed 04641 * function should behave exactly like 'TclStat' when called during that 04642 * time (see 'TclStat(...)' for more information). The function will be 04643 * added even if it already in the list. 04644 * 04645 * Results: 04646 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could 04647 * not be allocated. 04648 * 04649 * Side effects: 04650 * Memory allocated and modifies the link list for 'TclStat' functions. 04651 * 04652 *---------------------------------------------------------------------- 04653 */ 04654 04655 int 04656 TclStatInsertProc( 04657 TclStatProc_ *proc) 04658 { 04659 int retVal = TCL_ERROR; 04660 04661 if (proc != NULL) { 04662 StatProc *newStatProcPtr; 04663 04664 newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc)); 04665 04666 if (newStatProcPtr != NULL) { 04667 newStatProcPtr->proc = proc; 04668 Tcl_MutexLock(&obsoleteFsHookMutex); 04669 newStatProcPtr->nextPtr = statProcList; 04670 statProcList = newStatProcPtr; 04671 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04672 04673 retVal = TCL_OK; 04674 } 04675 } 04676 04677 return retVal; 04678 } 04679 04680 /* 04681 *---------------------------------------------------------------------- 04682 * 04683 * TclStatDeleteProc -- 04684 * 04685 * Removed the passed function pointer from the list of 'TclStat' 04686 * functions. Ensures that the built-in stat function is not removable. 04687 * 04688 * Results: 04689 * TCL_OK if the function pointer was successfully removed, TCL_ERROR 04690 * otherwise. 04691 * 04692 * Side effects: 04693 * Memory is deallocated and the respective list updated. 04694 * 04695 *---------------------------------------------------------------------- 04696 */ 04697 04698 int 04699 TclStatDeleteProc( 04700 TclStatProc_ *proc) 04701 { 04702 int retVal = TCL_ERROR; 04703 StatProc *tmpStatProcPtr; 04704 StatProc *prevStatProcPtr = NULL; 04705 04706 Tcl_MutexLock(&obsoleteFsHookMutex); 04707 tmpStatProcPtr = statProcList; 04708 04709 /* 04710 * Traverse the 'statProcList' looking for the particular node whose 04711 * 'proc' member matches 'proc' and remove that one from the list. Ensure 04712 * that the "default" node cannot be removed. 04713 */ 04714 04715 while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) { 04716 if (tmpStatProcPtr->proc == proc) { 04717 if (prevStatProcPtr == NULL) { 04718 statProcList = tmpStatProcPtr->nextPtr; 04719 } else { 04720 prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr; 04721 } 04722 04723 ckfree((char *)tmpStatProcPtr); 04724 04725 retVal = TCL_OK; 04726 } else { 04727 prevStatProcPtr = tmpStatProcPtr; 04728 tmpStatProcPtr = tmpStatProcPtr->nextPtr; 04729 } 04730 } 04731 04732 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04733 04734 return retVal; 04735 } 04736 04737 /* 04738 *---------------------------------------------------------------------- 04739 * 04740 * TclAccessInsertProc -- 04741 * 04742 * Insert the passed function pointer at the head of the list of 04743 * functions which are used during a call to 'TclAccess(...)'. The passed 04744 * function should behave exactly like 'TclAccess' when called during 04745 * that time (see 'TclAccess(...)' for more information). The function 04746 * will be added even if it already in the list. 04747 * 04748 * Results: 04749 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could 04750 * not be allocated. 04751 * 04752 * Side effects: 04753 * Memory allocated and modifies the link list for 'TclAccess' functions. 04754 * 04755 *---------------------------------------------------------------------- 04756 */ 04757 04758 int 04759 TclAccessInsertProc( 04760 TclAccessProc_ *proc) 04761 { 04762 int retVal = TCL_ERROR; 04763 04764 if (proc != NULL) { 04765 AccessProc *newAccessProcPtr; 04766 04767 newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc)); 04768 04769 if (newAccessProcPtr != NULL) { 04770 newAccessProcPtr->proc = proc; 04771 Tcl_MutexLock(&obsoleteFsHookMutex); 04772 newAccessProcPtr->nextPtr = accessProcList; 04773 accessProcList = newAccessProcPtr; 04774 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04775 04776 retVal = TCL_OK; 04777 } 04778 } 04779 04780 return retVal; 04781 } 04782 04783 /* 04784 *---------------------------------------------------------------------- 04785 * 04786 * TclAccessDeleteProc -- 04787 * 04788 * Removed the passed function pointer from the list of 'TclAccess' 04789 * functions. Ensures that the built-in access function is not removable. 04790 * 04791 * Results: 04792 * TCL_OK if the function pointer was successfully removed, TCL_ERROR 04793 * otherwise. 04794 * 04795 * Side effects: 04796 * Memory is deallocated and the respective list updated. 04797 * 04798 *---------------------------------------------------------------------- 04799 */ 04800 04801 int 04802 TclAccessDeleteProc( 04803 TclAccessProc_ *proc) 04804 { 04805 int retVal = TCL_ERROR; 04806 AccessProc *tmpAccessProcPtr; 04807 AccessProc *prevAccessProcPtr = NULL; 04808 04809 /* 04810 * Traverse the 'accessProcList' looking for the particular node whose 04811 * 'proc' member matches 'proc' and remove that one from the list. Ensure 04812 * that the "default" node cannot be removed. 04813 */ 04814 04815 Tcl_MutexLock(&obsoleteFsHookMutex); 04816 tmpAccessProcPtr = accessProcList; 04817 while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) { 04818 if (tmpAccessProcPtr->proc == proc) { 04819 if (prevAccessProcPtr == NULL) { 04820 accessProcList = tmpAccessProcPtr->nextPtr; 04821 } else { 04822 prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr; 04823 } 04824 04825 ckfree((char *)tmpAccessProcPtr); 04826 04827 retVal = TCL_OK; 04828 } else { 04829 prevAccessProcPtr = tmpAccessProcPtr; 04830 tmpAccessProcPtr = tmpAccessProcPtr->nextPtr; 04831 } 04832 } 04833 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04834 04835 return retVal; 04836 } 04837 04838 /* 04839 *---------------------------------------------------------------------- 04840 * 04841 * TclOpenFileChannelInsertProc -- 04842 * 04843 * Insert the passed function pointer at the head of the list of 04844 * functions which are used during a call to 'Tcl_OpenFileChannel(...)'. 04845 * The passed function should behave exactly like 'Tcl_OpenFileChannel' 04846 * when called during that time (see 'Tcl_OpenFileChannel(...)' for more 04847 * information). The function will be added even if it already in the 04848 * list. 04849 * 04850 * Results: 04851 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list could 04852 * not be allocated. 04853 * 04854 * Side effects: 04855 * Memory allocated and modifies the link list for 'Tcl_OpenFileChannel' 04856 * functions. 04857 * 04858 *---------------------------------------------------------------------- 04859 */ 04860 04861 int 04862 TclOpenFileChannelInsertProc( 04863 TclOpenFileChannelProc_ *proc) 04864 { 04865 int retVal = TCL_ERROR; 04866 04867 if (proc != NULL) { 04868 OpenFileChannelProc *newOpenFileChannelProcPtr; 04869 04870 newOpenFileChannelProcPtr = (OpenFileChannelProc *) 04871 ckalloc(sizeof(OpenFileChannelProc)); 04872 04873 newOpenFileChannelProcPtr->proc = proc; 04874 Tcl_MutexLock(&obsoleteFsHookMutex); 04875 newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList; 04876 openFileChannelProcList = newOpenFileChannelProcPtr; 04877 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04878 04879 retVal = TCL_OK; 04880 } 04881 04882 return retVal; 04883 } 04884 04885 /* 04886 *---------------------------------------------------------------------- 04887 * 04888 * TclOpenFileChannelDeleteProc -- 04889 * 04890 * Removed the passed function pointer from the list of 04891 * 'Tcl_OpenFileChannel' functions. Ensures that the built-in open file 04892 * channel function is not removable. 04893 * 04894 * Results: 04895 * TCL_OK if the function pointer was successfully removed, TCL_ERROR 04896 * otherwise. 04897 * 04898 * Side effects: 04899 * Memory is deallocated and the respective list updated. 04900 * 04901 *---------------------------------------------------------------------- 04902 */ 04903 04904 int 04905 TclOpenFileChannelDeleteProc( 04906 TclOpenFileChannelProc_ *proc) 04907 { 04908 int retVal = TCL_ERROR; 04909 OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList; 04910 OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL; 04911 04912 /* 04913 * Traverse the 'openFileChannelProcList' looking for the particular node 04914 * whose 'proc' member matches 'proc' and remove that one from the list. 04915 */ 04916 04917 Tcl_MutexLock(&obsoleteFsHookMutex); 04918 tmpOpenFileChannelProcPtr = openFileChannelProcList; 04919 while ((retVal == TCL_ERROR) && 04920 (tmpOpenFileChannelProcPtr != NULL)) { 04921 if (tmpOpenFileChannelProcPtr->proc == proc) { 04922 if (prevOpenFileChannelProcPtr == NULL) { 04923 openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr; 04924 } else { 04925 prevOpenFileChannelProcPtr->nextPtr = 04926 tmpOpenFileChannelProcPtr->nextPtr; 04927 } 04928 04929 ckfree((char *) tmpOpenFileChannelProcPtr); 04930 04931 retVal = TCL_OK; 04932 } else { 04933 prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr; 04934 tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr; 04935 } 04936 } 04937 Tcl_MutexUnlock(&obsoleteFsHookMutex); 04938 04939 return retVal; 04940 } 04941 #endif /* USE_OBSOLETE_FS_HOOKS */ 04942 04943 /* 04944 * Local Variables: 04945 * mode: c 04946 * c-basic-offset: 4 04947 * fill-column: 78 04948 * End: 04949 */
Generated on Wed Mar 12 12:18:18 2008 by 1.5.1 |