Attachment "2806250.patch" to
ticket [2806250fff]
added by
dgp
2009-08-20 04:49:20.
Index: generic/tclPathObj.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclPathObj.c,v
retrieving revision 1.66.2.8
diff -u -r1.66.2.8 tclPathObj.c
--- generic/tclPathObj.c 18 Aug 2009 14:43:58 -0000 1.66.2.8
+++ generic/tclPathObj.c 19 Aug 2009 21:43:14 -0000
@@ -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 @@
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 @@
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;
+}
/*
*---------------------------------------------------------------------------
@@ -1796,7 +1864,6 @@
Tcl_Obj *dir, *copy;
int cwdLen;
int pathType;
- const char *cwdStr;
ClientData clientData = NULL;
pathType = Tcl_FSGetPathType(fsPathPtr->cwdPtr);
@@ -1804,40 +1871,21 @@
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 +1985,12 @@
} 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 +2741,6 @@
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 +2748,8 @@
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;