tauvan.patch'þ¼ÕR}¼ÕRmBINµIndex: unix/tclUnixFCmd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixFCmd.c,v
retrieving revision 1.37
diff -u -p -r1.37 tclUnixFCmd.c
--- unix/tclUnixFCmd.c	26 Apr 2004 15:51:20 -0000	1.37
+++ unix/tclUnixFCmd.c	22 May 2004 22:09:58 -0000
@@ -1700,161 +1700,159 @@ GetModeFromPermString(interp, modeString
     return TCL_OK;
 }
 
+static void
+findPath(const char *orgPath, char **retPath, int *idx) {
+    struct stat curStatBuf;
+    char *newName = NULL;
+    char *pathBuffer = *retPath;
+    int pBdex = 0, oPdex = *idx, redex;
+    int oPlen = strlen(orgPath);
+    
+    errno = 0;
+    
+    if (pathBuffer == NULL) {
+        pathBuffer = (char *)malloc(2*MAXPATHLEN + 1);
+        bzero(pathBuffer, MAXPATHLEN);
+        *retPath = pathBuffer;
+    }
+    
+    if (oPdex == 0 && orgPath[0] != '/') {
+
+        /* home code needed for running cmdAH.test */
+        char *home = getenv("HOME");
+        int i = strlen(home);
+        if (i != oPlen) {
+            for (i--; i >= 0; i--) {
+                if (home[i + 1] != orgPath[i] && home[i + 1] != '/')
+                    break;
+            }
+            if (i < 0) {
+                oPlen = strlen(home);
+                do { pathBuffer[pBdex] = home[oPdex]; pBdex++; oPdex++; }
+                while (oPdex < oPlen);
+                pathBuffer[pBdex] = 0;
+                return;
+            }
+        } else if (orgPath[0] == '~') {
+            do { pathBuffer[pBdex] = home[oPdex]; pBdex++; oPdex++; }
+            while (oPdex < oPlen);
+            pathBuffer[pBdex] = 0;
+            return;
+        }
+
+        getcwd(pathBuffer, (2*MAXPATHLEN));
+        pBdex = *idx = strlen(pathBuffer);
+        
+        pathBuffer[pBdex++] = '/';
+        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
+        while (oPdex < oPlen);
+        pathBuffer[pBdex] = 0;
+        findPath(pathBuffer, &newName, idx);
+        free(pathBuffer);
+        *retPath = newName;
+        return;
+    }
+
+    while (pBdex < oPdex) pathBuffer[pBdex++] = orgPath[pBdex];
+    oPdex = pBdex;
+findnext:
+    redex = oPdex + 1;
+    do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
+    while (orgPath[oPdex] != '/' && oPdex < oPlen);
+    if (oPdex == oPlen) {
+        *idx = redex - 1;
+        return;
+    }
+    lstat(pathBuffer, &curStatBuf);
+    if (S_ISLNK(curStatBuf.st_mode)) {
+        int normPathLen;
+        
+        normPathLen = readlink(pathBuffer, (&pathBuffer[redex]), (2*MAXPATHLEN - redex));
+        pathBuffer[redex + normPathLen] = 0;
+        if (pathBuffer[redex] == '/') {
+            pBdex = 0;
+            do { pathBuffer[pBdex] = pathBuffer[redex]; pBdex++; redex++; }
+            while (--normPathLen > 0);
+        } else if (pathBuffer[redex] == '.' && pathBuffer[redex + 1] == '.' && pathBuffer[redex + 2] == '/') {
+            pBdex = redex;
+            do {
+                pBdex -= 2;
+                redex += 3;
+                normPathLen -= 3;
+                while (pathBuffer[pBdex] != '/' && pBdex > 0) pBdex--;
+            } while (pathBuffer[redex] == '.' && pathBuffer[redex + 1] == '.' && pathBuffer[redex + 2] == '/' && pBdex > 0);
+            if (pBdex == 0) { redex--; normPathLen++; }
+            do { pathBuffer[pBdex] = pathBuffer[redex]; pBdex++; redex++; }
+            while (--normPathLen > 0);
+        } else {
+            pBdex = redex + normPathLen;
+        }
+        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
+        while (oPdex < oPlen);
+        pathBuffer[pBdex] = 0;
+        *idx = 0;
+        findPath(pathBuffer, &newName, idx);
+        free(pathBuffer);
+        *retPath = newName;
+        return;
+    }
+    if (errno) {
+        do { pathBuffer[pBdex] = orgPath[oPdex]; pBdex++; oPdex++; }
+        while (oPdex < oPlen);
+        pathBuffer[pBdex] = 0;
+        *idx = redex - 1;
+        return;
+    }
+    goto findnext;
+}
+
 /*
- *---------------------------------------------------------------------------
+ *  TclObjNormalizePath.c
  *
- * TclpObjNormalizePath --
- *
- *	This function scans through a path specification and replaces
- *	it, in place, with a normalized version.  A normalized version
- *	is one in which all symlinks in the path are replaced with
- *	their expanded form (except a symlink at the very end of the
- *	path).
- *
- * Results:
- *	The new 'nextCheckpoint' value, giving as far as we could
- *	understand in the path.
- *
- * Side effects:
- *	The pathPtr string, is modified.
- *
- *---------------------------------------------------------------------------
+ *  	This function takes a path string and replaces it
+ *	with a normalized path. The entering path string
+ *	must be void of ../, ./ sequences. The returned path
+ *	will be normalized up to nextCheckpoint, which points to
+ *	either the last path separator or the offending separator.
+ *	The end word of the path will not be normalized, it is
+ *	totally ignored.
+ *  
+ *  Results:
+ *	pathPtr altered with result of normalization
+ *	nextCheckpoint points to end separator or offending,
+ *	ie.. the last good separator prior to problem path
+ *
+ *  Side effects:
+ *	if nextCheckpoint points to end separator, do not
+ *	make assumption of a fully qualified path.
+ *	Occurance: when "join" adds to a file instead of directory.
+ *	The file will be normalized but is not a directory.
+ *
+ *  Returns:
+ *	nextCheckpoint
+ *	pathPtr
  */
+
 int
 TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
     Tcl_Interp *interp;
     Tcl_Obj *pathPtr;
     int nextCheckpoint;
 {
-    char *currentPathEndPosition;
     int pathLen;
-    char cur;
+    char *resolvedPath = NULL;
     char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
-#ifndef NO_REALPATH
-    char normPath[MAXPATHLEN];
-    Tcl_DString ds;
-    CONST char *nativePath; 
-#endif
-    /* 
-     * We add '1' here because if nextCheckpoint is zero we know
-     * that '/' exists, and if it isn't zero, it must point at
-     * a directory separator which we also know exists.
-     */
-    currentPathEndPosition = path + nextCheckpoint;
-    if (*currentPathEndPosition == '/') {
-	currentPathEndPosition++;
-    }
-
-#ifndef NO_REALPATH
-    /* For speed, try to get the entire path in one go */
-    if (nextCheckpoint == 0) {
-        char *lastDir = strrchr(currentPathEndPosition, '/');
-	if (lastDir != NULL) {
-	    nativePath = Tcl_UtfToExternalDString(NULL, path, 
-						  lastDir - path, &ds);
-	    if (Realpath(nativePath, normPath) != NULL) {
-		nextCheckpoint = lastDir - path;
-		goto wholeStringOk;
-	    }
-	}
-    }
-    /* Else do it the slow way */
-#endif
-    
-    while (1) {
-	cur = *currentPathEndPosition;
-	if ((cur == '/') && (path != currentPathEndPosition)) {
-	    /* Reached directory separator */
-	    Tcl_DString ds;
-	    CONST char *nativePath;
-	    int accessOk;
-
-	    nativePath = Tcl_UtfToExternalDString(NULL, path, 
-		    currentPathEndPosition - path, &ds);
-	    accessOk = access(nativePath, F_OK);
-	    Tcl_DStringFree(&ds);
-	    if (accessOk != 0) {
-		/* File doesn't exist */
-		break;
-	    }
-	    /* Update the acceptable point */
-	    nextCheckpoint = currentPathEndPosition - path;
-	} else if (cur == 0) {
-	    /* Reached end of string */
-	    break;
-	}
-	currentPathEndPosition++;
-    }
-    /* 
-     * We should really now convert this to a canonical path.  We do
-     * that with 'realpath' if we have it available.  Otherwise we could
-     * step through every single path component, checking whether it is a 
-     * symlink, but that would be a lot of work, and most modern OSes 
-     * have 'realpath'.
-     */
-#ifndef NO_REALPATH
-    /* 
-     * If we only had '/foo' or '/' then we never increment nextCheckpoint
-     * and we don't need or want to go through 'Realpath'.  Also, on some
-     * platforms, passing an empty string to 'Realpath' will give us the
-     * normalized pwd, which is not what we want at all!
-     */
-    if (nextCheckpoint == 0) return 0;
-    
-    nativePath = Tcl_UtfToExternalDString(NULL, path, nextCheckpoint, &ds);
-    if (Realpath(nativePath, normPath) != NULL) {
-	int newNormLen;
-	wholeStringOk:
-	newNormLen = strlen(normPath);
-	if ((newNormLen == Tcl_DStringLength(&ds))
-		&& (strcmp(normPath, nativePath) == 0)) {
-	    /* String is unchanged */
-	    Tcl_DStringFree(&ds);
-	    /*
-	     * Enable this to have the native FS claim normalization of
-	     * the whole path for existing files.  That would permit the
-	     * caller to declare normalization complete without calls to
-	     * additional filesystems.  Saving lots of calls is probably
-	     * worth the extra access() time here.  When no other FS's
-	     * are registered though, things are less clear.
-	     *
-	    if (0 == access(normPath, F_OK)) {
-		return pathLen;
-	    }
-	     */
-	    return nextCheckpoint;
-	}
-	
-	/* 
-	 * Free up the native path and put in its place the
-	 * converted, normalized path.
-	 */
-	Tcl_DStringFree(&ds);
-	Tcl_ExternalToUtfDString(NULL, normPath, (int) newNormLen, &ds);
-
-	if (path[nextCheckpoint] != '\0') {
-	    /* not at end, append remaining path */
-	    int normLen = Tcl_DStringLength(&ds);
-	    Tcl_DStringAppend(&ds, path + nextCheckpoint,
-		    pathLen - nextCheckpoint);
-	    /* 
-	     * We recognise up to and including the directory
-	     * separator.
-	     */	
-	    nextCheckpoint = normLen + 1;
-	} else {
-	    /* We recognise the whole string */ 
-	    nextCheckpoint = Tcl_DStringLength(&ds);
-	}
-	/* 
-	 * Overwrite with the normalized path.
-	 */
-	Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds),
-		Tcl_DStringLength(&ds));
-    }
-    Tcl_DStringFree(&ds);
-#endif	/* !NO_REALPATH */
 
+    nextCheckpoint = 0;
+    findPath(path, &resolvedPath, &nextCheckpoint);
+    if (resolvedPath) {
+        Tcl_DString ds;
+        pathLen = strlen(resolvedPath);
+        Tcl_ExternalToUtfDString(NULL, resolvedPath, pathLen, &ds);
+        Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
+        Tcl_DStringFree(&ds);
+        free(resolvedPath);
+    }
     return nextCheckpoint;
 }
 
