summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--generic/tclPathObj.c203
2 files changed, 108 insertions, 104 deletions
diff --git a/ChangeLog b/ChangeLog
index 45e23f8..7ef505f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2009-08-20 Don Porter <dgp@users.sourceforge.net>
+
+ * generic/tclPathObj.c: [Bug 2806250] Prevent the storage of strings
+ starting with ~ in the "tail" part (normPathPtr field) of the path
+ intrep when PATHFLAGS != 0. This establishes the assumptions relied
+ on elsewhere that the name stored there is a relative path. Also
+ refactored to make an AppendPath() routine instead of the cut/paste
+ stanzas that were littered throughout.
+
2009-08-20 Donal K. Fellows <dkf@users.sf.net>
* generic/tclCmdIL.c (Tcl_LsortObjCmd): Plug memory leak.
diff --git a/generic/tclPathObj.c b/generic/tclPathObj.c
index 282b1fc..60220b9 100644
--- a/generic/tclPathObj.c
+++ b/generic/tclPathObj.c
@@ -10,7 +10,7 @@
* 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.66.2.8 2009/08/18 14:43:58 dgp Exp $
+ * RCS: @(#) $Id: tclPathObj.c,v 1.66.2.9 2009/08/20 14:59:53 dgp Exp $
*/
#include "tclInt.h"
@@ -20,6 +20,7 @@
* Prototypes for functions defined later in this file.
*/
+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);
@@ -1286,6 +1287,30 @@ TclNewFSPathObj(
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);
+
+ pathPtr = AppendPath(dirPtr, tail);
+ Tcl_DecrRefCount(tail);
+ return pathPtr;
+ }
+
tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
pathPtr = Tcl_NewObj();
@@ -1351,6 +1376,49 @@ TclNewFSPathObj(
return pathPtr;
}
+
+static Tcl_Obj *
+AppendPath(
+ Tcl_Obj *head,
+ Tcl_Obj *tail)
+{
+ int numBytes;
+ const char *bytes;
+ Tcl_Obj *copy = Tcl_DuplicateObj(head);
+
+ bytes = Tcl_GetStringFromObj(copy, &numBytes);
+
+ /*
+ * 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.
+ */
+
+ 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;
+}
/*
*---------------------------------------------------------------------------
@@ -1364,11 +1432,6 @@ TclNewFSPathObj(
* 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.
@@ -1396,6 +1459,26 @@ TclFSMakePathRelative(
&& 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 fsRecPtr, 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.
*/
@@ -1419,16 +1502,6 @@ TclFSMakePathRelative(
* 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(sizeof(FsPath));
/*
@@ -1796,7 +1869,6 @@ Tcl_FSGetNormalizedPath(
Tcl_Obj *dir, *copy;
int cwdLen;
int pathType;
- const char *cwdStr;
ClientData clientData = NULL;
pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
@@ -1804,40 +1876,21 @@ Tcl_FSGetNormalizedPath(
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);
+
+ copy = AppendPath(dir, fsPathPtr->normPathPtr);
Tcl_IncrRefCount(dir);
+ Tcl_IncrRefCount(copy);
/*
* We now own a reference on both 'dir' and '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.
- */
-
- 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);
+ (void) Tcl_GetStringFromObj(dir, &cwdLen);
+ cwdLen += (Tcl_GetString(copy)[cwdLen] == '/');
/* Normalize the combined string. */
@@ -1937,35 +1990,12 @@ Tcl_FSGetNormalizedPath(
} 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);
+ copy = AppendPath(fsPathPtr->cwdPtr, pathPtr);
- /*
- * 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.
- */
-
- 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
@@ -2716,7 +2746,6 @@ UpdateStringOfFsPath(
register Tcl_Obj *pathPtr) /* path obj with string rep to update. */
{
FsPath *fsPathPtr = PATHOBJ(pathPtr);
- const char *cwdStr;
int cwdLen;
Tcl_Obj *copy;
@@ -2724,42 +2753,8 @@ UpdateStringOfFsPath(
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.
- */
-
- 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;
- }
+ copy = AppendPath(fsPathPtr->cwdPtr, fsPathPtr->normPathPtr);
- Tcl_AppendObjToObj(copy, fsPathPtr->normPathPtr);
pathPtr->bytes = Tcl_GetStringFromObj(copy, &cwdLen);
pathPtr->length = cwdLen;
copy->bytes = tclEmptyStringRep;