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

通讯编程

开发平台:

Visual C++

  1.      * succeeded.  We call all the functions registered, since we want
  2.      * a list of all drives from all filesystems.
  3.      */
  4.     fsRecPtr = FsGetFirstFilesystem();
  5.     while (fsRecPtr != NULL) {
  6. Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
  7. if (proc != NULL) {
  8.     Tcl_Obj *thisFsVolumes = (*proc)();
  9.     if (thisFsVolumes != NULL) {
  10. Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
  11. Tcl_DecrRefCount(thisFsVolumes);
  12.     }
  13. }
  14. fsRecPtr = fsRecPtr->nextPtr;
  15.     }
  16.     
  17.     return resultPtr;
  18. }
  19. /*
  20.  *---------------------------------------------------------------------------
  21.  *
  22.  * FsListMounts --
  23.  *
  24.  * List all mounts within the given directory, which match the
  25.  * given pattern.
  26.  *
  27.  * Results:
  28.  * The list of mounts, in a list object which has refCount 0, or
  29.  * NULL if we didn't even find any filesystems to try to list
  30.  * mounts.
  31.  *
  32.  * Side effects:
  33.  * None
  34.  *
  35.  *---------------------------------------------------------------------------
  36.  */
  37. static Tcl_Obj*
  38. FsListMounts(pathPtr, pattern)
  39.     Tcl_Obj *pathPtr;         /* Contains path to directory to search. */
  40.     CONST char *pattern; /* Pattern to match against. */
  41. {
  42.     FilesystemRecord *fsRecPtr;
  43.     Tcl_GlobTypeData mountsOnly = { TCL_GLOB_TYPE_MOUNT, 0, NULL, NULL };
  44.     Tcl_Obj *resultPtr = NULL;
  45.     
  46.     /*
  47.      * Call each of the "listMounts" functions in succession.
  48.      * A non-NULL return value indicates the particular function has
  49.      * succeeded.  We call all the functions registered, since we want
  50.      * a list from each filesystems.
  51.      */
  52.     fsRecPtr = FsGetFirstFilesystem();
  53.     while (fsRecPtr != NULL) {
  54. if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
  55.     Tcl_FSMatchInDirectoryProc *proc = 
  56.   fsRecPtr->fsPtr->matchInDirectoryProc;
  57.     if (proc != NULL) {
  58. if (resultPtr == NULL) {
  59.     resultPtr = Tcl_NewObj();
  60. }
  61. (*proc)(NULL, resultPtr, pathPtr, pattern, &mountsOnly);
  62.     }
  63. }
  64. fsRecPtr = fsRecPtr->nextPtr;
  65.     }
  66.     
  67.     return resultPtr;
  68. }
  69. /*
  70.  *---------------------------------------------------------------------------
  71.  *
  72.  * Tcl_FSSplitPath --
  73.  *
  74.  *      This function takes the given Tcl_Obj, which should be a valid
  75.  *      path, and returns a Tcl List object containing each segment of
  76.  *      that path as an element.
  77.  *
  78.  * Results:
  79.  *      Returns list object with refCount of zero.  If the passed in
  80.  *      lenPtr is non-NULL, we use it to return the number of elements
  81.  *      in the returned list.
  82.  *
  83.  * Side effects:
  84.  * None.
  85.  *
  86.  *---------------------------------------------------------------------------
  87.  */
  88. Tcl_Obj* 
  89. Tcl_FSSplitPath(pathPtr, lenPtr)
  90.     Tcl_Obj *pathPtr; /* Path to split. */
  91.     int *lenPtr; /* int to store number of path elements. */
  92. {
  93.     Tcl_Obj *result = NULL;  /* Needed only to prevent gcc warnings. */
  94.     Tcl_Filesystem *fsPtr;
  95.     char separator = '/';
  96.     int driveNameLength;
  97.     char *p;
  98.     
  99.     /*
  100.      * Perform platform specific splitting. 
  101.      */
  102.     if (FSGetPathType(pathPtr, &fsPtr, &driveNameLength) 
  103. == TCL_PATH_ABSOLUTE) {
  104. if (fsPtr == &tclNativeFilesystem) {
  105.     return TclpNativeSplitPath(pathPtr, lenPtr);
  106. }
  107.     } else {
  108. return TclpNativeSplitPath(pathPtr, lenPtr);
  109.     }
  110.     /* We assume separators are single characters */
  111.     if (fsPtr->filesystemSeparatorProc != NULL) {
  112. Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(pathPtr);
  113. if (sep != NULL) {
  114.     separator = Tcl_GetString(sep)[0];
  115. }
  116.     }
  117.     
  118.     /* 
  119.      * Place the drive name as first element of the
  120.      * result list.  The drive name may contain strange
  121.      * characters, like colons and multiple forward slashes
  122.      * (for example 'ftp://' is a valid vfs drive name)
  123.      */
  124.     result = Tcl_NewObj();
  125.     p = Tcl_GetString(pathPtr);
  126.     Tcl_ListObjAppendElement(NULL, result, 
  127.      Tcl_NewStringObj(p, driveNameLength));
  128.     p+= driveNameLength;
  129.     
  130.     /* Add the remaining path elements to the list */
  131.     for (;;) {
  132. char *elementStart = p;
  133. int length;
  134. while ((*p != '') && (*p != separator)) {
  135.     p++;
  136. }
  137. length = p - elementStart;
  138. if (length > 0) {
  139.     Tcl_Obj *nextElt;
  140.     if (elementStart[0] == '~') {
  141. nextElt = Tcl_NewStringObj("./",2);
  142. Tcl_AppendToObj(nextElt, elementStart, length);
  143.     } else {
  144. nextElt = Tcl_NewStringObj(elementStart, length);
  145.     }
  146.     Tcl_ListObjAppendElement(NULL, result, nextElt);
  147. }
  148. if (*p++ == '') {
  149.     break;
  150. }
  151.     }
  152.      
  153.     /*
  154.      * Compute the number of elements in the result.
  155.      */
  156.     if (lenPtr != NULL) {
  157. Tcl_ListObjLength(NULL, result, lenPtr);
  158.     }
  159.     return result;
  160. }
  161. /* Simple helper function */
  162. Tcl_Obj* 
  163. TclFSInternalToNormalized(fromFilesystem, clientData, fsRecPtrPtr)
  164.     Tcl_Filesystem *fromFilesystem;
  165.     ClientData clientData;
  166.     FilesystemRecord **fsRecPtrPtr;
  167. {
  168.     FilesystemRecord *fsRecPtr = FsGetFirstFilesystem();
  169.     while (fsRecPtr != NULL) {
  170. if (fsRecPtr->fsPtr == fromFilesystem) {
  171.     *fsRecPtrPtr = fsRecPtr;
  172.     break;
  173. }
  174. fsRecPtr = fsRecPtr->nextPtr;
  175.     }
  176.     
  177.     if ((fsRecPtr != NULL) 
  178.       && (fromFilesystem->internalToNormalizedProc != NULL)) {
  179. return (*fromFilesystem->internalToNormalizedProc)(clientData);
  180.     } else {
  181. return NULL;
  182.     }
  183. }
  184. /*
  185.  *----------------------------------------------------------------------
  186.  *
  187.  * GetPathType --
  188.  *
  189.  * Helper function used by FSGetPathType.
  190.  *
  191.  * Results:
  192.  * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
  193.  * TCL_PATH_VOLUME_RELATIVE.  The filesystem reference will
  194.  * be set if and only if it is non-NULL and the function's 
  195.  * return value is TCL_PATH_ABSOLUTE.
  196.  *
  197.  * Side effects:
  198.  * None.
  199.  *
  200.  *----------------------------------------------------------------------
  201.  */
  202. static Tcl_PathType
  203. GetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr, driveNameRef)
  204.     Tcl_Obj *pathObjPtr;
  205.     Tcl_Filesystem **filesystemPtrPtr;
  206.     int *driveNameLengthPtr;
  207.     Tcl_Obj **driveNameRef;
  208. {
  209.     FilesystemRecord *fsRecPtr;
  210.     int pathLen;
  211.     char *path;
  212.     Tcl_PathType type = TCL_PATH_RELATIVE;
  213.     
  214.     path = Tcl_GetStringFromObj(pathObjPtr, &pathLen);
  215.     /*
  216.      * Call each of the "listVolumes" function in succession, checking
  217.      * whether the given path is an absolute path on any of the volumes
  218.      * returned (this is done by checking whether the path's prefix
  219.      * matches).
  220.      */
  221.     fsRecPtr = FsGetFirstFilesystem();
  222.     while (fsRecPtr != NULL) {
  223. Tcl_FSListVolumesProc *proc = fsRecPtr->fsPtr->listVolumesProc;
  224. /* 
  225.  * We want to skip the native filesystem in this loop because
  226.  * otherwise we won't necessarily pass all the Tcl testsuite --
  227.  * this is because some of the tests artificially change the
  228.  * current platform (between mac, win, unix) but the list
  229.  * of volumes we get by calling (*proc) will reflect the current
  230.  * (real) platform only and this may cause some tests to fail.
  231.  * In particular, on unix '/' will match the beginning of 
  232.  * certain absolute Windows paths starting '//' and those tests
  233.  * will go wrong.
  234.  * 
  235.  * Besides these test-suite issues, there is one other reason
  236.  * to skip the native filesystem --- since the tclFilename.c
  237.  * code has nice fast 'absolute path' checkers, we don't want
  238.  * to waste time repeating that effort here, and this 
  239.  * function is actually called quite often, so if we can
  240.  * save the overhead of the native filesystem returning us
  241.  * a list of volumes all the time, it is better.
  242.  */
  243. if ((fsRecPtr->fsPtr != &tclNativeFilesystem) && (proc != NULL)) {
  244.     int numVolumes;
  245.     Tcl_Obj *thisFsVolumes = (*proc)();
  246.     if (thisFsVolumes != NULL) {
  247. if (Tcl_ListObjLength(NULL, thisFsVolumes, 
  248.       &numVolumes) != TCL_OK) {
  249.     /* 
  250.      * This is VERY bad; the Tcl_FSListVolumesProc
  251.      * didn't return a valid list.  Set numVolumes to
  252.      * -1 so that we skip the while loop below and just
  253.      * return with the current value of 'type'.
  254.      * 
  255.      * It would be better if we could signal an error
  256.      * here (but panic seems a bit excessive).
  257.      */
  258.     numVolumes = -1;
  259. }
  260. while (numVolumes > 0) {
  261.     Tcl_Obj *vol;
  262.     int len;
  263.     char *strVol;
  264.     numVolumes--;
  265.     Tcl_ListObjIndex(NULL, thisFsVolumes, numVolumes, &vol);
  266.     strVol = Tcl_GetStringFromObj(vol,&len);
  267.     if (pathLen < len) {
  268. continue;
  269.     }
  270.     if (strncmp(strVol, path, (size_t) len) == 0) {
  271. type = TCL_PATH_ABSOLUTE;
  272. if (filesystemPtrPtr != NULL) {
  273.     *filesystemPtrPtr = fsRecPtr->fsPtr;
  274. }
  275. if (driveNameLengthPtr != NULL) {
  276.     *driveNameLengthPtr = len;
  277. }
  278. if (driveNameRef != NULL) {
  279.     *driveNameRef = vol;
  280.     Tcl_IncrRefCount(vol);
  281. }
  282. break;
  283.     }
  284. }
  285. Tcl_DecrRefCount(thisFsVolumes);
  286. if (type == TCL_PATH_ABSOLUTE) {
  287.     /* We don't need to examine any more filesystems */
  288.     break;
  289. }
  290.     }
  291. }
  292. fsRecPtr = fsRecPtr->nextPtr;
  293.     }
  294.     
  295.     if (type != TCL_PATH_ABSOLUTE) {
  296. type = TclpGetNativePathType(pathObjPtr, driveNameLengthPtr, 
  297.      driveNameRef);
  298. if ((type == TCL_PATH_ABSOLUTE) && (filesystemPtrPtr != NULL)) {
  299.     *filesystemPtrPtr = &tclNativeFilesystem;
  300. }
  301.     }
  302.     return type;
  303. }
  304. /*
  305.  *---------------------------------------------------------------------------
  306.  *
  307.  * Tcl_FSRenameFile --
  308.  *
  309.  * If the two paths given belong to the same filesystem, we call
  310.  * that filesystems rename function.  Otherwise we simply
  311.  * return the posix error 'EXDEV', and -1.
  312.  *
  313.  * Results:
  314.  *      Standard Tcl error code if a function was called.
  315.  *
  316.  * Side effects:
  317.  * A file may be renamed.
  318.  *
  319.  *---------------------------------------------------------------------------
  320.  */
  321. int
  322. Tcl_FSRenameFile(srcPathPtr, destPathPtr)
  323.     Tcl_Obj* srcPathPtr; /* Pathname of file or dir to be renamed
  324.  * (UTF-8). */
  325.     Tcl_Obj *destPathPtr; /* New pathname of file or directory
  326.  * (UTF-8). */
  327. {
  328.     int retVal = -1;
  329.     Tcl_Filesystem *fsPtr, *fsPtr2;
  330.     fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
  331.     fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
  332.     if (fsPtr == fsPtr2 && fsPtr != NULL) {
  333. Tcl_FSRenameFileProc *proc = fsPtr->renameFileProc;
  334. if (proc != NULL) {
  335.     retVal =  (*proc)(srcPathPtr, destPathPtr);
  336. }
  337.     }
  338.     if (retVal == -1) {
  339. Tcl_SetErrno(EXDEV);
  340.     }
  341.     return retVal;
  342. }
  343. /*
  344.  *---------------------------------------------------------------------------
  345.  *
  346.  * Tcl_FSCopyFile --
  347.  *
  348.  * If the two paths given belong to the same filesystem, we call
  349.  * that filesystem's copy function.  Otherwise we simply
  350.  * return the posix error 'EXDEV', and -1.
  351.  *
  352.  * Note that in the native filesystems, 'copyFileProc' is defined
  353.  * to copy soft links (i.e. it copies the links themselves, not
  354.  * the things they point to).
  355.  *
  356.  * Results:
  357.  *      Standard Tcl error code if a function was called.
  358.  *
  359.  * Side effects:
  360.  * A file may be copied.
  361.  *
  362.  *---------------------------------------------------------------------------
  363.  */
  364. int 
  365. Tcl_FSCopyFile(srcPathPtr, destPathPtr)
  366.     Tcl_Obj* srcPathPtr; /* Pathname of file to be copied (UTF-8). */
  367.     Tcl_Obj *destPathPtr; /* Pathname of file to copy to (UTF-8). */
  368. {
  369.     int retVal = -1;
  370.     Tcl_Filesystem *fsPtr, *fsPtr2;
  371.     fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
  372.     fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
  373.     if (fsPtr == fsPtr2 && fsPtr != NULL) {
  374. Tcl_FSCopyFileProc *proc = fsPtr->copyFileProc;
  375. if (proc != NULL) {
  376.     retVal = (*proc)(srcPathPtr, destPathPtr);
  377. }
  378.     }
  379.     if (retVal == -1) {
  380. Tcl_SetErrno(EXDEV);
  381.     }
  382.     return retVal;
  383. }
  384. /*
  385.  *---------------------------------------------------------------------------
  386.  *
  387.  * TclCrossFilesystemCopy --
  388.  *
  389.  * Helper for above function, and for Tcl_FSLoadFile, to copy
  390.  * files from one filesystem to another.  This function will
  391.  * overwrite the target file if it already exists.
  392.  *
  393.  * Results:
  394.  *      Standard Tcl error code.
  395.  *
  396.  * Side effects:
  397.  * A file may be created.
  398.  *
  399.  *---------------------------------------------------------------------------
  400.  */
  401. int 
  402. TclCrossFilesystemCopy(interp, source, target) 
  403.     Tcl_Interp *interp; /* For error messages */
  404.     Tcl_Obj *source; /* Pathname of file to be copied (UTF-8). */
  405.     Tcl_Obj *target; /* Pathname of file to copy to (UTF-8). */
  406. {
  407.     int result = TCL_ERROR;
  408.     int prot = 0666;
  409.     
  410.     Tcl_Channel out = Tcl_FSOpenFileChannel(interp, target, "w", prot);
  411.     if (out != NULL) {
  412. /* It looks like we can copy it over */
  413. Tcl_Channel in = Tcl_FSOpenFileChannel(interp, source, 
  414.        "r", prot);
  415. if (in == NULL) {
  416.     /* This is very strange, we checked this above */
  417.     Tcl_Close(interp, out);
  418. } else {
  419.     Tcl_StatBuf sourceStatBuf;
  420.     struct utimbuf tval;
  421.     /* 
  422.      * Copy it synchronously.  We might wish to add an
  423.      * asynchronous option to support vfs's which are
  424.      * slow (e.g. network sockets).
  425.      */
  426.     Tcl_SetChannelOption(interp, in, "-translation", "binary");
  427.     Tcl_SetChannelOption(interp, out, "-translation", "binary");
  428.     
  429.     if (TclCopyChannel(interp, in, out, -1, NULL) == TCL_OK) {
  430. result = TCL_OK;
  431.     }
  432.     /* 
  433.      * If the copy failed, assume that copy channel left
  434.      * a good error message.
  435.      */
  436.     Tcl_Close(interp, in);
  437.     Tcl_Close(interp, out);
  438.     
  439.     /* Set modification date of copied file */
  440.     if (Tcl_FSLstat(source, &sourceStatBuf) == 0) {
  441. tval.actime = sourceStatBuf.st_atime;
  442. tval.modtime = sourceStatBuf.st_mtime;
  443. Tcl_FSUtime(target, &tval);
  444.     }
  445. }
  446.     }
  447.     return result;
  448. }
  449. /*
  450.  *---------------------------------------------------------------------------
  451.  *
  452.  * Tcl_FSDeleteFile --
  453.  *
  454.  * The appropriate function for the filesystem to which pathPtr
  455.  * belongs will be called.
  456.  *
  457.  * Results:
  458.  *      Standard Tcl error code.
  459.  *
  460.  * Side effects:
  461.  * A file may be deleted.
  462.  *
  463.  *---------------------------------------------------------------------------
  464.  */
  465. int
  466. Tcl_FSDeleteFile(pathPtr)
  467.     Tcl_Obj *pathPtr; /* Pathname of file to be removed (UTF-8). */
  468. {
  469.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  470.     if (fsPtr != NULL) {
  471. Tcl_FSDeleteFileProc *proc = fsPtr->deleteFileProc;
  472. if (proc != NULL) {
  473.     return (*proc)(pathPtr);
  474. }
  475.     }
  476.     Tcl_SetErrno(ENOENT);
  477.     return -1;
  478. }
  479. /*
  480.  *---------------------------------------------------------------------------
  481.  *
  482.  * Tcl_FSCreateDirectory --
  483.  *
  484.  * The appropriate function for the filesystem to which pathPtr
  485.  * belongs will be called.
  486.  *
  487.  * Results:
  488.  *      Standard Tcl error code.
  489.  *
  490.  * Side effects:
  491.  * A directory may be created.
  492.  *
  493.  *---------------------------------------------------------------------------
  494.  */
  495. int
  496. Tcl_FSCreateDirectory(pathPtr)
  497.     Tcl_Obj *pathPtr; /* Pathname of directory to create (UTF-8). */
  498. {
  499.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  500.     if (fsPtr != NULL) {
  501. Tcl_FSCreateDirectoryProc *proc = fsPtr->createDirectoryProc;
  502. if (proc != NULL) {
  503.     return (*proc)(pathPtr);
  504. }
  505.     }
  506.     Tcl_SetErrno(ENOENT);
  507.     return -1;
  508. }
  509. /*
  510.  *---------------------------------------------------------------------------
  511.  *
  512.  * Tcl_FSCopyDirectory --
  513.  *
  514.  * If the two paths given belong to the same filesystem, we call
  515.  * that filesystems copy-directory function.  Otherwise we simply
  516.  * return the posix error 'EXDEV', and -1.
  517.  *
  518.  * Results:
  519.  *      Standard Tcl error code if a function was called.
  520.  *
  521.  * Side effects:
  522.  * A directory may be copied.
  523.  *
  524.  *---------------------------------------------------------------------------
  525.  */
  526. int
  527. Tcl_FSCopyDirectory(srcPathPtr, destPathPtr, errorPtr)
  528.     Tcl_Obj* srcPathPtr; /* Pathname of directory to be copied
  529.  * (UTF-8). */
  530.     Tcl_Obj *destPathPtr; /* Pathname of target directory (UTF-8). */
  531.     Tcl_Obj **errorPtr;         /* If non-NULL, then will be set to a
  532.                                  * new object containing name of file
  533.                                  * causing error, with refCount 1. */
  534. {
  535.     int retVal = -1;
  536.     Tcl_Filesystem *fsPtr, *fsPtr2;
  537.     fsPtr = Tcl_FSGetFileSystemForPath(srcPathPtr);
  538.     fsPtr2 = Tcl_FSGetFileSystemForPath(destPathPtr);
  539.     if (fsPtr == fsPtr2 && fsPtr != NULL) {
  540. Tcl_FSCopyDirectoryProc *proc = fsPtr->copyDirectoryProc;
  541. if (proc != NULL) {
  542.     retVal = (*proc)(srcPathPtr, destPathPtr, errorPtr);
  543. }
  544.     }
  545.     if (retVal == -1) {
  546. Tcl_SetErrno(EXDEV);
  547.     }
  548.     return retVal;
  549. }
  550. /*
  551.  *---------------------------------------------------------------------------
  552.  *
  553.  * Tcl_FSRemoveDirectory --
  554.  *
  555.  * The appropriate function for the filesystem to which pathPtr
  556.  * belongs will be called.
  557.  *
  558.  * Results:
  559.  *      Standard Tcl error code.
  560.  *
  561.  * Side effects:
  562.  * A directory may be deleted.
  563.  *
  564.  *---------------------------------------------------------------------------
  565.  */
  566. int
  567. Tcl_FSRemoveDirectory(pathPtr, recursive, errorPtr)
  568.     Tcl_Obj *pathPtr; /* Pathname of directory to be removed
  569.  * (UTF-8). */
  570.     int recursive; /* If non-zero, removes directories that
  571.  * are nonempty.  Otherwise, will only remove
  572.  * empty directories. */
  573.     Tcl_Obj **errorPtr;         /* If non-NULL, then will be set to a
  574.  * new object containing name of file
  575.  * causing error, with refCount 1. */
  576. {
  577.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
  578.     if (fsPtr != NULL) {
  579. Tcl_FSRemoveDirectoryProc *proc = fsPtr->removeDirectoryProc;
  580. if (proc != NULL) {
  581.     if (recursive) {
  582.         /* 
  583.          * We check whether the cwd lies inside this directory
  584.          * and move it if it does.
  585.          */
  586. Tcl_Obj *cwdPtr = Tcl_FSGetCwd(NULL);
  587. if (cwdPtr != NULL) {
  588.     char *cwdStr, *normPathStr;
  589.     int cwdLen, normLen;
  590.     Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr);
  591.     if (normPath != NULL) {
  592.         normPathStr = Tcl_GetStringFromObj(normPath, &normLen);
  593. cwdStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
  594. if ((cwdLen >= normLen) && (strncmp(normPathStr, 
  595. cwdStr, (size_t) normLen) == 0)) {
  596.     /* 
  597.      * the cwd is inside the directory, so we
  598.      * perform a 'cd [file dirname $path]'
  599.      */
  600.     Tcl_Obj *dirPtr = TclFileDirname(NULL, pathPtr);
  601.     Tcl_FSChdir(dirPtr);
  602.     Tcl_DecrRefCount(dirPtr);
  603. }
  604.     }
  605.     Tcl_DecrRefCount(cwdPtr);
  606. }
  607.     }
  608.     return (*proc)(pathPtr, recursive, errorPtr);
  609. }
  610.     }
  611.     Tcl_SetErrno(ENOENT);
  612.     return -1;
  613. }
  614. /*
  615.  *---------------------------------------------------------------------------
  616.  *
  617.  * Tcl_FSGetFileSystemForPath --
  618.  *
  619.  *      This function determines which filesystem to use for a
  620.  *      particular path object, and returns the filesystem which
  621.  *      accepts this file.  If no filesystem will accept this object
  622.  *      as a valid file path, then NULL is returned.
  623.  *
  624.  * Results:
  625. .*      NULL or a filesystem which will accept this path.
  626.  *
  627.  * Side effects:
  628.  * The object may be converted to a path type.
  629.  *
  630.  *---------------------------------------------------------------------------
  631.  */
  632. Tcl_Filesystem*
  633. Tcl_FSGetFileSystemForPath(pathObjPtr)
  634.     Tcl_Obj* pathObjPtr;
  635. {
  636.     FilesystemRecord *fsRecPtr;
  637.     Tcl_Filesystem* retVal = NULL;
  638.     
  639.     /* 
  640.      * If the object has a refCount of zero, we reject it.  This
  641.      * is to avoid possible segfaults or nondeterministic memory
  642.      * leaks (i.e. the user doesn't know if they should decrement
  643.      * the ref count on return or not).
  644.      */
  645.     
  646.     if (pathObjPtr->refCount == 0) {
  647. panic("Tcl_FSGetFileSystemForPath called with object with refCount == 0");
  648. return NULL;
  649.     }
  650.     
  651.     /* 
  652.      * Check if the filesystem has changed in some way since
  653.      * this object's internal representation was calculated.
  654.      * Before doing that, assure we have the most up-to-date
  655.      * copy of the master filesystem. This is accomplished
  656.      * by the FsGetFirstFilesystem() call.
  657.      */
  658.     fsRecPtr = FsGetFirstFilesystem();
  659.     if (TclFSEnsureEpochOk(pathObjPtr, &retVal) != TCL_OK) {
  660. return NULL;
  661.     }
  662.     /*
  663.      * Call each of the "pathInFilesystem" functions in succession.  A
  664.      * non-return value of -1 indicates the particular function has
  665.      * succeeded.
  666.      */
  667.     while ((retVal == NULL) && (fsRecPtr != NULL)) {
  668. Tcl_FSPathInFilesystemProc *proc = fsRecPtr->fsPtr->pathInFilesystemProc;
  669. if (proc != NULL) {
  670.     ClientData clientData = NULL;
  671.     int ret = (*proc)(pathObjPtr, &clientData);
  672.     if (ret != -1) {
  673. /* 
  674.  * We assume the type of pathObjPtr hasn't been changed 
  675.  * by the above call to the pathInFilesystemProc.
  676.  */
  677. TclFSSetPathDetails(pathObjPtr, fsRecPtr, clientData);
  678. retVal = fsRecPtr->fsPtr;
  679.     }
  680. }
  681. fsRecPtr = fsRecPtr->nextPtr;
  682.     }
  683.     return retVal;
  684. }
  685. /*
  686.  *---------------------------------------------------------------------------
  687.  *
  688.  * Tcl_FSGetNativePath --
  689.  *
  690.  *      This function is for use by the Win/Unix/MacOS native filesystems,
  691.  *      so that they can easily retrieve the native (char* or TCHAR*)
  692.  *      representation of a path.  Other filesystems will probably
  693.  *      want to implement similar functions.  They basically act as a 
  694.  *      safety net around Tcl_FSGetInternalRep.  Normally your file-
  695.  *      system procedures will always be called with path objects
  696.  *      already converted to the correct filesystem, but if for 
  697.  *      some reason they are called directly (i.e. by procedures 
  698.  *      not in this file), then one cannot necessarily guarantee that
  699.  *      the path object pointer is from the correct filesystem.
  700.  *      
  701.  *      Note: in the future it might be desireable to have separate
  702.  *      versions of this function with different signatures, for
  703.  *      example Tcl_FSGetNativeMacPath, Tcl_FSGetNativeUnixPath etc.
  704.  *      Right now, since native paths are all string based, we use just
  705.  *      one function.  On MacOS we could possibly use an FSSpec or
  706.  *      FSRef as the native representation.
  707.  *
  708.  * Results:
  709.  *      NULL or a valid native path.
  710.  *
  711.  * Side effects:
  712.  * See Tcl_FSGetInternalRep.
  713.  *
  714.  *---------------------------------------------------------------------------
  715.  */
  716. CONST char *
  717. Tcl_FSGetNativePath(pathObjPtr)
  718.     Tcl_Obj *pathObjPtr;
  719. {
  720.     return (CONST char *)Tcl_FSGetInternalRep(pathObjPtr, &tclNativeFilesystem);
  721. }
  722. /*
  723.  *---------------------------------------------------------------------------
  724.  *
  725.  * NativeCreateNativeRep --
  726.  *
  727.  *      Create a native representation for the given path.
  728.  *
  729.  * Results:
  730.  *      None.
  731.  *
  732.  * Side effects:
  733.  * None.
  734.  *
  735.  *---------------------------------------------------------------------------
  736.  */
  737. static ClientData 
  738. NativeCreateNativeRep(pathObjPtr)
  739.     Tcl_Obj* pathObjPtr;
  740. {
  741.     char *nativePathPtr;
  742.     Tcl_DString ds;
  743.     Tcl_Obj* validPathObjPtr;
  744.     int len;
  745.     char *str;
  746.     /* Make sure the normalized path is set */
  747.     validPathObjPtr = Tcl_FSGetNormalizedPath(NULL, pathObjPtr);
  748.     if (validPathObjPtr == NULL) {
  749. return NULL;
  750.     }
  751.     str = Tcl_GetStringFromObj(validPathObjPtr, &len);
  752. #ifdef __WIN32__
  753.     Tcl_WinUtfToTChar(str, len, &ds);
  754.     if (tclWinProcs->useWide) {
  755. len = Tcl_DStringLength(&ds) + sizeof(WCHAR);
  756.     } else {
  757. len = Tcl_DStringLength(&ds) + sizeof(char);
  758.     }
  759. #else
  760.     Tcl_UtfToExternalDString(NULL, str, len, &ds);
  761.     len = Tcl_DStringLength(&ds) + sizeof(char);
  762. #endif
  763.     nativePathPtr = ckalloc((unsigned) len);
  764.     memcpy((VOID*)nativePathPtr, (VOID*)Tcl_DStringValue(&ds), (size_t) len);
  765.     Tcl_DStringFree(&ds);
  766.     return (ClientData)nativePathPtr;
  767. }
  768. /*
  769.  *---------------------------------------------------------------------------
  770.  *
  771.  * TclpNativeToNormalized --
  772.  *
  773.  *      Convert native format to a normalized path object, with refCount
  774.  *      of zero.
  775.  *
  776.  * Results:
  777.  *      A valid normalized path.
  778.  *
  779.  * Side effects:
  780.  * None.
  781.  *
  782.  *---------------------------------------------------------------------------
  783.  */
  784. Tcl_Obj* 
  785. TclpNativeToNormalized(clientData)
  786.     ClientData clientData;
  787. {
  788.     Tcl_DString ds;
  789.     Tcl_Obj *objPtr;
  790.     CONST char *copy;
  791.     int len;
  792.     
  793. #ifdef __WIN32__
  794.     Tcl_WinTCharToUtf((CONST char*)clientData, -1, &ds);
  795. #else
  796.     Tcl_ExternalToUtfDString(NULL, (CONST char*)clientData, -1, &ds);
  797. #endif
  798.     
  799.     copy = Tcl_DStringValue(&ds);
  800.     len = Tcl_DStringLength(&ds);
  801. #ifdef __WIN32__
  802.     /* 
  803.      * Certain native path representations on Windows have this special
  804.      * prefix to indicate that they are to be treated specially.  For
  805.      * example extremely long paths, or symlinks 
  806.      */
  807.     if (*copy == '\') {
  808.         if (0 == strncmp(copy,"\??\",4)) {
  809.     copy += 4;
  810.     len -= 4;
  811. } else if (0 == strncmp(copy,"\\?\",4)) {
  812.     copy += 4;
  813.     len -= 4;
  814. }
  815.     }
  816. #endif
  817.     objPtr = Tcl_NewStringObj(copy,len);
  818.     Tcl_DStringFree(&ds);
  819.     
  820.     return objPtr;
  821. }
  822. /*
  823.  *---------------------------------------------------------------------------
  824.  *
  825.  * TclNativeDupInternalRep --
  826.  *
  827.  *      Duplicate the native representation.
  828.  *
  829.  * Results:
  830.  *      The copied native representation, or NULL if it is not possible
  831.  *      to copy the representation.
  832.  *
  833.  * Side effects:
  834.  * None.
  835.  *
  836.  *---------------------------------------------------------------------------
  837.  */
  838. ClientData 
  839. TclNativeDupInternalRep(clientData)
  840.     ClientData clientData;
  841. {
  842.     ClientData copy;
  843.     size_t len;
  844.     if (clientData == NULL) {
  845. return NULL;
  846.     }
  847. #ifdef __WIN32__
  848.     if (tclWinProcs->useWide) {
  849. /* unicode representation when running on NT/2K/XP */
  850. len = sizeof(WCHAR) + (wcslen((CONST WCHAR*)clientData) * sizeof(WCHAR));
  851.     } else {
  852. /* ansi representation when running on 95/98/ME */
  853. len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
  854.     }
  855. #else
  856.     /* ansi representation when running on Unix/MacOS */
  857.     len = sizeof(char) + (strlen((CONST char*)clientData) * sizeof(char));
  858. #endif
  859.     
  860.     copy = (ClientData) ckalloc(len);
  861.     memcpy((VOID*)copy, (VOID*)clientData, len);
  862.     return copy;
  863. }
  864. /*
  865.  *---------------------------------------------------------------------------
  866.  *
  867.  * NativeFreeInternalRep --
  868.  *
  869.  *      Free a native internal representation, which will be non-NULL.
  870.  *
  871.  * Results:
  872.  *      None.
  873.  *
  874.  * Side effects:
  875.  * Memory is released.
  876.  *
  877.  *---------------------------------------------------------------------------
  878.  */
  879. static void 
  880. NativeFreeInternalRep(clientData)
  881.     ClientData clientData;
  882. {
  883.     ckfree((char*)clientData);
  884. }
  885. /*
  886.  *---------------------------------------------------------------------------
  887.  *
  888.  * Tcl_FSFileSystemInfo --
  889.  *
  890.  *      This function returns a list of two elements.  The first
  891.  *      element is the name of the filesystem (e.g. "native" or "vfs"),
  892.  *      and the second is the particular type of the given path within
  893.  *      that filesystem.
  894.  *
  895.  * Results:
  896.  *      A list of two elements.
  897.  *
  898.  * Side effects:
  899.  * The object may be converted to a path type.
  900.  *
  901.  *---------------------------------------------------------------------------
  902.  */
  903. Tcl_Obj*
  904. Tcl_FSFileSystemInfo(pathObjPtr)
  905.     Tcl_Obj* pathObjPtr;
  906. {
  907.     Tcl_Obj *resPtr;
  908.     Tcl_FSFilesystemPathTypeProc *proc;
  909.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
  910.     
  911.     if (fsPtr == NULL) {
  912. return NULL;
  913.     }
  914.     
  915.     resPtr = Tcl_NewListObj(0,NULL);
  916.     
  917.     Tcl_ListObjAppendElement(NULL, resPtr, 
  918.      Tcl_NewStringObj(fsPtr->typeName,-1));
  919.     proc = fsPtr->filesystemPathTypeProc;
  920.     if (proc != NULL) {
  921. Tcl_Obj *typePtr = (*proc)(pathObjPtr);
  922. if (typePtr != NULL) {
  923.     Tcl_ListObjAppendElement(NULL, resPtr, typePtr);
  924. }
  925.     }
  926.     
  927.     return resPtr;
  928. }
  929. /*
  930.  *---------------------------------------------------------------------------
  931.  *
  932.  * Tcl_FSPathSeparator --
  933.  *
  934.  *      This function returns the separator to be used for a given
  935.  *      path.  The object returned should have a refCount of zero
  936.  *
  937.  * Results:
  938.  *      A Tcl object, with a refCount of zero.  If the caller
  939.  *      needs to retain a reference to the object, it should
  940.  *      call Tcl_IncrRefCount.
  941.  *
  942.  * Side effects:
  943.  * The path object may be converted to a path type.
  944.  *
  945.  *---------------------------------------------------------------------------
  946.  */
  947. Tcl_Obj*
  948. Tcl_FSPathSeparator(pathObjPtr)
  949.     Tcl_Obj* pathObjPtr;
  950. {
  951.     Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathObjPtr);
  952.     
  953.     if (fsPtr == NULL) {
  954. return NULL;
  955.     }
  956.     if (fsPtr->filesystemSeparatorProc != NULL) {
  957. return (*fsPtr->filesystemSeparatorProc)(pathObjPtr);
  958.     }
  959.     
  960.     return NULL;
  961. }
  962. /*
  963.  *---------------------------------------------------------------------------
  964.  *
  965.  * NativeFilesystemSeparator --
  966.  *
  967.  *      This function is part of the native filesystem support, and
  968.  *      returns the separator for the given path.
  969.  *
  970.  * Results:
  971.  *      String object containing the separator character.
  972.  *
  973.  * Side effects:
  974.  * None.
  975.  *
  976.  *---------------------------------------------------------------------------
  977.  */
  978. static Tcl_Obj*
  979. NativeFilesystemSeparator(pathObjPtr)
  980.     Tcl_Obj* pathObjPtr;
  981. {
  982.     char *separator = NULL; /* lint */
  983.     switch (tclPlatform) {
  984. case TCL_PLATFORM_UNIX:
  985.     separator = "/";
  986.     break;
  987. case TCL_PLATFORM_WINDOWS:
  988.     separator = "\";
  989.     break;
  990. case TCL_PLATFORM_MAC:
  991.     separator = ":";
  992.     break;
  993.     }
  994.     return Tcl_NewStringObj(separator,1);
  995. }
  996. /* Everything from here on is contained in this obsolete ifdef */
  997. #ifdef USE_OBSOLETE_FS_HOOKS
  998. /*
  999.  *----------------------------------------------------------------------
  1000.  *
  1001.  * TclStatInsertProc --
  1002.  *
  1003.  * Insert the passed procedure pointer at the head of the list of
  1004.  * functions which are used during a call to 'TclStat(...)'. The
  1005.  * passed function should behave exactly like 'TclStat' when called
  1006.  * during that time (see 'TclStat(...)' for more information).
  1007.  * The function will be added even if it already in the list.
  1008.  *
  1009.  * Results:
  1010.  *      Normally TCL_OK; TCL_ERROR if memory for a new node in the list
  1011.  * could not be allocated.
  1012.  *
  1013.  * Side effects:
  1014.  *      Memory allocated and modifies the link list for 'TclStat'
  1015.  * functions.
  1016.  *
  1017.  *----------------------------------------------------------------------
  1018.  */
  1019. int
  1020. TclStatInsertProc (proc)
  1021.     TclStatProc_ *proc;
  1022. {
  1023.     int retVal = TCL_ERROR;
  1024.     if (proc != NULL) {
  1025. StatProc *newStatProcPtr;
  1026. newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc));
  1027. if (newStatProcPtr != NULL) {
  1028.     newStatProcPtr->proc = proc;
  1029.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1030.     newStatProcPtr->nextPtr = statProcList;
  1031.     statProcList = newStatProcPtr;
  1032.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1033.     retVal = TCL_OK;
  1034. }
  1035.     }
  1036.     return retVal;
  1037. }
  1038. /*
  1039.  *----------------------------------------------------------------------
  1040.  *
  1041.  * TclStatDeleteProc --
  1042.  *
  1043.  * Removed the passed function pointer from the list of 'TclStat'
  1044.  * functions.  Ensures that the built-in stat function is not
  1045.  * removvable.
  1046.  *
  1047.  * Results:
  1048.  *      TCL_OK if the procedure pointer was successfully removed,
  1049.  * TCL_ERROR otherwise.
  1050.  *
  1051.  * Side effects:
  1052.  *      Memory is deallocated and the respective list updated.
  1053.  *
  1054.  *----------------------------------------------------------------------
  1055.  */
  1056. int
  1057. TclStatDeleteProc (proc)
  1058.     TclStatProc_ *proc;
  1059. {
  1060.     int retVal = TCL_ERROR;
  1061.     StatProc *tmpStatProcPtr;
  1062.     StatProc *prevStatProcPtr = NULL;
  1063.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1064.     tmpStatProcPtr = statProcList;
  1065.     /*
  1066.      * Traverse the 'statProcList' looking for the particular node
  1067.      * whose 'proc' member matches 'proc' and remove that one from
  1068.      * the list.  Ensure that the "default" node cannot be removed.
  1069.      */
  1070.     while ((retVal == TCL_ERROR) && (tmpStatProcPtr != NULL)) {
  1071. if (tmpStatProcPtr->proc == proc) {
  1072.     if (prevStatProcPtr == NULL) {
  1073. statProcList = tmpStatProcPtr->nextPtr;
  1074.     } else {
  1075. prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr;
  1076.     }
  1077.     ckfree((char *)tmpStatProcPtr);
  1078.     retVal = TCL_OK;
  1079. } else {
  1080.     prevStatProcPtr = tmpStatProcPtr;
  1081.     tmpStatProcPtr = tmpStatProcPtr->nextPtr;
  1082. }
  1083.     }
  1084.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1085.     return retVal;
  1086. }
  1087. /*
  1088.  *----------------------------------------------------------------------
  1089.  *
  1090.  * TclAccessInsertProc --
  1091.  *
  1092.  * Insert the passed procedure pointer at the head of the list of
  1093.  * functions which are used during a call to 'TclAccess(...)'.
  1094.  * The passed function should behave exactly like 'TclAccess' when
  1095.  * called during that time (see 'TclAccess(...)' for more
  1096.  * information).  The function will be added even if it already in
  1097.  * the list.
  1098.  *
  1099.  * Results:
  1100.  *      Normally TCL_OK; TCL_ERROR if memory for a new node in the list
  1101.  * could not be allocated.
  1102.  *
  1103.  * Side effects:
  1104.  *      Memory allocated and modifies the link list for 'TclAccess'
  1105.  * functions.
  1106.  *
  1107.  *----------------------------------------------------------------------
  1108.  */
  1109. int
  1110. TclAccessInsertProc(proc)
  1111.     TclAccessProc_ *proc;
  1112. {
  1113.     int retVal = TCL_ERROR;
  1114.     if (proc != NULL) {
  1115. AccessProc *newAccessProcPtr;
  1116. newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc));
  1117. if (newAccessProcPtr != NULL) {
  1118.     newAccessProcPtr->proc = proc;
  1119.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1120.     newAccessProcPtr->nextPtr = accessProcList;
  1121.     accessProcList = newAccessProcPtr;
  1122.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1123.     retVal = TCL_OK;
  1124. }
  1125.     }
  1126.     return retVal;
  1127. }
  1128. /*
  1129.  *----------------------------------------------------------------------
  1130.  *
  1131.  * TclAccessDeleteProc --
  1132.  *
  1133.  * Removed the passed function pointer from the list of 'TclAccess'
  1134.  * functions.  Ensures that the built-in access function is not
  1135.  * removvable.
  1136.  *
  1137.  * Results:
  1138.  *      TCL_OK if the procedure pointer was successfully removed,
  1139.  * TCL_ERROR otherwise.
  1140.  *
  1141.  * Side effects:
  1142.  *      Memory is deallocated and the respective list updated.
  1143.  *
  1144.  *----------------------------------------------------------------------
  1145.  */
  1146. int
  1147. TclAccessDeleteProc(proc)
  1148.     TclAccessProc_ *proc;
  1149. {
  1150.     int retVal = TCL_ERROR;
  1151.     AccessProc *tmpAccessProcPtr;
  1152.     AccessProc *prevAccessProcPtr = NULL;
  1153.     /*
  1154.      * Traverse the 'accessProcList' looking for the particular node
  1155.      * whose 'proc' member matches 'proc' and remove that one from
  1156.      * the list.  Ensure that the "default" node cannot be removed.
  1157.      */
  1158.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1159.     tmpAccessProcPtr = accessProcList;
  1160.     while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != NULL)) {
  1161. if (tmpAccessProcPtr->proc == proc) {
  1162.     if (prevAccessProcPtr == NULL) {
  1163. accessProcList = tmpAccessProcPtr->nextPtr;
  1164.     } else {
  1165. prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr;
  1166.     }
  1167.     ckfree((char *)tmpAccessProcPtr);
  1168.     retVal = TCL_OK;
  1169. } else {
  1170.     prevAccessProcPtr = tmpAccessProcPtr;
  1171.     tmpAccessProcPtr = tmpAccessProcPtr->nextPtr;
  1172. }
  1173.     }
  1174.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1175.     return retVal;
  1176. }
  1177. /*
  1178.  *----------------------------------------------------------------------
  1179.  *
  1180.  * TclOpenFileChannelInsertProc --
  1181.  *
  1182.  * Insert the passed procedure pointer at the head of the list of
  1183.  * functions which are used during a call to
  1184.  * 'Tcl_OpenFileChannel(...)'. The passed function should behave
  1185.  * exactly like 'Tcl_OpenFileChannel' when called during that time
  1186.  * (see 'Tcl_OpenFileChannel(...)' for more information). The
  1187.  * function will be added even if it already in the list.
  1188.  *
  1189.  * Results:
  1190.  *      Normally TCL_OK; TCL_ERROR if memory for a new node in the list
  1191.  * could not be allocated.
  1192.  *
  1193.  * Side effects:
  1194.  *      Memory allocated and modifies the link list for
  1195.  * 'Tcl_OpenFileChannel' functions.
  1196.  *
  1197.  *----------------------------------------------------------------------
  1198.  */
  1199. int
  1200. TclOpenFileChannelInsertProc(proc)
  1201.     TclOpenFileChannelProc_ *proc;
  1202. {
  1203.     int retVal = TCL_ERROR;
  1204.     if (proc != NULL) {
  1205. OpenFileChannelProc *newOpenFileChannelProcPtr;
  1206. newOpenFileChannelProcPtr =
  1207. (OpenFileChannelProc *)ckalloc(sizeof(OpenFileChannelProc));
  1208. if (newOpenFileChannelProcPtr != NULL) {
  1209.     newOpenFileChannelProcPtr->proc = proc;
  1210.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1211.     newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList;
  1212.     openFileChannelProcList = newOpenFileChannelProcPtr;
  1213.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1214.     retVal = TCL_OK;
  1215. }
  1216.     }
  1217.     return retVal;
  1218. }
  1219. /*
  1220.  *----------------------------------------------------------------------
  1221.  *
  1222.  * TclOpenFileChannelDeleteProc --
  1223.  *
  1224.  * Removed the passed function pointer from the list of
  1225.  * 'Tcl_OpenFileChannel' functions.  Ensures that the built-in
  1226.  * open file channel function is not removable.
  1227.  *
  1228.  * Results:
  1229.  *      TCL_OK if the procedure pointer was successfully removed,
  1230.  * TCL_ERROR otherwise.
  1231.  *
  1232.  * Side effects:
  1233.  *      Memory is deallocated and the respective list updated.
  1234.  *
  1235.  *----------------------------------------------------------------------
  1236.  */
  1237. int
  1238. TclOpenFileChannelDeleteProc(proc)
  1239.     TclOpenFileChannelProc_ *proc;
  1240. {
  1241.     int retVal = TCL_ERROR;
  1242.     OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList;
  1243.     OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL;
  1244.     /*
  1245.      * Traverse the 'openFileChannelProcList' looking for the particular
  1246.      * node whose 'proc' member matches 'proc' and remove that one from
  1247.      * the list.  
  1248.      */
  1249.     Tcl_MutexLock(&obsoleteFsHookMutex);
  1250.     tmpOpenFileChannelProcPtr = openFileChannelProcList;
  1251.     while ((retVal == TCL_ERROR) &&
  1252.     (tmpOpenFileChannelProcPtr != NULL)) {
  1253. if (tmpOpenFileChannelProcPtr->proc == proc) {
  1254.     if (prevOpenFileChannelProcPtr == NULL) {
  1255. openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr;
  1256.     } else {
  1257. prevOpenFileChannelProcPtr->nextPtr =
  1258. tmpOpenFileChannelProcPtr->nextPtr;
  1259.     }
  1260.     ckfree((char *)tmpOpenFileChannelProcPtr);
  1261.     retVal = TCL_OK;
  1262. } else {
  1263.     prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr;
  1264.     tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr;
  1265. }
  1266.     }
  1267.     Tcl_MutexUnlock(&obsoleteFsHookMutex);
  1268.     return retVal;
  1269. }
  1270. #endif /* USE_OBSOLETE_FS_HOOKS */
  1271. /*
  1272.  * Prototypes for procedures defined later in this file.
  1273.  */
  1274. static void DupFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
  1275.     Tcl_Obj *copyPtr));
  1276. static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *listPtr));
  1277. static void             UpdateStringOfFsPath _ANSI_ARGS_((Tcl_Obj *objPtr));
  1278. static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
  1279.     Tcl_Obj *objPtr));
  1280. static int  FindSplitPos _ANSI_ARGS_((char *path, char *separator));
  1281. /*
  1282.  * Define the 'path' object type, which Tcl uses to represent
  1283.  * file paths internally.
  1284.  */
  1285. static Tcl_ObjType tclFsPathType = {
  1286.     "path", /* name */
  1287.     FreeFsPathInternalRep, /* freeIntRepProc */
  1288.     DupFsPathInternalRep,         /* dupIntRepProc */
  1289.     UpdateStringOfFsPath, /* updateStringProc */
  1290.     SetFsPathFromAny /* setFromAnyProc */
  1291. };
  1292. /* 
  1293.  * struct FsPath --
  1294.  * 
  1295.  * Internal representation of a Tcl_Obj of "path" type.  This
  1296.  * can be used to represent relative or absolute paths, and has
  1297.  * certain optimisations when used to represent paths which are
  1298.  * already normalized and absolute.
  1299.  * 
  1300.  * Note that 'normPathPtr' can be a circular reference to the
  1301.  * container Tcl_Obj of this FsPath.
  1302.  */
  1303. typedef struct FsPath {
  1304.     Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences.
  1305.  * If this is NULL, then this is a 
  1306.  * pure normalized, absolute path
  1307.  * object, in which the parent Tcl_Obj's
  1308.  * string rep is already both translated
  1309.  * and normalized. */
  1310.     Tcl_Obj *normPathPtr;       /* Normalized absolute path, without 
  1311.  * ., .. or ~user sequences. If the 
  1312.  * Tcl_Obj containing 
  1313.  * this FsPath is already normalized, 
  1314.  * this may be a circular reference back
  1315.  * to the container.  If that is NOT the
  1316.  * case, we have a refCount on the object. */
  1317.     Tcl_Obj *cwdPtr;            /* If null, path is absolute, else
  1318.  * this points to the cwd object used
  1319.  * for this path.  We have a refCount
  1320.  * on the object. */
  1321.     int flags;                  /* Flags to describe interpretation */
  1322.     ClientData nativePathPtr;   /* Native representation of this path,
  1323.  * which is filesystem dependent. */
  1324.     int filesystemEpoch;        /* Used to ensure the path representation
  1325.  * was generated during the correct
  1326.  * filesystem epoch.  The epoch changes
  1327.  * when filesystem-mounts are changed. */ 
  1328.     struct FilesystemRecord *fsRecPtr;
  1329. /* Pointer to the filesystem record 
  1330.  * entry to use for this path. */
  1331. } FsPath;
  1332. /* 
  1333.  * Define some macros to give us convenient access to path-object
  1334.  * specific fields.
  1335.  */
  1336. #define PATHOBJ(objPtr) (objPtr->internalRep.otherValuePtr)
  1337. #define PATHFLAGS(objPtr) 
  1338.  (((FsPath*)(objPtr->internalRep.otherValuePtr))->flags)
  1339. #define TCLPATH_APPENDED 1
  1340. #define TCLPATH_RELATIVE 2
  1341. /*
  1342.  *----------------------------------------------------------------------
  1343.  *
  1344.  * Tcl_FSGetPathType --
  1345.  *
  1346.  * Determines whether a given path is relative to the current
  1347.  * directory, relative to the current volume, or absolute.  
  1348.  *
  1349.  * Results:
  1350.  * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
  1351.  * TCL_PATH_VOLUME_RELATIVE.
  1352.  *
  1353.  * Side effects:
  1354.  * None.
  1355.  *
  1356.  *----------------------------------------------------------------------
  1357.  */
  1358. Tcl_PathType
  1359. Tcl_FSGetPathType(pathObjPtr)
  1360.     Tcl_Obj *pathObjPtr;
  1361. {
  1362.     return FSGetPathType(pathObjPtr, NULL, NULL);
  1363. }
  1364. /*
  1365.  *----------------------------------------------------------------------
  1366.  *
  1367.  * FSGetPathType --
  1368.  *
  1369.  * Determines whether a given path is relative to the current
  1370.  * directory, relative to the current volume, or absolute.  If the
  1371.  * caller wishes to know which filesystem claimed the path (in the
  1372.  * case for which the path is absolute), then a reference to a
  1373.  * filesystem pointer can be passed in (but passing NULL is
  1374.  * acceptable).
  1375.  *
  1376.  * Results:
  1377.  * Returns one of TCL_PATH_ABSOLUTE, TCL_PATH_RELATIVE, or
  1378.  * TCL_PATH_VOLUME_RELATIVE.  The filesystem reference will
  1379.  * be set if and only if it is non-NULL and the function's 
  1380.  * return value is TCL_PATH_ABSOLUTE.
  1381.  *
  1382.  * Side effects:
  1383.  * None.
  1384.  *
  1385.  *----------------------------------------------------------------------
  1386.  */
  1387. static Tcl_PathType
  1388. FSGetPathType(pathObjPtr, filesystemPtrPtr, driveNameLengthPtr)
  1389.     Tcl_Obj *pathObjPtr;
  1390.     Tcl_Filesystem **filesystemPtrPtr;
  1391.     int *driveNameLengthPtr;
  1392. {
  1393.     if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
  1394. return GetPathType(pathObjPtr, filesystemPtrPtr, 
  1395.    driveNameLengthPtr, NULL);
  1396.     } else {
  1397. FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  1398. if (fsPathPtr->cwdPtr != NULL) {
  1399.     if (PATHFLAGS(pathObjPtr) == 0) {
  1400. return TCL_PATH_RELATIVE;
  1401.     }
  1402.     return FSGetPathType(fsPathPtr->cwdPtr, filesystemPtrPtr, 
  1403.  driveNameLengthPtr);
  1404. } else {
  1405.     return GetPathType(pathObjPtr, filesystemPtrPtr, 
  1406.        driveNameLengthPtr, NULL);
  1407. }
  1408.     }
  1409. }
  1410. /*
  1411.  *---------------------------------------------------------------------------
  1412.  *
  1413.  * Tcl_FSJoinPath --
  1414.  *
  1415.  *      This function takes the given Tcl_Obj, which should be a valid
  1416.  *      list, and returns the path object given by considering the
  1417.  *      first 'elements' elements as valid path segments.  If elements < 0,
  1418.  *      we use the entire list.
  1419.  *      
  1420.  * Results:
  1421.  *      Returns object with refCount of zero, (or if non-zero, it has
  1422.  *      references elsewhere in Tcl).  Either way, the caller must
  1423.  *      increment its refCount before use.
  1424.  *
  1425.  * Side effects:
  1426.  * None.
  1427.  *
  1428.  *---------------------------------------------------------------------------
  1429.  */
  1430. Tcl_Obj* 
  1431. Tcl_FSJoinPath(listObj, elements)
  1432.     Tcl_Obj *listObj;
  1433.     int elements;
  1434. {
  1435.     Tcl_Obj *res;
  1436.     int i;
  1437.     Tcl_Filesystem *fsPtr = NULL;
  1438.     
  1439.     if (elements < 0) {
  1440. if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) {
  1441.     return NULL;
  1442. }
  1443.     } else {
  1444. /* Just make sure it is a valid list */
  1445. int listTest;
  1446. if (Tcl_ListObjLength(NULL, listObj, &listTest) != TCL_OK) {
  1447.     return NULL;
  1448. }
  1449. /* 
  1450.  * Correct this if it is too large, otherwise we will
  1451.  * waste our time joining null elements to the path 
  1452.  */
  1453. if (elements > listTest) {
  1454.     elements = listTest;
  1455. }
  1456.     }
  1457.     
  1458.     res = Tcl_NewObj();
  1459.     
  1460.     for (i = 0; i < elements; i++) {
  1461. Tcl_Obj *elt;
  1462. int driveNameLength;
  1463. Tcl_PathType type;
  1464. char *strElt;
  1465. int strEltLen;
  1466. int length;
  1467. char *ptr;
  1468. Tcl_Obj *driveName = NULL;
  1469. Tcl_ListObjIndex(NULL, listObj, i, &elt);
  1470. /* 
  1471.  * This is a special case where we can be much more
  1472.  * efficient, where we are joining a single relative path
  1473.  * onto an object that is already of path type.  The 
  1474.  * 'TclNewFSPathObj' call below creates an object which
  1475.  * can be normalized more efficiently.  Currently we only
  1476.  * use the special case when we have exactly two elements,
  1477.  * but we could expand that in the future.
  1478.  */
  1479. if ((i == (elements-2)) && (i == 0) && (elt->typePtr == &tclFsPathType)
  1480.   && !(elt->bytes != NULL && (elt->bytes[0] == ''))) {
  1481.     Tcl_Obj *tail;
  1482.     Tcl_PathType type;
  1483.     Tcl_ListObjIndex(NULL, listObj, i+1, &tail);
  1484.     type = GetPathType(tail, NULL, NULL, NULL);
  1485.     if (type == TCL_PATH_RELATIVE) {
  1486. CONST char *str;
  1487. int len;
  1488. str = Tcl_GetStringFromObj(tail,&len);
  1489. if (len == 0) {
  1490.     /* 
  1491.      * This happens if we try to handle the root volume
  1492.      * '/'.  There's no need to return a special path
  1493.      * object, when the base itself is just fine!
  1494.      */
  1495.     Tcl_DecrRefCount(res);
  1496.     return elt;
  1497. }
  1498. /* 
  1499.  * If it doesn't begin with '.'  and is a mac or unix
  1500.  * path or it a windows path without backslashes, then we
  1501.  * can be very efficient here.  (In fact even a windows
  1502.  * path with backslashes can be joined efficiently, but
  1503.  * the path object would not have forward slashes only,
  1504.  * and this would therefore contradict our 'file join'
  1505.  * documentation).
  1506.  */
  1507. if (str[0] != '.' && ((tclPlatform != TCL_PLATFORM_WINDOWS) 
  1508.       || (strchr(str, '\') == NULL))) {
  1509.     /* 
  1510.      * Finally, on Windows, 'file join' is defined to 
  1511.      * convert all backslashes to forward slashes,
  1512.      * so the base part cannot have backslashes either.
  1513.      */
  1514.     if ((tclPlatform != TCL_PLATFORM_WINDOWS)
  1515. || (strchr(Tcl_GetString(elt), '\') == NULL)) {
  1516. if (res != NULL) {
  1517.     TclDecrRefCount(res);
  1518. }
  1519. return TclNewFSPathObj(elt, str, len);
  1520.     }
  1521. }
  1522. /* 
  1523.  * Otherwise we don't have an easy join, and
  1524.  * we must let the more general code below handle
  1525.  * things
  1526.  */
  1527.     } else {
  1528. if (tclPlatform == TCL_PLATFORM_UNIX) {
  1529.     Tcl_DecrRefCount(res);
  1530.     return tail;
  1531. } else {
  1532.     CONST char *str;
  1533.     int len;
  1534.     str = Tcl_GetStringFromObj(tail,&len);
  1535.     if (tclPlatform == TCL_PLATFORM_WINDOWS) {
  1536. if (strchr(str, '\') == NULL) {
  1537.     Tcl_DecrRefCount(res);
  1538.     return tail;
  1539. }
  1540.     } else if (tclPlatform == TCL_PLATFORM_MAC) {
  1541. if (strchr(str, '/') == NULL) {
  1542.     Tcl_DecrRefCount(res);
  1543.     return tail;
  1544. }
  1545.     }
  1546. }
  1547.     }
  1548. }
  1549. strElt = Tcl_GetStringFromObj(elt, &strEltLen);
  1550. type = GetPathType(elt, &fsPtr, &driveNameLength, &driveName);
  1551. if (type != TCL_PATH_RELATIVE) {
  1552.     /* Zero out the current result */
  1553.     Tcl_DecrRefCount(res);
  1554.     if (driveName != NULL) {
  1555. res = Tcl_DuplicateObj(driveName);
  1556. Tcl_DecrRefCount(driveName);
  1557.     } else {
  1558. res = Tcl_NewStringObj(strElt, driveNameLength);
  1559.     }
  1560.     strElt += driveNameLength;
  1561. }
  1562. ptr = Tcl_GetStringFromObj(res, &length);
  1563. /* 
  1564.  * Strip off any './' before a tilde, unless this is the
  1565.  * beginning of the path.
  1566.  */
  1567. if (length > 0 && strEltLen > 0) {
  1568.     if ((strElt[0] == '.') && (strElt[1] == '/') 
  1569.       && (strElt[2] == '~')) {
  1570. strElt += 2;
  1571.     }
  1572. }
  1573. /* 
  1574.  * A NULL value for fsPtr at this stage basically means
  1575.  * we're trying to join a relative path onto something
  1576.  * which is also relative (or empty).  There's nothing
  1577.  * particularly wrong with that.
  1578.  */
  1579. if (*strElt == '') continue;
  1580. if (fsPtr == &tclNativeFilesystem || fsPtr == NULL) {
  1581.     TclpNativeJoinPath(res, strElt);
  1582. } else {
  1583.     char separator = '/';
  1584.     int needsSep = 0;
  1585.     
  1586.     if (fsPtr->filesystemSeparatorProc != NULL) {
  1587. Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(res);
  1588. if (sep != NULL) {
  1589.     separator = Tcl_GetString(sep)[0];
  1590. }
  1591.     }
  1592.     if (length > 0 && ptr[length -1] != '/') {
  1593. Tcl_AppendToObj(res, &separator, 1);
  1594. length++;
  1595.     }
  1596.     Tcl_SetObjLength(res, length + (int) strlen(strElt));
  1597.     
  1598.     ptr = Tcl_GetString(res) + length;
  1599.     for (; *strElt != ''; strElt++) {
  1600. if (*strElt == separator) {
  1601.     while (strElt[1] == separator) {
  1602. strElt++;
  1603.     }
  1604.     if (strElt[1] != '') {
  1605. if (needsSep) {
  1606.     *ptr++ = separator;
  1607. }
  1608.     }
  1609. } else {
  1610.     *ptr++ = *strElt;
  1611.     needsSep = 1;
  1612. }
  1613.     }
  1614.     length = ptr - Tcl_GetString(res);
  1615.     Tcl_SetObjLength(res, length);
  1616. }
  1617.     }
  1618.     return res;
  1619. }
  1620. /*
  1621.  *---------------------------------------------------------------------------
  1622.  *
  1623.  * Tcl_FSConvertToPathType --
  1624.  *
  1625.  *      This function tries to convert the given Tcl_Obj to a valid
  1626.  *      Tcl path type, taking account of the fact that the cwd may
  1627.  *      have changed even if this object is already supposedly of
  1628.  *      the correct type.
  1629.  *      
  1630.  *      The filename may begin with "~" (to indicate current user's
  1631.  *      home directory) or "~<user>" (to indicate any user's home
  1632.  *      directory).
  1633.  *
  1634.  * Results:
  1635.  *      Standard Tcl error code.
  1636.  *
  1637.  * Side effects:
  1638.  * The old representation may be freed, and new memory allocated.
  1639.  *
  1640.  *---------------------------------------------------------------------------
  1641.  */
  1642. int 
  1643. Tcl_FSConvertToPathType(interp, objPtr)
  1644.     Tcl_Interp *interp; /* Interpreter in which to store error
  1645.  * message (if necessary). */
  1646.     Tcl_Obj *objPtr; /* Object to convert to a valid, current
  1647.  * path type. */
  1648. {
  1649.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1650.     /* 
  1651.      * While it is bad practice to examine an object's type directly,
  1652.      * this is actually the best thing to do here.  The reason is that
  1653.      * if we are converting this object to FsPath type for the first
  1654.      * time, we don't need to worry whether the 'cwd' has changed.
  1655.      * On the other hand, if this object is already of FsPath type,
  1656.      * and is a relative path, we do have to worry about the cwd.
  1657.      * If the cwd has changed, we must recompute the path.
  1658.      */
  1659.     if (objPtr->typePtr == &tclFsPathType) {
  1660. FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
  1661. if (fsPathPtr->filesystemEpoch != tsdPtr->filesystemEpoch) {
  1662.     if (objPtr->bytes == NULL) {
  1663. UpdateStringOfFsPath(objPtr);
  1664.     }
  1665.     FreeFsPathInternalRep(objPtr);
  1666.     objPtr->typePtr = NULL;
  1667.     return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
  1668. }
  1669. return TCL_OK;
  1670.     } else {
  1671. return Tcl_ConvertToType(interp, objPtr, &tclFsPathType);
  1672.     }
  1673. }
  1674. /* 
  1675.  * Helper function for SetFsPathFromAny.  Returns position of first
  1676.  * directory delimiter in the path.
  1677.  */
  1678. static int
  1679. FindSplitPos(path, separator)
  1680.     char *path;
  1681.     char *separator;
  1682. {
  1683.     int count = 0;
  1684.     switch (tclPlatform) {
  1685. case TCL_PLATFORM_UNIX:
  1686. case TCL_PLATFORM_MAC:
  1687.     while (path[count] != 0) {
  1688. if (path[count] == *separator) {
  1689.     return count;
  1690. }
  1691. count++;
  1692.     }
  1693.     break;
  1694. case TCL_PLATFORM_WINDOWS:
  1695.     while (path[count] != 0) {
  1696. if (path[count] == *separator || path[count] == '\') {
  1697.     return count;
  1698. }
  1699. count++;
  1700.     }
  1701.     break;
  1702.     }
  1703.     return count;
  1704. }
  1705. /*
  1706.  *---------------------------------------------------------------------------
  1707.  *
  1708.  * TclNewFSPathObj --
  1709.  *
  1710.  *      Creates a path object whose string representation is 
  1711.  *      '[file join dirPtr addStrRep]', but does so in a way that
  1712.  *      allows for more efficient caching of normalized paths.
  1713.  *      
  1714.  * Assumptions:
  1715.  *      'dirPtr' must be an absolute path.  
  1716.  *      'len' may not be zero.
  1717.  *      
  1718.  * Results:
  1719.  *      The new Tcl object, with refCount zero.
  1720.  *
  1721.  * Side effects:
  1722.  * Memory is allocated.  'dirPtr' gets an additional refCount.
  1723.  *
  1724.  *---------------------------------------------------------------------------
  1725.  */
  1726. Tcl_Obj*
  1727. TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
  1728. {
  1729.     FsPath *fsPathPtr;
  1730.     Tcl_Obj *objPtr;
  1731.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1732.     
  1733.     objPtr = Tcl_NewObj();
  1734.     fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
  1735.     
  1736.     if (tclPlatform == TCL_PLATFORM_MAC) { 
  1737. /* 
  1738.  * Mac relative paths may begin with a directory separator ':'. 
  1739.  * If present, we need to skip this ':' because we assume that 
  1740.  * we can join dirPtr and addStrRep by concatenating them as 
  1741.  * strings (and we ensure that dirPtr is terminated by a ':'). 
  1742.  */ 
  1743. if (addStrRep[0] == ':') { 
  1744.     addStrRep++; 
  1745.     len--; 
  1746.     } 
  1747.     /* Setup the path */
  1748.     fsPathPtr->translatedPathPtr = NULL;
  1749.     fsPathPtr->normPathPtr = Tcl_NewStringObj(addStrRep, len);
  1750.     Tcl_IncrRefCount(fsPathPtr->normPathPtr);
  1751.     fsPathPtr->cwdPtr = dirPtr;
  1752.     Tcl_IncrRefCount(dirPtr);
  1753.     fsPathPtr->nativePathPtr = NULL;
  1754.     fsPathPtr->fsRecPtr = NULL;
  1755.     fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  1756.     PATHOBJ(objPtr) = (VOID *) fsPathPtr;
  1757.     PATHFLAGS(objPtr) = TCLPATH_RELATIVE | TCLPATH_APPENDED;
  1758.     objPtr->typePtr = &tclFsPathType;
  1759.     objPtr->bytes = NULL;
  1760.     objPtr->length = 0;
  1761.     return objPtr;
  1762. }
  1763. /*
  1764.  *---------------------------------------------------------------------------
  1765.  *
  1766.  * TclFSMakePathRelative --
  1767.  *
  1768.  *      Only for internal use.
  1769.  *      
  1770.  *      Takes a path and a directory, where we _assume_ both path and
  1771.  *      directory are absolute, normalized and that the path lies
  1772.  *      inside the directory.  Returns a Tcl_Obj representing filename 
  1773.  *      of the path relative to the directory.
  1774.  *      
  1775.  *      In the case where the resulting path would start with a '~', we
  1776.  *      take special care to return an ordinary string.  This means to
  1777.  *      use that path (and not have it interpreted as a user name),
  1778.  *      one must prepend './'.  This may seem strange, but that is how
  1779.  *      'glob' is currently defined.
  1780.  *      
  1781.  * Results:
  1782.  *      NULL on error, otherwise a valid object, typically with
  1783.  *      refCount of zero, which it is assumed the caller will
  1784.  *      increment.
  1785.  *
  1786.  * Side effects:
  1787.  * The old representation may be freed, and new memory allocated.
  1788.  *
  1789.  *---------------------------------------------------------------------------
  1790.  */
  1791. Tcl_Obj*
  1792. TclFSMakePathRelative(interp, objPtr, cwdPtr)
  1793.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  1794.     Tcl_Obj *objPtr; /* The object we have. */
  1795.     Tcl_Obj *cwdPtr; /* Make it relative to this. */
  1796. {
  1797.     int cwdLen, len;
  1798.     CONST char *tempStr;
  1799.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1800.     
  1801.     if (objPtr->typePtr == &tclFsPathType) {
  1802. FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
  1803. if (PATHFLAGS(objPtr) != 0 
  1804. && fsPathPtr->cwdPtr == cwdPtr) {
  1805.     objPtr = fsPathPtr->normPathPtr;
  1806.     /* Free old representation */
  1807.     if (objPtr->typePtr != NULL) {
  1808. if (objPtr->bytes == NULL) {
  1809.     if (objPtr->typePtr->updateStringProc == NULL) {
  1810. if (interp != NULL) {
  1811.     Tcl_ResetResult(interp);
  1812.     Tcl_AppendResult(interp, "can't find object",
  1813.      "string representation", (char *) NULL);
  1814. }
  1815. return NULL;
  1816.     }
  1817.     objPtr->typePtr->updateStringProc(objPtr);
  1818. }
  1819. if ((objPtr->typePtr->freeIntRepProc) != NULL) {
  1820.     (*objPtr->typePtr->freeIntRepProc)(objPtr);
  1821. }
  1822.     }
  1823.     /* Now objPtr is a string object */
  1824.     
  1825.     if (Tcl_GetString(objPtr)[0] == '~') {
  1826. /* 
  1827.  * If the first character of the path is a tilde,
  1828.  * we must just return the path as is, to agree
  1829.  * with the defined behaviour of 'glob'.
  1830.  */
  1831. return objPtr;
  1832.     }
  1833.     fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
  1834.     /* Circular reference, by design */
  1835.     fsPathPtr->translatedPathPtr = objPtr;
  1836.     fsPathPtr->normPathPtr = NULL;
  1837.     fsPathPtr->cwdPtr = cwdPtr;
  1838.     Tcl_IncrRefCount(cwdPtr);
  1839.     fsPathPtr->nativePathPtr = NULL;
  1840.     fsPathPtr->fsRecPtr = NULL;
  1841.     fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  1842.     PATHOBJ(objPtr) = (VOID *) fsPathPtr;
  1843.     PATHFLAGS(objPtr) = 0;
  1844.     objPtr->typePtr = &tclFsPathType;
  1845.     return objPtr;
  1846. }
  1847.     }
  1848.     /* 
  1849.      * We know the cwd is a normalised object which does
  1850.      * not end in a directory delimiter, unless the cwd
  1851.      * is the name of a volume, in which case it will
  1852.      * end in a delimiter!  We handle this situation here.
  1853.      * A better test than the '!= sep' might be to simply
  1854.      * check if 'cwd' is a root volume.
  1855.      * 
  1856.      * Note that if we get this wrong, we will strip off
  1857.      * either too much or too little below, leading to
  1858.      * wrong answers returned by glob.
  1859.      */
  1860.     tempStr = Tcl_GetStringFromObj(cwdPtr, &cwdLen);
  1861.     /* 
  1862.      * Should we perhaps use 'Tcl_FSPathSeparator'?
  1863.      * But then what about the Windows special case?
  1864.      * Perhaps we should just check if cwd is a root
  1865.      * volume.
  1866.      */
  1867.     switch (tclPlatform) {
  1868. case TCL_PLATFORM_UNIX:
  1869.     if (tempStr[cwdLen-1] != '/') {
  1870. cwdLen++;
  1871.     }
  1872.     break;
  1873. case TCL_PLATFORM_WINDOWS:
  1874.     if (tempStr[cwdLen-1] != '/' 
  1875.     && tempStr[cwdLen-1] != '\') {
  1876. cwdLen++;
  1877.     }
  1878.     break;
  1879. case TCL_PLATFORM_MAC:
  1880.     if (tempStr[cwdLen-1] != ':') {
  1881. cwdLen++;
  1882.     }
  1883.     break;
  1884.     }
  1885.     tempStr = Tcl_GetStringFromObj(objPtr, &len);
  1886.     return Tcl_NewStringObj(tempStr + cwdLen, len - cwdLen);
  1887. }
  1888. /*
  1889.  *---------------------------------------------------------------------------
  1890.  *
  1891.  * TclFSMakePathFromNormalized --
  1892.  *
  1893.  *      Like SetFsPathFromAny, but assumes the given object is an
  1894.  *      absolute normalized path. Only for internal use.
  1895.  *      
  1896.  * Results:
  1897.  *      Standard Tcl error code.
  1898.  *
  1899.  * Side effects:
  1900.  * The old representation may be freed, and new memory allocated.
  1901.  *
  1902.  *---------------------------------------------------------------------------
  1903.  */
  1904. int
  1905. TclFSMakePathFromNormalized(interp, objPtr, nativeRep)
  1906.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  1907.     Tcl_Obj *objPtr; /* The object to convert. */
  1908.     ClientData nativeRep; /* The native rep for the object, if known
  1909.  * else NULL. */
  1910. {
  1911.     FsPath *fsPathPtr;
  1912.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1913.     if (objPtr->typePtr == &tclFsPathType) {
  1914. return TCL_OK;
  1915.     }
  1916.     
  1917.     /* Free old representation */
  1918.     if (objPtr->typePtr != NULL) {
  1919. if (objPtr->bytes == NULL) {
  1920.     if (objPtr->typePtr->updateStringProc == NULL) {
  1921. if (interp != NULL) {
  1922.     Tcl_ResetResult(interp);
  1923.     Tcl_AppendResult(interp, "can't find object",
  1924.      "string representation", (char *) NULL);
  1925. }
  1926. return TCL_ERROR;
  1927.     }
  1928.     objPtr->typePtr->updateStringProc(objPtr);
  1929. }
  1930. if ((objPtr->typePtr->freeIntRepProc) != NULL) {
  1931.     (*objPtr->typePtr->freeIntRepProc)(objPtr);
  1932. }
  1933.     }
  1934.     fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
  1935.     /* It's a pure normalized absolute path */
  1936.     fsPathPtr->translatedPathPtr = NULL;
  1937.     fsPathPtr->normPathPtr = objPtr;
  1938.     fsPathPtr->cwdPtr = NULL;
  1939.     fsPathPtr->nativePathPtr = nativeRep;
  1940.     fsPathPtr->fsRecPtr = NULL;
  1941.     fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  1942.     PATHOBJ(objPtr) = (VOID *) fsPathPtr;
  1943.     PATHFLAGS(objPtr) = 0;
  1944.     objPtr->typePtr = &tclFsPathType;
  1945.     return TCL_OK;
  1946. }
  1947. /*
  1948.  *---------------------------------------------------------------------------
  1949.  *
  1950.  * Tcl_FSNewNativePath --
  1951.  *
  1952.  *      This function performs the something like that reverse of the 
  1953.  *      usual obj->path->nativerep conversions.  If some code retrieves
  1954.  *      a path in native form (from, e.g. readlink or a native dialog),
  1955.  *      and that path is to be used at the Tcl level, then calling
  1956.  *      this function is an efficient way of creating the appropriate
  1957.  *      path object type.
  1958.  *      
  1959.  *      Any memory which is allocated for 'clientData' should be retained
  1960.  *      until clientData is passed to the filesystem's freeInternalRepProc
  1961.  *      when it can be freed.  The built in platform-specific filesystems
  1962.  *      use 'ckalloc' to allocate clientData, and ckfree to free it.
  1963.  *
  1964.  * Results:
  1965.  *      NULL or a valid path object pointer, with refCount zero.
  1966.  *
  1967.  * Side effects:
  1968.  * New memory may be allocated.
  1969.  *
  1970.  *---------------------------------------------------------------------------
  1971.  */
  1972. Tcl_Obj *
  1973. Tcl_FSNewNativePath(fromFilesystem, clientData)
  1974.     Tcl_Filesystem* fromFilesystem;
  1975.     ClientData clientData;
  1976. {
  1977.     Tcl_Obj *objPtr;
  1978.     FsPath *fsPathPtr;
  1979.     FilesystemRecord *fsFromPtr;
  1980.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1981.     
  1982.     objPtr = TclFSInternalToNormalized(fromFilesystem, clientData, &fsFromPtr);
  1983.     if (objPtr == NULL) {
  1984. return NULL;
  1985.     }
  1986.     
  1987.     /* 
  1988.      * Free old representation; shouldn't normally be any,
  1989.      * but best to be safe. 
  1990.      */
  1991.     if (objPtr->typePtr != NULL) {
  1992. if (objPtr->bytes == NULL) {
  1993.     if (objPtr->typePtr->updateStringProc == NULL) {
  1994. return NULL;
  1995.     }
  1996.     objPtr->typePtr->updateStringProc(objPtr);
  1997. }
  1998. if ((objPtr->typePtr->freeIntRepProc) != NULL) {
  1999.     (*objPtr->typePtr->freeIntRepProc)(objPtr);
  2000. }
  2001.     }
  2002.     
  2003.     fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
  2004.     fsPathPtr->translatedPathPtr = NULL;
  2005.     /* Circular reference, by design */
  2006.     fsPathPtr->normPathPtr = objPtr;
  2007.     fsPathPtr->cwdPtr = NULL;
  2008.     fsPathPtr->nativePathPtr = clientData;
  2009.     fsPathPtr->fsRecPtr = fsFromPtr;
  2010.     fsPathPtr->fsRecPtr->fileRefCount++;
  2011.     fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  2012.     PATHOBJ(objPtr) = (VOID *) fsPathPtr;
  2013.     PATHFLAGS(objPtr) = 0;
  2014.     objPtr->typePtr = &tclFsPathType;
  2015.     return objPtr;
  2016. }
  2017. /*
  2018.  *---------------------------------------------------------------------------
  2019.  *
  2020.  * Tcl_FSGetTranslatedPath --
  2021.  *
  2022.  *      This function attempts to extract the translated path
  2023.  *      from the given Tcl_Obj.  If the translation succeeds (i.e. the
  2024.  *      object is a valid path), then it is returned.  Otherwise NULL
  2025.  *      will be returned, and an error message may be left in the
  2026.  *      interpreter (if it is non-NULL)
  2027.  *
  2028.  * Results:
  2029.  *      NULL or a valid Tcl_Obj pointer.
  2030.  *
  2031.  * Side effects:
  2032.  * Only those of 'Tcl_FSConvertToPathType'
  2033.  *
  2034.  *---------------------------------------------------------------------------
  2035.  */
  2036. Tcl_Obj* 
  2037. Tcl_FSGetTranslatedPath(interp, pathPtr)
  2038.     Tcl_Interp *interp;
  2039.     Tcl_Obj* pathPtr;
  2040. {
  2041.     Tcl_Obj *retObj = NULL;
  2042.     FsPath *srcFsPathPtr;
  2043.     if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
  2044. return NULL;
  2045.     }
  2046.     srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
  2047.     if (srcFsPathPtr->translatedPathPtr == NULL) {
  2048. if (PATHFLAGS(pathPtr) != 0) {
  2049.     retObj = Tcl_FSGetNormalizedPath(interp, pathPtr);
  2050. } else {
  2051.     /* 
  2052.      * It is a pure absolute, normalized path object.
  2053.      * This is something like being a 'pure list'.  The
  2054.      * object's string, translatedPath and normalizedPath
  2055.      * are all identical.
  2056.      */
  2057.     retObj = srcFsPathPtr->normPathPtr;
  2058. }
  2059.     } else {
  2060. /* It is an ordinary path object */
  2061. retObj = srcFsPathPtr->translatedPathPtr;
  2062.     }
  2063.     if (retObj) {
  2064. Tcl_IncrRefCount(retObj);
  2065.     }
  2066.     return retObj;
  2067. }
  2068. /*
  2069.  *---------------------------------------------------------------------------
  2070.  *
  2071.  * Tcl_FSGetTranslatedStringPath --
  2072.  *
  2073.  *      This function attempts to extract the translated path
  2074.  *      from the given Tcl_Obj.  If the translation succeeds (i.e. the
  2075.  *      object is a valid path), then the path is returned.  Otherwise NULL
  2076.  *      will be returned, and an error message may be left in the
  2077.  *      interpreter (if it is non-NULL)
  2078.  *
  2079.  * Results:
  2080.  *      NULL or a valid string.
  2081.  *
  2082.  * Side effects:
  2083.  * Only those of 'Tcl_FSConvertToPathType'
  2084.  *
  2085.  *---------------------------------------------------------------------------
  2086.  */
  2087. CONST char*
  2088. Tcl_FSGetTranslatedStringPath(interp, pathPtr)
  2089.     Tcl_Interp *interp;
  2090.     Tcl_Obj* pathPtr;
  2091. {
  2092.     Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
  2093.     if (transPtr != NULL) {
  2094. int len;
  2095. CONST char *result, *orig;
  2096. orig = Tcl_GetStringFromObj(transPtr, &len);
  2097. result = (char*) ckalloc((unsigned)(len+1));
  2098. memcpy((VOID*) result, (VOID*) orig, (size_t) (len+1));
  2099. Tcl_DecrRefCount(transPtr);
  2100. return result;
  2101.     }
  2102.     return NULL;
  2103. }
  2104. /*
  2105.  *---------------------------------------------------------------------------
  2106.  *
  2107.  * Tcl_FSGetNormalizedPath --
  2108.  *
  2109.  *      This important function attempts to extract from the given Tcl_Obj
  2110.  *      a unique normalised path representation, whose string value can
  2111.  *      be used as a unique identifier for the file.
  2112.  *
  2113.  * Results:
  2114.  *      NULL or a valid path object pointer.
  2115.  *
  2116.  * Side effects:
  2117.  * New memory may be allocated.  The Tcl 'errno' may be modified
  2118.  *      in the process of trying to examine various path possibilities.
  2119.  *
  2120.  *---------------------------------------------------------------------------
  2121.  */
  2122. Tcl_Obj* 
  2123. Tcl_FSGetNormalizedPath(interp, pathObjPtr)
  2124.     Tcl_Interp *interp;
  2125.     Tcl_Obj* pathObjPtr;
  2126. {
  2127.     FsPath *fsPathPtr;
  2128.     if (Tcl_FSConvertToPathType(interp, pathObjPtr) != TCL_OK) {
  2129. return NULL;
  2130.     }
  2131.     fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2132.     if (PATHFLAGS(pathObjPtr) != 0) {
  2133. /* 
  2134.  * This is a special path object which is the result of
  2135.  * something like 'file join' 
  2136.  */
  2137. Tcl_Obj *dir, *copy;
  2138. int cwdLen;
  2139. int pathType;
  2140. CONST char *cwdStr;
  2141. ClientData clientData = NULL;
  2142. pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
  2143. dir = Tcl_FSGetNormalizedPath(interp, fsPathPtr->cwdPtr);
  2144. if (dir == NULL) {
  2145.     return NULL;
  2146. }
  2147. if (pathObjPtr->bytes == NULL) {
  2148.     UpdateStringOfFsPath(pathObjPtr);
  2149. }
  2150. copy = Tcl_DuplicateObj(dir);
  2151. Tcl_IncrRefCount(copy);
  2152. Tcl_IncrRefCount(dir);
  2153. /* We now own a reference on both 'dir' and 'copy' */
  2154. cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
  2155. /* 
  2156.  * Should we perhaps use 'Tcl_FSPathSeparator'?
  2157.  * But then what about the Windows special case?
  2158.  * Perhaps we should just check if cwd is a root volume.
  2159.  * We should never get cwdLen == 0 in this code path.
  2160.  */
  2161. switch (tclPlatform) {
  2162.     case TCL_PLATFORM_UNIX:
  2163. if (cwdStr[cwdLen-1] != '/') {
  2164.     Tcl_AppendToObj(copy, "/", 1);
  2165.     cwdLen++;
  2166. }
  2167. break;
  2168.     case TCL_PLATFORM_WINDOWS:
  2169. if (cwdStr[cwdLen-1] != '/' 
  2170. && cwdStr[cwdLen-1] != '\') {
  2171.     Tcl_AppendToObj(copy, "/", 1);
  2172.     cwdLen++;
  2173. }
  2174. break;
  2175.     case TCL_PLATFORM_MAC:
  2176. if (cwdStr[cwdLen-1] != ':') {
  2177.     Tcl_AppendToObj(copy, ":", 1);
  2178.     cwdLen++;
  2179. }
  2180. break;
  2181. }
  2182. Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
  2183. /* 
  2184.  * Normalize the combined string, but only starting after
  2185.  * the end of the previously normalized 'dir'.  This should
  2186.  * be much faster!  We use 'cwdLen-1' so that we are
  2187.  * already pointing at the dir-separator that we know about.
  2188.  * The normalization code will actually start off directly
  2189.  * after that separator.
  2190.  */
  2191. TclFSNormalizeToUniquePath(interp, copy, cwdLen-1, 
  2192.   (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
  2193. /* Now we need to construct the new path object */
  2194. if (pathType == TCL_PATH_RELATIVE) {
  2195.     FsPath* origDirFsPathPtr;
  2196.     Tcl_Obj *origDir = fsPathPtr->cwdPtr;
  2197.     origDirFsPathPtr = (FsPath*) PATHOBJ(origDir);
  2198.     
  2199.     fsPathPtr->cwdPtr = origDirFsPathPtr->cwdPtr;
  2200.     Tcl_IncrRefCount(fsPathPtr->cwdPtr);
  2201.     
  2202.     Tcl_DecrRefCount(fsPathPtr->normPathPtr);
  2203.     fsPathPtr->normPathPtr = copy;
  2204.     /* That's our reference to copy used */
  2205.     Tcl_DecrRefCount(dir);
  2206.     Tcl_DecrRefCount(origDir);
  2207. } else {
  2208.     Tcl_DecrRefCount(fsPathPtr->cwdPtr);
  2209.     fsPathPtr->cwdPtr = NULL;
  2210.     Tcl_DecrRefCount(fsPathPtr->normPathPtr);
  2211.     fsPathPtr->normPathPtr = copy;
  2212.     /* That's our reference to copy used */
  2213.     Tcl_DecrRefCount(dir);
  2214. }
  2215. if (clientData != NULL) {
  2216.     fsPathPtr->nativePathPtr = clientData;
  2217. }
  2218. PATHFLAGS(pathObjPtr) = 0;
  2219.     }
  2220.     /* Ensure cwd hasn't changed */
  2221.     if (fsPathPtr->cwdPtr != NULL) {
  2222. if (!TclFSCwdPointerEquals(fsPathPtr->cwdPtr)) {
  2223.     if (pathObjPtr->bytes == NULL) {
  2224. UpdateStringOfFsPath(pathObjPtr);
  2225.     }
  2226.     FreeFsPathInternalRep(pathObjPtr);
  2227.     pathObjPtr->typePtr = NULL;
  2228.     if (Tcl_ConvertToType(interp, pathObjPtr, 
  2229.   &tclFsPathType) != TCL_OK) {
  2230. return NULL;
  2231.     }
  2232.     fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2233. } else if (fsPathPtr->normPathPtr == NULL) {
  2234.     int cwdLen;
  2235.     Tcl_Obj *copy;
  2236.     CONST char *cwdStr;
  2237.     ClientData clientData = NULL;
  2238.     
  2239.     copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
  2240.     Tcl_IncrRefCount(copy);
  2241.     cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
  2242.     /* 
  2243.      * Should we perhaps use 'Tcl_FSPathSeparator'?
  2244.      * But then what about the Windows special case?
  2245.      * Perhaps we should just check if cwd is a root volume.
  2246.      * We should never get cwdLen == 0 in this code path.
  2247.      */
  2248.     switch (tclPlatform) {
  2249. case TCL_PLATFORM_UNIX:
  2250.     if (cwdStr[cwdLen-1] != '/') {
  2251. Tcl_AppendToObj(copy, "/", 1);
  2252. cwdLen++;
  2253.     }
  2254.     break;
  2255. case TCL_PLATFORM_WINDOWS:
  2256.     if (cwdStr[cwdLen-1] != '/' 
  2257.     && cwdStr[cwdLen-1] != '\') {
  2258. Tcl_AppendToObj(copy, "/", 1);
  2259. cwdLen++;
  2260.     }
  2261.     break;
  2262. case TCL_PLATFORM_MAC:
  2263.     if (cwdStr[cwdLen-1] != ':') {
  2264. Tcl_AppendToObj(copy, ":", 1);
  2265. cwdLen++;
  2266.     }
  2267.     break;
  2268.     }
  2269.     Tcl_AppendObjToObj(copy, pathObjPtr);
  2270.     /* 
  2271.      * Normalize the combined string, but only starting after
  2272.      * the end of the previously normalized 'dir'.  This should
  2273.      * be much faster!
  2274.      */
  2275.     TclFSNormalizeToUniquePath(interp, copy, cwdLen-1, 
  2276.       (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
  2277.     fsPathPtr->normPathPtr = copy;
  2278.     if (clientData != NULL) {
  2279. fsPathPtr->nativePathPtr = clientData;
  2280.     }
  2281. }
  2282.     }
  2283.     if (fsPathPtr->normPathPtr == NULL) {
  2284. ClientData clientData = NULL;
  2285. Tcl_Obj *useThisCwd = NULL;
  2286. /* 
  2287.  * Since normPathPtr is NULL, but this is a valid path
  2288.  * object, we know that the translatedPathPtr cannot be NULL.
  2289.  */
  2290. Tcl_Obj *absolutePath = fsPathPtr->translatedPathPtr;
  2291. char *path = Tcl_GetString(absolutePath);
  2292. /* 
  2293.  * We have to be a little bit careful here to avoid infinite loops
  2294.  * we're asking Tcl_FSGetPathType to return the path's type, but
  2295.  * that call can actually result in a lot of other filesystem
  2296.  * action, which might loop back through here.
  2297.  */
  2298. if (path[0] != '') {
  2299.     Tcl_PathType type = Tcl_FSGetPathType(pathObjPtr);
  2300.     if (type == TCL_PATH_RELATIVE) {
  2301. useThisCwd = Tcl_FSGetCwd(interp);
  2302. if (useThisCwd == NULL) return NULL;
  2303. absolutePath = Tcl_FSJoinToPath(useThisCwd, 1, &absolutePath);
  2304. Tcl_IncrRefCount(absolutePath);
  2305. /* We have a refCount on the cwd */
  2306. #ifdef __WIN32__
  2307.     } else if (type == TCL_PATH_VOLUME_RELATIVE) {
  2308. /* 
  2309.  * Only Windows has volume-relative paths.  These
  2310.  * paths are rather rare, but is is nice if Tcl can
  2311.  * handle them.  It is much better if we can
  2312.  * handle them here, rather than in the native fs code,
  2313.  * because we really need to have a real absolute path
  2314.  * just below.
  2315.  * 
  2316.  * We do not let this block compile on non-Windows
  2317.  * platforms because the test suite's manual forcing
  2318.  * of tclPlatform can otherwise cause this code path
  2319.  * to be executed, causing various errors because
  2320.  * volume-relative paths really do not exist.
  2321.  */
  2322. useThisCwd = Tcl_FSGetCwd(interp);
  2323. if (useThisCwd == NULL) return NULL;
  2324. if (path[0] == '/') {
  2325.     /* 
  2326.      * Path of form /foo/bar which is a path in the
  2327.      * root directory of the current volume.
  2328.      */
  2329.     CONST char *drive = Tcl_GetString(useThisCwd);
  2330.     absolutePath = Tcl_NewStringObj(drive,2);
  2331.     Tcl_AppendToObj(absolutePath, path, -1);
  2332.     Tcl_IncrRefCount(absolutePath);
  2333.     /* We have a refCount on the cwd */
  2334. } else {
  2335.     /* 
  2336.      * Path of form C:foo/bar, but this only makes
  2337.      * sense if the cwd is also on drive C.
  2338.      */
  2339.     CONST char *drive = Tcl_GetString(useThisCwd);
  2340.     char drive_c = path[0];
  2341.     if (drive_c >= 'a') {
  2342. drive_c -= ('a' - 'A');
  2343.     }
  2344.     if (drive[0] == drive_c) {
  2345. absolutePath = Tcl_DuplicateObj(useThisCwd);
  2346. /* We have a refCount on the cwd */
  2347.     } else {
  2348. Tcl_DecrRefCount(useThisCwd);
  2349. useThisCwd = NULL;
  2350. /* 
  2351.  * The path is not in the current drive, but
  2352.  * is volume-relative.  The way Tcl 8.3 handles
  2353.  * this is that it treats such a path as
  2354.  * relative to the root of the drive.  We
  2355.  * therefore behave the same here.
  2356.  */
  2357. absolutePath = Tcl_NewStringObj(path, 2);
  2358.     }
  2359.     Tcl_IncrRefCount(absolutePath);
  2360.     Tcl_AppendToObj(absolutePath, "/", 1);
  2361.     Tcl_AppendToObj(absolutePath, path+2, -1);
  2362. }
  2363. #endif /* __WIN32__ */
  2364.     }
  2365. }
  2366. /* Already has refCount incremented */
  2367. fsPathPtr->normPathPtr = TclFSNormalizeAbsolutePath(interp, absolutePath, 
  2368.        (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
  2369. if (0 && (clientData != NULL)) {
  2370.     fsPathPtr->nativePathPtr = 
  2371.       (*fsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc)(clientData);
  2372. }
  2373. if (!strcmp(Tcl_GetString(fsPathPtr->normPathPtr),
  2374.     Tcl_GetString(pathObjPtr))) {
  2375.     /* 
  2376.      * The path was already normalized.  
  2377.      * Get rid of the duplicate.
  2378.      */
  2379.     Tcl_DecrRefCount(fsPathPtr->normPathPtr);
  2380.     /* 
  2381.      * We do *not* increment the refCount for 
  2382.      * this circular reference 
  2383.      */
  2384.     fsPathPtr->normPathPtr = pathObjPtr;
  2385. }
  2386. if (useThisCwd != NULL) {
  2387.     /* This was returned by Tcl_FSJoinToPath above */
  2388.     Tcl_DecrRefCount(absolutePath);
  2389.     fsPathPtr->cwdPtr = useThisCwd;
  2390. }
  2391.     }
  2392.     return fsPathPtr->normPathPtr;
  2393. }
  2394. /*
  2395.  *---------------------------------------------------------------------------
  2396.  *
  2397.  * Tcl_FSGetInternalRep --
  2398.  *
  2399.  *      Extract the internal representation of a given path object,
  2400.  *      in the given filesystem.  If the path object belongs to a
  2401.  *      different filesystem, we return NULL.
  2402.  *      
  2403.  *      If the internal representation is currently NULL, we attempt
  2404.  *      to generate it, by calling the filesystem's 
  2405.  *      'Tcl_FSCreateInternalRepProc'.
  2406.  *
  2407.  * Results:
  2408.  *      NULL or a valid internal representation.
  2409.  *
  2410.  * Side effects:
  2411.  * An attempt may be made to convert the object.
  2412.  *
  2413.  *---------------------------------------------------------------------------
  2414.  */
  2415. ClientData 
  2416. Tcl_FSGetInternalRep(pathObjPtr, fsPtr)
  2417.     Tcl_Obj* pathObjPtr;
  2418.     Tcl_Filesystem *fsPtr;
  2419. {
  2420.     FsPath *srcFsPathPtr;
  2421.     
  2422.     if (Tcl_FSConvertToPathType(NULL, pathObjPtr) != TCL_OK) {
  2423. return NULL;
  2424.     }
  2425.     srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2426.     
  2427.     /* 
  2428.      * We will only return the native representation for the caller's
  2429.      * filesystem.  Otherwise we will simply return NULL. This means
  2430.      * that there must be a unique bi-directional mapping between paths
  2431.      * and filesystems, and that this mapping will not allow 'remapped'
  2432.      * files -- files which are in one filesystem but mapped into
  2433.      * another.  Another way of putting this is that 'stacked'
  2434.      * filesystems are not allowed.  We recognise that this is a
  2435.      * potentially useful feature for the future.
  2436.      * 
  2437.      * Even something simple like a 'pass through' filesystem which
  2438.      * logs all activity and passes the calls onto the native system
  2439.      * would be nice, but not easily achievable with the current
  2440.      * implementation.
  2441.      */
  2442.     if (srcFsPathPtr->fsRecPtr == NULL) {
  2443. /* 
  2444.  * This only usually happens in wrappers like TclpStat which
  2445.  * create a string object and pass it to TclpObjStat.  Code
  2446.  * which calls the Tcl_FS..  functions should always have a
  2447.  * filesystem already set.  Whether this code path is legal or
  2448.  * not depends on whether we decide to allow external code to
  2449.  * call the native filesystem directly.  It is at least safer
  2450.  * to allow this sub-optimal routing.
  2451.  */
  2452. Tcl_FSGetFileSystemForPath(pathObjPtr);
  2453. /* 
  2454.  * If we fail through here, then the path is probably not a
  2455.  * valid path in the filesystsem, and is most likely to be a
  2456.  * use of the empty path "" via a direct call to one of the
  2457.  * objectified interfaces (e.g. from the Tcl testsuite).
  2458.  */
  2459. srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2460. if (srcFsPathPtr->fsRecPtr == NULL) {
  2461.     return NULL;
  2462. }
  2463.     }
  2464.     if (fsPtr != srcFsPathPtr->fsRecPtr->fsPtr) {
  2465. /* 
  2466.  * There is still one possibility we should consider; if the
  2467.  * file belongs to a different filesystem, perhaps it is
  2468.  * actually linked through to a file in our own filesystem
  2469.  * which we do care about.  The way we can check for this
  2470.  * is we ask what filesystem this path belongs to.
  2471.  */
  2472. Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathObjPtr);
  2473. if (actualFs == fsPtr) {
  2474.     return Tcl_FSGetInternalRep(pathObjPtr, fsPtr);
  2475. }
  2476. return NULL;
  2477.     }
  2478.     if (srcFsPathPtr->nativePathPtr == NULL) {
  2479. Tcl_FSCreateInternalRepProc *proc;
  2480. char *nativePathPtr;
  2481. proc = srcFsPathPtr->fsRecPtr->fsPtr->createInternalRepProc;
  2482. if (proc == NULL) {
  2483.     return NULL;
  2484. }
  2485. nativePathPtr = (*proc)(pathObjPtr);
  2486. srcFsPathPtr  = (FsPath*) PATHOBJ(pathObjPtr);
  2487. srcFsPathPtr->nativePathPtr = nativePathPtr;
  2488.     }
  2489.     return srcFsPathPtr->nativePathPtr;
  2490. }
  2491. /*
  2492.  *---------------------------------------------------------------------------
  2493.  *
  2494.  * TclFSEnsureEpochOk --
  2495.  *
  2496.  *      This will ensure the pathObjPtr is up to date and can be
  2497.  *      converted into a "path" type, and that we are able to generate a
  2498.  *      complete normalized path which is used to determine the
  2499.  *      filesystem match.
  2500.  *
  2501.  * Results:
  2502.  *      Standard Tcl return code.
  2503.  *
  2504.  * Side effects:
  2505.  * An attempt may be made to convert the object.
  2506.  *
  2507.  *---------------------------------------------------------------------------
  2508.  */
  2509. int 
  2510. TclFSEnsureEpochOk(pathObjPtr, fsPtrPtr)
  2511.     Tcl_Obj* pathObjPtr;
  2512.     Tcl_Filesystem **fsPtrPtr;
  2513. {
  2514.     FsPath *srcFsPathPtr;
  2515.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2516.     /* 
  2517.      * SHOULD BE ABLE TO IMPROVE EFFICIENCY HERE.
  2518.      */
  2519.     if (Tcl_FSGetNormalizedPath(NULL, pathObjPtr) == NULL) {
  2520. return TCL_ERROR;
  2521.     }
  2522.     srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2523.     /* 
  2524.      * Check if the filesystem has changed in some way since
  2525.      * this object's internal representation was calculated.
  2526.      */
  2527.     if (srcFsPathPtr->filesystemEpoch != tsdPtr->filesystemEpoch) {
  2528. /* 
  2529.  * We have to discard the stale representation and 
  2530.  * recalculate it 
  2531.  */
  2532. if (pathObjPtr->bytes == NULL) {
  2533.     UpdateStringOfFsPath(pathObjPtr);
  2534. }
  2535. FreeFsPathInternalRep(pathObjPtr);
  2536. pathObjPtr->typePtr = NULL;
  2537. if (SetFsPathFromAny(NULL, pathObjPtr) != TCL_OK) {
  2538.     return TCL_ERROR;
  2539. }
  2540. srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2541.     }
  2542.     /* Check whether the object is already assigned to a fs */
  2543.     if (srcFsPathPtr->fsRecPtr != NULL) {
  2544. *fsPtrPtr = srcFsPathPtr->fsRecPtr->fsPtr;
  2545.     }
  2546.     return TCL_OK;
  2547. }
  2548. void 
  2549. TclFSSetPathDetails(pathObjPtr, fsRecPtr, clientData) 
  2550.     Tcl_Obj *pathObjPtr;
  2551.     FilesystemRecord *fsRecPtr;
  2552.     ClientData clientData;
  2553. {
  2554.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2555.     /* We assume pathObjPtr is already of the correct type */
  2556.     FsPath *srcFsPathPtr;
  2557.     
  2558.     srcFsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2559.     srcFsPathPtr->fsRecPtr = fsRecPtr;
  2560.     srcFsPathPtr->nativePathPtr = clientData;
  2561.     srcFsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  2562.     fsRecPtr->fileRefCount++;
  2563. }
  2564. /*
  2565.  *---------------------------------------------------------------------------
  2566.  *
  2567.  * Tcl_FSEqualPaths --
  2568.  *
  2569.  *      This function tests whether the two paths given are equal path
  2570.  *      objects.  If either or both is NULL, 0 is always returned.
  2571.  *
  2572.  * Results:
  2573.  *      1 or 0.
  2574.  *
  2575.  * Side effects:
  2576.  * None.
  2577.  *
  2578.  *---------------------------------------------------------------------------
  2579.  */
  2580. int 
  2581. Tcl_FSEqualPaths(firstPtr, secondPtr)
  2582.     Tcl_Obj* firstPtr;
  2583.     Tcl_Obj* secondPtr;
  2584. {
  2585.     if (firstPtr == secondPtr) {
  2586. return 1;
  2587.     } else {
  2588. char *firstStr, *secondStr;
  2589. int firstLen, secondLen, tempErrno;
  2590. if (firstPtr == NULL || secondPtr == NULL) {
  2591.     return 0;
  2592. }
  2593. firstStr  = Tcl_GetStringFromObj(firstPtr, &firstLen);
  2594. secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
  2595. if ((firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0)) {
  2596.     return 1;
  2597. }
  2598. /* 
  2599.  * Try the most thorough, correct method of comparing fully
  2600.  * normalized paths
  2601.  */
  2602. tempErrno = Tcl_GetErrno();
  2603. firstPtr = Tcl_FSGetNormalizedPath(NULL, firstPtr);
  2604. secondPtr = Tcl_FSGetNormalizedPath(NULL, secondPtr);
  2605. Tcl_SetErrno(tempErrno);
  2606. if (firstPtr == NULL || secondPtr == NULL) {
  2607.     return 0;
  2608. }
  2609. firstStr  = Tcl_GetStringFromObj(firstPtr, &firstLen);
  2610. secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
  2611. if ((firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0)) {
  2612.     return 1;
  2613. }
  2614.     }
  2615.     return 0;
  2616. }
  2617. /*
  2618.  *---------------------------------------------------------------------------
  2619.  *
  2620.  * SetFsPathFromAny --
  2621.  *
  2622.  *      This function tries to convert the given Tcl_Obj to a valid
  2623.  *      Tcl path type.
  2624.  *      
  2625.  *      The filename may begin with "~" (to indicate current user's
  2626.  *      home directory) or "~<user>" (to indicate any user's home
  2627.  *      directory).
  2628.  *
  2629.  * Results:
  2630.  *      Standard Tcl error code.
  2631.  *
  2632.  * Side effects:
  2633.  * The old representation may be freed, and new memory allocated.
  2634.  *
  2635.  *---------------------------------------------------------------------------
  2636.  */
  2637. static int
  2638. SetFsPathFromAny(interp, objPtr)
  2639.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  2640.     Tcl_Obj *objPtr; /* The object to convert. */
  2641. {
  2642.     int len;
  2643.     FsPath *fsPathPtr;
  2644.     Tcl_Obj *transPtr;
  2645.     char *name;
  2646.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2647.     
  2648.     if (objPtr->typePtr == &tclFsPathType) {
  2649. return TCL_OK;
  2650.     }
  2651.     
  2652.     /* 
  2653.      * First step is to translate the filename.  This is similar to
  2654.      * Tcl_TranslateFilename, but shouldn't convert everything to
  2655.      * windows backslashes on that platform.  The current
  2656.      * implementation of this piece is a slightly optimised version
  2657.      * of the various Tilde/Split/Join stuff to avoid multiple
  2658.      * split/join operations.
  2659.      * 
  2660.      * We remove any trailing directory separator.
  2661.      * 
  2662.      * However, the split/join routines are quite complex, and
  2663.      * one has to make sure not to break anything on Unix, Win
  2664.      * or MacOS (fCmd.test, fileName.test and cmdAH.test exercise
  2665.      * most of the code).
  2666.      */
  2667.     name = Tcl_GetStringFromObj(objPtr,&len);
  2668.     /*
  2669.      * Handle tilde substitutions, if needed.
  2670.      */
  2671.     if (name[0] == '~') {
  2672. char *expandedUser;
  2673. Tcl_DString temp;
  2674. int split;
  2675. char separator='/';
  2676. if (tclPlatform==TCL_PLATFORM_MAC) {
  2677.     if (strchr(name, ':') != NULL) separator = ':';
  2678. }
  2679. split = FindSplitPos(name, &separator);
  2680. if (split != len) {
  2681.     /* We have multiple pieces '~user/foo/bar...' */
  2682.     name[split] = '';
  2683. }
  2684. /* Do some tilde substitution */
  2685. if (name[1] == '') {
  2686.     /* We have just '~' */
  2687.     CONST char *dir;
  2688.     Tcl_DString dirString;
  2689.     if (split != len) { name[split] = separator; }
  2690.     
  2691.     dir = TclGetEnv("HOME", &dirString);
  2692.     if (dir == NULL) {
  2693. if (interp) {
  2694.     Tcl_ResetResult(interp);
  2695.     Tcl_AppendResult(interp, "couldn't find HOME environment ",
  2696.     "variable to expand path", (char *) NULL);
  2697. }
  2698. return TCL_ERROR;
  2699.     }
  2700.     Tcl_DStringInit(&temp);
  2701.     Tcl_JoinPath(1, &dir, &temp);
  2702.     Tcl_DStringFree(&dirString);
  2703. } else {
  2704.     /* We have a user name '~user' */
  2705.     Tcl_DStringInit(&temp);
  2706.     if (TclpGetUserHome(name+1, &temp) == NULL) {
  2707. if (interp != NULL) {
  2708.     Tcl_ResetResult(interp);
  2709.     Tcl_AppendResult(interp, "user "", (name+1), 
  2710.      "" doesn't exist", (char *) NULL);
  2711. }
  2712. Tcl_DStringFree(&temp);
  2713. if (split != len) { name[split] = separator; }
  2714. return TCL_ERROR;
  2715.     }
  2716.     if (split != len) { name[split] = separator; }
  2717. }
  2718. expandedUser = Tcl_DStringValue(&temp);
  2719. transPtr = Tcl_NewStringObj(expandedUser, Tcl_DStringLength(&temp));
  2720. if (split != len) {
  2721.     /* Join up the tilde substitution with the rest */
  2722.     if (name[split+1] == separator) {
  2723. /*
  2724.  * Somewhat tricky case like ~//foo/bar.
  2725.  * Make use of Split/Join machinery to get it right.
  2726.  * Assumes all paths beginning with ~ are part of the
  2727.  * native filesystem.
  2728.  */
  2729. int objc;
  2730. Tcl_Obj **objv;
  2731. Tcl_Obj *parts = TclpNativeSplitPath(objPtr, NULL);
  2732. Tcl_ListObjGetElements(NULL, parts, &objc, &objv);
  2733. /* Skip '~'.  It's replaced by its expansion */
  2734. objc--; objv++;
  2735. while (objc--) {
  2736.     TclpNativeJoinPath(transPtr, Tcl_GetString(*objv++));
  2737. }
  2738. Tcl_DecrRefCount(parts);
  2739.     } else {
  2740. /* Simple case. "rest" is relative path.  Just join it. */
  2741. Tcl_Obj *rest = Tcl_NewStringObj(name+split+1,-1);
  2742. transPtr = Tcl_FSJoinToPath(transPtr, 1, &rest);
  2743.     }
  2744. }
  2745. Tcl_DStringFree(&temp);
  2746.     } else {
  2747. transPtr = Tcl_FSJoinToPath(objPtr,0,NULL);
  2748.     }
  2749. #if defined(__CYGWIN__) && defined(__WIN32__)
  2750.     {
  2751.     extern int cygwin_conv_to_win32_path 
  2752. _ANSI_ARGS_((CONST char *, char *));
  2753.     char winbuf[MAX_PATH+1];
  2754.     /*
  2755.      * In the Cygwin world, call conv_to_win32_path in order to use the
  2756.      * mount table to translate the file name into something Windows will
  2757.      * understand.  Take care when converting empty strings!
  2758.      */
  2759.     name = Tcl_GetStringFromObj(transPtr, &len);
  2760.     if (len > 0) {
  2761. cygwin_conv_to_win32_path(name, winbuf);
  2762. TclWinNoBackslash(winbuf);
  2763. Tcl_SetStringObj(transPtr, winbuf, -1);
  2764.     }
  2765.     }
  2766. #endif /* __CYGWIN__ && __WIN32__ */
  2767.     /* 
  2768.      * Now we have a translated filename in 'transPtr'.  This will have
  2769.      * forward slashes on Windows, and will not contain any ~user
  2770.      * sequences.
  2771.      */
  2772.     
  2773.     fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
  2774.     fsPathPtr->translatedPathPtr = transPtr;
  2775.     Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
  2776.     fsPathPtr->normPathPtr = NULL;
  2777.     fsPathPtr->cwdPtr = NULL;
  2778.     fsPathPtr->nativePathPtr = NULL;
  2779.     fsPathPtr->fsRecPtr = NULL;
  2780.     fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
  2781.     /*
  2782.      * Free old representation before installing our new one.
  2783.      */
  2784.     if (objPtr->typePtr != NULL && objPtr->typePtr->freeIntRepProc != NULL) {
  2785. (objPtr->typePtr->freeIntRepProc)(objPtr);
  2786.     }
  2787.     PATHOBJ(objPtr) = (VOID *) fsPathPtr;
  2788.     PATHFLAGS(objPtr) = 0;
  2789.     objPtr->typePtr = &tclFsPathType;
  2790.     return TCL_OK;
  2791. }
  2792. static void
  2793. FreeFsPathInternalRep(pathObjPtr)
  2794.     Tcl_Obj *pathObjPtr; /* Path object with internal rep to free. */
  2795. {
  2796.     FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathObjPtr);
  2797.     if (fsPathPtr->translatedPathPtr != NULL) {
  2798. if (fsPathPtr->translatedPathPtr != pathObjPtr) {
  2799.     Tcl_DecrRefCount(fsPathPtr->translatedPathPtr);
  2800. }
  2801.     }
  2802.     if (fsPathPtr->normPathPtr != NULL) {
  2803. if (fsPathPtr->normPathPtr != pathObjPtr) {
  2804.     Tcl_DecrRefCount(fsPathPtr->normPathPtr);
  2805. }
  2806. fsPathPtr->normPathPtr = NULL;
  2807.     }
  2808.     if (fsPathPtr->cwdPtr != NULL) {
  2809. Tcl_DecrRefCount(fsPathPtr->cwdPtr);
  2810.     }
  2811.     if (fsPathPtr->nativePathPtr != NULL) {
  2812. if (fsPathPtr->fsRecPtr != NULL) {
  2813.     if (fsPathPtr->fsRecPtr->fsPtr->freeInternalRepProc != NULL) {
  2814. (*fsPathPtr->fsRecPtr->fsPtr
  2815.    ->freeInternalRepProc)(fsPathPtr->nativePathPtr);
  2816. fsPathPtr->nativePathPtr = NULL;
  2817.     }
  2818. }
  2819.     }
  2820.     if (fsPathPtr->fsRecPtr != NULL) {
  2821. fsPathPtr->fsRecPtr->fileRefCount--;
  2822. if (fsPathPtr->fsRecPtr->fileRefCount <= 0) {
  2823.     /* It has been unregistered already, so simply free it */
  2824.     ckfree((char *)fsPathPtr->fsRecPtr);
  2825. }
  2826.     }
  2827.     ckfree((char*) fsPathPtr);
  2828. }
  2829. static void
  2830. DupFsPathInternalRep(srcPtr, copyPtr)
  2831.     Tcl_Obj *srcPtr; /* Path obj with internal rep to copy. */
  2832.     Tcl_Obj *copyPtr; /* Path obj with internal rep to set. */
  2833. {
  2834.     FsPath *srcFsPathPtr = (FsPath*) PATHOBJ(srcPtr);
  2835.     FsPath *copyFsPathPtr = (FsPath*) ckalloc((unsigned)sizeof(FsPath));
  2836.       
  2837.     Tcl_FSDupInternalRepProc *dupProc;
  2838.     
  2839.     PATHOBJ(copyPtr) = (VOID *) copyFsPathPtr;
  2840.     if (srcFsPathPtr->translatedPathPtr != NULL) {
  2841. copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr;
  2842. if (copyFsPathPtr->translatedPathPtr != copyPtr) {
  2843.     Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr);
  2844. }
  2845.     } else {
  2846. copyFsPathPtr->translatedPathPtr = NULL;
  2847.     }
  2848.     
  2849.     if (srcFsPathPtr->normPathPtr != NULL) {
  2850. copyFsPathPtr->normPathPtr = srcFsPathPtr->normPathPtr;
  2851. if (copyFsPathPtr->normPathPtr != copyPtr) {
  2852.     Tcl_IncrRefCount(copyFsPathPtr->normPathPtr);
  2853. }
  2854.     } else {
  2855. copyFsPathPtr->normPathPtr = NULL;
  2856.     }
  2857.     
  2858.     if (srcFsPathPtr->cwdPtr != NULL) {
  2859. copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr;
  2860. Tcl_IncrRefCount(copyFsPathPtr->cwdPtr);
  2861.     } else {
  2862. copyFsPathPtr->cwdPtr = NULL;
  2863.     }
  2864.     copyFsPathPtr->flags = srcFsPathPtr->flags;
  2865.     
  2866.     if (srcFsPathPtr->fsRecPtr != NULL 
  2867.       && srcFsPathPtr->nativePathPtr != NULL) {
  2868. dupProc = srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc;
  2869. if (dupProc != NULL) {
  2870.     copyFsPathPtr->nativePathPtr = 
  2871.       (*dupProc)(srcFsPathPtr->nativePathPtr);
  2872. } else {
  2873.     copyFsPathPtr->nativePathPtr = NULL;
  2874. }
  2875.     } else {
  2876. copyFsPathPtr->nativePathPtr = NULL;
  2877.     }
  2878.     copyFsPathPtr->fsRecPtr = srcFsPathPtr->fsRecPtr;
  2879.     copyFsPathPtr->filesystemEpoch = srcFsPathPtr->filesystemEpoch;
  2880.     if (copyFsPathPtr->fsRecPtr != NULL) {
  2881. copyFsPathPtr->fsRecPtr->fileRefCount++;
  2882.     }
  2883.     copyPtr->typePtr = &tclFsPathType;
  2884. }
  2885. /*
  2886.  *---------------------------------------------------------------------------
  2887.  *
  2888.  * UpdateStringOfFsPath --
  2889.  *
  2890.  *      Gives an object a valid string rep.
  2891.  *      
  2892.  * Results:
  2893.  *      None.
  2894.  *
  2895.  * Side effects:
  2896.  * Memory may be allocated.
  2897.  *
  2898.  *---------------------------------------------------------------------------
  2899.  */
  2900. static void
  2901. UpdateStringOfFsPath(objPtr)
  2902.     register Tcl_Obj *objPtr; /* path obj with string rep to update. */
  2903. {
  2904.     FsPath *fsPathPtr = (FsPath*) PATHOBJ(objPtr);
  2905.     CONST char *cwdStr;
  2906.     int cwdLen;
  2907.     Tcl_Obj *copy;
  2908.     
  2909.     if (PATHFLAGS(objPtr) == 0 || fsPathPtr->cwdPtr == NULL) {
  2910. panic("Called UpdateStringOfFsPath with invalid object");
  2911.     }
  2912.     
  2913.     copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
  2914.     Tcl_IncrRefCount(copy);
  2915.     
  2916.     cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
  2917.     /* 
  2918.      * Should we perhaps use 'Tcl_FSPathSeparator'?
  2919.      * But then what about the Windows special case?
  2920.      * Perhaps we should just check if cwd is a root volume.
  2921.      * We should never get cwdLen == 0 in this code path.
  2922.      */
  2923.     switch (tclPlatform) {
  2924. case TCL_PLATFORM_UNIX:
  2925.     if (cwdStr[cwdLen-1] != '/') {
  2926. Tcl_AppendToObj(copy, "/", 1);
  2927. cwdLen++;
  2928.     }
  2929.     break;
  2930. case TCL_PLATFORM_WINDOWS:
  2931.     /* 
  2932.      * We need the extra 'cwdLen != 2', and ':' checks because 
  2933.      * a volume relative path doesn't get a '/'.  For example 
  2934.      * 'glob C:*cat*.exe' will return 'C:cat32.exe'
  2935.      */
  2936.     if (cwdStr[cwdLen-1] != '/'
  2937.     && cwdStr[cwdLen-1] != '\') {
  2938. if (cwdLen != 2 || cwdStr[1] != ':') {
  2939.     Tcl_AppendToObj(copy, "/", 1);
  2940.     cwdLen++;
  2941. }
  2942.     }
  2943.     break;
  2944. case TCL_PLATFORM_MAC:
  2945.     if (cwdStr[cwdLen-1] != ':') {
  2946. Tcl_AppendToObj(copy, ":", 1);
  2947. cwdLen++;
  2948.     }
  2949.     break;
  2950.     }
  2951.     Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
  2952.     objPtr->bytes = Tcl_GetStringFromObj(copy, &cwdLen);
  2953.     objPtr->length = cwdLen;
  2954.     copy->bytes = tclEmptyStringRep;
  2955.     copy->length = 0;
  2956.     Tcl_DecrRefCount(copy);
  2957. }
  2958. /*
  2959.  *---------------------------------------------------------------------------
  2960.  *
  2961.  * NativePathInFilesystem --
  2962.  *
  2963.  *      Any path object is acceptable to the native filesystem, by
  2964.  *      default (we will throw errors when illegal paths are actually
  2965.  *      tried to be used).
  2966.  *      
  2967.  *      However, this behavior means the native filesystem must be
  2968.  *      the last filesystem in the lookup list (otherwise it will
  2969.  *      claim all files belong to it, and other filesystems will
  2970.  *      never get a look in).
  2971.  *
  2972.  * Results:
  2973.  *      TCL_OK, to indicate 'yes', -1 to indicate no.
  2974.  *
  2975.  * Side effects:
  2976.  * None.
  2977.  *
  2978.  *---------------------------------------------------------------------------
  2979.  */
  2980. static int 
  2981. NativePathInFilesystem(pathPtr, clientDataPtr)
  2982.     Tcl_Obj *pathPtr;
  2983.     ClientData *clientDataPtr;
  2984. {
  2985.     /* 
  2986.      * A special case is required to handle the empty path "". 
  2987.      * This is a valid path (i.e. the user should be able
  2988.      * to do 'file exists ""' without throwing an error), but
  2989.      * equally the path doesn't exist.  Those are the semantics
  2990.      * of Tcl (at present anyway), so we have to abide by them
  2991.      * here.
  2992.      */
  2993.     if (pathPtr->typePtr == &tclFsPathType) {
  2994. if (pathPtr->bytes != NULL && pathPtr->bytes[0] == '') {
  2995.     /* We reject the empty path "" */
  2996.     return -1;
  2997. }
  2998. /* Otherwise there is no way this path can be empty */
  2999.     } else {
  3000. /* 
  3001.  * It is somewhat unusual to reach this code path without
  3002.  * the object being of tclFsPathType.  However, we do
  3003.  * our best to deal with the situation.
  3004.  */
  3005. int len;
  3006. Tcl_GetStringFromObj(pathPtr,&len);
  3007. if (len == 0) {
  3008.     /* We reject the empty path "" */
  3009.     return -1;
  3010. }
  3011.     }
  3012.     /* 
  3013.      * Path is of correct type, or is of non-zero length, 
  3014.      * so we accept it.
  3015.      */
  3016.     return TCL_OK;
  3017. }