summaryrefslogtreecommitdiffstats
path: root/generic/tclPathObj.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclPathObj.c')
-rw-r--r--generic/tclPathObj.c1319
1 files changed, 685 insertions, 634 deletions
diff --git a/generic/tclPathObj.c b/generic/tclPathObj.c
index 362d489..fe6063f 100644
--- a/generic/tclPathObj.c
+++ b/generic/tclPathObj.c
@@ -9,8 +9,6 @@
*
* 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.42 2005/07/21 14:38:50 dkf Exp $
*/
#include "tclInt.h"
@@ -20,22 +18,24 @@
* Prototypes for functions defined later in this file.
*/
-static void DupFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
- Tcl_Obj *copyPtr));
-static void FreeFsPathInternalRep _ANSI_ARGS_((Tcl_Obj *pathPtr));
-static void UpdateStringOfFsPath _ANSI_ARGS_((Tcl_Obj *pathPtr));
-static int SetFsPathFromAny _ANSI_ARGS_((Tcl_Interp *interp,
- Tcl_Obj *pathPtr));
-static int FindSplitPos _ANSI_ARGS_((CONST char *path, int separator));
-static int IsSeparatorOrNull _ANSI_ARGS_((int ch));
-static Tcl_Obj* GetExtension _ANSI_ARGS_((Tcl_Obj *pathPtr));
+static Tcl_Obj * AppendPath(Tcl_Obj *head, Tcl_Obj *tail);
+static void DupFsPathInternalRep(Tcl_Obj *srcPtr,
+ Tcl_Obj *copyPtr);
+static void FreeFsPathInternalRep(Tcl_Obj *pathPtr);
+static void UpdateStringOfFsPath(Tcl_Obj *pathPtr);
+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.
*/
-Tcl_ObjType tclFsPathType = {
+static const Tcl_ObjType tclFsPathType = {
"path", /* name */
FreeFsPathInternalRep, /* freeIntRepProc */
DupFsPathInternalRep, /* dupIntRepProc */
@@ -46,7 +46,7 @@ Tcl_ObjType tclFsPathType = {
/*
* struct FsPath --
*
- * Internal representation of a Tcl_Obj of "path" type. This can be used to
+ * Internal representation of a Tcl_Obj of "path" type. This can be used to
* represent relative or absolute paths, and has certain optimisations when
* used to represent paths which are already normalized and absolute.
*
@@ -58,8 +58,8 @@ Tcl_ObjType tclFsPathType = {
* (i) flags == 0, => Ordinary path.
*
* translatedPathPtr contains the translated path (which may be a circular
- * reference to the object itself). If it is NULL then the path is pure
- * normalized (and the normPathPtr will be a circular reference). cwdPtr is
+ * reference to the object itself). If it is NULL then the path is pure
+ * normalized (and the normPathPtr will be a circular reference). cwdPtr is
* null for an absolute path, and non-null for a relative path (unless the cwd
* has never been set, in which case the cwdPtr may also be null for a
* relative path).
@@ -72,7 +72,7 @@ Tcl_ObjType tclFsPathType = {
*/
typedef struct FsPath {
- Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences. If this
+ Tcl_Obj *translatedPathPtr; /* Name without any ~user sequences. If this
* is NULL, then this is a pure normalized,
* absolute path object, in which the parent
* Tcl_Obj's string rep is already both
@@ -84,19 +84,17 @@ typedef struct FsPath {
* container. If that is NOT the case, we have
* a refCount on the object. */
Tcl_Obj *cwdPtr; /* If null, path is absolute, else this points
- * to the cwd object used for this path. We
+ * to the cwd object used for this path. We
* have a refCount on the object. */
int flags; /* Flags to describe interpretation - see
* below. */
- ClientData nativePathPtr; /* Native representation of this path, which
+ ClientData nativePathPtr; /* Native representation of this path, which
* is filesystem dependent. */
int filesystemEpoch; /* Used to ensure the path representation was
* generated during the correct filesystem
* epoch. The epoch changes when
* filesystem-mounts are changed. */
- struct FilesystemRecord *fsRecPtr;
- /* Pointer to the filesystem record entry to
- * use for this path. */
+ const Tcl_Filesystem *fsPtr;/* The Tcl_Filesystem that claims this path */
} FsPath;
/*
@@ -104,16 +102,17 @@ typedef struct FsPath {
*/
#define TCLPATH_APPENDED 1
+#define TCLPATH_NEEDNORM 4
/*
* Define some macros to give us convenient access to path-object specific
* fields.
*/
-#define PATHOBJ(pathPtr) (pathPtr->internalRep.otherValuePtr)
-#define PATHFLAGS(pathPtr) \
- (((FsPath*)(pathPtr->internalRep.otherValuePtr))->flags)
-
+#define PATHOBJ(pathPtr) ((FsPath *) (pathPtr)->internalRep.twoPtrValue.ptr1)
+#define SETPATHOBJ(pathPtr,fsPathPtr) \
+ ((pathPtr)->internalRep.twoPtrValue.ptr1 = (void *) (fsPathPtr))
+#define PATHFLAGS(pathPtr) (PATHOBJ(pathPtr)->flags)
/*
*---------------------------------------------------------------------------
@@ -123,8 +122,8 @@ typedef struct FsPath {
* Takes an absolute path specification and computes a 'normalized' path
* from it.
*
- * A normalized path is one which has all '../', './' removed. Also it
- * is one which is in the 'standard' format for the native platform. On
+ * A normalized path is one which has all '../', './' removed. Also it is
+ * one which is in the 'standard' format for the native platform. On
* Unix, this means the path must be free of symbolic links/aliases, and
* on Windows it means we want the long form, with that long form's
* case-dependence (which gives us a unique, case-dependent path).
@@ -150,18 +149,12 @@ typedef struct FsPath {
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
- Tcl_Interp* interp; /* Interpreter to use */
- 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. */
+Tcl_Obj *
+TclFSNormalizeAbsolutePath(
+ Tcl_Interp *interp, /* Interpreter to use */
+ Tcl_Obj *pathPtr) /* Absolute path to normalize */
{
- ClientData clientData = NULL;
- CONST char *dirSep, *oldDirSep;
+ const char *dirSep, *oldDirSep;
int first = 1; /* Set to zero once we've passed the first
* directory separator - we can't use '..' to
* remove the volume in a path. */
@@ -169,6 +162,21 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
dirSep = TclGetString(pathPtr);
if (tclPlatform == TCL_PLATFORM_WINDOWS) {
+ if ( (dirSep[0] == '/' || dirSep[0] == '\\')
+ && (dirSep[1] == '/' || dirSep[1] == '\\')
+ && (dirSep[2] == '?')
+ && (dirSep[3] == '/' || dirSep[3] == '\\')) {
+ /* NT extended path */
+ dirSep += 4;
+
+ if ( (dirSep[0] == 'U' || dirSep[0] == 'u')
+ && (dirSep[1] == 'N' || dirSep[1] == 'n')
+ && (dirSep[2] == 'C' || dirSep[2] == 'c')
+ && (dirSep[3] == '/' || dirSep[3] == '\\')) {
+ /* NT extended UNC path */
+ dirSep += 4;
+ }
+ }
if (dirSep[0] != 0 && dirSep[1] == ':' &&
(dirSep[2] == '/' || dirSep[2] == '\\')) {
/* Do nothing */
@@ -189,7 +197,7 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
/*
* Scan forward from one directory separator to the next, checking for
- * '..' and '.' sequences which must be handled specially. In particular
+ * '..' and '.' sequences which must be handled specially. In particular
* handling of '..' can be complicated if the directory before is a link,
* since we will have to expand the link to be able to back up one level.
*/
@@ -216,12 +224,17 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
/*
* Need to skip '.' in the path.
*/
+ int curLen;
if (retVal == NULL) {
- CONST char *path = TclGetString(pathPtr);
+ const char *path = TclGetString(pathPtr);
retVal = Tcl_NewStringObj(path, dirSep - path);
Tcl_IncrRefCount(retVal);
}
+ Tcl_GetStringFromObj(retVal, &curLen);
+ if (curLen == 0) {
+ Tcl_AppendToObj(retVal, dirSep, 1);
+ }
dirSep += 2;
oldDirSep = dirSep;
if (dirSep[0] != 0 && dirSep[1] == '.') {
@@ -230,7 +243,7 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
continue;
}
if (dirSep[2] == '.' && IsSeparatorOrNull(dirSep[3])) {
- Tcl_Obj *link;
+ Tcl_Obj *linkObj;
int curLen;
char *linkStr;
@@ -239,28 +252,42 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
*/
if (retVal == NULL) {
- CONST char *path = TclGetString(pathPtr);
+ const char *path = TclGetString(pathPtr);
+
retVal = Tcl_NewStringObj(path, dirSep - path);
Tcl_IncrRefCount(retVal);
}
+ Tcl_GetStringFromObj(retVal, &curLen);
+ if (curLen == 0) {
+ Tcl_AppendToObj(retVal, dirSep, 1);
+ }
if (!first || (tclPlatform == TCL_PLATFORM_UNIX)) {
- link = Tcl_FSLink(retVal, NULL, 0);
- if (link != NULL) {
+ linkObj = 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 (linkObj != NULL) {
/*
* Got a link. Need to check if the link is relative
* or absolute, for those platforms where relative
* links exist.
*/
- if (tclPlatform != TCL_PLATFORM_WINDOWS &&
- Tcl_FSGetPathType(link) == TCL_PATH_RELATIVE) {
+ if (tclPlatform != TCL_PLATFORM_WINDOWS
+ && Tcl_FSGetPathType(linkObj)
+ == TCL_PATH_RELATIVE) {
/*
* We need to follow this link which is relative
* to retVal's directory. This means concatenating
* the link onto the directory of the path so far.
*/
- CONST char *path =
+ const char *path =
Tcl_GetStringFromObj(retVal, &curLen);
while (--curLen >= 0) {
@@ -268,19 +295,14 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
break;
}
}
- if (Tcl_IsShared(retVal)) {
- TclDecrRefCount(retVal);
- retVal = Tcl_DuplicateObj(retVal);
- Tcl_IncrRefCount(retVal);
- }
/*
* We want the trailing slash.
*/
Tcl_SetObjLength(retVal, curLen+1);
- Tcl_AppendObjToObj(retVal, link);
- TclDecrRefCount(link);
+ Tcl_AppendObjToObj(retVal, linkObj);
+ TclDecrRefCount(linkObj);
linkStr = Tcl_GetStringFromObj(retVal, &curLen);
} else {
/*
@@ -288,7 +310,12 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
*/
TclDecrRefCount(retVal);
- retVal = link;
+ if (Tcl_IsShared(linkObj)) {
+ retVal = Tcl_DuplicateObj(linkObj);
+ TclDecrRefCount(linkObj);
+ } else {
+ retVal = linkObj;
+ }
linkStr = Tcl_GetStringFromObj(retVal, &curLen);
/*
@@ -297,6 +324,7 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
if (tclPlatform == TCL_PLATFORM_WINDOWS) {
int i;
+
for (i = 0; i < curLen; i++) {
if (linkStr[i] == '\\') {
linkStr[i] = '/';
@@ -309,18 +337,28 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
}
/*
- * Either way, we now remove the last path element.
+ * Either way, we now remove the last path element (but
+ * not the first character of the path).
*/
while (--curLen >= 0) {
if (IsSeparatorOrNull(linkStr[curLen])) {
- Tcl_SetObjLength(retVal, curLen);
+ if (curLen) {
+ Tcl_SetObjLength(retVal, curLen);
+ } else {
+ Tcl_SetObjLength(retVal, 1);
+ }
break;
}
}
}
dirSep += 3;
oldDirSep = dirSep;
+
+ if ((curLen == 0) && (dirSep[0] != 0)) {
+ Tcl_SetObjLength(retVal, 0);
+ }
+
if (dirSep[0] != 0 && dirSep[1] == '.') {
goto again;
}
@@ -345,9 +383,9 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
/*
* Unfortunately, the platform-specific normalization code which
* will be called below has no way of dealing with the case where
- * an object is shared. It is expecting to modify an object in
- * place. So, we must duplicate this here to ensure an object
- * with a single ref-count.
+ * an object is shared. It is expecting to modify an object in
+ * place. So, we must duplicate this here to ensure an object with
+ * a single ref-count.
*
* If that changes in the future (e.g. the normalize proc is given
* one object and is able to return a different one), then we
@@ -361,12 +399,12 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
}
/*
- * 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) {
int len;
- CONST char *path = Tcl_GetStringFromObj(retVal, &len);
+ const char *path = Tcl_GetStringFromObj(retVal, &len);
if (len == 2 && path[0] != 0 && path[1] == ':') {
if (Tcl_IsShared(retVal)) {
@@ -380,7 +418,7 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
/*
* Now we have an absolute path, with no '..', '.' sequences, but it still
- * may not be in 'unique' form, depending on the platform. For instance,
+ * may not be in 'unique' form, depending on the platform. For instance,
* Unix is case-sensitive, so the path is ok. Windows is case-insensitive,
* and also has the weird 'longname/shortname' thing (e.g. C:/Program
* Files/ and C:/Progra~1/ are equivalent).
@@ -389,17 +427,14 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
* for normalizing a path.
*/
- TclFSNormalizeToUniquePath(interp, retVal, 0, &clientData);
+ TclFSNormalizeToUniquePath(interp, retVal, 0);
/*
* Since we know it is a normalized path, we can actually convert this
* object into an FsPath for greater efficiency
*/
- TclFSMakePathFromNormalized(interp, retVal, clientData);
- if (clientDataPtr != NULL) {
- *clientDataPtr = clientData;
- }
+ MakePathFromNormalized(interp, retVal);
/*
* This has a refCount of 1 for the caller, unlike many Tcl_Obj APIs.
@@ -427,8 +462,8 @@ TclFSNormalizeAbsolutePath(interp, pathPtr, clientDataPtr)
*/
Tcl_PathType
-Tcl_FSGetPathType(pathPtr)
- Tcl_Obj *pathPtr;
+Tcl_FSGetPathType(
+ Tcl_Obj *pathPtr)
{
return TclFSGetPathType(pathPtr, NULL, NULL);
}
@@ -457,28 +492,38 @@ Tcl_FSGetPathType(pathPtr)
*/
Tcl_PathType
-TclFSGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr)
- Tcl_Obj *pathPtr;
- Tcl_Filesystem **filesystemPtrPtr;
- int *driveNameLengthPtr;
+TclFSGetPathType(
+ Tcl_Obj *pathPtr,
+ const Tcl_Filesystem **filesystemPtrPtr,
+ int *driveNameLengthPtr)
{
+ FsPath *fsPathPtr;
+
if (Tcl_FSConvertToPathType(NULL, pathPtr) != TCL_OK) {
- return TclGetPathType(pathPtr, filesystemPtrPtr,
- driveNameLengthPtr, NULL);
- } else {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ return TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr,
+ NULL);
+ }
- if (fsPathPtr->cwdPtr != NULL) {
- if (PATHFLAGS(pathPtr) == 0) {
- return TCL_PATH_RELATIVE;
- }
- return TclFSGetPathType(fsPathPtr->cwdPtr, filesystemPtrPtr,
- driveNameLengthPtr);
- } else {
- return TclGetPathType(pathPtr, filesystemPtrPtr,
- driveNameLengthPtr, NULL);
- }
+ fsPathPtr = PATHOBJ(pathPtr);
+ if (fsPathPtr->cwdPtr == NULL) {
+ return TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr,
+ NULL);
}
+
+ if (PATHFLAGS(pathPtr) == 0) {
+ /* The path is not absolute... */
+#ifdef _WIN32
+ /* ... on Windows we must make another call to determine whether
+ * it's relative or volumerelative [Bug 2571597]. */
+ return TclGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr,
+ NULL);
+#else
+ /* On other systems, quickly deduce !absolute -> relative */
+ return TCL_PATH_RELATIVE;
+#endif
+ }
+ return TclFSGetPathType(fsPathPtr->cwdPtr, filesystemPtrPtr,
+ driveNameLengthPtr);
}
/*
@@ -494,7 +539,7 @@ TclFSGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr)
* - the extension ('file extension')
* - the root ('file root')
*
- * The 'portion' parameter dictates which of these to calculate. There
+ * The 'portion' parameter dictates which of these to calculate. There
* are a number of special cases both to be more efficient, and because
* the behaviour when given a path with only a single element is defined
* to require the expansion of that single element, where possible.
@@ -512,16 +557,16 @@ TclFSGetPathType(pathPtr, filesystemPtrPtr, driveNameLengthPtr)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-TclPathPart(interp, pathPtr, portion)
- Tcl_Interp *interp; /* Used for error reporting */
- Tcl_Obj *pathPtr; /* Path to take dirname of */
- Tcl_PathPart portion; /* Requested portion of name */
+Tcl_Obj *
+TclPathPart(
+ Tcl_Interp *interp, /* Used for error reporting */
+ Tcl_Obj *pathPtr, /* Path to take dirname of */
+ Tcl_PathPart portion) /* Requested portion of name */
{
if (pathPtr->typePtr == &tclFsPathType) {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- if (TclFSEpochOk(fsPathPtr->filesystemEpoch)
- && (PATHFLAGS(pathPtr) != 0)) {
+ FsPath *fsPathPtr = PATHOBJ(pathPtr);
+
+ if (PATHFLAGS(pathPtr) != 0) {
switch (portion) {
case TCL_PATH_DIRNAME: {
/*
@@ -532,11 +577,24 @@ TclPathPart(interp, pathPtr, portion)
* the standardPath code.
*/
- CONST char *rest = TclGetString(fsPathPtr->normPathPtr);
+ int numBytes;
+ const char *rest =
+ Tcl_GetStringFromObj(fsPathPtr->normPathPtr, &numBytes);
if (strchr(rest, '/') != NULL) {
goto standardPath;
}
+ /*
+ * If the joined-on bit is empty, then [file dirname] is
+ * documented to return all but the last non-empty element
+ * of the path, so we need to split apart the main part to
+ * get the right answer. We could do that here, but it's
+ * simpler to fall back to the standardPath code.
+ * [Bug 2710920]
+ */
+ if (numBytes == 0) {
+ goto standardPath;
+ }
if (tclPlatform == TCL_PLATFORM_WINDOWS
&& strchr(rest, '\\') != NULL) {
goto standardPath;
@@ -557,11 +615,24 @@ TclPathPart(interp, pathPtr, portion)
* we don't, and instead just use the standardPath code.
*/
- CONST char *rest = TclGetString(fsPathPtr->normPathPtr);
+ int numBytes;
+ const char *rest =
+ Tcl_GetStringFromObj(fsPathPtr->normPathPtr, &numBytes);
if (strchr(rest, '/') != NULL) {
goto standardPath;
}
+ /*
+ * If the joined-on bit is empty, then [file tail] is
+ * documented to return the last non-empty element
+ * of the path, so we need to split off the last element
+ * of the main part to get the right answer. We could do
+ * that here, but it's simpler to fall back to the
+ * standardPath code. [Bug 2710920]
+ */
+ if (numBytes == 0) {
+ goto standardPath;
+ }
if (tclPlatform == TCL_PLATFORM_WINDOWS
&& strchr(rest, '\\') != NULL) {
goto standardPath;
@@ -572,8 +643,7 @@ TclPathPart(interp, pathPtr, portion)
case TCL_PATH_EXTENSION:
return GetExtension(fsPathPtr->normPathPtr);
case TCL_PATH_ROOT: {
- /* Unimplemented */
- CONST char *fileName, *extension;
+ const char *fileName, *extension;
int length;
fileName = Tcl_GetStringFromObj(fsPathPtr->normPathPtr,
@@ -589,25 +659,18 @@ TclPathPart(interp, pathPtr, portion)
return pathPtr;
} else {
/*
- * Duplicate the object we were given and then trim off
- * the extension of the tail component of the path.
+ * 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.
*/
- FsPath *fsDupPtr;
- Tcl_Obj *root = Tcl_DuplicateObj(pathPtr);
+ Tcl_Obj *resultPtr =
+ TclNewFSPathObj(fsPathPtr->cwdPtr, fileName,
+ (int)(length - strlen(extension)));
- Tcl_IncrRefCount(root);
- fsDupPtr = (FsPath*) 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)));
- }
- return root;
+ Tcl_IncrRefCount(resultPtr);
+ return resultPtr;
}
}
default:
@@ -625,8 +688,7 @@ TclPathPart(interp, pathPtr, portion)
}
} else {
int splitElements;
- Tcl_Obj *splitPtr;
- Tcl_Obj *resultPtr;
+ Tcl_Obj *splitPtr, *resultPtr;
standardPath:
resultPtr = NULL;
@@ -634,7 +696,7 @@ TclPathPart(interp, pathPtr, portion)
return GetExtension(pathPtr);
} else if (portion == TCL_PATH_ROOT) {
int length;
- CONST char *fileName, *extension;
+ const char *fileName, *extension;
fileName = Tcl_GetStringFromObj(pathPtr, &length);
extension = TclGetExtension(fileName);
@@ -644,6 +706,7 @@ TclPathPart(interp, pathPtr, portion)
} else {
Tcl_Obj *root = Tcl_NewStringObj(fileName,
(int) (length - strlen(extension)));
+
Tcl_IncrRefCount(root);
return root;
}
@@ -692,7 +755,7 @@ TclPathPart(interp, pathPtr, portion)
resultPtr = Tcl_FSJoinPath(splitPtr, splitElements - 1);
} else if (splitElements == 0 ||
(Tcl_FSGetPathType(pathPtr) == TCL_PATH_RELATIVE)) {
- resultPtr = Tcl_NewStringObj(".", 1);
+ TclNewLiteralStringObj(resultPtr, ".");
} else {
Tcl_ListObjIndex(NULL, splitPtr, 0, &resultPtr);
}
@@ -707,11 +770,11 @@ TclPathPart(interp, pathPtr, portion)
* Simple helper function
*/
-static Tcl_Obj*
-GetExtension(pathPtr)
- Tcl_Obj *pathPtr;
+static Tcl_Obj *
+GetExtension(
+ Tcl_Obj *pathPtr)
{
- CONST char *tail, *extension;
+ const char *tail, *extension;
Tcl_Obj *ret;
tail = TclGetString(pathPtr);
@@ -759,54 +822,45 @@ GetExtension(pathPtr)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSJoinPath(listObj, elements)
- Tcl_Obj *listObj; /* Path elements to join, may have a zero
+Tcl_Obj *
+Tcl_FSJoinPath(
+ Tcl_Obj *listObj, /* Path elements to join, may have a zero
* reference count. */
- int elements; /* Number of elements to use (-1 = all) */
+ int elements) /* Number of elements to use (-1 = all) */
{
- Tcl_Obj *res;
- int i;
- Tcl_Filesystem *fsPtr = NULL;
+ Tcl_Obj *copy, *res;
+ int objc;
+ Tcl_Obj **objv;
- if (elements < 0) {
- if (Tcl_ListObjLength(NULL, listObj, &elements) != TCL_OK) {
- return NULL;
- }
- } else {
- /*
- * Just make sure it is a valid list.
- */
-
- int listTest;
-
- if (Tcl_ListObjLength(NULL, listObj, &listTest) != TCL_OK) {
- return NULL;
- }
+ if (Tcl_ListObjLength(NULL, listObj, &objc) != TCL_OK) {
+ return NULL;
+ }
- /*
- * Correct this if it is too large, otherwise we will waste our time
- * joining null elements to the path.
- */
+ elements = ((elements >= 0) && (elements <= objc)) ? elements : objc;
+ copy = TclListObjCopy(NULL, listObj);
+ Tcl_ListObjGetElements(NULL, listObj, &objc, &objv);
+ res = TclJoinPath(elements, objv);
+ Tcl_DecrRefCount(copy);
+ return res;
+}
- if (elements > listTest) {
- elements = listTest;
- }
- }
+Tcl_Obj *
+TclJoinPath(
+ int elements,
+ Tcl_Obj * const objv[])
+{
+ Tcl_Obj *res;
+ int i;
+ const Tcl_Filesystem *fsPtr = NULL;
res = NULL;
for (i = 0; i < elements; i++) {
- Tcl_Obj *elt;
- int driveNameLength;
+ int driveNameLength, strEltLen, length;
Tcl_PathType type;
- char *strElt;
- int strEltLen;
- int length;
- char *ptr;
+ char *strElt, *ptr;
Tcl_Obj *driveName = NULL;
-
- Tcl_ListObjIndex(NULL, listObj, i, &elt);
+ Tcl_Obj *elt = objv[i];
/*
* This is a special case where we can be much more efficient, where
@@ -817,18 +871,17 @@ Tcl_FSJoinPath(listObj, elements)
* could expand that in the future.
*/
- if ((i == (elements-2)) && (i == 0) && (elt->typePtr == &tclFsPathType)
- && !(elt->bytes != NULL && (elt->bytes[0] == '\0'))) {
- Tcl_Obj *tail;
- Tcl_PathType type;
+ if ((i == (elements-2)) && (i == 0)
+ && (elt->typePtr == &tclFsPathType)
+ && !((elt->bytes != NULL) && (elt->bytes[0] == '\0'))) {
+ Tcl_Obj *tailObj = objv[i+1];
- Tcl_ListObjIndex(NULL, listObj, i+1, &tail);
- type = TclGetPathType(tail, NULL, NULL, NULL);
+ type = TclGetPathType(tailObj, NULL, NULL, NULL);
if (type == TCL_PATH_RELATIVE) {
- CONST char *str;
+ const char *str;
int len;
- str = Tcl_GetStringFromObj(tail, &len);
+ str = Tcl_GetStringFromObj(tailObj, &len);
if (len == 0) {
/*
* This happens if we try to handle the root volume '/'.
@@ -843,7 +896,7 @@ Tcl_FSJoinPath(listObj, elements)
}
/*
- * If it doesn't begin with '.' and is a unix path or it a
+ * If it doesn't begin with '.' and is a unix path or it a
* windows path without backslashes, then we can be very
* efficient here. (In fact even a windows path with
* backslashes can be joined efficiently, but the path object
@@ -870,24 +923,22 @@ Tcl_FSJoinPath(listObj, elements)
/*
* 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) {
TclDecrRefCount(res);
}
- return tail;
+ return tailObj;
} else {
- CONST char *str;
- int len;
+ const char *str = TclGetString(tailObj);
- str = Tcl_GetStringFromObj(tail, &len);
if (tclPlatform == TCL_PLATFORM_WINDOWS) {
if (strchr(str, '\\') == NULL) {
if (res != NULL) {
TclDecrRefCount(res);
}
- return tail;
+ return tailObj;
}
}
}
@@ -923,20 +974,22 @@ Tcl_FSJoinPath(listObj, elements)
res = Tcl_NewStringObj(strElt, driveNameLength);
}
strElt += driveNameLength;
+ } else if (driveName != NULL) {
+ Tcl_DecrRefCount(driveName);
}
/*
* Optimisation block: if this is the last element to be examined, and
* it is absolute or the only element, and the drive-prefix was ok (if
* there is one), it might be that the path is already in a suitable
- * form to be returned. Then we can short-cut the rest of this
+ * form to be returned. Then we can short-cut the rest of this
* function.
*/
if ((driveName == NULL) && (i == (elements - 1))
&& (type != TCL_PATH_RELATIVE || res == NULL)) {
/*
- * It's the last path segment. Perform a quick check if the path
+ * It's the last path segment. Perform a quick check if the path
* is already in a suitable form.
*/
@@ -952,6 +1005,7 @@ Tcl_FSJoinPath(listObj, elements)
* We have a repeated file separator, which means the path
* is not in normalized form
*/
+
goto noQuickReturn;
}
ptr++;
@@ -961,8 +1015,8 @@ Tcl_FSJoinPath(listObj, elements)
}
/*
- * 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;
@@ -974,7 +1028,6 @@ Tcl_FSJoinPath(listObj, elements)
*/
noQuickReturn:
-
if (res == NULL) {
res = Tcl_NewObj();
ptr = Tcl_GetStringFromObj(res, &length);
@@ -1009,15 +1062,22 @@ Tcl_FSJoinPath(listObj, elements)
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);
- length++;
+ Tcl_GetStringFromObj(res, &length);
}
Tcl_SetObjLength(res, length + (int) strlen(strElt));
@@ -1069,56 +1129,55 @@ Tcl_FSJoinPath(listObj, elements)
*/
int
-Tcl_FSConvertToPathType(interp, pathPtr)
- Tcl_Interp *interp; /* Interpreter in which to store error message
+Tcl_FSConvertToPathType(
+ Tcl_Interp *interp, /* Interpreter in which to store error message
* (if necessary). */
- Tcl_Obj *pathPtr; /* Object to convert to a valid, current path
+ Tcl_Obj *pathPtr) /* Object to convert to a valid, current path
* type. */
{
/*
* While it is bad practice to examine an object's type directly, this is
- * actually the best thing to do here. The reason is that if we are
+ * actually the best thing to do here. The reason is that if we are
* converting this object to FsPath type for the first time, we don't need
- * to worry whether the 'cwd' has changed. On the other hand, if this
+ * to worry whether the 'cwd' has changed. On the other hand, if this
* object is already of FsPath type, and is a relative path, we do have to
- * worry about the cwd. If the cwd has changed, we must recompute the
+ * worry about the cwd. If the cwd has changed, we must recompute the
* path.
*/
if (pathPtr->typePtr == &tclFsPathType) {
- FsPath *fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- if (!TclFSEpochOk(fsPathPtr->filesystemEpoch)) {
- if (pathPtr->bytes == NULL) {
- UpdateStringOfFsPath(pathPtr);
- }
- FreeFsPathInternalRep(pathPtr);
- pathPtr->typePtr = NULL;
- return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType);
+ if (TclFSEpochOk(PATHOBJ(pathPtr)->filesystemEpoch)) {
+ return TCL_OK;
}
- return TCL_OK;
- /*
- * We used to have more complex code here:
- *
- * if (fsPathPtr->cwdPtr == NULL || PATHFLAGS(pathPtr) != 0) {
- * return TCL_OK;
- * } else {
- * if (TclFSCwdPointerEquals(&fsPathPtr->cwdPtr)) {
- * return TCL_OK;
- * } else {
- * if (pathPtr->bytes == NULL) {
- * UpdateStringOfFsPath(pathPtr);
- * }
- * FreeFsPathInternalRep(pathPtr);
- * pathPtr->typePtr = NULL;
- * return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType);
- * }
- * }
- *
- * But we no longer believe this is necessary.
- */
- } else {
- return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType);
+
+ if (pathPtr->bytes == NULL) {
+ UpdateStringOfFsPath(pathPtr);
+ }
+ FreeFsPathInternalRep(pathPtr);
}
+
+ return SetFsPathFromAny(interp, pathPtr);
+
+ /*
+ * We used to have more complex code here:
+ *
+ * FsPath *fsPathPtr = PATHOBJ(pathPtr);
+ * if (fsPathPtr->cwdPtr == NULL || PATHFLAGS(pathPtr) != 0) {
+ * return TCL_OK;
+ * } else {
+ * if (TclFSCwdPointerEquals(&fsPathPtr->cwdPtr)) {
+ * return TCL_OK;
+ * } else {
+ * if (pathPtr->bytes == NULL) {
+ * UpdateStringOfFsPath(pathPtr);
+ * }
+ * FreeFsPathInternalRep(pathPtr);
+ * return Tcl_ConvertToType(interp, pathPtr, &tclFsPathType);
+ * }
+ * }
+ *
+ * But we no longer believe this is necessary.
+ */
}
/*
@@ -1126,8 +1185,8 @@ Tcl_FSConvertToPathType(interp, pathPtr)
*/
static int
-IsSeparatorOrNull(ch)
- int ch;
+IsSeparatorOrNull(
+ int ch)
{
if (ch == 0) {
return 1;
@@ -1142,15 +1201,15 @@ IsSeparatorOrNull(ch)
}
/*
- * Helper function for SetFsPathFromAny. Returns position of first directory
- * delimiter in the path. If no separator is found, then returns the position
+ * Helper function for SetFsPathFromAny. Returns position of first directory
+ * delimiter in the path. If no separator is found, then returns the position
* of the end of the string.
*/
static int
-FindSplitPos(path, separator)
- CONST char *path;
- int separator;
+FindSplitPos(
+ const char *path,
+ int separator)
{
int count = 0;
switch (tclPlatform) {
@@ -1186,28 +1245,54 @@ FindSplitPos(path, separator)
* 'file dirname', 'file tail', etc.
*
* Assumptions:
- * 'dirPtr' must be an absolute path. 'len' may not be zero.
+ * 'dirPtr' must be an absolute path. 'len' may not be zero.
*
* Results:
* The new Tcl object, with refCount zero.
*
* Side effects:
- * Memory is allocated. 'dirPtr' gets an additional refCount.
+ * Memory is allocated. 'dirPtr' gets an additional refCount.
*
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
+Tcl_Obj *
+TclNewFSPathObj(
+ Tcl_Obj *dirPtr,
+ const char *addStrRep,
+ int len)
{
FsPath *fsPathPtr;
Tcl_Obj *pathPtr;
- ThreadSpecificData *tsdPtr;
+ const char *p;
+ int state = 0, count = 0;
+
+ /* [Bug 2806250] - this is only a partial solution of the problem.
+ * The PATHFLAGS != 0 representation assumes in many places that
+ * the "tail" part stored in the normPathPtr field is itself a
+ * relative path. Strings that begin with "~" are not relative paths,
+ * so we must prevent their storage in the normPathPtr field.
+ *
+ * More generally we ought to be testing "addStrRep" for any value
+ * that is not a relative path, but in an unconstrained VFS world
+ * that could be just about anything, and testing could be expensive.
+ * Since this routine plays a big role in [glob], anything that slows
+ * it down would be unwelcome. For now, continue the risk of further
+ * bugs when some Tcl_Filesystem uses otherwise relative path strings
+ * as absolute path strings. Sensible Tcl_Filesystems will avoid
+ * that by mounting on path prefixes like foo:// which cannot be the
+ * name of a file or directory read from a native [glob] operation.
+ */
+ if (addStrRep[0] == '~') {
+ Tcl_Obj *tail = Tcl_NewStringObj(addStrRep, len);
- tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ pathPtr = AppendPath(dirPtr, tail);
+ Tcl_DecrRefCount(tail);
+ return pathPtr;
+ }
pathPtr = Tcl_NewObj();
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+ fsPathPtr = ckalloc(sizeof(FsPath));
/*
* Set up the path.
@@ -1219,17 +1304,82 @@ TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
fsPathPtr->cwdPtr = dirPtr;
Tcl_IncrRefCount(dirPtr);
fsPathPtr->nativePathPtr = NULL;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
+ fsPathPtr->fsPtr = NULL;
+ fsPathPtr->filesystemEpoch = 0;
- PATHOBJ(pathPtr) = (VOID *) fsPathPtr;
+ SETPATHOBJ(pathPtr, fsPathPtr);
PATHFLAGS(pathPtr) = TCLPATH_APPENDED;
pathPtr->typePtr = &tclFsPathType;
pathPtr->bytes = NULL;
pathPtr->length = 0;
+ /*
+ * 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.
+ */
+ 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;
+ }
+ }
+ }
+ if (len == 0 && count) {
+ PATHFLAGS(pathPtr) |= TCLPATH_NEEDNORM;
+ }
+
return pathPtr;
}
+
+static Tcl_Obj *
+AppendPath(
+ Tcl_Obj *head,
+ Tcl_Obj *tail)
+{
+ int numBytes;
+ const char *bytes;
+ Tcl_Obj *copy = Tcl_DuplicateObj(head);
+
+ /*
+ * 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 TclJoinPath() too.
+ */
+ bytes = Tcl_GetStringFromObj(tail, &numBytes);
+ if (numBytes == 0) {
+ Tcl_AppendToObj(copy, "/", 1);
+ } else {
+ TclpNativeJoinPath(copy, bytes);
+ }
+ return copy;
+}
/*
*---------------------------------------------------------------------------
@@ -1240,15 +1390,9 @@ TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
*
* Takes a path and a directory, where we _assume_ both path and
* directory are absolute, normalized and that the path lies inside the
- * directory. Returns a Tcl_Obj representing filename of the path
+ * directory. Returns a Tcl_Obj representing filename of the path
* relative to the directory.
*
- * In the case where the resulting path would start with a '~', we take
- * special care to return an ordinary string. This means to use that
- * path (and not have it interpreted as a user name), one must prepend
- * './'. This may seem strange, but that is how 'glob' is currently
- * defined.
- *
* Results:
* NULL on error, otherwise a valid object, typically with refCount of
* zero, which it is assumed the caller will increment.
@@ -1259,73 +1403,20 @@ TclNewFSPathObj(Tcl_Obj *dirPtr, CONST char *addStrRep, int len)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-TclFSMakePathRelative(interp, pathPtr, cwdPtr)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *pathPtr; /* The path we have. */
- Tcl_Obj *cwdPtr; /* Make it relative to this. */
+Tcl_Obj *
+TclFSMakePathRelative(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ Tcl_Obj *pathPtr, /* The path we have. */
+ Tcl_Obj *cwdPtr) /* Make it relative to this. */
{
int cwdLen, len;
- CONST char *tempStr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
+ const char *tempStr;
if (pathPtr->typePtr == &tclFsPathType) {
- FsPath* fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- if (PATHFLAGS(pathPtr) != 0
- && fsPathPtr->cwdPtr == cwdPtr) {
- pathPtr = fsPathPtr->normPathPtr;
-
- /*
- * 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", (char *) NULL);
- }
- return NULL;
- }
- pathPtr->typePtr->updateStringProc(pathPtr);
- }
- TclFreeIntRep(pathPtr);
- }
+ FsPath *fsPathPtr = PATHOBJ(pathPtr);
- /*
- * Now pathPtr is a string object.
- */
-
- if (Tcl_GetString(pathPtr)[0] == '~') {
- /*
- * If the first character of the path is a tilde, we must just
- * return the path as is, to agree with the defined behaviour
- * of 'glob'.
- */
- return pathPtr;
- }
-
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
-
- /*
- * Circular reference, by design.
- */
-
- fsPathPtr->translatedPathPtr = pathPtr;
- fsPathPtr->normPathPtr = NULL;
- fsPathPtr->cwdPtr = cwdPtr;
- Tcl_IncrRefCount(cwdPtr);
- fsPathPtr->nativePathPtr = NULL;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
-
- PATHOBJ(pathPtr) = (VOID *) fsPathPtr;
- PATHFLAGS(pathPtr) = 0;
- pathPtr->typePtr = &tclFsPathType;
-
- return pathPtr;
+ if (PATHFLAGS(pathPtr) != 0 && fsPathPtr->cwdPtr == cwdPtr) {
+ return fsPathPtr->normPathPtr;
}
}
@@ -1368,7 +1459,7 @@ TclFSMakePathRelative(interp, pathPtr, cwdPtr)
/*
*---------------------------------------------------------------------------
*
- * TclFSMakePathFromNormalized --
+ * MakePathFromNormalized --
*
* Like SetFsPathFromAny, but assumes the given object is an absolute
* normalized path. Only for internal use.
@@ -1382,15 +1473,12 @@ TclFSMakePathRelative(interp, pathPtr, cwdPtr)
*---------------------------------------------------------------------------
*/
-int
-TclFSMakePathFromNormalized(interp, pathPtr, nativeRep)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *pathPtr; /* The object to convert. */
- ClientData nativeRep; /* The native rep for the object, if known
- * else NULL. */
+static int
+MakePathFromNormalized(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ Tcl_Obj *pathPtr) /* The object to convert. */
{
FsPath *fsPathPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
if (pathPtr->typePtr == &tclFsPathType) {
return TCL_OK;
@@ -1404,9 +1492,10 @@ TclFSMakePathFromNormalized(interp, pathPtr, nativeRep)
if (pathPtr->bytes == NULL) {
if (pathPtr->typePtr->updateStringProc == NULL) {
if (interp != NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "can't find object",
- "string representation", (char *) NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "can't find object string representation", -1));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH", "WTF",
+ NULL);
}
return TCL_ERROR;
}
@@ -1415,7 +1504,7 @@ TclFSMakePathFromNormalized(interp, pathPtr, nativeRep)
TclFreeIntRep(pathPtr);
}
- fsPathPtr = (FsPath*)ckalloc((unsigned)sizeof(FsPath));
+ fsPathPtr = ckalloc(sizeof(FsPath));
/*
* It's a pure normalized absolute path.
@@ -1429,11 +1518,12 @@ TclFSMakePathFromNormalized(interp, pathPtr, nativeRep)
fsPathPtr->normPathPtr = pathPtr;
fsPathPtr->cwdPtr = NULL;
- fsPathPtr->nativePathPtr = nativeRep;
- fsPathPtr->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
+ fsPathPtr->nativePathPtr = NULL;
+ fsPathPtr->fsPtr = NULL;
+ /* Remember the epoch under which we decided pathPtr was normalized */
+ fsPathPtr->filesystemEpoch = TclFSEpoch();
- PATHOBJ(pathPtr) = (VOID *) fsPathPtr;
+ SETPATHOBJ(pathPtr, fsPathPtr);
PATHFLAGS(pathPtr) = 0;
pathPtr->typePtr = &tclFsPathType;
@@ -1466,18 +1556,17 @@ TclFSMakePathFromNormalized(interp, pathPtr, nativeRep)
*/
Tcl_Obj *
-Tcl_FSNewNativePath(fromFilesystem, clientData)
- Tcl_Filesystem* fromFilesystem;
- ClientData clientData;
+Tcl_FSNewNativePath(
+ const Tcl_Filesystem *fromFilesystem,
+ ClientData clientData)
{
- Tcl_Obj *pathPtr;
+ Tcl_Obj *pathPtr = NULL;
FsPath *fsPathPtr;
- FilesystemRecord *fsFromPtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
- pathPtr = TclFSInternalToNormalized(fromFilesystem, clientData,
- &fsFromPtr);
+ if (fromFilesystem->internalToNormalizedProc != NULL) {
+ pathPtr = (*fromFilesystem->internalToNormalizedProc)(clientData);
+ }
if (pathPtr == NULL) {
return NULL;
}
@@ -1497,7 +1586,7 @@ Tcl_FSNewNativePath(fromFilesystem, clientData)
TclFreeIntRep(pathPtr);
}
- fsPathPtr = (FsPath *) ckalloc(sizeof(FsPath));
+ fsPathPtr = ckalloc(sizeof(FsPath));
fsPathPtr->translatedPathPtr = NULL;
@@ -1508,11 +1597,10 @@ Tcl_FSNewNativePath(fromFilesystem, clientData)
fsPathPtr->normPathPtr = pathPtr;
fsPathPtr->cwdPtr = NULL;
fsPathPtr->nativePathPtr = clientData;
- fsPathPtr->fsRecPtr = fsFromPtr;
- fsPathPtr->fsRecPtr->fileRefCount++;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
+ fsPathPtr->fsPtr = fromFilesystem;
+ fsPathPtr->filesystemEpoch = TclFSEpoch();
- PATHOBJ(pathPtr) = (VOID *) fsPathPtr;
+ SETPATHOBJ(pathPtr, fsPathPtr);
PATHFLAGS(pathPtr) = 0;
pathPtr->typePtr = &tclFsPathType;
@@ -1538,10 +1626,10 @@ Tcl_FSNewNativePath(fromFilesystem, clientData)
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSGetTranslatedPath(interp, pathPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathPtr;
+Tcl_Obj *
+Tcl_FSGetTranslatedPath(
+ Tcl_Interp *interp,
+ Tcl_Obj *pathPtr)
{
Tcl_Obj *retObj = NULL;
FsPath *srcFsPathPtr;
@@ -1549,14 +1637,37 @@ Tcl_FSGetTranslatedPath(interp, pathPtr)
if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
return NULL;
}
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ srcFsPathPtr = PATHOBJ(pathPtr);
if (srcFsPathPtr->translatedPathPtr == NULL) {
if (PATHFLAGS(pathPtr) != 0) {
- retObj = Tcl_FSGetNormalizedPath(interp, pathPtr);
+ /*
+ * We lack a translated path result, but we have a directory
+ * (cwdPtr) and a tail (normPathPtr), and if we join the
+ * translated version of cwdPtr to normPathPtr, we'll get the
+ * translated result we need, and can store it for future use.
+ */
+
+ Tcl_Obj *translatedCwdPtr = Tcl_FSGetTranslatedPath(interp,
+ srcFsPathPtr->cwdPtr);
+ if (translatedCwdPtr == NULL) {
+ return NULL;
+ }
+
+ retObj = Tcl_FSJoinToPath(translatedCwdPtr, 1,
+ &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 {
/*
- * It is a pure absolute, normalized path object. This is
- * something like being a 'pure list'. The object's string,
+ * It is a pure absolute, normalized path object. This is
+ * something like being a 'pure list'. The object's string,
* translatedPath and normalizedPath are all identical.
*/
@@ -1570,7 +1681,9 @@ Tcl_FSGetTranslatedPath(interp, pathPtr)
retObj = srcFsPathPtr->translatedPathPtr;
}
- Tcl_IncrRefCount(retObj);
+ if (retObj != NULL) {
+ Tcl_IncrRefCount(retObj);
+ }
return retObj;
}
@@ -1593,20 +1706,19 @@ Tcl_FSGetTranslatedPath(interp, pathPtr)
*---------------------------------------------------------------------------
*/
-CONST char*
-Tcl_FSGetTranslatedStringPath(interp, pathPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathPtr;
+const char *
+Tcl_FSGetTranslatedStringPath(
+ Tcl_Interp *interp,
+ Tcl_Obj *pathPtr)
{
Tcl_Obj *transPtr = Tcl_FSGetTranslatedPath(interp, pathPtr);
if (transPtr != NULL) {
int len;
- CONST char *result, *orig;
+ const char *orig = Tcl_GetStringFromObj(transPtr, &len);
+ char *result = ckalloc(len+1);
- orig = Tcl_GetStringFromObj(transPtr, &len);
- result = (char*) ckalloc((unsigned)(len+1));
- memcpy((VOID*) result, (VOID*) orig, (size_t) (len+1));
+ memcpy(result, orig, (size_t) len+1);
TclDecrRefCount(transPtr);
return result;
}
@@ -1627,23 +1739,23 @@ Tcl_FSGetTranslatedStringPath(interp, pathPtr)
* NULL or a valid path object pointer.
*
* Side effects:
- * New memory may be allocated. The Tcl 'errno' may be modified in the
+ * New memory may be allocated. The Tcl 'errno' may be modified in the
* process of trying to examine various path possibilities.
*
*---------------------------------------------------------------------------
*/
-Tcl_Obj*
-Tcl_FSGetNormalizedPath(interp, pathPtr)
- Tcl_Interp *interp;
- Tcl_Obj* pathPtr;
+Tcl_Obj *
+Tcl_FSGetNormalizedPath(
+ Tcl_Interp *interp,
+ Tcl_Obj *pathPtr)
{
FsPath *fsPathPtr;
if (Tcl_FSConvertToPathType(interp, pathPtr) != TCL_OK) {
return NULL;
}
- fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ fsPathPtr = PATHOBJ(pathPtr);
if (PATHFLAGS(pathPtr) != 0) {
/*
@@ -1652,70 +1764,77 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
*/
Tcl_Obj *dir, *copy;
- int cwdLen;
- int pathType;
- CONST char *cwdStr;
- ClientData clientData = NULL;
+ int tailLen, cwdLen, pathType;
pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
dir = Tcl_FSGetNormalizedPath(interp, fsPathPtr->cwdPtr);
if (dir == NULL) {
return NULL;
}
+ /* TODO: Figure out why this is needed. */
if (pathPtr->bytes == NULL) {
UpdateStringOfFsPath(pathPtr);
}
- copy = Tcl_DuplicateObj(dir);
- Tcl_IncrRefCount(copy);
+
+ Tcl_GetStringFromObj(fsPathPtr->normPathPtr, &tailLen);
+ if (tailLen) {
+ copy = AppendPath(dir, fsPathPtr->normPathPtr);
+ } else {
+ copy = Tcl_DuplicateObj(dir);
+ }
Tcl_IncrRefCount(dir);
+ Tcl_IncrRefCount(copy);
/*
* We now own a reference on both 'dir' and 'copy'
*/
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
+ (void) Tcl_GetStringFromObj(dir, &cwdLen);
+ cwdLen += (Tcl_GetString(copy)[cwdLen] == '/');
- /*
- * 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 cwdLen == 0 in this code path.
- */
+ /* Normalize the combined string. */
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- if (cwdStr[cwdLen-1] != '/' && cwdStr[cwdLen-1] != '\\') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- }
- Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
+ 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] ...
+ */
- /*
- * Normalize the combined string, but only starting after the end of
- * the previously normalized 'dir'. 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.
- */
+ Tcl_Obj *newCopy = TclFSNormalizeAbsolutePath(interp, copy);
- TclFSNormalizeToUniquePath(interp, copy, cwdLen-1,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : 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.
+ */
- /*
- * Now we need to construct the new path object
- */
+ TclFSNormalizeToUniquePath(interp, copy, cwdLen-1);
+ }
+
+ /* Now we need to construct the new path object. */
if (pathType == TCL_PATH_RELATIVE) {
- FsPath* origDirFsPathPtr;
Tcl_Obj *origDir = fsPathPtr->cwdPtr;
- origDirFsPathPtr = (FsPath*) PATHOBJ(origDir);
+
+ /*
+ * NOTE: here we are (dangerously?) assuming that origDir points
+ * 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.
+ */
+
+ FsPath *origDirFsPathPtr = PATHOBJ(origDir);
fsPathPtr->cwdPtr = origDirFsPathPtr->cwdPtr;
Tcl_IncrRefCount(fsPathPtr->cwdPtr);
@@ -1741,9 +1860,6 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
TclDecrRefCount(dir);
}
- if (clientData != NULL) {
- fsPathPtr->nativePathPtr = clientData;
- }
PATHFLAGS(pathPtr) = 0;
}
@@ -1757,60 +1873,32 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
UpdateStringOfFsPath(pathPtr);
}
FreeFsPathInternalRep(pathPtr);
- pathPtr->typePtr = NULL;
- if (Tcl_ConvertToType(interp, pathPtr, &tclFsPathType) != TCL_OK) {
+ if (SetFsPathFromAny(interp, pathPtr) != TCL_OK) {
return NULL;
}
- fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ fsPathPtr = PATHOBJ(pathPtr);
} else if (fsPathPtr->normPathPtr == NULL) {
int cwdLen;
Tcl_Obj *copy;
- CONST char *cwdStr;
- ClientData clientData = NULL;
-
- copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
- Tcl_IncrRefCount(copy);
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
- /*
- * 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 cwdLen == 0 in
- * this code path.
- */
+ copy = AppendPath(fsPathPtr->cwdPtr, pathPtr);
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- case TCL_PLATFORM_WINDOWS:
- if (cwdStr[cwdLen-1] != '/' && cwdStr[cwdLen-1] != '\\') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
- }
- Tcl_AppendObjToObj(copy, pathPtr);
+ (void) Tcl_GetStringFromObj(fsPathPtr->cwdPtr, &cwdLen);
+ cwdLen += (Tcl_GetString(copy)[cwdLen] == '/');
/*
* Normalize the combined string, but only starting after the end
* of the previously normalized 'dir'. This should be much faster!
*/
- TclFSNormalizeToUniquePath(interp, copy, cwdLen-1,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
+ TclFSNormalizeToUniquePath(interp, copy, cwdLen-1);
fsPathPtr->normPathPtr = copy;
- if (clientData != NULL) {
- fsPathPtr->nativePathPtr = clientData;
- }
+ Tcl_IncrRefCount(fsPathPtr->normPathPtr);
}
}
if (fsPathPtr->normPathPtr == NULL) {
- ClientData clientData = NULL;
Tcl_Obj *useThisCwd = NULL;
+ int pureNormalized = 1;
/*
* Since normPathPtr is NULL, but this is a valid path object, we know
@@ -1818,7 +1906,9 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
*/
Tcl_Obj *absolutePath = fsPathPtr->translatedPathPtr;
- CONST char *path = TclGetString(absolutePath);
+ const char *path = TclGetString(absolutePath);
+
+ Tcl_IncrRefCount(absolutePath);
/*
* We have to be a little bit careful here to avoid infinite loops
@@ -1827,7 +1917,20 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
* might loop back through here.
*/
- if (path[0] != '\0') {
+ 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.
+ *
+ * In particular, capture the cwd value and save so it can be
+ * stored in the cwdPtr field below.
+ */
+
+ useThisCwd = Tcl_FSGetCwd(interp);
+ } else {
/*
* We don't ask for the type of 'pathPtr' here, because that is
* not correct for our purposes when we have a path like '~'. Tcl
@@ -1845,23 +1948,28 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
return NULL;
}
+ pureNormalized = 0;
+ Tcl_DecrRefCount(absolutePath);
absolutePath = Tcl_FSJoinToPath(useThisCwd, 1, &absolutePath);
Tcl_IncrRefCount(absolutePath);
/*
* We have a refCount on the cwd.
*/
-#ifdef __WIN32__
+#ifdef _WIN32
} else if (type == TCL_PATH_VOLUME_RELATIVE) {
/*
* Only Windows has volume-relative paths.
*/
+
+ Tcl_DecrRefCount(absolutePath);
absolutePath = TclWinVolumeRelativeNormalize(interp,
path, &useThisCwd);
if (absolutePath == NULL) {
return NULL;
}
-#endif /* __WIN32__ */
+ pureNormalized = 0;
+#endif /* _WIN32 */
}
}
@@ -1870,21 +1978,20 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
*/
fsPathPtr->normPathPtr = TclFSNormalizeAbsolutePath(interp,
- absolutePath,
- (fsPathPtr->nativePathPtr == NULL ? &clientData : NULL));
- if (0 && (clientData != NULL)) {
- fsPathPtr->nativePathPtr =
- (*fsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc)(clientData);
- }
+ absolutePath);
/*
* Check if path is pure normalized (this can only be the case if it
* is an absolute path).
*/
- if (useThisCwd == NULL) {
- if (!strcmp(TclGetString(fsPathPtr->normPathPtr),
- TclGetString(pathPtr))) {
+ if (pureNormalized) {
+ int normPathLen, pathLen;
+ const char *normPath;
+
+ path = TclGetStringFromObj(pathPtr, &pathLen);
+ normPath = TclGetStringFromObj(fsPathPtr->normPathPtr, &normPathLen);
+ if ((pathLen == normPathLen) && !memcmp(path, normPath, pathLen)) {
/*
* The path was already normalized. Get rid of the duplicate.
*/
@@ -1898,16 +2005,17 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
fsPathPtr->normPathPtr = pathPtr;
}
- } else {
+ }
+ if (useThisCwd != NULL) {
/*
* We just need to free an object we allocated above for relative
* paths (this was returned by Tcl_FSJoinToPath above), and then
* of course store the cwd.
*/
- TclDecrRefCount(absolutePath);
fsPathPtr->cwdPtr = useThisCwd;
}
+ TclDecrRefCount(absolutePath);
}
return fsPathPtr->normPathPtr;
@@ -1919,7 +2027,7 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
* Tcl_FSGetInternalRep --
*
* Extract the internal representation of a given path object, in the
- * given filesystem. If the path object belongs to a different
+ * given filesystem. If the path object belongs to a different
* filesystem, we return NULL.
*
* If the internal representation is currently NULL, we attempt to
@@ -1936,38 +2044,38 @@ Tcl_FSGetNormalizedPath(interp, pathPtr)
*/
ClientData
-Tcl_FSGetInternalRep(pathPtr, fsPtr)
- Tcl_Obj* pathPtr;
- Tcl_Filesystem *fsPtr;
+Tcl_FSGetInternalRep(
+ Tcl_Obj *pathPtr,
+ const Tcl_Filesystem *fsPtr)
{
- FsPath* srcFsPathPtr;
+ FsPath *srcFsPathPtr;
if (Tcl_FSConvertToPathType(NULL, pathPtr) != TCL_OK) {
return NULL;
}
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ srcFsPathPtr = PATHOBJ(pathPtr);
/*
* We will only return the native representation for the caller's
- * filesystem. Otherwise we will simply return NULL. This means that
- * there must be a unique bi-directional mapping between paths and
- * filesystems, and that this mapping will not allow 'remapped' files --
- * files which are in one filesystem but mapped into another. Another way
- * of putting this is that 'stacked' filesystems are not allowed. We
- * recognise that this is a potentially useful feature for the future.
+ * filesystem. Otherwise we will simply return NULL. This means that there
+ * must be a unique bi-directional mapping between paths and filesystems,
+ * and that this mapping will not allow 'remapped' files -- files which
+ * are in one filesystem but mapped into another. Another way of putting
+ * this is that 'stacked' filesystems are not allowed. We recognise that
+ * this is a potentially useful feature for the future.
*
* Even something simple like a 'pass through' filesystem which logs all
* activity and passes the calls onto the native system would be nice, but
* not easily achievable with the current implementation.
*/
- if (srcFsPathPtr->fsRecPtr == NULL) {
+ if (srcFsPathPtr->fsPtr == NULL) {
/*
* This only usually happens in wrappers like TclpStat which create a
* string object and pass it to TclpObjStat. Code which calls the
* Tcl_FS.. functions should always have a filesystem already set.
* Whether this code path is legal or not depends on whether we decide
- * to allow external code to call the native filesystem directly. It
+ * to allow external code to call the native filesystem directly. It
* is at least safer to allow this sub-optimal routing.
*/
@@ -1980,8 +2088,8 @@ Tcl_FSGetInternalRep(pathPtr, fsPtr)
* (e.g. from the Tcl testsuite).
*/
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- if (srcFsPathPtr->fsRecPtr == NULL) {
+ srcFsPathPtr = PATHOBJ(pathPtr);
+ if (srcFsPathPtr->fsPtr == NULL) {
return NULL;
}
}
@@ -1989,12 +2097,12 @@ Tcl_FSGetInternalRep(pathPtr, fsPtr)
/*
* There is still one possibility we should consider; if the file belongs
* to a different filesystem, perhaps it is actually linked through to a
- * file in our own filesystem which we do care about. The way we can
- * check for this is we ask what filesystem this path belongs to.
+ * file in our own filesystem which we do care about. The way we can check
+ * for this is we ask what filesystem this path belongs to.
*/
- if (fsPtr != srcFsPathPtr->fsRecPtr->fsPtr) {
- Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathPtr);
+ if (fsPtr != srcFsPathPtr->fsPtr) {
+ const Tcl_Filesystem *actualFs = Tcl_FSGetFileSystemForPath(pathPtr);
if (actualFs == fsPtr) {
return Tcl_FSGetInternalRep(pathPtr, fsPtr);
@@ -2004,12 +2112,16 @@ Tcl_FSGetInternalRep(pathPtr, fsPtr)
if (srcFsPathPtr->nativePathPtr == NULL) {
Tcl_FSCreateInternalRepProc *proc;
- proc = srcFsPathPtr->fsRecPtr->fsPtr->createInternalRepProc;
+ char *nativePathPtr;
+ proc = srcFsPathPtr->fsPtr->createInternalRepProc;
if (proc == NULL) {
return NULL;
}
- srcFsPathPtr->nativePathPtr = (*proc)(pathPtr);
+
+ nativePathPtr = proc(pathPtr);
+ srcFsPathPtr = PATHOBJ(pathPtr);
+ srcFsPathPtr->nativePathPtr = nativePathPtr;
}
return srcFsPathPtr->nativePathPtr;
@@ -2034,17 +2146,17 @@ Tcl_FSGetInternalRep(pathPtr, fsPtr)
*/
int
-TclFSEnsureEpochOk(pathPtr, fsPtrPtr)
- Tcl_Obj* pathPtr;
- Tcl_Filesystem **fsPtrPtr;
+TclFSEnsureEpochOk(
+ Tcl_Obj *pathPtr,
+ const Tcl_Filesystem **fsPtrPtr)
{
- FsPath* srcFsPathPtr;
+ FsPath *srcFsPathPtr;
if (pathPtr->typePtr != &tclFsPathType) {
return TCL_OK;
}
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ srcFsPathPtr = PATHOBJ(pathPtr);
/*
* Check if the filesystem has changed in some way since this object's
@@ -2060,19 +2172,18 @@ TclFSEnsureEpochOk(pathPtr, fsPtrPtr)
UpdateStringOfFsPath(pathPtr);
}
FreeFsPathInternalRep(pathPtr);
- pathPtr->typePtr = NULL;
if (SetFsPathFromAny(NULL, pathPtr) != TCL_OK) {
return TCL_ERROR;
}
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ srcFsPathPtr = PATHOBJ(pathPtr);
}
/*
* Check whether the object is already assigned to a fs.
*/
- if (srcFsPathPtr->fsRecPtr != NULL) {
- *fsPtrPtr = srcFsPathPtr->fsRecPtr->fsPtr;
+ if (srcFsPathPtr->fsPtr != NULL) {
+ *fsPtrPtr = srcFsPathPtr->fsPtr;
}
return TCL_OK;
}
@@ -2094,13 +2205,12 @@ TclFSEnsureEpochOk(pathPtr, fsPtrPtr)
*/
void
-TclFSSetPathDetails(pathPtr, fsRecPtr, clientData)
- Tcl_Obj *pathPtr;
- FilesystemRecord *fsRecPtr;
- ClientData clientData;
+TclFSSetPathDetails(
+ Tcl_Obj *pathPtr,
+ const Tcl_Filesystem *fsPtr,
+ ClientData clientData)
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
- FsPath* srcFsPathPtr;
+ FsPath *srcFsPathPtr;
/*
* Make sure pathPtr is of the correct type.
@@ -2112,11 +2222,10 @@ TclFSSetPathDetails(pathPtr, fsRecPtr, clientData)
}
}
- srcFsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- srcFsPathPtr->fsRecPtr = fsRecPtr;
+ srcFsPathPtr = PATHOBJ(pathPtr);
+ srcFsPathPtr->fsPtr = fsPtr;
srcFsPathPtr->nativePathPtr = clientData;
- srcFsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
- fsRecPtr->fileRefCount++;
+ srcFsPathPtr->filesystemEpoch = TclFSEpoch();
}
/*
@@ -2137,11 +2246,11 @@ TclFSSetPathDetails(pathPtr, fsRecPtr, clientData)
*/
int
-Tcl_FSEqualPaths(firstPtr, secondPtr)
- Tcl_Obj* firstPtr;
- Tcl_Obj* secondPtr;
+Tcl_FSEqualPaths(
+ Tcl_Obj *firstPtr,
+ Tcl_Obj *secondPtr)
{
- char *firstStr, *secondStr;
+ const char *firstStr, *secondStr;
int firstLen, secondLen, tempErrno;
if (firstPtr == secondPtr) {
@@ -2151,9 +2260,9 @@ Tcl_FSEqualPaths(firstPtr, secondPtr)
if (firstPtr == NULL || secondPtr == NULL) {
return 0;
}
- firstStr = Tcl_GetStringFromObj(firstPtr, &firstLen);
- secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
- if ((firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0)) {
+ firstStr = TclGetStringFromObj(firstPtr, &firstLen);
+ secondStr = TclGetStringFromObj(secondPtr, &secondLen);
+ if ((firstLen == secondLen) && !memcmp(firstStr, secondStr, firstLen)) {
return 1;
}
@@ -2171,9 +2280,9 @@ Tcl_FSEqualPaths(firstPtr, secondPtr)
return 0;
}
- firstStr = Tcl_GetStringFromObj(firstPtr, &firstLen);
- secondStr = Tcl_GetStringFromObj(secondPtr, &secondLen);
- return (firstLen == secondLen) && (strcmp(firstStr, secondStr) == 0);
+ firstStr = TclGetStringFromObj(firstPtr, &firstLen);
+ secondStr = TclGetStringFromObj(secondPtr, &secondLen);
+ return ((firstLen == secondLen) && !memcmp(firstStr, secondStr, firstLen));
}
/*
@@ -2197,15 +2306,14 @@ Tcl_FSEqualPaths(firstPtr, secondPtr)
*/
static int
-SetFsPathFromAny(interp, pathPtr)
- Tcl_Interp *interp; /* Used for error reporting if not NULL. */
- Tcl_Obj *pathPtr; /* The object to convert. */
+SetFsPathFromAny(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ Tcl_Obj *pathPtr) /* The object to convert. */
{
int len;
FsPath *fsPathPtr;
Tcl_Obj *transPtr;
char *name;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
if (pathPtr->typePtr == &tclFsPathType) {
return TCL_OK;
@@ -2232,14 +2340,16 @@ SetFsPathFromAny(interp, pathPtr)
*/
if (name[0] == '~') {
- char *expandedUser;
Tcl_DString temp;
int split;
- char separator='/';
+ char separator = '/';
split = FindSplitPos(name, separator);
if (split != len) {
- /* We have multiple pieces '~user/foo/bar...' */
+ /*
+ * We have multiple pieces '~user/foo/bar...'
+ */
+
name[split] = '\0';
}
@@ -2252,7 +2362,7 @@ SetFsPathFromAny(interp, pathPtr)
* We have just '~'
*/
- CONST char *dir;
+ const char *dir;
Tcl_DString dirString;
if (split != len) {
@@ -2262,9 +2372,11 @@ SetFsPathFromAny(interp, pathPtr)
dir = TclGetEnv("HOME", &dirString);
if (dir == NULL) {
if (interp) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "couldn't find HOME environment ",
- "variable to expand path", (char *) NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "couldn't find HOME environment variable to"
+ " expand path", -1));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH",
+ "HOMELESS", NULL);
}
return TCL_ERROR;
}
@@ -2279,9 +2391,10 @@ SetFsPathFromAny(interp, pathPtr)
Tcl_DStringInit(&temp);
if (TclpGetUserHome(name+1, &temp) == NULL) {
if (interp != NULL) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "user \"", (name+1),
- "\" doesn't exist", (char *) NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "user \"%s\" doesn't exist", name+1));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH", "NOUSER",
+ NULL);
}
Tcl_DStringFree(&temp);
if (split != len) {
@@ -2294,8 +2407,7 @@ SetFsPathFromAny(interp, pathPtr)
}
}
- expandedUser = Tcl_DStringValue(&temp);
- transPtr = Tcl_NewStringObj(expandedUser, Tcl_DStringLength(&temp));
+ transPtr = TclDStringToObj(&temp);
if (split != len) {
/*
@@ -2321,7 +2433,7 @@ SetFsPathFromAny(interp, pathPtr)
objc--; objv++;
while (objc--) {
- TclpNativeJoinPath(transPtr, TclGetString(*objv++));
+ TclpNativeJoinPath(transPtr, Tcl_GetString(*objv++));
}
TclDecrRefCount(parts);
} else {
@@ -2340,65 +2452,46 @@ SetFsPathFromAny(interp, pathPtr)
transPtr = joined;
}
}
- Tcl_DStringFree(&temp);
} else {
- transPtr = Tcl_FSJoinToPath(pathPtr, 0, NULL);
- }
-
-#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);
- }
+ transPtr = TclJoinPath(1, &pathPtr);
}
-#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.
*/
- fsPathPtr = (FsPath *) ckalloc((unsigned)sizeof(FsPath));
+ fsPathPtr = ckalloc(sizeof(FsPath));
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->fsRecPtr = NULL;
- fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
+ fsPathPtr->fsPtr = NULL;
/*
* Free old representation before installing our new one.
*/
TclFreeIntRep(pathPtr);
- PATHOBJ(pathPtr) = (VOID *) fsPathPtr;
+ SETPATHOBJ(pathPtr, fsPathPtr);
PATHFLAGS(pathPtr) = 0;
pathPtr->typePtr = &tclFsPathType;
-
return TCL_OK;
}
static void
-FreeFsPathInternalRep(pathPtr)
- Tcl_Obj *pathPtr; /* Path object with internal rep to free. */
+FreeFsPathInternalRep(
+ Tcl_Obj *pathPtr) /* Path object with internal rep to free. */
{
- FsPath* fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
+ FsPath *fsPathPtr = PATHOBJ(pathPtr);
if (fsPathPtr->translatedPathPtr != NULL) {
if (fsPathPtr->translatedPathPtr != pathPtr) {
@@ -2414,83 +2507,73 @@ FreeFsPathInternalRep(pathPtr)
if (fsPathPtr->cwdPtr != NULL) {
TclDecrRefCount(fsPathPtr->cwdPtr);
}
- if (fsPathPtr->nativePathPtr != NULL && fsPathPtr->fsRecPtr != NULL) {
+ if (fsPathPtr->nativePathPtr != NULL && fsPathPtr->fsPtr != NULL) {
Tcl_FSFreeInternalRepProc *freeProc =
- fsPathPtr->fsRecPtr->fsPtr->freeInternalRepProc;
+ fsPathPtr->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);
+ ckfree(fsPathPtr);
+ pathPtr->typePtr = NULL;
}
static void
-DupFsPathInternalRep(srcPtr, copyPtr)
- Tcl_Obj *srcPtr; /* Path obj with internal rep to copy. */
- Tcl_Obj *copyPtr; /* Path obj with internal rep to set. */
+DupFsPathInternalRep(
+ Tcl_Obj *srcPtr, /* Path obj with internal rep to copy. */
+ Tcl_Obj *copyPtr) /* Path obj with internal rep to set. */
{
- FsPath* srcFsPathPtr = (FsPath*) PATHOBJ(srcPtr);
- FsPath* copyFsPathPtr = (FsPath*) ckalloc((unsigned)sizeof(FsPath));
+ FsPath *srcFsPathPtr = PATHOBJ(srcPtr);
+ FsPath *copyFsPathPtr = ckalloc(sizeof(FsPath));
- PATHOBJ(copyPtr) = (VOID *) copyFsPathPtr;
+ SETPATHOBJ(copyPtr, copyFsPathPtr);
- if (srcFsPathPtr->translatedPathPtr != NULL) {
+ if (srcFsPathPtr->translatedPathPtr == srcPtr) {
+ /* Cycle in src -> make cycle in copy. */
+ copyFsPathPtr->translatedPathPtr = copyPtr;
+ } else {
copyFsPathPtr->translatedPathPtr = srcFsPathPtr->translatedPathPtr;
- if (copyFsPathPtr->translatedPathPtr != copyPtr) {
+ if (copyFsPathPtr->translatedPathPtr != NULL) {
Tcl_IncrRefCount(copyFsPathPtr->translatedPathPtr);
}
- } else {
- copyFsPathPtr->translatedPathPtr = NULL;
}
- if (srcFsPathPtr->normPathPtr != NULL) {
+ if (srcFsPathPtr->normPathPtr == srcPtr) {
+ /* Cycle in src -> make cycle in copy. */
+ copyFsPathPtr->normPathPtr = copyPtr;
+ } else {
copyFsPathPtr->normPathPtr = srcFsPathPtr->normPathPtr;
- if (copyFsPathPtr->normPathPtr != copyPtr) {
+ if (copyFsPathPtr->normPathPtr != NULL) {
Tcl_IncrRefCount(copyFsPathPtr->normPathPtr);
}
- } else {
- copyFsPathPtr->normPathPtr = NULL;
}
- if (srcFsPathPtr->cwdPtr != NULL) {
- copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr;
+ copyFsPathPtr->cwdPtr = srcFsPathPtr->cwdPtr;
+ if (copyFsPathPtr->cwdPtr != NULL) {
Tcl_IncrRefCount(copyFsPathPtr->cwdPtr);
- } else {
- copyFsPathPtr->cwdPtr = NULL;
}
copyFsPathPtr->flags = srcFsPathPtr->flags;
- if (srcFsPathPtr->fsRecPtr != NULL
+ if (srcFsPathPtr->fsPtr != NULL
&& srcFsPathPtr->nativePathPtr != NULL) {
Tcl_FSDupInternalRepProc *dupProc =
- srcFsPathPtr->fsRecPtr->fsPtr->dupInternalRepProc;
+ srcFsPathPtr->fsPtr->dupInternalRepProc;
+
if (dupProc != NULL) {
copyFsPathPtr->nativePathPtr =
- (*dupProc)(srcFsPathPtr->nativePathPtr);
+ dupProc(srcFsPathPtr->nativePathPtr);
} else {
copyFsPathPtr->nativePathPtr = NULL;
}
} else {
copyFsPathPtr->nativePathPtr = NULL;
}
- copyFsPathPtr->fsRecPtr = srcFsPathPtr->fsRecPtr;
+ copyFsPathPtr->fsPtr = srcFsPathPtr->fsPtr;
copyFsPathPtr->filesystemEpoch = srcFsPathPtr->filesystemEpoch;
- if (copyFsPathPtr->fsRecPtr != NULL) {
- copyFsPathPtr->fsRecPtr->fileRefCount++;
- }
copyPtr->typePtr = &tclFsPathType;
}
@@ -2512,11 +2595,10 @@ DupFsPathInternalRep(srcPtr, copyPtr)
*/
static void
-UpdateStringOfFsPath(pathPtr)
- register Tcl_Obj *pathPtr; /* path obj with string rep to update. */
+UpdateStringOfFsPath(
+ register Tcl_Obj *pathPtr) /* path obj with string rep to update. */
{
- FsPath* fsPathPtr = (FsPath*) PATHOBJ(pathPtr);
- CONST char *cwdStr;
+ FsPath *fsPathPtr = PATHOBJ(pathPtr);
int cwdLen;
Tcl_Obj *copy;
@@ -2524,42 +2606,8 @@ UpdateStringOfFsPath(pathPtr)
Tcl_Panic("Called UpdateStringOfFsPath with invalid object");
}
- copy = Tcl_DuplicateObj(fsPathPtr->cwdPtr);
- Tcl_IncrRefCount(copy);
-
- cwdStr = Tcl_GetStringFromObj(copy, &cwdLen);
-
- /*
- * 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 cwdLen == 0 in this code path.
- */
+ copy = AppendPath(fsPathPtr->cwdPtr, fsPathPtr->normPathPtr);
- switch (tclPlatform) {
- case TCL_PLATFORM_UNIX:
- if (cwdStr[cwdLen-1] != '/') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- break;
-
- case TCL_PLATFORM_WINDOWS:
- /*
- * We need the extra 'cwdLen != 2', and ':' checks because a volume
- * relative path doesn't get a '/'. For example 'glob C:*cat*.exe'
- * will return 'C:cat32.exe'
- */
-
- if (cwdStr[cwdLen-1] != '/' && cwdStr[cwdLen-1] != '\\') {
- if (cwdLen != 2 || cwdStr[1] != ':') {
- Tcl_AppendToObj(copy, "/", 1);
- cwdLen++;
- }
- }
- break;
- }
-
- Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
pathPtr->bytes = Tcl_GetStringFromObj(copy, &cwdLen);
pathPtr->length = cwdLen;
copy->bytes = tclEmptyStringRep;
@@ -2589,9 +2637,9 @@ UpdateStringOfFsPath(pathPtr)
*/
int
-TclNativePathInFilesystem(pathPtr, clientDataPtr)
- Tcl_Obj *pathPtr;
- ClientData *clientDataPtr;
+TclNativePathInFilesystem(
+ Tcl_Obj *pathPtr,
+ ClientData *clientDataPtr)
{
/*
* A special case is required to handle the empty path "". This is a valid
@@ -2605,8 +2653,10 @@ TclNativePathInFilesystem(pathPtr, clientDataPtr)
/*
* We reject the empty path "".
*/
+
return -1;
}
+
/*
* Otherwise there is no way this path can be empty.
*/
@@ -2619,11 +2669,12 @@ TclNativePathInFilesystem(pathPtr, clientDataPtr)
int len;
- Tcl_GetStringFromObj(pathPtr, &len);
+ (void) Tcl_GetStringFromObj(pathPtr, &len);
if (len == 0) {
/*
* We reject the empty path "".
*/
+
return -1;
}
}