diff options
Diffstat (limited to 'generic/tclPathObj.c')
-rw-r--r-- | generic/tclPathObj.c | 539 |
1 files changed, 279 insertions, 260 deletions
diff --git a/generic/tclPathObj.c b/generic/tclPathObj.c index 95c57bf..3b907a4 100644 --- a/generic/tclPathObj.c +++ b/generic/tclPathObj.c @@ -9,6 +9,8 @@ * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * RCS: @(#) $Id: tclPathObj.c,v 1.82 2009/08/20 15:17:39 dgp Exp $ */ #include "tclInt.h" @@ -27,15 +29,13 @@ static int SetFsPathFromAny(Tcl_Interp *interp, Tcl_Obj *pathPtr); static int FindSplitPos(const char *path, int separator); static int IsSeparatorOrNull(int ch); static Tcl_Obj * GetExtension(Tcl_Obj *pathPtr); -static int MakePathFromNormalized(Tcl_Interp *interp, - Tcl_Obj *pathPtr); /* * Define the 'path' object type, which Tcl uses to represent file paths * internally. */ -static Tcl_ObjType tclFsPathType = { +static const Tcl_ObjType tclFsPathType = { "path", /* name */ FreeFsPathInternalRep, /* freeIntRepProc */ DupFsPathInternalRep, /* dupIntRepProc */ @@ -94,7 +94,9 @@ typedef struct FsPath { * generated during the correct filesystem * epoch. The epoch changes when * filesystem-mounts are changed. */ - Tcl_Filesystem *fsPtr; /* The Tcl_Filesystem that claims this path */ + struct FilesystemRecord *fsRecPtr; + /* Pointer to the filesystem record entry to + * use for this path. */ } FsPath; /* @@ -109,9 +111,9 @@ typedef struct FsPath { * fields. */ -#define PATHOBJ(pathPtr) ((FsPath *) (pathPtr)->internalRep.twoPtrValue.ptr1) +#define PATHOBJ(pathPtr) ((FsPath *) (pathPtr)->internalRep.otherValuePtr) #define SETPATHOBJ(pathPtr,fsPathPtr) \ - ((pathPtr)->internalRep.twoPtrValue.ptr1 = (void *) (fsPathPtr)) + ((pathPtr)->internalRep.otherValuePtr = (void *) (fsPathPtr)) #define PATHFLAGS(pathPtr) (PATHOBJ(pathPtr)->flags) /* @@ -152,8 +154,14 @@ typedef struct FsPath { Tcl_Obj * TclFSNormalizeAbsolutePath( Tcl_Interp *interp, /* Interpreter to use */ - Tcl_Obj *pathPtr) /* Absolute path to normalize */ + Tcl_Obj *pathPtr, /* Absolute path to normalize */ + ClientData *clientDataPtr) /* If non-NULL, then may be set to the + * fs-specific clientData for this path. This + * will happen when that extra information can + * be calculated efficiently as a side-effect + * of normalization. */ { + ClientData clientData = NULL; const char *dirSep, *oldDirSep; int first = 1; /* Set to zero once we've passed the first * directory separator - we can't use '..' to @@ -231,7 +239,7 @@ TclFSNormalizeAbsolutePath( retVal = Tcl_NewStringObj(path, dirSep - path); Tcl_IncrRefCount(retVal); } - (void) Tcl_GetStringFromObj(retVal, &curLen); + Tcl_GetStringFromObj(retVal, &curLen); if (curLen == 0) { Tcl_AppendToObj(retVal, dirSep, 1); } @@ -253,24 +261,15 @@ TclFSNormalizeAbsolutePath( if (retVal == NULL) { const char *path = TclGetString(pathPtr); - retVal = Tcl_NewStringObj(path, dirSep - path); Tcl_IncrRefCount(retVal); } - (void) Tcl_GetStringFromObj(retVal, &curLen); + Tcl_GetStringFromObj(retVal, &curLen); if (curLen == 0) { Tcl_AppendToObj(retVal, dirSep, 1); } if (!first || (tclPlatform == TCL_PLATFORM_UNIX)) { link = Tcl_FSLink(retVal, NULL, 0); - - /* Safety check in case driver caused sharing */ - if (Tcl_IsShared(retVal)) { - TclDecrRefCount(retVal); - retVal = Tcl_DuplicateObj(retVal); - Tcl_IncrRefCount(retVal); - } - if (link != NULL) { /* * Got a link. Need to check if the link is relative @@ -294,6 +293,11 @@ TclFSNormalizeAbsolutePath( break; } } + if (Tcl_IsShared(retVal)) { + TclDecrRefCount(retVal); + retVal = Tcl_DuplicateObj(retVal); + Tcl_IncrRefCount(retVal); + } /* * We want the trailing slash. @@ -309,12 +313,7 @@ TclFSNormalizeAbsolutePath( */ TclDecrRefCount(retVal); - if (Tcl_IsShared(link)) { - retVal = Tcl_DuplicateObj(link); - TclDecrRefCount(link); - } else { - retVal = link; - } + retVal = link; linkStr = Tcl_GetStringFromObj(retVal, &curLen); /* @@ -336,8 +335,8 @@ TclFSNormalizeAbsolutePath( } /* - * Either way, we now remove the last path element. - * (but not the first character of the path) + * Either way, we now remove the last path element (but + * not the first character of the path). */ while (--curLen >= 0) { @@ -398,7 +397,7 @@ TclFSNormalizeAbsolutePath( } /* - * Ensure a windows drive like C:/ has a trailing separator + * Ensure a windows drive like C:/ has a trailing separator. */ if (tclPlatform == TCL_PLATFORM_WINDOWS) { @@ -426,14 +425,17 @@ TclFSNormalizeAbsolutePath( * for normalizing a path. */ - TclFSNormalizeToUniquePath(interp, retVal, 0); + TclFSNormalizeToUniquePath(interp, retVal, 0, &clientData); /* * Since we know it is a normalized path, we can actually convert this * object into an FsPath for greater efficiency */ - MakePathFromNormalized(interp, retVal); + TclFSMakePathFromNormalized(interp, retVal, clientData); + if (clientDataPtr != NULL) { + *clientDataPtr = clientData; + } /* * This has a refCount of 1 for the caller, unlike many Tcl_Obj APIs. @@ -493,7 +495,7 @@ Tcl_FSGetPathType( Tcl_PathType TclFSGetPathType( Tcl_Obj *pathPtr, - Tcl_Filesystem **filesystemPtrPtr, + const Tcl_Filesystem **filesystemPtrPtr, int *driveNameLengthPtr) { FsPath *fsPathPtr; @@ -565,7 +567,8 @@ TclPathPart( if (pathPtr->typePtr == &tclFsPathType) { FsPath *fsPathPtr = PATHOBJ(pathPtr); - if (PATHFLAGS(pathPtr) != 0) { + if (TclFSEpochOk(fsPathPtr->filesystemEpoch) + && (PATHFLAGS(pathPtr) != 0)) { switch (portion) { case TCL_PATH_DIRNAME: { /* @@ -658,18 +661,34 @@ TclPathPart( return pathPtr; } else { /* - * Need to return the whole path with the extension - * suffix removed. Do that by joining our "head" to - * our "tail" with the extension suffix removed from - * the tail. + * Duplicate the object we were given and then trim off + * the extension of the tail component of the path. */ - Tcl_Obj *resultPtr = - TclNewFSPathObj(fsPathPtr->cwdPtr, fileName, - (int)(length - strlen(extension))); + FsPath *fsDupPtr; + Tcl_Obj *root = Tcl_DuplicateObj(pathPtr); + + Tcl_IncrRefCount(root); + fsDupPtr = PATHOBJ(root); + if (Tcl_IsShared(fsDupPtr->normPathPtr)) { + TclDecrRefCount(fsDupPtr->normPathPtr); + fsDupPtr->normPathPtr = Tcl_NewStringObj(fileName, + (int)(length - strlen(extension))); + Tcl_IncrRefCount(fsDupPtr->normPathPtr); + } else { + Tcl_SetObjLength(fsDupPtr->normPathPtr, + (int)(length - strlen(extension))); + } + + /* + * Must also trim the string representation if we have it. + */ - Tcl_IncrRefCount(resultPtr); - return resultPtr; + if (root->bytes != NULL && root->length > 0) { + root->length -= strlen(extension); + root->bytes[root->length] = 0; + } + return root; } } default: @@ -829,7 +848,7 @@ Tcl_FSJoinPath( { Tcl_Obj *res; int i; - Tcl_Filesystem *fsPtr = NULL; + const Tcl_Filesystem *fsPtr = NULL; if (elements < 0) { if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) { @@ -878,6 +897,7 @@ Tcl_FSJoinPath( if ((i == (elements-2)) && (i == 0) && (elt->typePtr == &tclFsPathType) && !(elt->bytes != NULL && (elt->bytes[0] == '\0'))) { Tcl_Obj *tail; + Tcl_PathType type; Tcl_ListObjIndex(NULL, listObj, i+1, &tail); type = TclGetPathType(tail, NULL, NULL, NULL); @@ -927,7 +947,7 @@ Tcl_FSJoinPath( /* * Otherwise we don't have an easy join, and we must let the - * more general code below handle things. + * more general code below handle things */ } else if (tclPlatform == TCL_PLATFORM_UNIX) { if (res != NULL) { @@ -935,7 +955,7 @@ Tcl_FSJoinPath( } return tail; } else { - const char *str = TclGetString(tail); + const char *str = Tcl_GetString(tail); if (tclPlatform == TCL_PLATFORM_WINDOWS) { if (strchr(str, '\\') == NULL) { @@ -1019,8 +1039,8 @@ Tcl_FSJoinPath( } /* - * This element is just what we want to return already - no - * further manipulation is requred. + * This element is just what we want to return already; no further + * manipulation is requred. */ return elt; @@ -1066,22 +1086,16 @@ Tcl_FSJoinPath( int needsSep = 0; if (fsPtr->filesystemSeparatorProc != NULL) { - Tcl_Obj *sep = (*fsPtr->filesystemSeparatorProc)(res); + Tcl_Obj *sep = fsPtr->filesystemSeparatorProc(res); if (sep != NULL) { separator = TclGetString(sep)[0]; } - /* Safety check in case the VFS driver caused sharing */ - if (Tcl_IsShared(res)) { - TclDecrRefCount(res); - res = Tcl_DuplicateObj(res); - Tcl_IncrRefCount(res); - } } if (length > 0 && ptr[length -1] != '/') { Tcl_AppendToObj(res, &separator, 1); - Tcl_GetStringFromObj(res, &length); + length++; } Tcl_SetObjLength(res, length + (int) strlen(strElt)); @@ -1158,6 +1172,7 @@ Tcl_FSConvertToPathType( UpdateStringOfFsPath(pathPtr); } FreeFsPathInternalRep(pathPtr); + pathPtr->typePtr = NULL; } return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType); @@ -1176,6 +1191,7 @@ Tcl_FSConvertToPathType( * UpdateStringOfFsPath(pathPtr); * } * FreeFsPathInternalRep(pathPtr); + * pathPtr->typePtr = NULL; * return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType); * } * } @@ -1268,6 +1284,7 @@ TclNewFSPathObj( { FsPath *fsPathPtr; Tcl_Obj *pathPtr; + ThreadSpecificData *tsdPtr; const char *p; int state = 0, count = 0; @@ -1295,6 +1312,8 @@ TclNewFSPathObj( return pathPtr; } + tsdPtr = TCL_TSD_INIT(&tclFsDataKey); + pathPtr = Tcl_NewObj(); fsPathPtr = (FsPath *) ckalloc(sizeof(FsPath)); @@ -1308,8 +1327,8 @@ TclNewFSPathObj( fsPathPtr->cwdPtr = dirPtr; Tcl_IncrRefCount(dirPtr); fsPathPtr->nativePathPtr = NULL; - fsPathPtr->fsPtr = NULL; - fsPathPtr->filesystemEpoch = 0; + fsPathPtr->fsRecPtr = NULL; + fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch; SETPATHOBJ(pathPtr, fsPathPtr); PATHFLAGS(pathPtr) = TCLPATH_APPENDED; @@ -1319,41 +1338,41 @@ TclNewFSPathObj( /* * Look for path components made up of only "." - * This is overly conservative analysis to keep simple. It may - * mark some things as needing more aggressive normalization - * that don't actually need it. No harm done. + * This is overly conservative analysis to keep simple. It may mark some + * things as needing more aggressive normalization that don't actually + * need it. No harm done. */ for (p = addStrRep; len > 0; p++, len--) { - switch (state) { - case 0: /* So far only "." since last dirsep or start */ - switch (*p) { - case '.': - count++; - break; - case '/': - case '\\': - case ':': - if (count) { - PATHFLAGS(pathPtr) |= TCLPATH_NEEDNORM; - len = 0; - } - break; - default: - count = 0; - state = 1; - } - case 1: /* Scanning for next dirsep */ - switch (*p) { - case '/': - case '\\': - case ':': - state = 0; - break; - } - } + switch (state) { + case 0: /* So far only "." since last dirsep or start */ + switch (*p) { + case '.': + count++; + break; + case '/': + case '\\': + case ':': + if (count) { + PATHFLAGS(pathPtr) |= TCLPATH_NEEDNORM; + len = 0; + } + break; + default: + count = 0; + state = 1; + } + case 1: /* Scanning for next dirsep */ + switch (*p) { + case '/': + case '\\': + case ':': + state = 0; + break; + } + } } if (len == 0 && count) { - PATHFLAGS(pathPtr) |= TCLPATH_NEEDNORM; + PATHFLAGS(pathPtr) |= TCLPATH_NEEDNORM; } return pathPtr; @@ -1368,20 +1387,37 @@ AppendPath( const char *bytes; Tcl_Obj *copy = Tcl_DuplicateObj(head); + bytes = Tcl_GetStringFromObj(copy, &numBytes); + /* - * This is likely buggy when dealing with virtual filesystem drivers - * that use some character other than "/" as a path separator. I know - * of no evidence that such a foolish thing exists. This solution was - * chosen so that "JoinPath" operations that pass through either path - * intrep produce the same results; that is, bugward compatibility. If - * we need to fix that bug here, it needs fixing in Tcl_FSJoinPath() too. + * Should we perhaps use 'Tcl_FSPathSeparator'? But then what about the + * Windows special case? Perhaps we should just check if cwd is a root + * volume. We should never get numBytes == 0 in this code path. */ - bytes = Tcl_GetStringFromObj(tail, &numBytes); - if (numBytes == 0) { - Tcl_AppendToObj(copy, "/", 1); - } else { - TclpNativeJoinPath(copy, bytes); + + switch (tclPlatform) { + case TCL_PLATFORM_UNIX: + if (bytes[numBytes-1] != '/') { + Tcl_AppendToObj(copy, "/", 1); + } + break; + + case TCL_PLATFORM_WINDOWS: + /* + * We need the extra 'numBytes != 2', and ':' checks because a volume + * relative path doesn't get a '/'. For example 'glob C:*cat*.exe' + * will return 'C:cat32.exe' + */ + + if (bytes[numBytes-1] != '/' && bytes[numBytes-1] != '\\') { + if (numBytes!= 2 || bytes[1] != ':') { + Tcl_AppendToObj(copy, "/", 1); + } + } + break; } + + Tcl_AppendObjToObj(copy, tail); return copy; } @@ -1421,70 +1457,7 @@ TclFSMakePathRelative( if (PATHFLAGS(pathPtr) != 0 && fsPathPtr->cwdPtr == cwdPtr) { - pathPtr = fsPathPtr->normPathPtr; - - /* TODO: Determine how much, if any, of this forcing - * the relative path tail into the "path" Tcl_ObjType - * with a recorded cwdPtr context has any actual value. - * - * Nothing is getting cached. Not normPathPtr, not nativePathPtr, - * nor fsPtr, so storing the cwdPtr context against which such - * cached values might later be validated appears to be of no - * value. Take that away, and all this code is just a mildly - * optimized equivalent of a call to SetFsPathFromAny(). That - * optimization may have some value, *if* these value in fact - * get used as "path" values before used as something else. - * If not, though, whatever cost we pay below to convert to - * one of the "path" intreps is just a waste, it seems. The - * usual convention in the core is to delay ObjType conversion - * until it is needed and demanded, and I don't see why this - * section of code should be an exception to that. Leaving it - * in place for the rest of the 8.5.* releases just for sake - * of stability. - */ - - /* - * Free old representation. - */ - - if (pathPtr->typePtr != NULL) { - if (pathPtr->bytes == NULL) { - if (pathPtr->typePtr->updateStringProc == NULL) { - if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "can't find object" - "string representation", NULL); - } - return NULL; - } - pathPtr->typePtr->updateStringProc(pathPtr); - } - TclFreeIntRep(pathPtr); - } - - /* - * Now pathPtr is a string object. - */ - - fsPathPtr = (FsPath *) ckalloc(sizeof(FsPath)); - - /* - * Circular reference, by design. - */ - - fsPathPtr->translatedPathPtr = pathPtr; - fsPathPtr->normPathPtr = NULL; - fsPathPtr->cwdPtr = cwdPtr; - Tcl_IncrRefCount(cwdPtr); - fsPathPtr->nativePathPtr = NULL; - fsPathPtr->fsPtr = NULL; - fsPathPtr->filesystemEpoch = 0; - - SETPATHOBJ(pathPtr, fsPathPtr); - PATHFLAGS(pathPtr) = 0; - pathPtr->typePtr = &tclFsPathType; - - return pathPtr; + return fsPathPtr->normPathPtr; } } @@ -1527,7 +1500,7 @@ TclFSMakePathRelative( /* *--------------------------------------------------------------------------- * - * MakePathFromNormalized -- + * TclFSMakePathFromNormalized -- * * Like SetFsPathFromAny, but assumes the given object is an absolute * normalized path. Only for internal use. @@ -1541,12 +1514,15 @@ TclFSMakePathRelative( *--------------------------------------------------------------------------- */ -static int -MakePathFromNormalized( +int +TclFSMakePathFromNormalized( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ - Tcl_Obj *pathPtr) /* The object to convert. */ + Tcl_Obj *pathPtr, /* The object to convert. */ + ClientData nativeRep) /* The native rep for the object, if known + * else NULL. */ { FsPath *fsPathPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); if (pathPtr->typePtr == &tclFsPathType) { return TCL_OK; @@ -1585,10 +1561,9 @@ MakePathFromNormalized( fsPathPtr->normPathPtr = pathPtr; fsPathPtr->cwdPtr = NULL; - fsPathPtr->nativePathPtr = NULL; - fsPathPtr->fsPtr = NULL; - /* Remember the epoch under which we decided pathPtr was normalized */ - fsPathPtr->filesystemEpoch = TclFSEpoch(); + fsPathPtr->nativePathPtr = nativeRep; + fsPathPtr->fsRecPtr = NULL; + fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch; SETPATHOBJ(pathPtr, fsPathPtr); PATHFLAGS(pathPtr) = 0; @@ -1624,16 +1599,17 @@ MakePathFromNormalized( Tcl_Obj * Tcl_FSNewNativePath( - Tcl_Filesystem *fromFilesystem, + const Tcl_Filesystem *fromFilesystem, ClientData clientData) { - Tcl_Obj *pathPtr = NULL; + Tcl_Obj *pathPtr; FsPath *fsPathPtr; + FilesystemRecord *fsFromPtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); - if (fromFilesystem->internalToNormalizedProc != NULL) { - pathPtr = (*fromFilesystem->internalToNormalizedProc)(clientData); - } + pathPtr = TclFSInternalToNormalized(fromFilesystem, clientData, + &fsFromPtr); if (pathPtr == NULL) { return NULL; } @@ -1664,8 +1640,9 @@ Tcl_FSNewNativePath( fsPathPtr->normPathPtr = pathPtr; fsPathPtr->cwdPtr = NULL; fsPathPtr->nativePathPtr = clientData; - fsPathPtr->fsPtr = fromFilesystem; - fsPathPtr->filesystemEpoch = TclFSEpoch(); + fsPathPtr->fsRecPtr = fsFromPtr; + fsPathPtr->fsRecPtr->fileRefCount++; + fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch; SETPATHOBJ(pathPtr, fsPathPtr); PATHFLAGS(pathPtr) = 0; @@ -1721,14 +1698,8 @@ Tcl_FSGetTranslatedPath( } retObj = Tcl_FSJoinToPath(translatedCwdPtr, 1, - &(srcFsPathPtr->normPathPtr)); + &srcFsPathPtr->normPathPtr); srcFsPathPtr->translatedPathPtr = retObj; - if (translatedCwdPtr->typePtr == &tclFsPathType) { - srcFsPathPtr->filesystemEpoch - = PATHOBJ(translatedCwdPtr)->filesystemEpoch; - } else { - srcFsPathPtr->filesystemEpoch = 0; - } Tcl_IncrRefCount(retObj); Tcl_DecrRefCount(translatedCwdPtr); } else { @@ -1783,7 +1754,7 @@ Tcl_FSGetTranslatedStringPath( if (transPtr != NULL) { int len; const char *orig = Tcl_GetStringFromObj(transPtr, &len); - char *result = (char *) ckalloc((unsigned) len+1); + char *result = ckalloc((unsigned) len+1); memcpy(result, orig, (size_t) len+1); TclDecrRefCount(transPtr); @@ -1831,7 +1802,8 @@ Tcl_FSGetNormalizedPath( */ Tcl_Obj *dir, *copy; - int tailLen, cwdLen, pathType; + int cwdLen, pathType; + ClientData clientData = NULL; pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr); dir = Tcl_FSGetNormalizedPath(interp, fsPathPtr->cwdPtr); @@ -1843,12 +1815,7 @@ Tcl_FSGetNormalizedPath( UpdateStringOfFsPath(pathPtr); } - Tcl_GetStringFromObj(fsPathPtr->normPathPtr, &tailLen); - if (tailLen) { - copy = AppendPath(dir, fsPathPtr->normPathPtr); - } else { - copy = Tcl_DuplicateObj(dir); - } + copy = AppendPath(dir, fsPathPtr->normPathPtr); Tcl_IncrRefCount(dir); Tcl_IncrRefCount(copy); @@ -1863,28 +1830,29 @@ Tcl_FSGetNormalizedPath( if (PATHFLAGS(pathPtr) & TCLPATH_NEEDNORM) { /* - * If the "tail" part has components (like /../) that cause - * the combined path to need more complete normalizing, - * call on the more powerful routine to accomplish that so - * we avoid [Bug 2385549] ... + * If the "tail" part has components (like /../) that cause the + * combined path to need more complete normalizing, call on the + * more powerful routine to accomplish that so we avoid [Bug + * 2385549] ... */ - Tcl_Obj *newCopy = TclFSNormalizeAbsolutePath(interp, copy); + Tcl_Obj *newCopy = TclFSNormalizeAbsolutePath(interp, copy, NULL); + Tcl_DecrRefCount(copy); copy = newCopy; } else { /* - * ... but in most cases where we join a trouble free tail - * to a normalized head, we can more efficiently normalize the - * combined path by passing over only the unnormalized tail - * portion. When this is sufficient, prior developers claim - * this should be much faster. We use 'cwdLen-1' so that we are - * already pointing at the dir-separator that we know about. - * The normalization code will actually start off directly - * after that separator. + * ... but in most cases where we join a trouble free tail to a + * normalized head, we can more efficiently normalize the combined + * path by passing over only the unnormalized tail portion. When + * this is sufficient, prior developers claim this should be much + * faster. We use 'cwdLen-1' so that we are already pointing at + * the dir-separator that we know about. The normalization code + * will actually start off directly after that separator. */ - TclFSNormalizeToUniquePath(interp, copy, cwdLen-1); + TclFSNormalizeToUniquePath(interp, copy, cwdLen-1, + (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL)); } /* Now we need to construct the new path object. */ @@ -1894,11 +1862,11 @@ Tcl_FSGetNormalizedPath( /* * NOTE: here we are (dangerously?) assuming that origDir points - * to a Tcl_Obj with Tcl_ObjType == &tclFsPathType . The + * to a Tcl_Obj with Tcl_ObjType == &tclFsPathType. The * pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr); - * above that set the pathType value should have established - * that, but it's far less clear on what basis we know there's - * been no shimmering since then. + * above that set the pathType value should have established that, + * but it's far less clear on what basis we know there's been no + * shimmering since then. */ FsPath *origDirFsPathPtr = PATHOBJ(origDir); @@ -1927,6 +1895,15 @@ Tcl_FSGetNormalizedPath( TclDecrRefCount(dir); } + if (clientData != NULL) { + /* + * This may be unnecessary. It appears that the + * TclFSNormalizeToUniquePath call above should have already set + * this up. Not changing out of fear of the unknown. + */ + + fsPathPtr->nativePathPtr = clientData; + } PATHFLAGS(pathPtr) = 0; } @@ -1940,6 +1917,7 @@ Tcl_FSGetNormalizedPath( UpdateStringOfFsPath(pathPtr); } FreeFsPathInternalRep(pathPtr); + pathPtr->typePtr = NULL; if (Tcl_ConvertToType(interp, pathPtr, &tclFsPathType) != TCL_OK) { return NULL; } @@ -1947,6 +1925,7 @@ Tcl_FSGetNormalizedPath( } else if (fsPathPtr->normPathPtr == NULL) { int cwdLen; Tcl_Obj *copy; + ClientData clientData = NULL; copy = AppendPath(fsPathPtr->cwdPtr, pathPtr); @@ -1958,12 +1937,16 @@ Tcl_FSGetNormalizedPath( * of the previously normalized 'dir'. This should be much faster! */ - TclFSNormalizeToUniquePath(interp, copy, cwdLen-1); + TclFSNormalizeToUniquePath(interp, copy, cwdLen-1, + (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL)); fsPathPtr->normPathPtr = copy; - Tcl_IncrRefCount(fsPathPtr->normPathPtr); + if (clientData != NULL) { + fsPathPtr->nativePathPtr = clientData; + } } } if (fsPathPtr->normPathPtr == NULL) { + ClientData clientData = NULL; Tcl_Obj *useThisCwd = NULL; int pureNormalized = 1; @@ -1986,11 +1969,11 @@ Tcl_FSGetNormalizedPath( if (path[0] == '\0') { /* - * Special handling for the empty string value. This one is - * very weird with [file normalize {}] => {}. (The reasoning - * supporting this is unknown to DGP, but he fears changing it.) - * Attempt here to keep the expectations of other parts of - * Tcl_Filesystem code about state of the FsPath fields satisfied. + * Special handling for the empty string value. This one is very + * weird with [file normalize {}] => {}. (The reasoning supporting + * this is unknown to DGP, but he fears changing it.) Attempt here + * to keep the expectations of other parts of Tcl_Filesystem code + * about state of the FsPath fields satisfied. * * In particular, capture the cwd value and save so it can be * stored in the cwdPtr field below. @@ -2045,7 +2028,12 @@ Tcl_FSGetNormalizedPath( */ fsPathPtr->normPathPtr = TclFSNormalizeAbsolutePath(interp, - absolutePath); + absolutePath, + (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL)); + if (0 && (clientData != NULL)) { + fsPathPtr->nativePathPtr = + fsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc(clientData); + } /* * Check if path is pure normalized (this can only be the case if it @@ -2109,7 +2097,7 @@ Tcl_FSGetNormalizedPath( ClientData Tcl_FSGetInternalRep( Tcl_Obj *pathPtr, - Tcl_Filesystem *fsPtr) + const Tcl_Filesystem *fsPtr) { FsPath *srcFsPathPtr; @@ -2132,7 +2120,7 @@ Tcl_FSGetInternalRep( * not easily achievable with the current implementation. */ - if (srcFsPathPtr->fsPtr == NULL) { + if (srcFsPathPtr->fsRecPtr == NULL) { /* * This only usually happens in wrappers like TclpStat which create a * string object and pass it to TclpObjStat. Code which calls the @@ -2152,7 +2140,7 @@ Tcl_FSGetInternalRep( */ srcFsPathPtr = PATHOBJ(pathPtr); - if (srcFsPathPtr->fsPtr == NULL) { + if (srcFsPathPtr->fsRecPtr == NULL) { return NULL; } } @@ -2164,7 +2152,7 @@ Tcl_FSGetInternalRep( * for this is we ask what filesystem this path belongs to. */ - if (fsPtr != srcFsPathPtr->fsPtr) { + if (fsPtr != srcFsPathPtr->fsRecPtr->fsPtr) { const Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathPtr); if (actualFs == fsPtr) { @@ -2177,12 +2165,12 @@ Tcl_FSGetInternalRep( Tcl_FSCreateInternalRepProc *proc; char *nativePathPtr; - proc = srcFsPathPtr->fsPtr->createInternalRepProc; + proc = srcFsPathPtr->fsRecPtr->fsPtr->createInternalRepProc; if (proc == NULL) { return NULL; } - nativePathPtr = (*proc)(pathPtr); + nativePathPtr = proc(pathPtr); srcFsPathPtr = PATHOBJ(pathPtr); srcFsPathPtr->nativePathPtr = nativePathPtr; } @@ -2211,7 +2199,7 @@ Tcl_FSGetInternalRep( int TclFSEnsureEpochOk( Tcl_Obj *pathPtr, - Tcl_Filesystem **fsPtrPtr) + const Tcl_Filesystem **fsPtrPtr) { FsPath *srcFsPathPtr; @@ -2235,6 +2223,7 @@ TclFSEnsureEpochOk( UpdateStringOfFsPath(pathPtr); } FreeFsPathInternalRep(pathPtr); + pathPtr->typePtr = NULL; if (SetFsPathFromAny(NULL, pathPtr) != TCL_OK) { return TCL_ERROR; } @@ -2245,8 +2234,8 @@ TclFSEnsureEpochOk( * Check whether the object is already assigned to a fs. */ - if (srcFsPathPtr->fsPtr != NULL) { - *fsPtrPtr = srcFsPathPtr->fsPtr; + if (srcFsPathPtr->fsRecPtr != NULL) { + *fsPtrPtr = srcFsPathPtr->fsRecPtr->fsPtr; } return TCL_OK; } @@ -2270,9 +2259,10 @@ TclFSEnsureEpochOk( void TclFSSetPathDetails( Tcl_Obj *pathPtr, - Tcl_Filesystem *fsPtr, + FilesystemRecord *fsRecPtr, ClientData clientData) { + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); FsPath *srcFsPathPtr; /* @@ -2286,9 +2276,10 @@ TclFSSetPathDetails( } srcFsPathPtr = PATHOBJ(pathPtr); - srcFsPathPtr->fsPtr = fsPtr; + srcFsPathPtr->fsRecPtr = fsRecPtr; srcFsPathPtr->nativePathPtr = clientData; - srcFsPathPtr->filesystemEpoch = TclFSEpoch(); + srcFsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch; + fsRecPtr->fileRefCount++; } /* @@ -2313,7 +2304,7 @@ Tcl_FSEqualPaths( Tcl_Obj *firstPtr, Tcl_Obj *secondPtr) { - char *firstStr, *secondStr; + const char *firstStr, *secondStr; int firstLen, secondLen, tempErrno; if (firstPtr == secondPtr) { @@ -2377,6 +2368,7 @@ SetFsPathFromAny( FsPath *fsPathPtr; Tcl_Obj *transPtr; char *name; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey); if (pathPtr->typePtr == &tclFsPathType) { return TCL_OK; @@ -2516,12 +2508,29 @@ SetFsPathFromAny( } Tcl_DStringFree(&temp); } else { - /* Bug 3479689: protect 0-refcount pathPth from getting freed */ - pathPtr->refCount++; transPtr = Tcl_FSJoinToPath(pathPtr, 0, NULL); - pathPtr->refCount--; } +#if defined(__CYGWIN__) && defined(__WIN32__) + { + extern int cygwin_conv_to_win32_path(const char *, char *); + char winbuf[MAX_PATH+1]; + + /* + * In the Cygwin world, call conv_to_win32_path in order to use the + * mount table to translate the file name into something Windows will + * understand. Take care when converting empty strings! + */ + + name = Tcl_GetStringFromObj(transPtr, &len); + if (len > 0) { + cygwin_conv_to_win32_path(name, winbuf); + TclWinNoBackslash(winbuf); + Tcl_SetStringObj(transPtr, winbuf, -1); + } + } +#endif /* __CYGWIN__ && __WIN32__ */ + /* * Now we have a translated filename in 'transPtr'. This will have forward * slashes on Windows, and will not contain any ~user sequences. @@ -2532,15 +2541,12 @@ SetFsPathFromAny( fsPathPtr->translatedPathPtr = transPtr; if (transPtr != pathPtr) { Tcl_IncrRefCount(fsPathPtr->translatedPathPtr); - /* Redo translation when $env(HOME) changes */ - fsPathPtr->filesystemEpoch = TclFSEpoch(); - } else { - fsPathPtr->filesystemEpoch = 0; } fsPathPtr->normPathPtr = NULL; fsPathPtr->cwdPtr = NULL; fsPathPtr->nativePathPtr = NULL; - fsPathPtr->fsPtr = NULL; + fsPathPtr->fsRecPtr = NULL; + fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch; /* * Free old representation before installing our new one. @@ -2550,6 +2556,7 @@ SetFsPathFromAny( SETPATHOBJ(pathPtr, fsPathPtr); PATHFLAGS(pathPtr) = 0; pathPtr->typePtr = &tclFsPathType; + return TCL_OK; } @@ -2573,18 +2580,27 @@ FreeFsPathInternalRep( if (fsPathPtr->cwdPtr != NULL) { TclDecrRefCount(fsPathPtr->cwdPtr); } - if (fsPathPtr->nativePathPtr != NULL && fsPathPtr->fsPtr != NULL) { + if (fsPathPtr->nativePathPtr != NULL && fsPathPtr->fsRecPtr != NULL) { Tcl_FSFreeInternalRepProc *freeProc = - fsPathPtr->fsPtr->freeInternalRepProc; + fsPathPtr->fsRecPtr->fsPtr->freeInternalRepProc; if (freeProc != NULL) { - (*freeProc)(fsPathPtr->nativePathPtr); + freeProc(fsPathPtr->nativePathPtr); fsPathPtr->nativePathPtr = NULL; } } + if (fsPathPtr->fsRecPtr != NULL) { + fsPathPtr->fsRecPtr->fileRefCount--; + if (fsPathPtr->fsRecPtr->fileRefCount <= 0) { + /* + * It has been unregistered already. + */ + + ckfree((char *) fsPathPtr->fsRecPtr); + } + } - ckfree((char *) fsPathPtr); - pathPtr->typePtr = NULL; + ckfree((char*) fsPathPtr); } static void @@ -2597,49 +2613,52 @@ DupFsPathInternalRep( SETPATHOBJ(copyPtr, copyFsPathPtr); - if (srcFsPathPtr->translatedPathPtr == srcPtr) { - /* Cycle in src -> make cycle in copy. */ - copyFsPathPtr->translatedPathPtr = copyPtr; - } else { + if (srcFsPathPtr->translatedPathPtr != NULL) { copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr; - if (copyFsPathPtr->translatedPathPtr != NULL) { + if (copyFsPathPtr->translatedPathPtr != copyPtr) { Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr); } + } else { + copyFsPathPtr->translatedPathPtr = NULL; } - if (srcFsPathPtr->normPathPtr == srcPtr) { - /* Cycle in src -> make cycle in copy. */ - copyFsPathPtr->normPathPtr = copyPtr; - } else { + if (srcFsPathPtr->normPathPtr != NULL) { copyFsPathPtr->normPathPtr = srcFsPathPtr->normPathPtr; - if (copyFsPathPtr->normPathPtr != NULL) { + if (copyFsPathPtr->normPathPtr != copyPtr) { Tcl_IncrRefCount(copyFsPathPtr->normPathPtr); } + } else { + copyFsPathPtr->normPathPtr = NULL; } - copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr; - if (copyFsPathPtr->cwdPtr != NULL) { + if (srcFsPathPtr->cwdPtr != NULL) { + copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr; Tcl_IncrRefCount(copyFsPathPtr->cwdPtr); + } else { + copyFsPathPtr->cwdPtr = NULL; } copyFsPathPtr->flags = srcFsPathPtr->flags; - if (srcFsPathPtr->fsPtr != NULL + if (srcFsPathPtr->fsRecPtr != NULL && srcFsPathPtr->nativePathPtr != NULL) { Tcl_FSDupInternalRepProc *dupProc = - srcFsPathPtr->fsPtr->dupInternalRepProc; + srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc; if (dupProc != NULL) { copyFsPathPtr->nativePathPtr = - (*dupProc)(srcFsPathPtr->nativePathPtr); + dupProc(srcFsPathPtr->nativePathPtr); } else { copyFsPathPtr->nativePathPtr = NULL; } } else { copyFsPathPtr->nativePathPtr = NULL; } - copyFsPathPtr->fsPtr = srcFsPathPtr->fsPtr; + copyFsPathPtr->fsRecPtr = srcFsPathPtr->fsRecPtr; copyFsPathPtr->filesystemEpoch = srcFsPathPtr->filesystemEpoch; + if (copyFsPathPtr->fsRecPtr != NULL) { + copyFsPathPtr->fsRecPtr->fileRefCount++; + } copyPtr->typePtr = &tclFsPathType; } @@ -2735,7 +2754,7 @@ TclNativePathInFilesystem( int len; - (void) Tcl_GetStringFromObj(pathPtr, &len); + Tcl_GetStringFromObj(pathPtr, &len); if (len == 0) { /* * We reject the empty path "". |