tclIOUtil.c

Go 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  doxygen 1.5.1