summaryrefslogtreecommitdiffstats
path: root/generic/tclPathObj.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclPathObj.c')
-rw-r--r--generic/tclPathObj.c539
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 "".