tclIOUtil.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:187k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclIOUtil.c --
  3.  *
  4.  * This file contains the implementation of Tcl's generic
  5.  * filesystem code, which supports a pluggable filesystem
  6.  * architecture allowing both platform specific filesystems and
  7.  * 'virtual filesystems'.  All filesystem access should go through
  8.  * the functions defined in this file.  Most of this code was
  9.  * contributed by Vince Darley.
  10.  *
  11.  * Parts of this file are based on code contributed by Karl
  12.  * Lehenbauer, Mark Diekhans and Peter da Silva.
  13.  *
  14.  * Copyright (c) 1991-1994 The Regents of the University of California.
  15.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  16.  *
  17.  * See the file "license.terms" for information on usage and redistribution
  18.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  19.  *
  20.  * RCS: @(#) $Id: tclIOUtil.c,v 1.77.2.35 2007/12/14 02:29:21 hobbs Exp $
  21.  */
  22. #include "tclInt.h"
  23. #include "tclPort.h"
  24. #ifdef MAC_TCL
  25. #include "tclMacInt.h"
  26. #endif
  27. #ifdef __WIN32__
  28. /* for tclWinProcs->useWide */
  29. #include "tclWinInt.h"
  30. #endif
  31. /* 
  32.  * struct FilesystemRecord --
  33.  * 
  34.  * A filesystem record is used to keep track of each
  35.  * filesystem currently registered with the core,
  36.  * in a linked list.  Pointers to these structures
  37.  * are also kept by each "path" Tcl_Obj, and we must
  38.  * retain a refCount on the number of such references.
  39.  */
  40. typedef struct FilesystemRecord {
  41.     ClientData      clientData;  /* Client specific data for the new
  42.    * filesystem (can be NULL) */
  43.     Tcl_Filesystem *fsPtr;        /* Pointer to filesystem dispatch
  44.    * table. */
  45.     int fileRefCount;             /* How many Tcl_Obj's use this
  46.    * filesystem. */
  47.     struct FilesystemRecord *nextPtr;  
  48.   /* The next filesystem registered
  49.    * to Tcl, or NULL if no more. */
  50.     struct FilesystemRecord *prevPtr;  
  51.   /* The previous filesystem registered
  52.    * to Tcl, or NULL if no more. */
  53. } FilesystemRecord;
  54. /* 
  55.  * The internal TclFS API provides routines for handling and
  56.  * manipulating paths efficiently, taking direct advantage of
  57.  * the "path" Tcl_Obj type.
  58.  * 
  59.  * These functions are not exported at all at present.
  60.  */
  61. int      TclFSCwdPointerEquals _ANSI_ARGS_((Tcl_Obj* objPtr));
  62. int  TclFSMakePathFromNormalized _ANSI_ARGS_((Tcl_Interp *interp, 
  63. Tcl_Obj *objPtr, ClientData clientData));
  64. int      TclFSNormalizeToUniquePath _ANSI_ARGS_((Tcl_Interp *interp, 
  65. Tcl_Obj *pathPtr, int startAt, ClientData *clientDataPtr));
  66. Tcl_Obj* TclFSMakePathRelative _ANSI_ARGS_((Tcl_Interp *interp, 
  67. Tcl_Obj *objPtr, Tcl_Obj *cwdPtr));
  68. Tcl_Obj* TclFSInternalToNormalized _ANSI_ARGS_((
  69. Tcl_Filesystem *fromFilesystem, ClientData clientData,
  70. FilesystemRecord **fsRecPtrPtr));
  71. int      TclFSEnsureEpochOk _ANSI_ARGS_((Tcl_Obj* pathObjPtr,
  72. Tcl_Filesystem **fsPtrPtr));
  73. void     TclFSSetPathDetails _ANSI_ARGS_((Tcl_Obj *pathObjPtr, 
  74. FilesystemRecord *fsRecPtr, ClientData clientData)); 
  75. /* 
  76.  * Private variables for use in this file
  77.  */
  78. extern Tcl_Filesystem tclNativeFilesystem;
  79. extern int theFilesystemEpoch;
  80. /* 
  81.  * Private functions for use in this file
  82.  */
  83. static Tcl_PathType     FSGetPathType  _ANSI_ARGS_((Tcl_Obj *pathObjPtr, 
  84.     Tcl_Filesystem **filesystemPtrPtr, 
  85.     int *driveNameLengthPtr));
  86. static Tcl_PathType     GetPathType  _ANSI_ARGS_((Tcl_Obj *pathObjPtr, 
  87.     Tcl_Filesystem **filesystemPtrPtr, 
  88.     int *driveNameLengthPtr, Tcl_Obj **driveNameRef));
  89. static Tcl_FSPathInFilesystemProc NativePathInFilesystem;
  90. static Tcl_Obj*  TclFSNormalizeAbsolutePath 
  91.     _ANSI_ARGS_((Tcl_Interp* interp, Tcl_Obj *pathPtr,
  92.  ClientData *clientDataPtr));
  93. /*
  94.  * Prototypes for procedures defined later in this file.
  95.  */
  96. static FilesystemRecord* FsGetFirstFilesystem(void);
  97. static void FsThrExitProc(ClientData cd);
  98. static Tcl_Obj* FsListMounts          _ANSI_ARGS_((Tcl_Obj *pathPtr, 
  99.    CONST char *pattern));
  100. static Tcl_Obj* FsAddMountsToGlobResult  _ANSI_ARGS_((Tcl_Obj *result, 
  101.    Tcl_Obj *pathPtr, CONST char *pattern, Tcl_GlobTypeData *types));
  102. #ifdef TCL_THREADS
  103. static void FsRecacheFilesystemList(void);
  104. #endif
  105. /* 
  106.  * These form part of the native filesystem support.  They are needed
  107.  * here because we have a few native filesystem functions (which are
  108.  * the same for mac/win/unix) in this file.  There is no need to place
  109.  * them in tclInt.h, because they are not (and should not be) used
  110.  * anywhere else.
  111.  */
  112. extern CONST char * tclpFileAttrStrings[];
  113. extern CONST TclFileAttrProcs tclpFileAttrProcs[];
  114. /* 
  115.  * The following functions are obsolete string based APIs, and should
  116.  * be removed in a future release (Tcl 9 would be a good time).
  117.  */
  118. /* Obsolete */
  119. int
  120. Tcl_Stat(path, oldStyleBuf)
  121.     CONST char *path; /* Path of file to stat (in current CP). */
  122.     struct stat *oldStyleBuf; /* Filled with results of stat call. */
  123. {
  124.     int ret;
  125.     Tcl_StatBuf buf;
  126.     Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
  127.     Tcl_IncrRefCount(pathPtr);
  128.     ret = Tcl_FSStat(pathPtr, &buf);
  129.     Tcl_DecrRefCount(pathPtr);
  130.     if (ret != -1) {
  131. #ifndef TCL_WIDE_INT_IS_LONG
  132. #   define OUT_OF_RANGE(x) 
  133. (((Tcl_WideInt)(x)) < Tcl_LongAsWide(LONG_MIN) || 
  134.  ((Tcl_WideInt)(x)) > Tcl_LongAsWide(LONG_MAX))
  135. #if defined(__GNUC__) && __GNUC__ >= 2
  136. /*
  137.  * Workaround gcc warning of "comparison is always false due to limited range of
  138.  * data type" in this macro by checking max type size, and when necessary ANDing
  139.  * with the complement of ULONG_MAX instead of the comparison:
  140.  */
  141. #   define OUT_OF_URANGE(x) 
  142. ((((Tcl_WideUInt)(~ (__typeof__(x)) 0)) > (Tcl_WideUInt)ULONG_MAX) && 
  143.  (((Tcl_WideUInt)(x)) & ~(Tcl_WideUInt)ULONG_MAX))
  144. #else
  145. #   define OUT_OF_URANGE(x) 
  146. (((Tcl_WideUInt)(x)) > (Tcl_WideUInt)ULONG_MAX)
  147. #endif
  148. /*
  149.  * Perform the result-buffer overflow check manually.
  150.  *
  151.  * Note that ino_t/ino64_t is unsigned...
  152.  */
  153.         if (OUT_OF_URANGE(buf.st_ino) || OUT_OF_RANGE(buf.st_size)
  154. #ifdef HAVE_ST_BLOCKS
  155. || OUT_OF_RANGE(buf.st_blocks)
  156. #endif
  157.     ) {
  158. #ifdef EFBIG
  159.     errno = EFBIG;
  160. #else
  161. #  ifdef EOVERFLOW
  162.     errno = EOVERFLOW;
  163. #  else
  164. #    error  "What status should be returned for file size out of range?"
  165. #  endif
  166. #endif
  167.     return -1;
  168. }
  169. #   undef OUT_OF_RANGE
  170. #   undef OUT_OF_URANGE
  171. #endif /* !TCL_WIDE_INT_IS_LONG */
  172. /*
  173.  * Copy across all supported fields, with possible type
  174.  * coercions on those fields that change between the normal
  175.  * and lf64 versions of the stat structure (on Solaris at
  176.  * least.)  This is slow when the structure sizes coincide,
  177.  * but that's what you get for using an obsolete interface.
  178.  */
  179. oldStyleBuf->st_mode    = buf.st_mode;
  180. oldStyleBuf->st_ino     = (ino_t) buf.st_ino;
  181. oldStyleBuf->st_dev     = buf.st_dev;
  182. oldStyleBuf->st_rdev    = buf.st_rdev;
  183. oldStyleBuf->st_nlink   = buf.st_nlink;
  184. oldStyleBuf->st_uid     = buf.st_uid;
  185. oldStyleBuf->st_gid     = buf.st_gid;
  186. oldStyleBuf->st_size    = (off_t) buf.st_size;
  187. oldStyleBuf->st_atime   = buf.st_atime;
  188. oldStyleBuf->st_mtime   = buf.st_mtime;
  189. oldStyleBuf->st_ctime   = buf.st_ctime;
  190. #ifdef HAVE_ST_BLOCKS
  191. oldStyleBuf->st_blksize = buf.st_blksize;
  192. oldStyleBuf->st_blocks  = (blkcnt_t) buf.st_blocks;
  193. #endif
  194.     }
  195.     return ret;
  196. }
  197. /* Obsolete */
  198. int
  199. Tcl_Access(path, mode)
  200.     CONST char *path; /* Path of file to access (in current CP). */
  201.     int mode;                   /* Permission setting. */
  202. {
  203.     int ret;
  204.     Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
  205.     Tcl_IncrRefCount(pathPtr);
  206.     ret = Tcl_FSAccess(pathPtr,mode);
  207.     Tcl_DecrRefCount(pathPtr);
  208.     return ret;
  209. }
  210. /* Obsolete */
  211. Tcl_Channel
  212. Tcl_OpenFileChannel(interp, path, modeString, permissions)
  213.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  214.  * can be NULL. */
  215.     CONST char *path;                   /* Name of file to open. */
  216.     CONST char *modeString;             /* A list of POSIX open modes or
  217.  * a string such as "rw". */
  218.     int permissions;                    /* If the open involves creating a
  219.  * file, with what modes to create
  220.  * it? */
  221. {
  222.     Tcl_Channel ret;
  223.     Tcl_Obj *pathPtr = Tcl_NewStringObj(path,-1);
  224.     Tcl_IncrRefCount(pathPtr);
  225.     ret = Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions);
  226.     Tcl_DecrRefCount(pathPtr);
  227.     return ret;
  228. }
  229. /* Obsolete */
  230. int
  231. Tcl_Chdir(dirName)
  232.     CONST char *dirName;
  233. {
  234.     int ret;
  235.     Tcl_Obj *pathPtr = Tcl_NewStringObj(dirName,-1);
  236.     Tcl_IncrRefCount(pathPtr);
  237.     ret = Tcl_FSChdir(pathPtr);
  238.     Tcl_DecrRefCount(pathPtr);
  239.     return ret;
  240. }
  241. /* Obsolete */
  242. char *
  243. Tcl_GetCwd(interp, cwdPtr)
  244.     Tcl_Interp *interp;
  245.     Tcl_DString *cwdPtr;
  246. {
  247.     Tcl_Obj *cwd;
  248.     cwd = Tcl_FSGetCwd(interp);
  249.     if (cwd == NULL) {
  250. return NULL;
  251.     } else {
  252. Tcl_DStringInit(cwdPtr);
  253. Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1);
  254. Tcl_DecrRefCount(cwd);
  255. return Tcl_DStringValue(cwdPtr);
  256.     }
  257. }
  258. /* Obsolete */
  259. int
  260. Tcl_EvalFile(interp, fileName)
  261.     Tcl_Interp *interp; /* Interpreter in which to process file. */
  262.     CONST char *fileName; /* Name of file to process.  Tilde-substitution
  263.  * will be performed on this name. */
  264. {
  265.     int ret;
  266.     Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
  267.     Tcl_IncrRefCount(pathPtr);
  268.     ret = Tcl_FSEvalFile(interp, pathPtr);
  269.     Tcl_DecrRefCount(pathPtr);
  270.     return ret;
  271. }
  272. /* 
  273.  * The 3 hooks for Stat, Access and OpenFileChannel are obsolete.  The
  274.  * complete, general hooked filesystem APIs should be used instead.
  275.  * This define decides whether to include the obsolete hooks and
  276.  * related code.  If these are removed, we'll also want to remove them
  277.  * from stubs/tclInt.  The only known users of these APIs are prowrap
  278.  * and mktclapp.  New code/extensions should not use them, since they
  279.  * do not provide as full support as the full filesystem API.
  280.  * 
  281.  * As soon as prowrap and mktclapp are updated to use the full
  282.  * filesystem support, I suggest all these hooks are removed.
  283.  */
  284. #define USE_OBSOLETE_FS_HOOKS
  285. #ifdef USE_OBSOLETE_FS_HOOKS
  286. /*
  287.  * The following typedef declarations allow for hooking into the chain
  288.  * of functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' &
  289.  * 'Tcl_OpenFileChannel(...)'.  Basically for each hookable function
  290.  * a linked list is defined.
  291.  */
  292. typedef struct StatProc {
  293.     TclStatProc_ *proc;  /* Function to process a 'stat()' call */
  294.     struct StatProc *nextPtr;    /* The next 'stat()' function to call */
  295. } StatProc;
  296. typedef struct AccessProc {
  297.     TclAccessProc_ *proc;  /* Function to process a 'access()' call */
  298.     struct AccessProc *nextPtr;  /* The next 'access()' function to call */
  299. } AccessProc;
  300. typedef struct OpenFileChannelProc {
  301.     TclOpenFileChannelProc_ *proc;  /* Function to process a
  302.      * 'Tcl_OpenFileChannel()' call */
  303.     struct OpenFileChannelProc *nextPtr;
  304.     /* The next 'Tcl_OpenFileChannel()'
  305.      * function to call */
  306. } OpenFileChannelProc;
  307. /*
  308.  * For each type of (obsolete) hookable function, a static node is
  309.  * declared to hold the function pointer for the "built-in" routine
  310.  * (e.g. 'TclpStat(...)') and the respective list is initialized as a
  311.  * pointer to that node.
  312.  * 
  313.  * The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that
  314.  * these statically declared list entry cannot be inadvertently removed.
  315.  *
  316.  * This method avoids the need to call any sort of "initialization"
  317.  * function.
  318.  *
  319.  * All three lists are protected by a global obsoleteFsHookMutex.
  320.  */
  321. static StatProc *statProcList = NULL;
  322. static AccessProc *accessProcList = NULL;
  323. static OpenFileChannelProc *openFileChannelProcList = NULL;
  324. TCL_DECLARE_MUTEX(obsoleteFsHookMutex)
  325. #endif /* USE_OBSOLETE_FS_HOOKS */
  326. /* 
  327.  * Declare the native filesystem support.  These functions should
  328.  * be considered private to Tcl, and should really not be called
  329.  * directly by any code other than this file (i.e. neither by
  330.  * Tcl's core nor by extensions).  Similarly, the old string-based
  331.  * Tclp... native filesystem functions should not be called.
  332.  * 
  333.  * The correct API to use now is the Tcl_FS... set of functions,
  334.  * which ensure correct and complete virtual filesystem support.
  335.  * 
  336.  * We cannot make all of these static, since some of them
  337.  * are implemented in the platform-specific directories.
  338.  */
  339. static Tcl_FSFilesystemSeparatorProc NativeFilesystemSeparator;
  340. static Tcl_FSFreeInternalRepProc NativeFreeInternalRep;
  341. static Tcl_FSCreateInternalRepProc NativeCreateNativeRep;
  342. static Tcl_FSFileAttrStringsProc NativeFileAttrStrings;
  343. static Tcl_FSFileAttrsGetProc NativeFileAttrsGet;
  344. static Tcl_FSFileAttrsSetProc NativeFileAttrsSet;
  345. /* 
  346.  * The only reason these functions are not static is that they
  347.  * are either called by code in the native (win/unix/mac) directories
  348.  * or they are actually implemented in those directories.  They
  349.  * should simply not be called by code outside Tcl's native
  350.  * filesystem core.  i.e. they should be considered 'static' to
  351.  * Tcl's filesystem code (if we ever built the native filesystem
  352.  * support into a separate code library, this could actually be
  353.  * enforced).
  354.  */
  355. Tcl_FSFilesystemPathTypeProc TclpFilesystemPathType;
  356. Tcl_FSInternalToNormalizedProc TclpNativeToNormalized;
  357. Tcl_FSStatProc TclpObjStat;
  358. Tcl_FSAccessProc TclpObjAccess;     
  359. Tcl_FSMatchInDirectoryProc TclpMatchInDirectory;  
  360. Tcl_FSGetCwdProc TclpObjGetCwd;     
  361. Tcl_FSChdirProc TclpObjChdir;     
  362. Tcl_FSLstatProc TclpObjLstat;     
  363. Tcl_FSCopyFileProc TclpObjCopyFile; 
  364. Tcl_FSDeleteFileProc TclpObjDeleteFile;     
  365. Tcl_FSRenameFileProc TclpObjRenameFile;     
  366. Tcl_FSCreateDirectoryProc TclpObjCreateDirectory;     
  367. Tcl_FSCopyDirectoryProc TclpObjCopyDirectory;     
  368. Tcl_FSRemoveDirectoryProc TclpObjRemoveDirectory;     
  369. Tcl_FSUnloadFileProc TclpUnloadFile;     
  370. Tcl_FSLinkProc TclpObjLink; 
  371. Tcl_FSListVolumesProc TclpObjListVolumes;     
  372. /* 
  373.  * Define the native filesystem dispatch table.  If necessary, it
  374.  * is ok to make this non-static, but it should only be accessed
  375.  * by the functions actually listed within it (or perhaps other
  376.  * helper functions of them).  Anything which is not part of this
  377.  * 'native filesystem implementation' should not be delving inside
  378.  * here!
  379.  */
  380. Tcl_Filesystem tclNativeFilesystem = {
  381.     "native",
  382.     sizeof(Tcl_Filesystem),
  383.     TCL_FILESYSTEM_VERSION_1,
  384.     &NativePathInFilesystem,
  385.     &TclNativeDupInternalRep,
  386.     &NativeFreeInternalRep,
  387.     &TclpNativeToNormalized,
  388.     &NativeCreateNativeRep,
  389.     &TclpObjNormalizePath,
  390.     &TclpFilesystemPathType,
  391.     &NativeFilesystemSeparator,
  392.     &TclpObjStat,
  393.     &TclpObjAccess,
  394.     &TclpOpenFileChannel,
  395.     &TclpMatchInDirectory,
  396.     &TclpUtime,
  397. #ifndef S_IFLNK
  398.     NULL,
  399. #else
  400.     &TclpObjLink,
  401. #endif /* S_IFLNK */
  402.     &TclpObjListVolumes,
  403.     &NativeFileAttrStrings,
  404.     &NativeFileAttrsGet,
  405.     &NativeFileAttrsSet,
  406.     &TclpObjCreateDirectory,
  407.     &TclpObjRemoveDirectory, 
  408.     &TclpObjDeleteFile,
  409.     &TclpObjCopyFile,
  410.     &TclpObjRenameFile,
  411.     &TclpObjCopyDirectory, 
  412.     &TclpObjLstat,
  413.     &TclpDlopen,
  414.     &TclpObjGetCwd,
  415.     &TclpObjChdir
  416. };
  417. /* 
  418.  * Define the tail of the linked list.  Note that for unconventional
  419.  * uses of Tcl without a native filesystem, we may in the future wish
  420.  * to modify the current approach of hard-coding the native filesystem
  421.  * in the lookup list 'filesystemList' below.
  422.  * 
  423.  * We initialize the record so that it thinks one file uses it.  This
  424.  * means it will never be freed.
  425.  */
  426. static FilesystemRecord nativeFilesystemRecord = {
  427.     NULL,
  428.     &tclNativeFilesystem,
  429.     1,
  430.     NULL
  431. };
  432. /* 
  433.  * This is incremented each time we modify the linked list of
  434.  * filesystems.  Any time it changes, all cached filesystem
  435.  * representations are suspect and must be freed.
  436.  * For multithreading builds, change of the filesystem epoch
  437.  * will trigger cache cleanup in all threads.  
  438.  */
  439. int theFilesystemEpoch = 0;
  440. /*
  441.  * Stores the linked list of filesystems. A 1:1 copy of this
  442.  * list is also maintained in the TSD for each thread. This
  443.  * is to avoid synchronization issues.
  444.  */
  445. static FilesystemRecord *filesystemList = &nativeFilesystemRecord;
  446. TCL_DECLARE_MUTEX(filesystemMutex)
  447. /* 
  448.  * Used to implement Tcl_FSGetCwd in a file-system independent way.
  449.  */
  450. static Tcl_Obj* cwdPathPtr = NULL;
  451. static int cwdPathEpoch = 0;
  452. TCL_DECLARE_MUTEX(cwdMutex)
  453. /*
  454.  * This structure holds per-thread private copies of
  455.  * some global data. This way we avoid most of the
  456.  * synchronization calls which boosts performance, at
  457.  * cost of having to update this information each
  458.  * time the corresponding epoch counter changes.
  459.  * 
  460.  */
  461. typedef struct ThreadSpecificData {
  462.     int initialized;
  463.     int cwdPathEpoch;
  464.     int filesystemEpoch; 
  465.     Tcl_Obj *cwdPathPtr;
  466.     FilesystemRecord *filesystemList;
  467. } ThreadSpecificData;
  468. static Tcl_ThreadDataKey dataKey;
  469. /* 
  470.  * Declare fallback support function and 
  471.  * information for Tcl_FSLoadFile 
  472.  */
  473. static Tcl_FSUnloadFileProc FSUnloadTempFile;
  474. /*
  475.  * One of these structures is used each time we successfully load a
  476.  * file from a file system by way of making a temporary copy of the
  477.  * file on the native filesystem.  We need to store both the actual
  478.  * unloadProc/clientData combination which was used, and the original
  479.  * and modified filenames, so that we can correctly undo the entire
  480.  * operation when we want to unload the code.
  481.  */
  482. typedef struct FsDivertLoad {
  483.     Tcl_LoadHandle loadHandle;
  484.     Tcl_FSUnloadFileProc *unloadProcPtr;
  485.     Tcl_Obj *divertedFile;
  486.     Tcl_Filesystem *divertedFilesystem;
  487.     ClientData divertedFileNativeRep;
  488. } FsDivertLoad;
  489. /* Now move on to the basic filesystem implementation */
  490. static void
  491. FsThrExitProc(cd)
  492.     ClientData cd;
  493. {
  494.     ThreadSpecificData *tsdPtr = (ThreadSpecificData*)cd;
  495.     FilesystemRecord *fsRecPtr = NULL, *tmpFsRecPtr = NULL;
  496.     /* Trash the cwd copy */
  497.     if (tsdPtr->cwdPathPtr != NULL) {
  498. Tcl_DecrRefCount(tsdPtr->cwdPathPtr);
  499. tsdPtr->cwdPathPtr = NULL;
  500.     }
  501.     /* Trash the filesystems cache */
  502.     fsRecPtr = tsdPtr->filesystemList;
  503.     while (fsRecPtr != NULL) {
  504. tmpFsRecPtr = fsRecPtr->nextPtr;
  505. if (--fsRecPtr->fileRefCount <= 0) {
  506.     ckfree((char *)fsRecPtr);
  507. }
  508. fsRecPtr = tmpFsRecPtr;
  509.     }
  510.     tsdPtr->initialized = 0;
  511. }
  512. int 
  513. TclFSCwdPointerEquals(objPtr)
  514.     Tcl_Obj* objPtr;
  515. {
  516.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  517.     Tcl_MutexLock(&cwdMutex);
  518.     if (tsdPtr->cwdPathPtr == NULL) {
  519. if (cwdPathPtr == NULL) {
  520.     tsdPtr->cwdPathPtr = NULL;
  521. } else {
  522.     tsdPtr->cwdPathPtr = Tcl_DuplicateObj(cwdPathPtr);
  523.     Tcl_IncrRefCount(tsdPtr->cwdPathPtr);
  524. }
  525. tsdPtr->cwdPathEpoch = cwdPathEpoch;
  526.     } else if (tsdPtr->cwdPathEpoch != cwdPathEpoch) { 
  527. Tcl_DecrRefCount(tsdPtr->cwdPathPtr);
  528. if (cwdPathPtr == NULL) {
  529.     tsdPtr->cwdPathPtr = NULL;
  530. } else {
  531.     tsdPtr->cwdPathPtr = Tcl_DuplicateObj(cwdPathPtr);
  532.     Tcl_IncrRefCount(tsdPtr->cwdPathPtr);
  533. }
  534.     }
  535.     Tcl_MutexUnlock(&cwdMutex);
  536.     if (tsdPtr->initialized == 0) {
  537. Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData)tsdPtr);
  538. tsdPtr->initialized = 1;
  539.     }
  540.     return (tsdPtr->cwdPathPtr == objPtr); 
  541. }
  542. #ifdef TCL_THREADS
  543. static void
  544. FsRecacheFilesystemList(void)
  545. {
  546.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  547.     FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL;
  548.     /* Trash the current cache */
  549.     fsRecPtr = tsdPtr->filesystemList;
  550.     while (fsRecPtr != NULL) {
  551. tmpFsRecPtr = fsRecPtr->nextPtr;
  552. if (--fsRecPtr->fileRefCount <= 0) {
  553.     ckfree((char *)fsRecPtr);
  554. }
  555. fsRecPtr = tmpFsRecPtr;
  556.     }
  557.     tsdPtr->filesystemList = NULL;
  558.     /*
  559.      * Code below operates on shared data. We
  560.      * are already called under mutex lock so   
  561.      * we can safely proceed.
  562.      */
  563.     /* Locate tail of the global filesystem list */
  564.     fsRecPtr = filesystemList;
  565.     while (fsRecPtr != NULL) {
  566. tmpFsRecPtr = fsRecPtr;
  567. fsRecPtr = fsRecPtr->nextPtr;
  568.     }
  569.     
  570.     /* Refill the cache honouring the order */
  571.     fsRecPtr = tmpFsRecPtr;
  572.     while (fsRecPtr != NULL) {
  573. tmpFsRecPtr = (FilesystemRecord *)ckalloc(sizeof(FilesystemRecord));
  574. *tmpFsRecPtr = *fsRecPtr;
  575. tmpFsRecPtr->nextPtr = tsdPtr->filesystemList;
  576. tmpFsRecPtr->prevPtr = NULL;
  577. if (tsdPtr->filesystemList) {
  578.     tsdPtr->filesystemList->prevPtr = tmpFsRecPtr;
  579. }
  580. tsdPtr->filesystemList = tmpFsRecPtr;
  581.         fsRecPtr = fsRecPtr->prevPtr;
  582.     }
  583.     /* Make sure the above gets released on thread exit */
  584.     if (tsdPtr->initialized == 0) {
  585. Tcl_CreateThreadExitHandler(FsThrExitProc, (ClientData)tsdPtr);
  586. tsdPtr->initialized = 1;
  587.     }
  588. }
  589. #endif
  590. static FilesystemRecord *
  591. FsGetFirstFilesystem(void) {
  592.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  593.     FilesystemRecord *fsRecPtr;
  594. #ifndef TCL_THREADS
  595.     tsdPtr->filesystemEpoch = theFilesystemEpoch;
  596.     fsRecPtr = filesystemList;
  597. #else
  598.     Tcl_MutexLock(&filesystemMutex);
  599.     if (tsdPtr->filesystemList == NULL
  600.     || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) {
  601.   FsRecacheFilesystemList();
  602. tsdPtr->filesystemEpoch = theFilesystemEpoch;
  603.     }
  604.     Tcl_MutexUnlock(&filesystemMutex);
  605.     fsRecPtr = tsdPtr->filesystemList;
  606. #endif
  607.     return fsRecPtr;
  608. }
  609. static void
  610. FsUpdateCwd(cwdObj)
  611.     Tcl_Obj *cwdObj;
  612. {
  613.     int len;
  614.     char *str = NULL;
  615.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  616.     if (cwdObj != NULL) {
  617. str = Tcl_GetStringFromObj(cwdObj, &len);
  618.     }
  619.     Tcl_MutexLock(&cwdMutex);
  620.     if (cwdPathPtr != NULL) {
  621.         Tcl_DecrRefCount(cwdPathPtr);
  622.     }
  623.     if (cwdObj == NULL) {
  624. cwdPathPtr = NULL;
  625.     } else {
  626. /* This MUST be stored as string object! */
  627. cwdPathPtr = Tcl_NewStringObj(str, len); 
  628.      Tcl_IncrRefCount(cwdPathPtr);
  629.     }
  630.     cwdPathEpoch++;
  631.     tsdPtr->cwdPathEpoch = cwdPathEpoch;
  632.     Tcl_MutexUnlock(&cwdMutex);
  633.     if (tsdPtr->cwdPathPtr) {
  634.         Tcl_DecrRefCount(tsdPtr->cwdPathPtr);
  635.     }
  636.     if (cwdObj == NULL) {
  637. tsdPtr->cwdPathPtr = NULL;
  638.     } else {
  639. tsdPtr->cwdPathPtr = Tcl_NewStringObj(str, len); 
  640. Tcl_IncrRefCount(tsdPtr->cwdPathPtr);
  641.     }
  642. }
  643. /*
  644.  *----------------------------------------------------------------------
  645.  *
  646.  * TclFinalizeFilesystem --
  647.  *
  648.  * Clean up the filesystem.  After this, calls to all Tcl_FS...
  649.  * functions will fail.
  650.  *
  651.  * We will later call TclResetFilesystem to restore the FS
  652.  * to a pristine state.
  653.  *
  654.  * Results:
  655.  * None.
  656.  *
  657.  * Side effects:
  658.  * Frees any memory allocated by the filesystem.
  659.  *
  660.  *----------------------------------------------------------------------
  661.  */
  662. void
  663. TclFinalizeFilesystem()
  664. {
  665.     FilesystemRecord *fsRecPtr;
  666.     /* 
  667.      * Assumption that only one thread is active now.  Otherwise
  668.      * we would need to put various mutexes around this code.
  669.      */
  670.     
  671.     if (cwdPathPtr != NULL) {
  672. Tcl_DecrRefCount(cwdPathPtr);
  673. cwdPathPtr = NULL;
  674.         cwdPathEpoch = 0;
  675.     }
  676.     /* 
  677.      * Remove all filesystems, freeing any allocated memory
  678.      * that is no longer needed
  679.      */
  680.     fsRecPtr = filesystemList;
  681.     while (fsRecPtr != NULL) {
  682. FilesystemRecord *tmpFsRecPtr = fsRecPtr->nextPtr;
  683. if (fsRecPtr->fileRefCount <= 0) {
  684.     /* The native filesystem is static, so we don't free it */
  685.     if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
  686. ckfree((char *)fsRecPtr);
  687.     }
  688. }
  689. fsRecPtr = tmpFsRecPtr;
  690.     }
  691.     filesystemList = NULL;
  692.     /*
  693.      * Now filesystemList is NULL.  This means that any attempt
  694.      * to use the filesystem is likely to fail.
  695.      */
  696.     statProcList = NULL;
  697.     accessProcList = NULL;
  698.     openFileChannelProcList = NULL;
  699. #ifdef __WIN32__
  700.     TclWinEncodingsCleanup();
  701. #endif
  702. }
  703. /*
  704.  *----------------------------------------------------------------------
  705.  *
  706.  * TclResetFilesystem --
  707.  *
  708.  * Restore the filesystem to a pristine state.
  709.  *
  710.  * Results:
  711.  * None.
  712.  *
  713.  * Side effects:
  714.  * None.
  715.  *
  716.  *----------------------------------------------------------------------
  717.  */
  718. void
  719. TclResetFilesystem()
  720. {
  721.     filesystemList = &nativeFilesystemRecord;
  722.     /* 
  723.      * Note, at this point, I believe nativeFilesystemRecord ->
  724.      * fileRefCount should equal 1 and if not, we should try to track
  725.      * down the cause.
  726.      */
  727.     
  728. #ifdef __WIN32__
  729.     /* 
  730.      * Cleans up the win32 API filesystem proc lookup table. This must
  731.      * happen very late in finalization so that deleting of copied
  732.      * dlls can occur.
  733.      */
  734.     TclWinResetInterfaces();
  735. #endif
  736. }
  737. /*
  738.  *----------------------------------------------------------------------
  739.  *
  740.  * Tcl_FSRegister --
  741.  *
  742.  *    Insert the filesystem function table at the head of the list of
  743.  *    functions which are used during calls to all file-system
  744.  *    operations.  The filesystem will be added even if it is 
  745.  *    already in the list.  (You can use Tcl_FSData to
  746.  *    check if it is in the list, provided the ClientData used was
  747.  *    not NULL).
  748.  *    
  749.  *    Note that the filesystem handling is head-to-tail of the list.
  750.  *    Each filesystem is asked in turn whether it can handle a
  751.  *    particular request, _until_ one of them says 'yes'. At that
  752.  *    point no further filesystems are asked.
  753.  *    
  754.  *    In particular this means if you want to add a diagnostic
  755.  *    filesystem (which simply reports all fs activity), it must be 
  756.  *    at the head of the list: i.e. it must be the last registered.
  757.  *
  758.  * Results:
  759.  *    Normally TCL_OK; TCL_ERROR if memory for a new node in the list
  760.  *    could not be allocated.
  761.  *
  762.  * Side effects:
  763.  *    Memory allocated and modifies the link list for filesystems.
  764.  *
  765.  *----------------------------------------------------------------------
  766.  */
  767. int
  768. Tcl_FSRegister(clientData, fsPtr)
  769.     ClientData clientData;    /* Client specific data for this fs */
  770.     Tcl_Filesystem  *fsPtr;   /* The filesystem record for the new fs. */
  771. {
  772.     FilesystemRecord *newFilesystemPtr;
  773.     if (fsPtr == NULL) {
  774. return TCL_ERROR;
  775.     }
  776.     newFilesystemPtr = (FilesystemRecord *) ckalloc(sizeof(FilesystemRecord));
  777.     newFilesystemPtr->clientData = clientData;
  778.     newFilesystemPtr->fsPtr = fsPtr;
  779.     /* 
  780.      * We start with a refCount of 1.  If this drops to zero, then
  781.      * anyone is welcome to ckfree us.
  782.      */
  783.     newFilesystemPtr->fileRefCount = 1;
  784.     /* 
  785.      * Is this lock and wait strictly speaking necessary?  Since any
  786.      * iterators out there will have grabbed a copy of the head of
  787.      * the list and be iterating away from that, if we add a new
  788.      * element to the head of the list, it can't possibly have any
  789.      * effect on any of their loops.  In fact it could be better not
  790.      * to wait, since we are adjusting the filesystem epoch, any
  791.      * cached representations calculated by existing iterators are
  792.      * going to have to be thrown away anyway.
  793.      * 
  794.      * However, since registering and unregistering filesystems is
  795.      * a very rare action, this is not a very important point.
  796.      */
  797.     Tcl_MutexLock(&filesystemMutex);
  798.     newFilesystemPtr->nextPtr = filesystemList;
  799.     newFilesystemPtr->prevPtr = NULL;
  800.     if (filesystemList) {
  801. filesystemList->prevPtr = newFilesystemPtr;
  802.     }
  803.     filesystemList = newFilesystemPtr;
  804.     /* 
  805.      * Increment the filesystem epoch counter, since existing paths
  806.      * might conceivably now belong to different filesystems.
  807.      */
  808.     theFilesystemEpoch++;
  809.     Tcl_MutexUnlock(&filesystemMutex);
  810.     return TCL_OK;
  811. }
  812. /*
  813.  *----------------------------------------------------------------------
  814.  *
  815.  * Tcl_FSUnregister --
  816.  *
  817.  *    Remove the passed filesystem from the list of filesystem
  818.  *    function tables.  It also ensures that the built-in
  819.  *    (native) filesystem is not removable, although we may wish
  820.  *    to change that decision in the future to allow a smaller
  821.  *    Tcl core, in which the native filesystem is not used at
  822.  *    all (we could, say, initialise Tcl completely over a network
  823.  *    connection).
  824.  *
  825.  * Results:
  826.  *    TCL_OK if the procedure pointer was successfully removed,
  827.  *    TCL_ERROR otherwise.
  828.  *
  829.  * Side effects:
  830.  *    Memory may be deallocated (or will be later, once no "path" 
  831.  *    objects refer to this filesystem), but the list of registered
  832.  *    filesystems is updated immediately.
  833.  *
  834.  *----------------------------------------------------------------------
  835.  */
  836. int
  837. Tcl_FSUnregister(fsPtr)
  838.     Tcl_Filesystem  *fsPtr;   /* The filesystem record to remove. */
  839. {
  840.     int retVal = TCL_ERROR;
  841.     FilesystemRecord *fsRecPtr;
  842.     Tcl_MutexLock(&filesystemMutex);
  843.     /*
  844.      * Traverse the 'filesystemList' looking for the particular node
  845.      * whose 'fsPtr' member matches 'fsPtr' and remove that one from
  846.      * the list.  Ensure that the "default" node cannot be removed.
  847.      */
  848.     fsRecPtr = filesystemList;
  849.     while ((retVal == TCL_ERROR) && (fsRecPtr->fsPtr != &tclNativeFilesystem)) {
  850. if (fsRecPtr->fsPtr == fsPtr) {
  851.     if (fsRecPtr->prevPtr) {
  852. fsRecPtr->prevPtr->nextPtr = fsRecPtr->nextPtr;
  853.     } else {
  854. filesystemList = fsRecPtr->nextPtr;
  855.     }
  856.     if (fsRecPtr->nextPtr) {
  857. fsRecPtr->nextPtr->prevPtr = fsRecPtr->prevPtr;
  858.     }
  859.     /* 
  860.      * Increment the filesystem epoch counter, since existing
  861.      * paths might conceivably now belong to different
  862.      * filesystems.  This should also ensure that paths which
  863.      * have cached the filesystem which is about to be deleted
  864.      * do not reference that filesystem (which would of course
  865.      * lead to memory exceptions).
  866.      */
  867.     theFilesystemEpoch++;
  868.     
  869.     fsRecPtr->fileRefCount--;
  870.     if (fsRecPtr->fileRefCount <= 0) {
  871.         ckfree((char *)fsRecPtr);
  872.     }
  873.     retVal = TCL_OK;
  874. } else {
  875.     fsRecPtr = fsRecPtr->nextPtr;
  876. }
  877.     }
  878.     Tcl_MutexUnlock(&filesystemMutex);
  879.     return (retVal);
  880. }
  881. /*
  882.  *----------------------------------------------------------------------
  883.  *
  884.  * Tcl_FSMatchInDirectory --
  885.  *
  886.  * This routine is used by the globbing code to search a directory
  887.  * for all files which match a given pattern.  The appropriate
  888.  * function for the filesystem to which pathPtr belongs will be
  889.  * called.  If pathPtr does not belong to any filesystem and if it
  890.  * is NULL or the empty string, then we assume the pattern is to be
  891.  * matched in the current working directory.  To avoid each
  892.  * filesystem's Tcl_FSMatchInDirectoryProc having to deal with this
  893.  * issue, we create a pathPtr on the fly (equal to the cwd), and
  894.  * then remove it from the results returned.  This makes filesystems
  895.  * easy to write, since they can assume the pathPtr passed to them
  896.  * is an ordinary path.  In fact this means we could remove such
  897.  * special case handling from Tcl's native filesystems.
  898.  *
  899.  * If 'pattern' is NULL, then pathPtr is assumed to be a fully
  900.  * specified path of a single file/directory which must be
  901.  * checked for existence and correct type.
  902.  *
  903.  * Results: 
  904.  *
  905.  * The return value is a standard Tcl result indicating whether an
  906.  * error occurred in globbing.  Error messages are placed in
  907.  * interp, but good results are placed in the resultPtr given.
  908.  *
  909.  * Recursive searches, e.g.
  910.  *
  911.  *    glob -dir $dir -join * pkgIndex.tcl
  912.  *    
  913.  * which must recurse through each directory matching '*' are
  914.  * handled internally by Tcl, by passing specific flags in a 
  915.  * modified 'types' parameter.  This means the actual filesystem
  916.  * only ever sees patterns which match in a single directory.
  917.  *
  918.  * Side effects:
  919.  * The interpreter may have an error message inserted into it.
  920.  *
  921.  *---------------------------------------------------------------------- 
  922.  */
  923. int
  924. Tcl_FSMatchInDirectory(interp, result, pathPtr, pattern, types)
  925.     Tcl_Interp *interp; /* Interpreter to receive error messages. */
  926.     Tcl_Obj *result; /* List object to receive results. */
  927.     Tcl_Obj *pathPtr;         /* Contains path to directory to search. */
  928.     CONST char *pattern; /* Pattern to match against. */
  929.     Tcl_GlobTypeData *types; /* Object containing list of acceptable types.
  930.  * May be NULL. In particular the directory
  931.  * flag is very important. */
  932. {
  933.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  934.     if (fsPtr != NULL) {
  935. Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc;
  936. if (proc != NULL) {
  937.     int ret = (*proc)(interp, result, pathPtr, pattern, types);
  938.     if (ret == TCL_OK && pattern != NULL) {
  939. result = FsAddMountsToGlobResult(result, pathPtr, 
  940.  pattern, types);
  941.     }
  942.     return ret;
  943. }
  944.     } else {
  945. Tcl_Obj* cwd;
  946. int ret = -1;
  947. if (pathPtr != NULL) {
  948.     int len;
  949.     Tcl_GetStringFromObj(pathPtr,&len);
  950.     if (len != 0) {
  951. /* 
  952.  * We have no idea how to match files in a directory
  953.  * which belongs to no known filesystem
  954.  */
  955. Tcl_SetErrno(ENOENT);
  956. return -1;
  957.     }
  958. }
  959. /* 
  960.  * We have an empty or NULL path.  This is defined to mean we
  961.  * must search for files within the current 'cwd'.  We
  962.  * therefore use that, but then since the proc we call will
  963.  * return results which include the cwd we must then trim it
  964.  * off the front of each path in the result.  We choose to deal
  965.  * with this here (in the generic code), since if we don't,
  966.  * every single filesystem's implementation of
  967.  * Tcl_FSMatchInDirectory will have to deal with it for us.
  968.  */
  969. cwd = Tcl_FSGetCwd(NULL);
  970. if (cwd == NULL) {
  971.     if (interp != NULL) {
  972. Tcl_SetResult(interp, "glob couldn't determine "
  973.   "the current working directory", TCL_STATIC);
  974.     }
  975.     return TCL_ERROR;
  976. }
  977. fsPtr = Tcl_FSGetFileSystemForPath(cwd);
  978. if (fsPtr != NULL) {
  979.     Tcl_FSMatchInDirectoryProc *proc = fsPtr->matchInDirectoryProc;
  980.     if (proc != NULL) {
  981. Tcl_Obj* tmpResultPtr = Tcl_NewListObj(0, NULL);
  982. Tcl_IncrRefCount(tmpResultPtr);
  983. ret = (*proc)(interp, tmpResultPtr, cwd, pattern, types);
  984. if (ret == TCL_OK) {
  985.     int resLength;
  986.     tmpResultPtr = FsAddMountsToGlobResult(tmpResultPtr, cwd,
  987.    pattern, types);
  988.     ret = Tcl_ListObjLength(interp, tmpResultPtr, &resLength);
  989.     if (ret == TCL_OK) {
  990. int i;
  991. for (i = 0; i < resLength; i++) {
  992.     Tcl_Obj *elt;
  993.     
  994.     Tcl_ListObjIndex(interp, tmpResultPtr, i, &elt);
  995.     Tcl_ListObjAppendElement(interp, result, 
  996. TclFSMakePathRelative(interp, elt, cwd));
  997. }
  998.     }
  999. }
  1000. Tcl_DecrRefCount(tmpResultPtr);
  1001.     }
  1002. }
  1003. Tcl_DecrRefCount(cwd);
  1004. return ret;
  1005.     }
  1006.     Tcl_SetErrno(ENOENT);
  1007.     return -1;
  1008. }
  1009. /*
  1010.  *----------------------------------------------------------------------
  1011.  *
  1012.  * FsAddMountsToGlobResult --
  1013.  *
  1014.  * This routine is used by the globbing code to take the results
  1015.  * of a directory listing and add any mounted paths to that
  1016.  * listing.  This is required so that simple things like 
  1017.  * 'glob *' merge mounts and listings correctly.
  1018.  *
  1019.  * Results: 
  1020.  *
  1021.  * The passed in 'result' may be modified (in place, if
  1022.  * necessary), and the correct list is returned.
  1023.  *
  1024.  * Side effects:
  1025.  * None.
  1026.  *
  1027.  *---------------------------------------------------------------------- 
  1028.  */
  1029. static Tcl_Obj*
  1030. FsAddMountsToGlobResult(result, pathPtr, pattern, types)
  1031.     Tcl_Obj *result;    /* The current list of matching paths */
  1032.     Tcl_Obj *pathPtr;   /* The directory in question */
  1033.     CONST char *pattern;
  1034.     Tcl_GlobTypeData *types;
  1035. {
  1036.     int mLength, gLength, i;
  1037.     int dir = (types == NULL || (types->type & TCL_GLOB_TYPE_DIR));
  1038.     Tcl_Obj *mounts = FsListMounts(pathPtr, pattern);
  1039.     if (mounts == NULL) return result; 
  1040.     if (Tcl_ListObjLength(NULL, mounts, &mLength) != TCL_OK || mLength == 0) {
  1041. goto endOfMounts;
  1042.     }
  1043.     if (Tcl_ListObjLength(NULL, result, &gLength) != TCL_OK) {
  1044. goto endOfMounts;
  1045.     }
  1046.     for (i = 0; i < mLength; i++) {
  1047. Tcl_Obj *mElt;
  1048. int j;
  1049. int found = 0;
  1050. Tcl_ListObjIndex(NULL, mounts, i, &mElt);
  1051. for (j = 0; j < gLength; j++) {
  1052.     Tcl_Obj *gElt;
  1053.     Tcl_ListObjIndex(NULL, result, j, &gElt);
  1054.     if (Tcl_FSEqualPaths(mElt, gElt)) {
  1055. found = 1;
  1056. if (!dir) {
  1057.     /* We don't want to list this */
  1058.     if (Tcl_IsShared(result)) {
  1059. Tcl_Obj *newList;
  1060. newList = Tcl_DuplicateObj(result);
  1061. Tcl_DecrRefCount(result);
  1062. result = newList;
  1063.     }
  1064.     Tcl_ListObjReplace(NULL, result, j, 1, 0, NULL);
  1065.     gLength--;
  1066. }
  1067. /* Break out of for loop */
  1068. break;
  1069.     }
  1070. }
  1071. if (!found && dir) {
  1072.     if (Tcl_IsShared(result)) {
  1073. Tcl_Obj *newList;
  1074. newList = Tcl_DuplicateObj(result);
  1075. Tcl_DecrRefCount(result);
  1076. result = newList;
  1077.     }
  1078.     Tcl_ListObjAppendElement(NULL, result, mElt);
  1079.     /* 
  1080.      * No need to increment gLength, since we
  1081.      * don't want to compare mounts against
  1082.      * mounts.
  1083.      */
  1084. }
  1085.     }
  1086.   endOfMounts:
  1087.     Tcl_DecrRefCount(mounts);
  1088.     return result;
  1089. }
  1090. /*
  1091.  *----------------------------------------------------------------------
  1092.  *
  1093.  * Tcl_FSMountsChanged --
  1094.  *
  1095.  *    Notify the filesystem that the available mounted filesystems
  1096.  *    (or within any one filesystem type, the number or location of
  1097.  *    mount points) have changed.
  1098.  *
  1099.  * Results:
  1100.  *    None.
  1101.  *
  1102.  * Side effects:
  1103.  *    The global filesystem variable 'theFilesystemEpoch' is
  1104.  *    incremented.  The effect of this is to make all cached
  1105.  *    path representations invalid.  Clearly it should only therefore
  1106.  *    be called when it is really required!  There are a few 
  1107.  *    circumstances when it should be called:
  1108.  *    
  1109.  *    (1) when a new filesystem is registered or unregistered.  
  1110.  *    Strictly speaking this is only necessary if the new filesystem
  1111.  *    accepts file paths as is (normally the filesystem itself is
  1112.  *    really a shell which hasn't yet had any mount points established
  1113.  *    and so its 'pathInFilesystem' proc will always fail).  However,
  1114.  *    for safety, Tcl always calls this for you in these circumstances.
  1115.  * 
  1116.  *    (2) when additional mount points are established inside any
  1117.  *    existing filesystem (except the native fs)
  1118.  *    
  1119.  *    (3) when any filesystem (except the native fs) changes the list
  1120.  *    of available volumes.
  1121.  *    
  1122.  *    (4) when the mapping from a string representation of a file to
  1123.  *    a full, normalized path changes.  For example, if 'env(HOME)' 
  1124.  *    is modified, then any path containing '~' will map to a different
  1125.  *    filesystem location.  Therefore all such paths need to have
  1126.  *    their internal representation invalidated.
  1127.  *    
  1128.  *    Tcl has no control over (2) and (3), so any registered filesystem
  1129.  *    must make sure it calls this function when those situations
  1130.  *    occur.
  1131.  *    
  1132.  *    (Note: the reason for the exception in 2,3 for the native
  1133.  *    filesystem is that the native filesystem by default claims all
  1134.  *    unknown files even if it really doesn't understand them or if
  1135.  *    they don't exist).
  1136.  *
  1137.  *----------------------------------------------------------------------
  1138.  */
  1139. void
  1140. Tcl_FSMountsChanged(fsPtr)
  1141.     Tcl_Filesystem *fsPtr;
  1142. {
  1143.     /* 
  1144.      * We currently don't do anything with this parameter.  We
  1145.      * could in the future only invalidate files for this filesystem
  1146.      * or otherwise take more advanced action.
  1147.      */
  1148.     (void)fsPtr;
  1149.     /* 
  1150.      * Increment the filesystem epoch counter, since existing paths
  1151.      * might now belong to different filesystems.
  1152.      */
  1153.     Tcl_MutexLock(&filesystemMutex);
  1154.     theFilesystemEpoch++;
  1155.     Tcl_MutexUnlock(&filesystemMutex);
  1156. }
  1157. /*
  1158.  *----------------------------------------------------------------------
  1159.  *
  1160.  * Tcl_FSData --
  1161.  *
  1162.  *    Retrieve the clientData field for the filesystem given,
  1163.  *    or NULL if that filesystem is not registered.
  1164.  *
  1165.  * Results:
  1166.  *    A clientData value, or NULL.  Note that if the filesystem
  1167.  *    was registered with a NULL clientData field, this function
  1168.  *    will return that NULL value.
  1169.  *
  1170.  * Side effects:
  1171.  *    None.
  1172.  *
  1173.  *----------------------------------------------------------------------
  1174.  */
  1175. ClientData
  1176. Tcl_FSData(fsPtr)
  1177.     Tcl_Filesystem  *fsPtr;   /* The filesystem record to query. */
  1178. {
  1179.     ClientData retVal = NULL;
  1180.     FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();
  1181.     /*
  1182.      * Traverse the 'filesystemList' looking for the particular node
  1183.      * whose 'fsPtr' member matches 'fsPtr' and remove that one from
  1184.      * the list.  Ensure that the "default" node cannot be removed.
  1185.      */
  1186.     while ((retVal == NULL) && (fsRecPtr != NULL)) {
  1187. if (fsRecPtr->fsPtr == fsPtr) {
  1188.     retVal = fsRecPtr->clientData;
  1189. }
  1190. fsRecPtr = fsRecPtr->nextPtr;
  1191.     }
  1192.     return retVal;
  1193. }
  1194. /*
  1195.  *---------------------------------------------------------------------------
  1196.  *
  1197.  * TclFSNormalizeAbsolutePath --
  1198.  *
  1199.  * Description:
  1200.  * Takes an absolute path specification and computes a 'normalized'
  1201.  * path from it.
  1202.  *
  1203.  * A normalized path is one which has all '../', './' removed.
  1204.  * Also it is one which is in the 'standard' format for the native
  1205.  * platform.  On MacOS, Unix, this means the path must be free of
  1206.  * symbolic links/aliases, and on Windows it means we want the
  1207.  * long form, with that long form's case-dependence (which gives
  1208.  * us a unique, case-dependent path).
  1209.  *
  1210.  * The behaviour of this function if passed a non-absolute path
  1211.  * is NOT defined.
  1212.  *
  1213.  * Results:
  1214.  * The result is returned in a Tcl_Obj with a refCount of 1,
  1215.  * which is therefore owned by the caller.  It must be
  1216.  * freed (with Tcl_DecrRefCount) by the caller when no longer needed.
  1217.  *
  1218.  * Side effects:
  1219.  * None (beyond the memory allocation for the result).
  1220.  *
  1221.  * Special note:
  1222.  * This code is based on code from Matt Newman and Jean-Claude
  1223.  * Wippler, with additions from Vince Darley and is copyright 
  1224.  * those respective authors.
  1225.  *
  1226.  *---------------------------------------------------------------------------
  1227.  */
  1228. static Tcl_Obj *
  1229. TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
  1230.     Tcl_Interp* interp;    /* Interpreter to use */
  1231.     Tcl_Obj *pathPtr;      /* Absolute path to normalize */
  1232.     ClientData *clientDataPtr;
  1233. {
  1234.     int splen = 0, nplen, eltLen, i;
  1235.     char *eltName;
  1236.     Tcl_Obj *retVal;
  1237.     Tcl_Obj *split;
  1238.     Tcl_Obj *elt;
  1239.     
  1240.     /* Split has refCount zero */
  1241.     split = Tcl_FSSplitPath(pathPtr, &splen);
  1242.     /* 
  1243.      * Modify the list of entries in place, by removing '.', and
  1244.      * removing '..' and the entry before -- unless that entry before
  1245.      * is the top-level entry, i.e. the name of a volume.
  1246.      */
  1247.     nplen = 0;
  1248.     for (i = 0; i < splen; i++) {
  1249. Tcl_ListObjIndex(NULL, split, nplen, &elt);
  1250. eltName = Tcl_GetStringFromObj(elt, &eltLen);
  1251. if ((eltLen == 1) && (eltName[0] == '.')) {
  1252.     Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
  1253. } else if ((eltLen == 2)
  1254. && (eltName[0] == '.') && (eltName[1] == '.')) {
  1255.     if (nplen > 1) {
  1256.         nplen--;
  1257. Tcl_ListObjReplace(NULL, split, nplen, 2, 0, NULL);
  1258.     } else {
  1259. Tcl_ListObjReplace(NULL, split, nplen, 1, 0, NULL);
  1260.     }
  1261. } else {
  1262.     nplen++;
  1263. }
  1264.     }
  1265.     if (nplen > 0) {
  1266. ClientData clientData = NULL;
  1267. retVal = Tcl_FSJoinPath(split, nplen);
  1268. /* 
  1269.  * Now we have an absolute path, with no '..', '.' sequences,
  1270.  * but it still may not be in 'unique' form, depending on the
  1271.  * platform.  For instance, Unix is case-sensitive, so the
  1272.  * path is ok.  Windows is case-insensitive, and also has the
  1273.  * weird 'longname/shortname' thing (e.g. C:/Program Files/ and
  1274.  * C:/Progra~1/ are equivalent).  MacOS is case-insensitive.
  1275.  * 
  1276.  * Virtual file systems which may be registered may have
  1277.  * other criteria for normalizing a path.
  1278.  */
  1279. Tcl_IncrRefCount(retVal);
  1280. TclFSNormalizeToUniquePath(interp, retVal, 0, &clientData);
  1281. /* 
  1282.  * Since we know it is a normalized path, we can
  1283.  * actually convert this object into an "path" object for
  1284.  * greater efficiency 
  1285.  */
  1286. TclFSMakePathFromNormalized(interp, retVal, clientData);
  1287. if (clientDataPtr != NULL) {
  1288.     *clientDataPtr = clientData;
  1289. }
  1290.     } else {
  1291. /* Init to an empty string */
  1292. retVal = Tcl_NewStringObj("",0);
  1293. Tcl_IncrRefCount(retVal);
  1294.     }
  1295.     /* 
  1296.      * We increment and then decrement the refCount of split to free
  1297.      * it.  We do this right at the end, in case there are
  1298.      * optimisations in Tcl_FSJoinPath(split, nplen) above which would
  1299.      * let it make use of split more effectively if it has a refCount
  1300.      * of zero.  Also we can't just decrement the ref count, in case
  1301.      * 'split' was actually returned by the join call above, in a
  1302.      * single-element optimisation when nplen == 1.
  1303.      */
  1304.     Tcl_IncrRefCount(split);
  1305.     Tcl_DecrRefCount(split);
  1306.     /* This has a refCount of 1 for the caller */
  1307.     return retVal;
  1308. }
  1309. /*
  1310.  *---------------------------------------------------------------------------
  1311.  *
  1312.  * TclFSNormalizeToUniquePath --
  1313.  *
  1314.  * Description:
  1315.  * Takes a path specification containing no ../, ./ sequences,
  1316.  * and converts it into a unique path for the given platform.
  1317.  *      On MacOS, Unix, this means the path must be free of
  1318.  * symbolic links/aliases, and on Windows it means we want the
  1319.  * long form, with that long form's case-dependence (which gives
  1320.  * us a unique, case-dependent path).
  1321.  *
  1322.  * Results:
  1323.  * The pathPtr is modified in place.  The return value is
  1324.  * the last byte offset which was recognised in the path
  1325.  * string.
  1326.  *
  1327.  * Side effects:
  1328.  * None (beyond the memory allocation for the result).
  1329.  *
  1330.  * Special notes:
  1331.  * If the filesystem-specific normalizePathProcs can re-introduce
  1332.  * ../, ./ sequences into the path, then this function will
  1333.  * not return the correct result.  This may be possible with
  1334.  * symbolic links on unix/macos.
  1335.  *
  1336.  *      Important assumption: if startAt is non-zero, it must point
  1337.  *      to a directory separator that we know exists and is already
  1338.  *      normalized (so it is important not to point to the char just
  1339.  *      after the separator).
  1340.  *---------------------------------------------------------------------------
  1341.  */
  1342. int
  1343. TclFSNormalizeToUniquePath(interp, pathPtr, startAt, clientDataPtr)
  1344.     Tcl_Interp *interp;
  1345.     Tcl_Obj *pathPtr;
  1346.     int startAt;
  1347.     ClientData *clientDataPtr;
  1348. {
  1349.     FilesystemRecord *fsRecPtr, *firstFsRecPtr;
  1350.     /* Ignore this variable */
  1351.     (void)clientDataPtr;
  1352.     
  1353.     /*
  1354.      * Call each of the "normalise path" functions in succession. This is
  1355.      * a special case, in which if we have a native filesystem handler,
  1356.      * we call it first.  This is because the root of Tcl's filesystem
  1357.      * is always a native filesystem (i.e. '/' on unix is native).
  1358.      */
  1359.     firstFsRecPtr = FsGetFirstFilesystem();
  1360.     fsRecPtr = firstFsRecPtr;
  1361.     while (fsRecPtr != NULL) {
  1362.         if (fsRecPtr->fsPtr == &tclNativeFilesystem) {
  1363.     Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
  1364.     if (proc != NULL) {
  1365. startAt = (*proc)(interp, pathPtr, startAt);
  1366.     }
  1367.     break;
  1368.         }
  1369. fsRecPtr = fsRecPtr->nextPtr;
  1370.     }
  1371.     
  1372.     fsRecPtr = firstFsRecPtr; 
  1373.     while (fsRecPtr != NULL) {
  1374. /* Skip the native system next time through */
  1375. if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
  1376.     Tcl_FSNormalizePathProc *proc = fsRecPtr->fsPtr->normalizePathProc;
  1377.     if (proc != NULL) {
  1378. startAt = (*proc)(interp, pathPtr, startAt);
  1379.     }
  1380.     /* 
  1381.      * We could add an efficiency check like this:
  1382.      * 
  1383.      *   if (retVal == length-of(pathPtr)) {break;}
  1384.      * 
  1385.      * but there's not much benefit.
  1386.      */
  1387. }
  1388. fsRecPtr = fsRecPtr->nextPtr;
  1389.     }
  1390.     return startAt;
  1391. }
  1392. /*
  1393.  *---------------------------------------------------------------------------
  1394.  *
  1395.  * TclGetOpenMode --
  1396.  *
  1397.  * Description:
  1398.  * Computes a POSIX mode mask for opening a file, from a given string,
  1399.  * and also sets a flag to indicate whether the caller should seek to
  1400.  * EOF after opening the file.
  1401.  *
  1402.  * Results:
  1403.  * On success, returns mode to pass to "open". If an error occurs, the
  1404.  * return value is -1 and if interp is not NULL, sets interp's result
  1405.  * object to an error message.
  1406.  *
  1407.  * Side effects:
  1408.  * Sets the integer referenced by seekFlagPtr to 1 to tell the caller
  1409.  * to seek to EOF after opening the file.
  1410.  *
  1411.  * Special note:
  1412.  * This code is based on a prototype implementation contributed
  1413.  * by Mark Diekhans.
  1414.  *
  1415.  *---------------------------------------------------------------------------
  1416.  */
  1417. int
  1418. TclGetOpenMode(interp, string, seekFlagPtr)
  1419.     Tcl_Interp *interp; /* Interpreter to use for error
  1420.  * reporting - may be NULL. */
  1421.     CONST char *string; /* Mode string, e.g. "r+" or
  1422.  * "RDONLY CREAT". */
  1423.     int *seekFlagPtr; /* Set this to 1 if the caller
  1424.                                          * should seek to EOF during the
  1425.                                          * opening of the file. */
  1426. {
  1427.     int mode, modeArgc, c, i, gotRW;
  1428.     CONST char **modeArgv, *flag;
  1429. #define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR)
  1430.     /*
  1431.      * Check for the simpler fopen-like access modes (e.g. "r").  They
  1432.      * are distinguished from the POSIX access modes by the presence
  1433.      * of a lower-case first letter.
  1434.      */
  1435.     *seekFlagPtr = 0;
  1436.     mode = 0;
  1437.     /*
  1438.      * Guard against international characters before using byte oriented
  1439.      * routines.
  1440.      */
  1441.     if (!(string[0] & 0x80)
  1442.     && islower(UCHAR(string[0]))) { /* INTL: ISO only. */
  1443. switch (string[0]) {
  1444.     case 'r':
  1445. mode = O_RDONLY;
  1446. break;
  1447.     case 'w':
  1448. mode = O_WRONLY|O_CREAT|O_TRUNC;
  1449. break;
  1450.     case 'a':
  1451.         /* [Bug 680143].
  1452.  * Added O_APPEND for proper automatic
  1453.  * seek-to-end-on-write by the OS.
  1454.  */
  1455.         mode = O_WRONLY|O_CREAT|O_APPEND;
  1456.                 *seekFlagPtr = 1;
  1457. break;
  1458.     default:
  1459. error:
  1460.                 if (interp != (Tcl_Interp *) NULL) {
  1461.                     Tcl_AppendResult(interp,
  1462.                             "illegal access mode "", string, """,
  1463.                             (char *) NULL);
  1464.                 }
  1465. return -1;
  1466. }
  1467. if (string[1] == '+') {
  1468.     /*
  1469.      * Must remove the O_APPEND flag so that the seek command
  1470.      * works. [Bug 1773127]
  1471.      */
  1472.     mode &= ~(O_RDONLY|O_WRONLY|O_APPEND);
  1473.     mode |= O_RDWR;
  1474.     if (string[2] != 0) {
  1475. goto error;
  1476.     }
  1477. } else if (string[1] != 0) {
  1478.     goto error;
  1479. }
  1480.         return mode;
  1481.     }
  1482.     /*
  1483.      * The access modes are specified using a list of POSIX modes
  1484.      * such as O_CREAT.
  1485.      *
  1486.      * IMPORTANT NOTE: We rely on Tcl_SplitList working correctly when
  1487.      * a NULL interpreter is passed in.
  1488.      */
  1489.     if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) {
  1490.         if (interp != (Tcl_Interp *) NULL) {
  1491.             Tcl_AddErrorInfo(interp,
  1492.                     "n    while processing open access modes "");
  1493.             Tcl_AddErrorInfo(interp, string);
  1494.             Tcl_AddErrorInfo(interp, """);
  1495.         }
  1496.         return -1;
  1497.     }
  1498.     
  1499.     gotRW = 0;
  1500.     for (i = 0; i < modeArgc; i++) {
  1501. flag = modeArgv[i];
  1502. c = flag[0];
  1503. if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) {
  1504.     mode = (mode & ~RW_MODES) | O_RDONLY;
  1505.     gotRW = 1;
  1506. } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) {
  1507.     mode = (mode & ~RW_MODES) | O_WRONLY;
  1508.     gotRW = 1;
  1509. } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) {
  1510.     mode = (mode & ~RW_MODES) | O_RDWR;
  1511.     gotRW = 1;
  1512. } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) {
  1513.     mode |= O_APPEND;
  1514.             *seekFlagPtr = 1;
  1515. } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) {
  1516.     mode |= O_CREAT;
  1517. } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) {
  1518.     mode |= O_EXCL;
  1519. } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) {
  1520. #ifdef O_NOCTTY
  1521.     mode |= O_NOCTTY;
  1522. #else
  1523.     if (interp != (Tcl_Interp *) NULL) {
  1524.                 Tcl_AppendResult(interp, "access mode "", flag,
  1525.                         "" not supported by this system", (char *) NULL);
  1526.             }
  1527.             ckfree((char *) modeArgv);
  1528.     return -1;
  1529. #endif
  1530. } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) {
  1531. #if defined(O_NDELAY) || defined(O_NONBLOCK)
  1532. #   ifdef O_NONBLOCK
  1533.     mode |= O_NONBLOCK;
  1534. #   else
  1535.     mode |= O_NDELAY;
  1536. #   endif
  1537. #else
  1538.             if (interp != (Tcl_Interp *) NULL) {
  1539.                 Tcl_AppendResult(interp, "access mode "", flag,
  1540.                         "" not supported by this system", (char *) NULL);
  1541.             }
  1542.             ckfree((char *) modeArgv);
  1543.     return -1;
  1544. #endif
  1545. } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) {
  1546.     mode |= O_TRUNC;
  1547. } else {
  1548.             if (interp != (Tcl_Interp *) NULL) {
  1549.                 Tcl_AppendResult(interp, "invalid access mode "", flag,
  1550.                         "": must be RDONLY, WRONLY, RDWR, APPEND, CREAT",
  1551.                         " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL);
  1552.             }
  1553.     ckfree((char *) modeArgv);
  1554.     return -1;
  1555. }
  1556.     }
  1557.     ckfree((char *) modeArgv);
  1558.     if (!gotRW) {
  1559.         if (interp != (Tcl_Interp *) NULL) {
  1560.             Tcl_AppendResult(interp, "access mode must include either",
  1561.                     " RDONLY, WRONLY, or RDWR", (char *) NULL);
  1562.         }
  1563. return -1;
  1564.     }
  1565.     return mode;
  1566. }
  1567. /*
  1568.  *----------------------------------------------------------------------
  1569.  *
  1570.  * Tcl_FSEvalFile --
  1571.  *
  1572.  * Read in a file and process the entire file as one gigantic
  1573.  * Tcl command.
  1574.  *
  1575.  * Results:
  1576.  * A standard Tcl result, which is either the result of executing
  1577.  * the file or an error indicating why the file couldn't be read.
  1578.  *
  1579.  * Side effects:
  1580.  * Depends on the commands in the file.  During the evaluation
  1581.  * of the contents of the file, iPtr->scriptFile is made to
  1582.  * point to pathPtr (the old value is cached and replaced when
  1583.  * this function returns).
  1584.  *
  1585.  *----------------------------------------------------------------------
  1586.  */
  1587. int
  1588. Tcl_FSEvalFile(interp, pathPtr)
  1589.     Tcl_Interp *interp; /* Interpreter in which to process file. */
  1590.     Tcl_Obj *pathPtr; /* Path of file to process.  Tilde-substitution
  1591.  * will be performed on this name. */
  1592. {
  1593.     int result, length;
  1594.     Tcl_StatBuf statBuf;
  1595.     Tcl_Obj *oldScriptFile;
  1596.     Interp *iPtr;
  1597.     char *string;
  1598.     Tcl_Channel chan;
  1599.     Tcl_Obj *objPtr;
  1600.     if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) {
  1601. return TCL_ERROR;
  1602.     }
  1603.     result = TCL_ERROR;
  1604.     objPtr = Tcl_NewObj();
  1605.     Tcl_IncrRefCount(objPtr);
  1606.     if (Tcl_FSStat(pathPtr, &statBuf) == -1) {
  1607.         Tcl_SetErrno(errno);
  1608. Tcl_AppendResult(interp, "couldn't read file "", 
  1609. Tcl_GetString(pathPtr),
  1610. "": ", Tcl_PosixError(interp), (char *) NULL);
  1611. goto end;
  1612.     }
  1613.     chan = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0644);
  1614.     if (chan == (Tcl_Channel) NULL) {
  1615.         Tcl_ResetResult(interp);
  1616. Tcl_AppendResult(interp, "couldn't read file "", 
  1617. Tcl_GetString(pathPtr),
  1618. "": ", Tcl_PosixError(interp), (char *) NULL);
  1619. goto end;
  1620.     }
  1621.     /*
  1622.      * The eofchar is 32 (^Z).  This is the usual on Windows, but we
  1623.      * effect this cross-platform to allow for scripted documents.
  1624.      * [Bug: 2040]
  1625.      */
  1626.     Tcl_SetChannelOption(interp, chan, "-eofchar", "32");
  1627.     if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
  1628.         Tcl_Close(interp, chan);
  1629. Tcl_AppendResult(interp, "couldn't read file "", 
  1630. Tcl_GetString(pathPtr),
  1631. "": ", Tcl_PosixError(interp), (char *) NULL);
  1632. goto end;
  1633.     }
  1634.     if (Tcl_Close(interp, chan) != TCL_OK) {
  1635.         goto end;
  1636.     }
  1637.     iPtr = (Interp *) interp;
  1638.     oldScriptFile = iPtr->scriptFile;
  1639.     iPtr->scriptFile = pathPtr;
  1640.     Tcl_IncrRefCount(iPtr->scriptFile);
  1641.     string = Tcl_GetStringFromObj(objPtr, &length);
  1642. #ifdef TCL_TIP280
  1643.     /* TIP #280 Force the evaluator to open a frame for a sourced
  1644.      * file. */
  1645.     iPtr->evalFlags |= TCL_EVAL_FILE;
  1646. #endif
  1647.     result = Tcl_EvalEx(interp, string, length, 0);
  1648.     /* 
  1649.      * Now we have to be careful; the script may have changed the
  1650.      * iPtr->scriptFile value, so we must reset it without
  1651.      * assuming it still points to 'pathPtr'.
  1652.      */
  1653.     if (iPtr->scriptFile != NULL) {
  1654. Tcl_DecrRefCount(iPtr->scriptFile);
  1655.     }
  1656.     iPtr->scriptFile = oldScriptFile;
  1657.     if (result == TCL_RETURN) {
  1658. result = TclUpdateReturnInfo(iPtr);
  1659.     } else if (result == TCL_ERROR) {
  1660. char msg[200 + TCL_INTEGER_SPACE];
  1661. /*
  1662.  * Record information telling where the error occurred.
  1663.  */
  1664. sprintf(msg, "n    (file "%.150s" line %d)", Tcl_GetString(pathPtr),
  1665. interp->errorLine);
  1666. Tcl_AddErrorInfo(interp, msg);
  1667.     }
  1668.     end:
  1669.     Tcl_DecrRefCount(objPtr);
  1670.     return result;
  1671. }
  1672. /*
  1673.  *----------------------------------------------------------------------
  1674.  *
  1675.  * Tcl_GetErrno --
  1676.  *
  1677.  * Gets the current value of the Tcl error code variable. This is
  1678.  * currently the global variable "errno" but could in the future
  1679.  * change to something else.
  1680.  *
  1681.  * Results:
  1682.  * The value of the Tcl error code variable.
  1683.  *
  1684.  * Side effects:
  1685.  * None. Note that the value of the Tcl error code variable is
  1686.  * UNDEFINED if a call to Tcl_SetErrno did not precede this call.
  1687.  *
  1688.  *----------------------------------------------------------------------
  1689.  */
  1690. int
  1691. Tcl_GetErrno()
  1692. {
  1693.     return errno;
  1694. }
  1695. /*
  1696.  *----------------------------------------------------------------------
  1697.  *
  1698.  * Tcl_SetErrno --
  1699.  *
  1700.  * Sets the Tcl error code variable to the supplied value.
  1701.  *
  1702.  * Results:
  1703.  * None.
  1704.  *
  1705.  * Side effects:
  1706.  * Modifies the value of the Tcl error code variable.
  1707.  *
  1708.  *----------------------------------------------------------------------
  1709.  */
  1710. void
  1711. Tcl_SetErrno(err)
  1712.     int err; /* The new value. */
  1713. {
  1714.     errno = err;
  1715. }
  1716. /*
  1717.  *----------------------------------------------------------------------
  1718.  *
  1719.  * Tcl_PosixError --
  1720.  *
  1721.  * This procedure is typically called after UNIX kernel calls
  1722.  * return errors.  It stores machine-readable information about
  1723.  * the error in $errorCode returns an information string for
  1724.  * the caller's use.
  1725.  *
  1726.  * Results:
  1727.  * The return value is a human-readable string describing the
  1728.  * error.
  1729.  *
  1730.  * Side effects:
  1731.  * The global variable $errorCode is reset.
  1732.  *
  1733.  *----------------------------------------------------------------------
  1734.  */
  1735. CONST char *
  1736. Tcl_PosixError(interp)
  1737.     Tcl_Interp *interp; /* Interpreter whose $errorCode variable
  1738.  * is to be changed. */
  1739. {
  1740.     CONST char *id, *msg;
  1741.     msg = Tcl_ErrnoMsg(errno);
  1742.     id = Tcl_ErrnoId();
  1743.     if (interp) {
  1744. Tcl_SetErrorCode(interp, "POSIX", id, msg, (char *) NULL);
  1745.     }
  1746.     return msg;
  1747. }
  1748. /*
  1749.  *----------------------------------------------------------------------
  1750.  *
  1751.  * Tcl_FSStat --
  1752.  *
  1753.  * This procedure replaces the library version of stat and lsat.
  1754.  *
  1755.  * The appropriate function for the filesystem to which pathPtr
  1756.  * belongs will be called.
  1757.  *
  1758.  * Results:
  1759.  *      See stat documentation.
  1760.  *
  1761.  * Side effects:
  1762.  *      See stat documentation.
  1763.  *
  1764.  *----------------------------------------------------------------------
  1765.  */
  1766. int
  1767. Tcl_FSStat(pathPtr, buf)
  1768.     Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
  1769.     Tcl_StatBuf *buf; /* Filled with results of stat call. */
  1770. {
  1771.     Tcl_Filesystem *fsPtr;
  1772. #ifdef USE_OBSOLETE_FS_HOOKS
  1773.     struct stat oldStyleStatBuffer;
  1774.     int retVal = -1;
  1775.     /*
  1776.      * Call each of the "stat" function in succession.  A non-return
  1777.      * value of -1 indicates the particular function has succeeded.
  1778.      */
  1779.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1780.     
  1781.     if (statProcList != NULL) {
  1782. StatProc *statProcPtr;
  1783. char *path;
  1784. Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
  1785. if (transPtr == NULL) {
  1786.     path = NULL;
  1787. } else {
  1788.     path = Tcl_GetString(transPtr);
  1789. }
  1790. statProcPtr = statProcList;
  1791. while ((retVal == -1) && (statProcPtr != NULL)) {
  1792.     retVal = (*statProcPtr->proc)(path, &oldStyleStatBuffer);
  1793.     statProcPtr = statProcPtr->nextPtr;
  1794. }
  1795. if (transPtr != NULL) {
  1796.     Tcl_DecrRefCount(transPtr);
  1797. }
  1798.     }
  1799.     
  1800.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1801.     if (retVal != -1) {
  1802. /*
  1803.  * Note that EOVERFLOW is not a problem here, and these
  1804.  * assignments should all be widening (if not identity.)
  1805.  */
  1806. buf->st_mode = oldStyleStatBuffer.st_mode;
  1807. buf->st_ino = oldStyleStatBuffer.st_ino;
  1808. buf->st_dev = oldStyleStatBuffer.st_dev;
  1809. buf->st_rdev = oldStyleStatBuffer.st_rdev;
  1810. buf->st_nlink = oldStyleStatBuffer.st_nlink;
  1811. buf->st_uid = oldStyleStatBuffer.st_uid;
  1812. buf->st_gid = oldStyleStatBuffer.st_gid;
  1813. buf->st_size = Tcl_LongAsWide(oldStyleStatBuffer.st_size);
  1814. buf->st_atime = oldStyleStatBuffer.st_atime;
  1815. buf->st_mtime = oldStyleStatBuffer.st_mtime;
  1816. buf->st_ctime = oldStyleStatBuffer.st_ctime;
  1817. #ifdef HAVE_ST_BLOCKS
  1818. buf->st_blksize = oldStyleStatBuffer.st_blksize;
  1819. buf->st_blocks = Tcl_LongAsWide(oldStyleStatBuffer.st_blocks);
  1820. #endif
  1821.         return retVal;
  1822.     }
  1823. #endif /* USE_OBSOLETE_FS_HOOKS */
  1824.     fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  1825.     if (fsPtr != NULL) {
  1826. Tcl_FSStatProc *proc = fsPtr->statProc;
  1827. if (proc != NULL) {
  1828.     return (*proc)(pathPtr, buf);
  1829. }
  1830.     }
  1831.     Tcl_SetErrno(ENOENT);
  1832.     return -1;
  1833. }
  1834. /*
  1835.  *----------------------------------------------------------------------
  1836.  *
  1837.  * Tcl_FSLstat --
  1838.  *
  1839.  * This procedure replaces the library version of lstat.
  1840.  * The appropriate function for the filesystem to which pathPtr
  1841.  * belongs will be called.  If no 'lstat' function is listed,
  1842.  * but a 'stat' function is, then Tcl will fall back on the
  1843.  * stat function.
  1844.  *
  1845.  * Results:
  1846.  *      See lstat documentation.
  1847.  *
  1848.  * Side effects:
  1849.  *      See lstat documentation.
  1850.  *
  1851.  *----------------------------------------------------------------------
  1852.  */
  1853. int
  1854. Tcl_FSLstat(pathPtr, buf)
  1855.     Tcl_Obj *pathPtr; /* Path of file to stat (in current CP). */
  1856.     Tcl_StatBuf *buf; /* Filled with results of stat call. */
  1857. {
  1858.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  1859.     if (fsPtr != NULL) {
  1860. Tcl_FSLstatProc *proc = fsPtr->lstatProc;
  1861. if (proc != NULL) {
  1862.     return (*proc)(pathPtr, buf);
  1863. } else {
  1864.     Tcl_FSStatProc *sproc = fsPtr->statProc;
  1865.     if (sproc != NULL) {
  1866. return (*sproc)(pathPtr, buf);
  1867.     }
  1868. }
  1869.     }
  1870.     Tcl_SetErrno(ENOENT);
  1871.     return -1;
  1872. }
  1873. /*
  1874.  *----------------------------------------------------------------------
  1875.  *
  1876.  * Tcl_FSAccess --
  1877.  *
  1878.  * This procedure replaces the library version of access.
  1879.  * The appropriate function for the filesystem to which pathPtr
  1880.  * belongs will be called.
  1881.  *
  1882.  * Results:
  1883.  *      See access documentation.
  1884.  *
  1885.  * Side effects:
  1886.  *      See access documentation.
  1887.  *
  1888.  *----------------------------------------------------------------------
  1889.  */
  1890. int
  1891. Tcl_FSAccess(pathPtr, mode)
  1892.     Tcl_Obj *pathPtr; /* Path of file to access (in current CP). */
  1893.     int mode;                   /* Permission setting. */
  1894. {
  1895.     Tcl_Filesystem *fsPtr;
  1896. #ifdef USE_OBSOLETE_FS_HOOKS
  1897.     int retVal = -1;
  1898.     /*
  1899.      * Call each of the "access" function in succession.  A non-return
  1900.      * value of -1 indicates the particular function has succeeded.
  1901.      */
  1902.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1903.     if (accessProcList != NULL) {
  1904. AccessProc *accessProcPtr;
  1905. char *path;
  1906. Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
  1907. if (transPtr == NULL) {
  1908.     path = NULL;
  1909. } else {
  1910.     path = Tcl_GetString(transPtr);
  1911. }
  1912. accessProcPtr = accessProcList;
  1913. while ((retVal == -1) && (accessProcPtr != NULL)) {
  1914.     retVal = (*accessProcPtr->proc)(path, mode);
  1915.     accessProcPtr = accessProcPtr->nextPtr;
  1916. }
  1917. if (transPtr != NULL) {
  1918.     Tcl_DecrRefCount(transPtr);
  1919. }
  1920.     }
  1921.     
  1922.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1923.     if (retVal != -1) {
  1924. return retVal;
  1925.     }
  1926. #endif /* USE_OBSOLETE_FS_HOOKS */
  1927.     fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  1928.     if (fsPtr != NULL) {
  1929. Tcl_FSAccessProc *proc = fsPtr->accessProc;
  1930. if (proc != NULL) {
  1931.     return (*proc)(pathPtr, mode);
  1932. }
  1933.     }
  1934.     Tcl_SetErrno(ENOENT);
  1935.     return -1;
  1936. }
  1937. /*
  1938.  *----------------------------------------------------------------------
  1939.  *
  1940.  * Tcl_FSOpenFileChannel --
  1941.  *
  1942.  * The appropriate function for the filesystem to which pathPtr
  1943.  * belongs will be called.
  1944.  *
  1945.  * Results:
  1946.  * The new channel or NULL, if the named file could not be opened.
  1947.  *
  1948.  * Side effects:
  1949.  * May open the channel and may cause creation of a file on the
  1950.  * file system.
  1951.  *
  1952.  *----------------------------------------------------------------------
  1953.  */
  1954.  
  1955. Tcl_Channel
  1956. Tcl_FSOpenFileChannel(interp, pathPtr, modeString, permissions)
  1957.     Tcl_Interp *interp;                 /* Interpreter for error reporting;
  1958.                                          * can be NULL. */
  1959.     Tcl_Obj *pathPtr;                   /* Name of file to open. */
  1960.     CONST char *modeString;             /* A list of POSIX open modes or
  1961.                                          * a string such as "rw". */
  1962.     int permissions;                    /* If the open involves creating a
  1963.                                          * file, with what modes to create
  1964.                                          * it? */
  1965. {
  1966.     Tcl_Filesystem *fsPtr;
  1967. #ifdef USE_OBSOLETE_FS_HOOKS
  1968.     Tcl_Channel retVal = NULL;
  1969.     /*
  1970.      * Call each of the "Tcl_OpenFileChannel" functions in succession.
  1971.      * A non-NULL return value indicates the particular function has
  1972.      * succeeded.
  1973.      */
  1974.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1975.     if (openFileChannelProcList != NULL) {
  1976. OpenFileChannelProc *openFileChannelProcPtr;
  1977. char *path;
  1978. Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
  1979. if (transPtr == NULL) {
  1980.     path = NULL;
  1981. } else {
  1982.     path = Tcl_GetString(transPtr);
  1983. }
  1984. openFileChannelProcPtr = openFileChannelProcList;
  1985. while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) {
  1986.     retVal = (*openFileChannelProcPtr->proc)(interp, path,
  1987.      modeString, permissions);
  1988.     openFileChannelProcPtr = openFileChannelProcPtr->nextPtr;
  1989. }
  1990. if (transPtr != NULL) {
  1991.     Tcl_DecrRefCount(transPtr);
  1992. }
  1993.     }
  1994.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1995.     if (retVal != NULL) {
  1996. return retVal;
  1997.     }
  1998. #endif /* USE_OBSOLETE_FS_HOOKS */
  1999.     
  2000.     /* 
  2001.      * We need this just to ensure we return the correct error messages
  2002.      * under some circumstances.
  2003.      */
  2004.     if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) {
  2005.         return NULL;
  2006.     }
  2007.     
  2008.     fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2009.     if (fsPtr != NULL) {
  2010. Tcl_FSOpenFileChannelProc *proc = fsPtr->openFileChannelProc;
  2011. if (proc != NULL) {
  2012.     int mode, seekFlag;
  2013.     mode = TclGetOpenMode(interp, modeString, &seekFlag);
  2014.     if (mode == -1) {
  2015.         return NULL;
  2016.     }
  2017.     retVal = (*proc)(interp, pathPtr, mode, permissions);
  2018.     if (retVal != NULL) {
  2019. if (seekFlag) {
  2020.     if (Tcl_Seek(retVal, (Tcl_WideInt)0, 
  2021.  SEEK_END) < (Tcl_WideInt)0) {
  2022. if (interp != (Tcl_Interp *) NULL) {
  2023.     Tcl_AppendResult(interp,
  2024.       "could not seek to end of file while opening "",
  2025.       Tcl_GetString(pathPtr), "": ", 
  2026.       Tcl_PosixError(interp), (char *) NULL);
  2027. }
  2028. Tcl_Close(NULL, retVal);
  2029. return NULL;
  2030.     }
  2031. }
  2032.     }
  2033.     return retVal;
  2034. }
  2035.     }
  2036.     /* File doesn't belong to any filesystem that can open it */
  2037.     Tcl_SetErrno(ENOENT);
  2038.     if (interp != NULL) {
  2039. Tcl_AppendResult(interp, "couldn't open "", 
  2040.  Tcl_GetString(pathPtr), "": ",
  2041.  Tcl_PosixError(interp), (char *) NULL);
  2042.     }
  2043.     return NULL;
  2044. }
  2045. /*
  2046.  *----------------------------------------------------------------------
  2047.  *
  2048.  * Tcl_FSUtime --
  2049.  *
  2050.  * This procedure replaces the library version of utime.
  2051.  * The appropriate function for the filesystem to which pathPtr
  2052.  * belongs will be called.
  2053.  *
  2054.  * Results:
  2055.  *      See utime documentation.
  2056.  *
  2057.  * Side effects:
  2058.  *      See utime documentation.
  2059.  *
  2060.  *----------------------------------------------------------------------
  2061.  */
  2062. int 
  2063. Tcl_FSUtime (pathPtr, tval)
  2064.     Tcl_Obj *pathPtr;       /* File to change access/modification times */
  2065.     struct utimbuf *tval;   /* Structure containing access/modification 
  2066.                              * times to use.  Should not be modified. */
  2067. {
  2068.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2069.     if (fsPtr != NULL) {
  2070. Tcl_FSUtimeProc *proc = fsPtr->utimeProc;
  2071. if (proc != NULL) {
  2072.     return (*proc)(pathPtr, tval);
  2073. }
  2074.     }
  2075.     return -1;
  2076. }
  2077. /*
  2078.  *----------------------------------------------------------------------
  2079.  *
  2080.  * NativeFileAttrStrings --
  2081.  *
  2082.  * This procedure implements the platform dependent 'file
  2083.  * attributes' subcommand, for the native filesystem, for listing
  2084.  * the set of possible attribute strings.  This function is part
  2085.  * of Tcl's native filesystem support, and is placed here because
  2086.  * it is shared by Unix, MacOS and Windows code.
  2087.  *
  2088.  * Results:
  2089.  *      An array of strings
  2090.  *
  2091.  * Side effects:
  2092.  *      None.
  2093.  *
  2094.  *----------------------------------------------------------------------
  2095.  */
  2096. static CONST char**
  2097. NativeFileAttrStrings(pathPtr, objPtrRef)
  2098.     Tcl_Obj *pathPtr;
  2099.     Tcl_Obj** objPtrRef;
  2100. {
  2101.     return tclpFileAttrStrings;
  2102. }
  2103. /*
  2104.  *----------------------------------------------------------------------
  2105.  *
  2106.  * NativeFileAttrsGet --
  2107.  *
  2108.  * This procedure implements the platform dependent
  2109.  * 'file attributes' subcommand, for the native
  2110.  * filesystem, for 'get' operations.  This function is part
  2111.  * of Tcl's native filesystem support, and is placed here
  2112.  * because it is shared by Unix, MacOS and Windows code.
  2113.  *
  2114.  * Results:
  2115.  *      Standard Tcl return code.  The object placed in objPtrRef
  2116.  *      (if TCL_OK was returned) is likely to have a refCount of zero.
  2117.  *      Either way we must either store it somewhere (e.g. the Tcl 
  2118.  *      result), or Incr/Decr its refCount to ensure it is properly
  2119.  *      freed.
  2120.  *
  2121.  * Side effects:
  2122.  *      None.
  2123.  *
  2124.  *----------------------------------------------------------------------
  2125.  */
  2126. static int
  2127. NativeFileAttrsGet(interp, index, pathPtr, objPtrRef)
  2128.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2129.     int index; /* index of the attribute command. */
  2130.     Tcl_Obj *pathPtr; /* path of file we are operating on. */
  2131.     Tcl_Obj **objPtrRef; /* for output. */
  2132. {
  2133.     return (*tclpFileAttrProcs[index].getProc)(interp, index, 
  2134.        pathPtr, objPtrRef);
  2135. }
  2136. /*
  2137.  *----------------------------------------------------------------------
  2138.  *
  2139.  * NativeFileAttrsSet --
  2140.  *
  2141.  * This procedure implements the platform dependent
  2142.  * 'file attributes' subcommand, for the native
  2143.  * filesystem, for 'set' operations. This function is part
  2144.  * of Tcl's native filesystem support, and is placed here
  2145.  * because it is shared by Unix, MacOS and Windows code.
  2146.  *
  2147.  * Results:
  2148.  *      Standard Tcl return code.
  2149.  *
  2150.  * Side effects:
  2151.  *      None.
  2152.  *
  2153.  *----------------------------------------------------------------------
  2154.  */
  2155. static int
  2156. NativeFileAttrsSet(interp, index, pathPtr, objPtr)
  2157.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2158.     int index; /* index of the attribute command. */
  2159.     Tcl_Obj *pathPtr; /* path of file we are operating on. */
  2160.     Tcl_Obj *objPtr; /* set to this value. */
  2161. {
  2162.     return (*tclpFileAttrProcs[index].setProc)(interp, index,
  2163.        pathPtr, objPtr);
  2164. }
  2165. /*
  2166.  *----------------------------------------------------------------------
  2167.  *
  2168.  * Tcl_FSFileAttrStrings --
  2169.  *
  2170.  * This procedure implements part of the hookable 'file
  2171.  * attributes' subcommand.  The appropriate function for the
  2172.  * filesystem to which pathPtr belongs will be called.
  2173.  *
  2174.  * Results:
  2175.  *      The called procedure may either return an array of strings,
  2176.  *      or may instead return NULL and place a Tcl list into the 
  2177.  *      given objPtrRef.  Tcl will take that list and first increment
  2178.  *      its refCount before using it.  On completion of that use, Tcl
  2179.  *      will decrement its refCount.  Hence if the list should be
  2180.  *      disposed of by Tcl when done, it should have a refCount of zero,
  2181.  *      and if the list should not be disposed of, the filesystem
  2182.  *      should ensure it retains a refCount on the object.
  2183.  *
  2184.  * Side effects:
  2185.  *      None.
  2186.  *
  2187.  *----------------------------------------------------------------------
  2188.  */
  2189. CONST char **
  2190. Tcl_FSFileAttrStrings(pathPtr, objPtrRef)
  2191.     Tcl_Obj* pathPtr;
  2192.     Tcl_Obj** objPtrRef;
  2193. {
  2194.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2195.     if (fsPtr != NULL) {
  2196. Tcl_FSFileAttrStringsProc *proc = fsPtr->fileAttrStringsProc;
  2197. if (proc != NULL) {
  2198.     return (*proc)(pathPtr, objPtrRef);
  2199. }
  2200.     }
  2201.     Tcl_SetErrno(ENOENT);
  2202.     return NULL;
  2203. }
  2204. /*
  2205.  *----------------------------------------------------------------------
  2206.  *
  2207.  * Tcl_FSFileAttrsGet --
  2208.  *
  2209.  * This procedure implements read access for the hookable 'file
  2210.  * attributes' subcommand.  The appropriate function for the
  2211.  * filesystem to which pathPtr belongs will be called.
  2212.  *
  2213.  * Results:
  2214.  *      Standard Tcl return code.  The object placed in objPtrRef
  2215.  *      (if TCL_OK was returned) is likely to have a refCount of zero.
  2216.  *      Either way we must either store it somewhere (e.g. the Tcl 
  2217.  *      result), or Incr/Decr its refCount to ensure it is properly
  2218.  *      freed.
  2219.  *
  2220.  * Side effects:
  2221.  *      None.
  2222.  *
  2223.  *----------------------------------------------------------------------
  2224.  */
  2225. int
  2226. Tcl_FSFileAttrsGet(interp, index, pathPtr, objPtrRef)
  2227.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2228.     int index; /* index of the attribute command. */
  2229.     Tcl_Obj *pathPtr; /* filename we are operating on. */
  2230.     Tcl_Obj **objPtrRef; /* for output. */
  2231. {
  2232.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2233.     if (fsPtr != NULL) {
  2234. Tcl_FSFileAttrsGetProc *proc = fsPtr->fileAttrsGetProc;
  2235. if (proc != NULL) {
  2236.     return (*proc)(interp, index, pathPtr, objPtrRef);
  2237. }
  2238.     }
  2239.     Tcl_SetErrno(ENOENT);
  2240.     return -1;
  2241. }
  2242. /*
  2243.  *----------------------------------------------------------------------
  2244.  *
  2245.  * Tcl_FSFileAttrsSet --
  2246.  *
  2247.  * This procedure implements write access for the hookable 'file
  2248.  * attributes' subcommand.  The appropriate function for the
  2249.  * filesystem to which pathPtr belongs will be called.
  2250.  *
  2251.  * Results:
  2252.  *      Standard Tcl return code.
  2253.  *
  2254.  * Side effects:
  2255.  *      None.
  2256.  *
  2257.  *----------------------------------------------------------------------
  2258.  */
  2259. int
  2260. Tcl_FSFileAttrsSet(interp, index, pathPtr, objPtr)
  2261.     Tcl_Interp *interp; /* The interpreter for error reporting. */
  2262.     int index; /* index of the attribute command. */
  2263.     Tcl_Obj *pathPtr; /* filename we are operating on. */
  2264.     Tcl_Obj *objPtr; /* Input value. */
  2265. {
  2266.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2267.     if (fsPtr != NULL) {
  2268. Tcl_FSFileAttrsSetProc *proc = fsPtr->fileAttrsSetProc;
  2269. if (proc != NULL) {
  2270.     return (*proc)(interp, index, pathPtr, objPtr);
  2271. }
  2272.     }
  2273.     Tcl_SetErrno(ENOENT);
  2274.     return -1;
  2275. }
  2276. /*
  2277.  *----------------------------------------------------------------------
  2278.  *
  2279.  * Tcl_FSGetCwd --
  2280.  *
  2281.  * This function replaces the library version of getcwd().
  2282.  *
  2283.  * Most VFS's will *not* implement a 'cwdProc'.  Tcl now maintains
  2284.  * its own record (in a Tcl_Obj) of the cwd, and an attempt
  2285.  * is made to synchronise this with the cwd's containing filesystem,
  2286.  * if that filesystem provides a cwdProc (e.g. the native filesystem).
  2287.  *
  2288.  * Note that if Tcl's cwd is not in the native filesystem, then of
  2289.  * course Tcl's cwd and the native cwd are different: extensions
  2290.  * should therefore ensure they only access the cwd through this
  2291.  * function to avoid confusion.
  2292.  *
  2293.  * If a global cwdPathPtr already exists, it is cached in the thread's
  2294.  * private data structures and reference to the cached copy is returned,
  2295.  * subject to a synchronisation attempt in that cwdPathPtr's fs.
  2296.  *
  2297.  * Otherwise, the chain of functions that have been "inserted"
  2298.  * into the filesystem will be called in succession until either a
  2299.  * value other than NULL is returned, or the entire list is
  2300.  * visited.
  2301.  *
  2302.  * Results:
  2303.  * The result is a pointer to a Tcl_Obj specifying the current
  2304.  * directory, or NULL if the current directory could not be
  2305.  * determined.  If NULL is returned, an error message is left in the
  2306.  * interp's result.  
  2307.  *
  2308.  * The result already has its refCount incremented for the caller.
  2309.  * When it is no longer needed, that refCount should be decremented.
  2310.  *
  2311.  * Side effects:
  2312.  * Various objects may be freed and allocated.
  2313.  *
  2314.  *----------------------------------------------------------------------
  2315.  */
  2316. Tcl_Obj*
  2317. Tcl_FSGetCwd(interp)
  2318.     Tcl_Interp *interp;
  2319. {
  2320.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2321.     
  2322.     if (TclFSCwdPointerEquals(NULL)) {
  2323. FilesystemRecord *fsRecPtr;
  2324. Tcl_Obj *retVal = NULL;
  2325. /* 
  2326.  * We've never been called before, try to find a cwd.  Call
  2327.  * each of the "Tcl_GetCwd" function in succession.  A non-NULL
  2328.  * return value indicates the particular function has
  2329.  * succeeded.
  2330.  */
  2331. fsRecPtr = FsGetFirstFilesystem();
  2332. while ((retVal == NULL) && (fsRecPtr != NULL)) {
  2333.     Tcl_FSGetCwdProc *proc = fsRecPtr->fsPtr->getCwdProc;
  2334.     if (proc != NULL) {
  2335. retVal = (*proc)(interp);
  2336.     }
  2337.     fsRecPtr = fsRecPtr->nextPtr;
  2338. }
  2339. /* 
  2340.  * Now the 'cwd' may NOT be normalized, at least on some
  2341.  * platforms.  For the sake of efficiency, we want a completely
  2342.  * normalized cwd at all times.
  2343.  * 
  2344.  * Finally, if retVal is NULL, we do not have a cwd, which
  2345.  * could be problematic.
  2346.  */
  2347. if (retVal != NULL) {
  2348.     Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal, NULL);
  2349.     if (norm != NULL) {
  2350. /* 
  2351.  * We found a cwd, which is now in our global storage.
  2352.  * We must make a copy. Norm already has a refCount of 1.
  2353.  * 
  2354.  * Threading issue: note that multiple threads at system
  2355.  * startup could in principle call this procedure 
  2356.  * simultaneously.  They will therefore each set the
  2357.  * cwdPathPtr independently.  That behaviour is a bit
  2358.  * peculiar, but should be fine.  Once we have a cwd,
  2359.  * we'll always be in the 'else' branch below which
  2360.  * is simpler.
  2361.  */
  2362. FsUpdateCwd(norm);
  2363. Tcl_DecrRefCount(norm);
  2364.     }
  2365.     Tcl_DecrRefCount(retVal);
  2366. }
  2367.     } else {
  2368. /* 
  2369.  * We already have a cwd cached, but we want to give the
  2370.  * filesystem it is in a chance to check whether that cwd
  2371.  * has changed, or is perhaps no longer accessible.  This
  2372.  * allows an error to be thrown if, say, the permissions on
  2373.  * that directory have changed.
  2374.  */
  2375. Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr);
  2376. /* 
  2377.  * If the filesystem couldn't be found, or if no cwd function
  2378.  * exists for this filesystem, then we simply assume the cached
  2379.  * cwd is ok.  If we do call a cwd, we must watch for errors
  2380.  * (if the cwd returns NULL).  This ensures that, say, on Unix
  2381.  * if the permissions of the cwd change, 'pwd' does actually
  2382.  * throw the correct error in Tcl.  (This is tested for in the
  2383.  * test suite on unix).
  2384.  */
  2385. if (fsPtr != NULL) {
  2386.     Tcl_FSGetCwdProc *proc = fsPtr->getCwdProc;
  2387.     if (proc != NULL) {
  2388. Tcl_Obj *retVal = (*proc)(interp);
  2389. if (retVal != NULL) {
  2390.     Tcl_Obj *norm = TclFSNormalizeAbsolutePath(interp, retVal, NULL);
  2391.     /* 
  2392.      * Check whether cwd has changed from the value
  2393.      * previously stored in cwdPathPtr.  Really 'norm'
  2394.      * shouldn't be null, but we are careful.
  2395.      */
  2396.     if (norm == NULL) {
  2397. /* Do nothing */
  2398.     } else if (Tcl_FSEqualPaths(tsdPtr->cwdPathPtr, norm)) {
  2399. /* 
  2400.  * If the paths were equal, we can be more
  2401.  * efficient and retain the old path object
  2402.  * which will probably already be shared.  In
  2403.  * this case we can simply free the normalized
  2404.  * path we just calculated.
  2405.  */
  2406. Tcl_DecrRefCount(norm);
  2407.     } else {
  2408. FsUpdateCwd(norm);
  2409. Tcl_DecrRefCount(norm);
  2410.     }
  2411.     Tcl_DecrRefCount(retVal);
  2412. } else {
  2413.     /* The 'cwd' function returned an error; reset the cwd */
  2414.     FsUpdateCwd(NULL);
  2415. }
  2416.     }
  2417. }
  2418.     }
  2419.     
  2420.     if (tsdPtr->cwdPathPtr != NULL) {
  2421. Tcl_IncrRefCount(tsdPtr->cwdPathPtr);
  2422.     }
  2423.     
  2424.     return tsdPtr->cwdPathPtr; 
  2425. }
  2426. /*
  2427.  *----------------------------------------------------------------------
  2428.  *
  2429.  * Tcl_FSChdir --
  2430.  *
  2431.  * This function replaces the library version of chdir().
  2432.  *
  2433.  * The path is normalized and then passed to the filesystem
  2434.  * which claims it.
  2435.  *
  2436.  * Results:
  2437.  * See chdir() documentation.  If successful, we keep a 
  2438.  * record of the successful path in cwdPathPtr for subsequent 
  2439.  * calls to getcwd.
  2440.  *
  2441.  * Side effects:
  2442.  * See chdir() documentation.  The global cwdPathPtr may 
  2443.  * change value.
  2444.  *
  2445.  *----------------------------------------------------------------------
  2446.  */
  2447. int
  2448. Tcl_FSChdir(pathPtr)
  2449.     Tcl_Obj *pathPtr;
  2450. {
  2451.     Tcl_Filesystem *fsPtr;
  2452.     int retVal = -1;
  2453.     
  2454. #ifdef WIN32
  2455.     /*
  2456.      * This complete hack addresses the bug tested in winFCmd-16.12,
  2457.      * where having your HOME as "C:" (IOW, a seemingly path relative
  2458.      * dir) would cause a crash when you cd'd to it and requested 'pwd'.
  2459.      * The work-around is to force such a dir into an absolute path by
  2460.      * tacking on '/'.
  2461.      *
  2462.      * We check for '~' specifically because that's what Tcl_CdObjCmd
  2463.      * passes in that triggers the bug.  A direct 'cd C:' call will not
  2464.      * because that gets the volumerelative pwd.
  2465.      *
  2466.      * This is not an issue for 8.5 as that has a more elaborate change
  2467.      * that requires the use of TCL_FILESYSTEM_VERSION_2.
  2468.      */
  2469.     Tcl_Obj *objPtr = NULL;
  2470.     if (pathPtr->bytes && pathPtr->length == 1 && pathPtr->bytes[0] == '~') {
  2471. int len;
  2472. char *str;
  2473. objPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr);
  2474. if (objPtr == NULL) {
  2475.     Tcl_SetErrno(ENOENT);
  2476.     return -1;
  2477. }
  2478. Tcl_IncrRefCount(objPtr);
  2479. str = Tcl_GetStringFromObj(objPtr, &len);
  2480. if (len == 2 && str[1] == ':') {
  2481.     pathPtr = Tcl_NewStringObj(str, len);
  2482.     Tcl_AppendToObj(pathPtr, "/", 1);
  2483.     Tcl_IncrRefCount(pathPtr);
  2484.     Tcl_DecrRefCount(objPtr);
  2485.     objPtr = pathPtr;
  2486. } else {
  2487.     Tcl_DecrRefCount(objPtr);
  2488.     objPtr = NULL;
  2489. }
  2490.     }
  2491. #endif
  2492.     if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) {
  2493. #ifdef WIN32
  2494. if (objPtr) { Tcl_DecrRefCount(objPtr); }
  2495. #endif
  2496. Tcl_SetErrno(ENOENT);
  2497.         return -1;
  2498.     }
  2499.     
  2500.     fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2501.     if (fsPtr != NULL) {
  2502. Tcl_FSChdirProc *proc = fsPtr->chdirProc;
  2503. if (proc != NULL) {
  2504.     retVal = (*proc)(pathPtr);
  2505. } else {
  2506.     /* Fallback on stat-based implementation */
  2507.     Tcl_StatBuf buf;
  2508.     /* If the file can be stat'ed and is a directory and
  2509.      * is readable, then we can chdir. */
  2510.     if ((Tcl_FSStat(pathPtr, &buf) == 0) 
  2511.       && (S_ISDIR(buf.st_mode))
  2512.       && (Tcl_FSAccess(pathPtr, R_OK) == 0)) {
  2513. /* We allow the chdir */
  2514. retVal = 0;
  2515.     }
  2516. }
  2517.     }
  2518.     if (retVal != -1) {
  2519. /* 
  2520.  * The cwd changed, or an error was thrown.  If an error was
  2521.  * thrown, we can just continue (and that will report the error
  2522.  * to the user).  If there was no error we must assume that the
  2523.  * cwd was actually changed to the normalized value we
  2524.  * calculated above, and we must therefore cache that
  2525.  * information.
  2526.  */
  2527. if (retVal == 0) {
  2528.     /* 
  2529.      * Note that this normalized path may be different to what
  2530.      * we found above (or at least a different object), if the
  2531.      * filesystem epoch changed recently.  This can actually
  2532.      * happen with scripted documents very easily.  Therefore
  2533.      * we ask for the normalized path again (the correct value
  2534.      * will have been cached as a result of the
  2535.      * Tcl_FSGetFileSystemForPath call above anyway).
  2536.      */
  2537.     Tcl_Obj *normDirName = Tcl_FSGetNormalizedPath(NULL, pathPtr);
  2538.     if (normDirName == NULL) {
  2539. #ifdef WIN32
  2540. if (objPtr) { Tcl_DecrRefCount(objPtr); }
  2541. #endif
  2542. Tcl_SetErrno(ENOENT);
  2543.         return -1;
  2544.     }
  2545.     FsUpdateCwd(normDirName);
  2546. }
  2547.     } else {
  2548. Tcl_SetErrno(ENOENT);
  2549.     }
  2550.     
  2551. #ifdef WIN32
  2552.     if (objPtr) { Tcl_DecrRefCount(objPtr); }
  2553. #endif
  2554.     return (retVal);
  2555. }
  2556. /*
  2557.  *----------------------------------------------------------------------
  2558.  *
  2559.  * Tcl_FSLoadFile --
  2560.  *
  2561.  * Dynamically loads a binary code file into memory and returns
  2562.  * the addresses of two procedures within that file, if they are
  2563.  * defined.  The appropriate function for the filesystem to which
  2564.  * pathPtr belongs will be called.
  2565.  *
  2566.  * Note that the native filesystem doesn't actually assume
  2567.  * 'pathPtr' is a path.  Rather it assumes filename is either
  2568.  * a path or just the name of a file which can be found somewhere
  2569.  * in the environment's loadable path.  This behaviour is not
  2570.  * very compatible with virtual filesystems (and has other problems
  2571.  * documented in the load man-page), so it is advised that full
  2572.  * paths are always used.
  2573.  *
  2574.  * Results:
  2575.  * A standard Tcl completion code.  If an error occurs, an error
  2576.  * message is left in the interp's result.
  2577.  *
  2578.  * Side effects:
  2579.  * New code suddenly appears in memory.  This may later be
  2580.  * unloaded by passing the clientData to the unloadProc.
  2581.  *
  2582.  *----------------------------------------------------------------------
  2583.  */
  2584. int
  2585. Tcl_FSLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr, 
  2586.        handlePtr, unloadProcPtr)
  2587.     Tcl_Interp *interp; /* Used for error reporting. */
  2588.     Tcl_Obj *pathPtr; /* Name of the file containing the desired
  2589.  * code. */
  2590.     CONST char *sym1, *sym2; /* Names of two procedures to look up in
  2591.  * the file's symbol table. */
  2592.     Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
  2593. /* Where to return the addresses corresponding
  2594.  * to sym1 and sym2. */
  2595.     Tcl_LoadHandle *handlePtr; /* Filled with token for dynamically loaded
  2596.  * file which will be passed back to 
  2597.  * (*unloadProcPtr)() to unload the file. */
  2598.     Tcl_FSUnloadFileProc **unloadProcPtr;
  2599.                                 /* Filled with address of Tcl_FSUnloadFileProc
  2600.                                  * function which should be used for
  2601.                                  * this file. */
  2602. {
  2603.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2604.     if (fsPtr != NULL) {
  2605. Tcl_FSLoadFileProc *proc = fsPtr->loadFileProc;
  2606. if (proc != NULL) {
  2607.     int retVal = (*proc)(interp, pathPtr, handlePtr, unloadProcPtr);
  2608.     if (retVal != TCL_OK) {
  2609. return retVal;
  2610.     }
  2611.     if (*handlePtr == NULL) {
  2612. return TCL_ERROR;
  2613.     }
  2614.     if (sym1 != NULL) {
  2615.         *proc1Ptr = TclpFindSymbol(interp, *handlePtr, sym1);
  2616.     }
  2617.     if (sym2 != NULL) {
  2618.         *proc2Ptr = TclpFindSymbol(interp, *handlePtr, sym2);
  2619.     }
  2620.     return retVal;
  2621. } else {
  2622.     Tcl_Filesystem *copyFsPtr;
  2623.     Tcl_Obj *copyToPtr;
  2624.     
  2625.     /* First check if it is readable -- and exists! */
  2626.     if (Tcl_FSAccess(pathPtr, R_OK) != 0) {
  2627. Tcl_AppendResult(interp, "couldn't load library "",
  2628.  Tcl_GetString(pathPtr), "": ", 
  2629.  Tcl_PosixError(interp), (char *) NULL);
  2630. return TCL_ERROR;
  2631.     }
  2632.     
  2633. #ifdef TCL_LOAD_FROM_MEMORY
  2634. /* 
  2635.  * The platform supports loading code from memory, so ask for a
  2636.  * buffer of the appropriate size, read the file into it and 
  2637.  * load the code from the buffer:
  2638.  */
  2639. do {
  2640.             int ret, size;
  2641.             void *buffer;
  2642.             Tcl_StatBuf statBuf;
  2643.             Tcl_Channel data;
  2644.             
  2645.             ret = Tcl_FSStat(pathPtr, &statBuf);
  2646.             if (ret < 0) {
  2647.                 break;
  2648.             }
  2649.             size = (int) statBuf.st_size;
  2650.             /* Tcl_Read takes an int: check that file size isn't wide */
  2651.             if (size != (Tcl_WideInt)statBuf.st_size) {
  2652.                 break;
  2653.             }
  2654.     data = Tcl_FSOpenFileChannel(interp, pathPtr, "r", 0666);
  2655.             if (!data) {
  2656.                 break;
  2657.             }
  2658.             buffer = TclpLoadMemoryGetBuffer(interp, size);
  2659.             if (!buffer) {
  2660.                 Tcl_Close(interp, data);
  2661.                 break;
  2662.             }
  2663.             Tcl_SetChannelOption(interp, data, "-translation", "binary");
  2664.             ret = Tcl_Read(data, buffer, size);
  2665.             Tcl_Close(interp, data);
  2666.             ret = TclpLoadMemory(interp, buffer, size, ret, handlePtr, unloadProcPtr);
  2667.             if (ret == TCL_OK) {
  2668. if (*handlePtr == NULL) {
  2669.     break;
  2670. }
  2671.                 if (sym1 != NULL) {
  2672.                     *proc1Ptr = TclpFindSymbol(interp, *handlePtr, sym1);
  2673.                 }
  2674.                 if (sym2 != NULL) {
  2675.                     *proc2Ptr = TclpFindSymbol(interp, *handlePtr, sym2);
  2676.                 }
  2677. return TCL_OK;
  2678.     }
  2679. } while (0); 
  2680. Tcl_ResetResult(interp);
  2681. #endif
  2682.     /* 
  2683.      * Get a temporary filename to use, first to
  2684.      * copy the file into, and then to load. 
  2685.      */
  2686.     copyToPtr = TclpTempFileName();
  2687.     if (copyToPtr == NULL) {
  2688.         return -1;
  2689.     }
  2690.     Tcl_IncrRefCount(copyToPtr);
  2691.     
  2692.     copyFsPtr = Tcl_FSGetFileSystemForPath(copyToPtr);
  2693.     if ((copyFsPtr == NULL) || (copyFsPtr == fsPtr)) {
  2694. /* 
  2695.  * We already know we can't use Tcl_FSLoadFile from 
  2696.  * this filesystem, and we must avoid a possible
  2697.  * infinite loop.  Try to delete the file we
  2698.  * probably created, and then exit.
  2699.  */
  2700. Tcl_FSDeleteFile(copyToPtr);
  2701. Tcl_DecrRefCount(copyToPtr);
  2702. return -1;
  2703.     }
  2704.     
  2705.     if (TclCrossFilesystemCopy(interp, pathPtr, 
  2706.        copyToPtr) == TCL_OK) {
  2707. Tcl_LoadHandle newLoadHandle = NULL;
  2708. Tcl_FSUnloadFileProc *newUnloadProcPtr = NULL;
  2709. FsDivertLoad *tvdlPtr;
  2710. int retVal;
  2711. #if !defined(__WIN32__) && !defined(MAC_TCL)
  2712. /* 
  2713.  * Do we need to set appropriate permissions 
  2714.  * on the file?  This may be required on some
  2715.  * systems.  On Unix we could loop over
  2716.  * the file attributes, and set any that are
  2717.  * called "-permissions" to 0700.  However,
  2718.  * we just do this directly, like this:
  2719.  */
  2720. Tcl_Obj* perm = Tcl_NewStringObj("0700",-1);
  2721. Tcl_IncrRefCount(perm);
  2722. Tcl_FSFileAttrsSet(NULL, 2, copyToPtr, perm);
  2723. Tcl_DecrRefCount(perm);
  2724. #endif
  2725. /* 
  2726.  * We need to reset the result now, because the cross-
  2727.  * filesystem copy may have stored the number of bytes
  2728.  * in the result
  2729.  */
  2730. Tcl_ResetResult(interp);
  2731. retVal = Tcl_FSLoadFile(interp, copyToPtr, sym1, sym2,
  2732. proc1Ptr, proc2Ptr, 
  2733. &newLoadHandle,
  2734. &newUnloadProcPtr);
  2735.         if (retVal != TCL_OK) {
  2736.     /* The file didn't load successfully */
  2737.     Tcl_FSDeleteFile(copyToPtr);
  2738.     Tcl_DecrRefCount(copyToPtr);
  2739.     return retVal;
  2740. }
  2741. /* 
  2742.  * Try to delete the file immediately -- this is
  2743.  * possible in some OSes, and avoids any worries
  2744.  * about leaving the copy laying around on exit. 
  2745.  */
  2746. if (Tcl_FSDeleteFile(copyToPtr) == TCL_OK) {
  2747.     Tcl_DecrRefCount(copyToPtr);
  2748.     /* 
  2749.      * We tell our caller about the real shared
  2750.      * library which was loaded.  Note that this
  2751.      * does mean that the package list maintained
  2752.      * by 'load' will store the original (vfs)
  2753.      * path alongside the temporary load handle
  2754.      * and unload proc ptr.
  2755.      */
  2756.     (*handlePtr) = newLoadHandle;
  2757.     (*unloadProcPtr) = newUnloadProcPtr;
  2758.     return TCL_OK;
  2759. }
  2760. /* 
  2761.  * When we unload this file, we need to divert the 
  2762.  * unloading so we can unload and cleanup the 
  2763.  * temporary file correctly.
  2764.  */
  2765. tvdlPtr = (FsDivertLoad*) ckalloc(sizeof(FsDivertLoad));
  2766. /* 
  2767.  * Remember three pieces of information.  This allows
  2768.  * us to cleanup the diverted load completely, on
  2769.  * platforms which allow proper unloading of code.
  2770.  */
  2771. tvdlPtr->loadHandle = newLoadHandle;
  2772. tvdlPtr->unloadProcPtr = newUnloadProcPtr;
  2773. if (copyFsPtr != &tclNativeFilesystem) {
  2774.     /* copyToPtr is already incremented for this reference */
  2775.     tvdlPtr->divertedFile = copyToPtr;
  2776.     /* 
  2777.      * This is the filesystem we loaded it into.  Since
  2778.      * we have a reference to 'copyToPtr', we already
  2779.      * have a refCount on this filesystem, so we don't
  2780.      * need to worry about it disappearing on us.
  2781.      */
  2782.     tvdlPtr->divertedFilesystem = copyFsPtr;
  2783.     tvdlPtr->divertedFileNativeRep = NULL;
  2784. } else {
  2785.     /* We need the native rep */
  2786.     tvdlPtr->divertedFileNativeRep = 
  2787.       TclNativeDupInternalRep(Tcl_FSGetInternalRep(copyToPtr, 
  2788. copyFsPtr));
  2789.     /* 
  2790.      * We don't need or want references to the copied
  2791.      * Tcl_Obj or the filesystem if it is the native
  2792.      * one.
  2793.      */
  2794.     tvdlPtr->divertedFile = NULL;
  2795.     tvdlPtr->divertedFilesystem = NULL;
  2796.     Tcl_DecrRefCount(copyToPtr);
  2797. }
  2798. copyToPtr = NULL;
  2799. (*handlePtr) = (Tcl_LoadHandle) tvdlPtr;
  2800. (*unloadProcPtr) = &FSUnloadTempFile;
  2801. return retVal;
  2802.     } else {
  2803. /* Cross-platform copy failed */
  2804. Tcl_FSDeleteFile(copyToPtr);
  2805. Tcl_DecrRefCount(copyToPtr);
  2806. return TCL_ERROR;
  2807.     }
  2808. }
  2809.     }
  2810.     Tcl_SetErrno(ENOENT);
  2811.     return -1;
  2812. }
  2813. /* 
  2814.  * This function used to be in the platform specific directories, but it
  2815.  * has now been made to work cross-platform
  2816.  */
  2817. int
  2818. TclpLoadFile(interp, pathPtr, sym1, sym2, proc1Ptr, proc2Ptr, 
  2819.      clientDataPtr, unloadProcPtr)
  2820.     Tcl_Interp *interp; /* Used for error reporting. */
  2821.     Tcl_Obj *pathPtr; /* Name of the file containing the desired
  2822.  * code (UTF-8). */
  2823.     CONST char *sym1, *sym2; /* Names of two procedures to look up in
  2824.  * the file's symbol table. */
  2825.     Tcl_PackageInitProc **proc1Ptr, **proc2Ptr;
  2826. /* Where to return the addresses corresponding
  2827.  * to sym1 and sym2. */
  2828.     ClientData *clientDataPtr; /* Filled with token for dynamically loaded
  2829.  * file which will be passed back to 
  2830.  * (*unloadProcPtr)() to unload the file. */
  2831.     Tcl_FSUnloadFileProc **unloadProcPtr;
  2832. /* Filled with address of Tcl_FSUnloadFileProc
  2833.  * function which should be used for
  2834.  * this file. */
  2835. {
  2836.     Tcl_LoadHandle handle = NULL;
  2837.     int res;
  2838.     
  2839.     res = TclpDlopen(interp, pathPtr, &handle, unloadProcPtr);
  2840.     
  2841.     if (res != TCL_OK) {
  2842.         return res;
  2843.     }
  2844.     if (handle == NULL) {
  2845. return TCL_ERROR;
  2846.     }
  2847.     
  2848.     *clientDataPtr = (ClientData)handle;
  2849.     
  2850.     *proc1Ptr = TclpFindSymbol(interp, handle, sym1);
  2851.     *proc2Ptr = TclpFindSymbol(interp, handle, sym2);
  2852.     return TCL_OK;
  2853. }
  2854. /*
  2855.  *---------------------------------------------------------------------------
  2856.  *
  2857.  * FSUnloadTempFile --
  2858.  *
  2859.  * This function is called when we loaded a library of code via
  2860.  * an intermediate temporary file.  This function ensures
  2861.  * the library is correctly unloaded and the temporary file
  2862.  * is correctly deleted.
  2863.  *
  2864.  * Results:
  2865.  * None.
  2866.  *
  2867.  * Side effects:
  2868.  * The effects of the 'unload' function called, and of course
  2869.  * the temporary file will be deleted.
  2870.  *
  2871.  *---------------------------------------------------------------------------
  2872.  */
  2873. static void 
  2874. FSUnloadTempFile(loadHandle)
  2875.     Tcl_LoadHandle loadHandle; /* loadHandle returned by a previous call
  2876.        * to Tcl_FSLoadFile().  The loadHandle is 
  2877.        * a token that represents the loaded 
  2878.        * file. */
  2879. {
  2880.     FsDivertLoad *tvdlPtr = (FsDivertLoad*)loadHandle;
  2881.     /* 
  2882.      * This test should never trigger, since we give
  2883.      * the client data in the function above.
  2884.      */
  2885.     if (tvdlPtr == NULL) { return; }
  2886.     
  2887.     /* 
  2888.      * Call the real 'unloadfile' proc we actually used. It is very
  2889.      * important that we call this first, so that the shared library
  2890.      * is actually unloaded by the OS.  Otherwise, the following
  2891.      * 'delete' may well fail because the shared library is still in
  2892.      * use.
  2893.      */
  2894.     if (tvdlPtr->unloadProcPtr != NULL) {
  2895. (*tvdlPtr->unloadProcPtr)(tvdlPtr->loadHandle);
  2896.     }
  2897.     
  2898.     if (tvdlPtr->divertedFilesystem == NULL) {
  2899. /* 
  2900.  * It was the native filesystem, and we have a special
  2901.  * function available just for this purpose, which we 
  2902.  * know works even at this late stage.
  2903.  */
  2904. TclpDeleteFile(tvdlPtr->divertedFileNativeRep);
  2905. NativeFreeInternalRep(tvdlPtr->divertedFileNativeRep);
  2906.     } else {
  2907. /* 
  2908.  * Remove the temporary file we created.  Note, we may crash
  2909.  * here because encodings have been taken down already.
  2910.  */
  2911. if (tvdlPtr->divertedFilesystem->deleteFileProc(tvdlPtr->divertedFile)
  2912.     != TCL_OK) {
  2913.     /* 
  2914.      * The above may have failed because the filesystem, or something
  2915.      * it depends upon (e.g. encodings) have been taken down because
  2916.      * Tcl is exiting.
  2917.      * 
  2918.      * We may need to work out how to delete this file more
  2919.      * robustly (or give the filesystem the information it needs
  2920.      * to delete the file more robustly).
  2921.      * 
  2922.      * In particular, one problem might be that the filesystem
  2923.      * cannot extract the information it needs from the above
  2924.      * path object because Tcl's entire filesystem apparatus
  2925.      * (the code in this file) has been finalized, and it
  2926.      * refuses to pass the internal representation to the
  2927.      * filesystem.
  2928.      */
  2929. }
  2930. /* 
  2931.  * And free up the allocations.  This will also of course remove
  2932.  * a refCount from the Tcl_Filesystem to which this file belongs,
  2933.  * which could then free up the filesystem if we are exiting.
  2934.  */
  2935. Tcl_DecrRefCount(tvdlPtr->divertedFile);
  2936.     }
  2937.     ckfree((char*)tvdlPtr);
  2938. }
  2939. /*
  2940.  *---------------------------------------------------------------------------
  2941.  *
  2942.  * Tcl_FSLink --
  2943.  *
  2944.  * This function replaces the library version of readlink() and
  2945.  * can also be used to make links.  The appropriate function for
  2946.  * the filesystem to which pathPtr belongs will be called.
  2947.  *
  2948.  * Results:
  2949.  *      If toPtr is NULL, then the result is a Tcl_Obj specifying the 
  2950.  *      contents of the symbolic link given by 'pathPtr', or NULL if
  2951.  *      the symbolic link could not be read.  The result is owned by
  2952.  *      the caller, which should call Tcl_DecrRefCount when the result
  2953.  *      is no longer needed.
  2954.  *      
  2955.  *      If toPtr is non-NULL, then the result is toPtr if the link action
  2956.  *      was successful, or NULL if not.  In this case the result has no
  2957.  *      additional reference count, and need not be freed.  The actual
  2958.  *      action to perform is given by the 'linkAction' flags, which is
  2959.  *      an or'd combination of:
  2960.  *      
  2961.  *        TCL_CREATE_SYMBOLIC_LINK
  2962.  *        TCL_CREATE_HARD_LINK
  2963.  *      
  2964.  *      Note that most filesystems will not support linking across
  2965.  *      to different filesystems, so this function will usually
  2966.  *      fail unless toPtr is in the same FS as pathPtr.
  2967.  *      
  2968.  * Side effects:
  2969.  * See readlink() documentation.  A new filesystem link 
  2970.  * object may appear
  2971.  *
  2972.  *---------------------------------------------------------------------------
  2973.  */
  2974. Tcl_Obj *
  2975. Tcl_FSLink(pathPtr, toPtr, linkAction)
  2976.     Tcl_Obj *pathPtr; /* Path of file to readlink or link */
  2977.     Tcl_Obj *toPtr; /* NULL or path to be linked to */
  2978.     int linkAction;             /* Action to perform */
  2979. {
  2980.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  2981.     if (fsPtr != NULL) {
  2982. Tcl_FSLinkProc *proc = fsPtr->linkProc;
  2983. if (proc != NULL) {
  2984.     return (*proc)(pathPtr, toPtr, linkAction);
  2985. }
  2986.     }
  2987.     /*
  2988.      * If S_IFLNK isn't defined it means that the machine doesn't
  2989.      * support symbolic links, so the file can't possibly be a
  2990.      * symbolic link.  Generate an EINVAL error, which is what
  2991.      * happens on machines that do support symbolic links when
  2992.      * you invoke readlink on a file that isn't a symbolic link.
  2993.      */
  2994. #ifndef S_IFLNK
  2995.     errno = EINVAL;
  2996. #else
  2997.     Tcl_SetErrno(ENOENT);
  2998. #endif /* S_IFLNK */
  2999.     return NULL;
  3000. }
  3001. /*
  3002.  *---------------------------------------------------------------------------
  3003.  *
  3004.  * Tcl_FSListVolumes --
  3005.  *
  3006.  * Lists the currently mounted volumes.  The chain of functions
  3007.  * that have been "inserted" into the filesystem will be called in
  3008.  * succession; each may return a list of volumes, all of which are
  3009.  * added to the result until all mounted file systems are listed.
  3010.  *
  3011.  * Notice that we assume the lists returned by each filesystem
  3012.  * (if non NULL) have been given a refCount for us already.
  3013.  * However, we are NOT allowed to hang on to the list itself
  3014.  * (it belongs to the filesystem we called).  Therefore we
  3015.  * quite naturally add its contents to the result we are
  3016.  * building, and then decrement the refCount.
  3017.  *
  3018.  * Results:
  3019.  * The list of volumes, in an object which has refCount 0.
  3020.  *
  3021.  * Side effects:
  3022.  * None
  3023.  *
  3024.  *---------------------------------------------------------------------------
  3025.  */
  3026. Tcl_Obj*
  3027. Tcl_FSListVolumes(void)
  3028. {
  3029.     FilesystemRecord *fsRecPtr;
  3030.     Tcl_Obj *resultPtr = Tcl_NewObj();
  3031.     
  3032.     /*
  3033.      * Call each of the "listVolumes" function in succession.
  3034.      * A non-NULL return value indicates the particular function has