summaryrefslogtreecommitdiffstats
path: root/win/tclWinFCmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinFCmd.c')
-rw-r--r--win/tclWinFCmd.c292
1 files changed, 237 insertions, 55 deletions
diff --git a/win/tclWinFCmd.c b/win/tclWinFCmd.c
index 744ab2b..72f1678 100644
--- a/win/tclWinFCmd.c
+++ b/win/tclWinFCmd.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFCmd.c,v 1.15 2001/10/22 17:10:00 vincentdarley Exp $
+ * RCS: @(#) $Id: tclWinFCmd.c,v 1.16 2001/11/19 17:45:12 vincentdarley Exp $
*/
#include "tclWinInt.h"
@@ -1711,6 +1711,82 @@ TclpObjListVolumes(void)
return resultPtr;
}
+/*
+ * This function could be thoroughly tested and then substituted in
+ * below to speed up file normalization on Windows NT/2000/XP
+ */
+#if 0
+
+void WinGetLongPathName(CONST TCHAR* origPath, Tcl_DString *dsPtr);
+
+#define IsDirSep(a) (a == '/' || a == '\\')
+
+void WinGetLongPathName(CONST TCHAR* pszOriginal, Tcl_DString *dsPtr) {
+ TCHAR szResult[_MAX_PATH * 2 + 1];
+
+ TCHAR* pchResult = szResult;
+ const TCHAR* pchScan = pszOriginal;
+ WIN32_FIND_DATA wfd;
+
+ /* Do Drive Letter check... */
+ if (pchScan[0] && pchScan[1] == ':') {
+ /* Copy drive letter and colon, ensuring drive is upper case. */
+ char drive = *pchScan++;
+ *pchResult++ = (drive < 97 ? drive : drive - 32);
+ *pchResult++ = *pchScan++;
+ } else if (IsDirSep(pchScan[0]) && IsDirSep(pchScan[1])) {
+ /* Copy \\ and machine name. */
+ *pchResult++ = *pchScan++;
+ *pchResult++ = *pchScan++;
+ while (*pchScan && !IsDirSep(*pchScan)) {
+ *pchResult++ = *pchScan++;
+ }
+ /*
+ * Note that the code below will fail since FindFirstFile
+ * on a UNC path seems not to work on directory name searches?
+ */
+ }
+
+ if (!IsDirSep(*pchScan)) {
+ while ((*pchResult++ = *pchScan++) != '\0');
+ } else {
+ /* Now loop through directories and files... */
+ while (IsDirSep(*pchScan)) {
+ char* pchReplace;
+ const TCHAR* pchEnd;
+ HANDLE hFind;
+
+ *pchResult++ = *pchScan++;
+ pchReplace = pchResult;
+
+ pchEnd = pchScan;
+ while (*pchEnd && !IsDirSep(*pchEnd)) {
+ *pchResult++ = *pchEnd++;
+ }
+
+ *pchResult = '\0';
+
+ /* Now run this through FindFirstFile... */
+ hFind = FindFirstFileA(szResult, &wfd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ FindClose(hFind);
+ strcpy(pchReplace, wfd.cFileName);
+ pchResult = pchReplace + strlen(pchReplace);
+ } else {
+ /* Copy rest of input path & end. */
+ strcat(pchResult, pchEnd);
+ break;
+ }
+ pchScan = pchEnd;
+ }
+ }
+ /* Copy it over */
+ Tcl_ExternalToUtfDString(NULL, szResult, -1, dsPtr);
+}
+
+#endif
+
+
/*
*---------------------------------------------------------------------------
*
@@ -1737,73 +1813,179 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
Tcl_Obj *pathPtr;
int nextCheckpoint;
{
- char *currentPathEndPosition;
char *lastValidPathEnd = NULL;
- char *path = Tcl_GetString(pathPtr);
-
- currentPathEndPosition = path + nextCheckpoint;
+ Tcl_DString ds;
+ int pathLen;
- while (1) {
- char cur = *currentPathEndPosition;
- if ((cur == '/' || cur == 0) && (path != currentPathEndPosition)) {
- /* Reached directory separator, or end of string */
- Tcl_DString ds;
- DWORD attr;
- char * nativePath;
- nativePath = Tcl_WinUtfToTChar(path, currentPathEndPosition - path,
- &ds);
- attr = (*tclWinProcs->getFileAttributesProc)(nativePath);
- Tcl_DStringFree(&ds);
-
- if (attr == 0xffffffff) {
- /* File doesn't exist */
+ char *path = Tcl_GetStringFromObj(pathPtr, &pathLen);
+
+ if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) {
+ Tcl_DString eDs;
+ char *nativePath;
+ int nativeLen;
+
+ nativePath = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
+ nativeLen = Tcl_DStringLength(&ds);
+
+ /* We're on Windows 95/98 */
+ lastValidPathEnd = nativePath + Tcl_DStringLength(&ds);
+
+ while (1) {
+ DWORD res = GetShortPathNameA(nativePath, nativePath, 1+nativeLen);
+ if (res != 0) {
+ /* We found an ok path */
break;
}
- lastValidPathEnd = currentPathEndPosition;
- /* File does exist */
- if (cur == 0) {
- break;
+ /* Undo the null-termination we put in before */
+ if (lastValidPathEnd != (nativePath + nativeLen)) {
+ *lastValidPathEnd = '/';
+ }
+ /*
+ * The path doesn't exist. Back up the path, one component
+ * (directory/file) at a time, until one does exist.
+ */
+ while (1) {
+ char cur;
+ lastValidPathEnd--;
+ if (lastValidPathEnd == nativePath) {
+ /* We didn't accept any of the path */
+ Tcl_DStringFree(&ds);
+ return nextCheckpoint;
+ }
+ cur = *(--lastValidPathEnd);
+ if (cur == '/' || cur == '\\') {
+ /* Reached directory separator */
+ break;
+ }
}
+ /* Temporarily terminate the string */
+ *lastValidPathEnd = '\0';
}
- currentPathEndPosition++;
- }
- nextCheckpoint = currentPathEndPosition - path;
- if (lastValidPathEnd != NULL) {
- Tcl_Obj *tmpPathPtr;
/*
- * The leading end of the path description was acceptable to
- * us. We therefore convert it to its long form, and return
- * that.
+ * If we get here, we found a valid path, which we've converted to
+ * short form, and the valid string ends at or before 'lastValidPathEnd'
+ * and the invalid string starts at 'lastValidPathEnd'.
*/
- Tcl_Obj* objPtr = NULL;
- int endOfString;
- int useLength = lastValidPathEnd - path;
- if (*lastValidPathEnd == 0) {
- tmpPathPtr = Tcl_NewStringObj(path, useLength);
- endOfString = 1;
- } else {
- tmpPathPtr = Tcl_NewStringObj(path, useLength + 1);
- endOfString = 0;
+
+ /* Copy over the valid part of the path and find its length */
+ path = Tcl_ExternalToUtfDString(NULL, nativePath, -1, &eDs);
+ if (path[1] == ':') {
+ if (path[0] >= 'a' && path[0] <= 'z') {
+ /* Make uppercase */
+ path[0] -= 32;
+ }
+ }
+ nextCheckpoint = Tcl_DStringLength(&eDs);
+ Tcl_SetStringObj(pathPtr, path, Tcl_DStringLength(&eDs));
+ Tcl_DStringFree(&eDs);
+ if (lastValidPathEnd != (nativePath + nativeLen)) {
+ *lastValidPathEnd = '/';
+ /* Now copy over the invalid (i.e. non-existent) part of the path */
+ path = Tcl_ExternalToUtfDString(NULL, lastValidPathEnd, -1, &eDs);
+ Tcl_AppendToObj(pathPtr, path, Tcl_DStringLength(&eDs));
+ Tcl_DStringFree(&eDs);
}
+ Tcl_DStringFree(&ds);
+ } else {
+ /* We're on WinNT or 2000 or XP */
+ char *nativePath;
+#if 0
/*
- * If this returns an error, we have a strange situation; the
- * file exists, but we can't get its long name. We will have
- * to assume the name we have is ok.
+ * We don't use this simpler version, because the speed
+ * increase does not seem significant at present and the version
+ * below is thoroughly debugged.
*/
- Tcl_IncrRefCount(tmpPathPtr);
- if (ConvertFileNameFormat(interp, 0, tmpPathPtr, 1, &objPtr) == TCL_OK) {
- int len;
- (void) Tcl_GetStringFromObj(objPtr,&len);
- if (!endOfString) {
- /* Be nice and fix the string before we clear it */
- Tcl_AppendToObj(objPtr, lastValidPathEnd, -1);
+ int nativeLen;
+ Tcl_DString eDs;
+ nativePath = Tcl_UtfToExternalDString(NULL, path, -1, &ds);
+ nativeLen = Tcl_DStringLength(&ds);
+ WinGetLongPathName(nativePath, &eDs);
+ /*
+ * We need to add code here to calculate the new value of
+ * 'nextCheckpoint' -- i.e. the longest part of the path
+ * which is an existing file.
+ */
+ Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&eDs), Tcl_DStringLength(&eDs));
+ Tcl_DStringFree(&eDs);
+ Tcl_DStringFree(&ds);
+#else
+ char *currentPathEndPosition;
+ WIN32_FILE_ATTRIBUTE_DATA data;
+ nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
+
+ if ((*tclWinProcs->getFileAttributesExProc)(nativePath,
+ GetFileExInfoStandard,
+ &data) == TRUE) {
+ currentPathEndPosition = path + pathLen;
+ nextCheckpoint = pathLen;
+ lastValidPathEnd = currentPathEndPosition;
+ Tcl_DStringFree(&ds);
+ } else {
+ Tcl_DStringFree(&ds);
+ currentPathEndPosition = path + nextCheckpoint;
+ while (1) {
+ char cur = *currentPathEndPosition;
+ if ((cur == '/' || cur == 0) && (path != currentPathEndPosition)) {
+ /* Reached directory separator, or end of string */
+ nativePath = Tcl_WinUtfToTChar(path, currentPathEndPosition - path,
+ &ds);
+ if ((*tclWinProcs->getFileAttributesExProc)(nativePath,
+ GetFileExInfoStandard, &data) != TRUE) {
+ /* File doesn't exist */
+ Tcl_DStringFree(&ds);
+ break;
+ }
+ Tcl_DStringFree(&ds);
+
+ lastValidPathEnd = currentPathEndPosition;
+ /* File does exist */
+ if (cur == 0) {
+ break;
+ }
+ }
+ currentPathEndPosition++;
+ }
+ nextCheckpoint = currentPathEndPosition - path;
+ }
+ if (lastValidPathEnd != NULL) {
+ Tcl_Obj *tmpPathPtr;
+ /*
+ * The leading end of the path description was acceptable to
+ * us. We therefore convert it to its long form, and return
+ * that.
+ */
+ Tcl_Obj* objPtr = NULL;
+ int endOfString;
+ int useLength = lastValidPathEnd - path;
+ if (*lastValidPathEnd == 0) {
+ tmpPathPtr = Tcl_NewStringObj(path, useLength);
+ endOfString = 1;
+ } else {
+ tmpPathPtr = Tcl_NewStringObj(path, useLength + 1);
+ endOfString = 0;
}
- nextCheckpoint += (len - useLength);
- path = Tcl_GetStringFromObj(objPtr,&len);
- Tcl_SetStringObj(pathPtr,path, len);
- Tcl_DecrRefCount(objPtr);
+ /*
+ * If this returns an error, we have a strange situation; the
+ * file exists, but we can't get its long name. We will have
+ * to assume the name we have is ok.
+ */
+ Tcl_IncrRefCount(tmpPathPtr);
+ if (ConvertFileNameFormat(interp, 0, tmpPathPtr, 1, &objPtr) == TCL_OK) {
+ int len;
+ (void) Tcl_GetStringFromObj(objPtr,&len);
+ if (!endOfString) {
+ /* Be nice and fix the string before we clear it */
+ Tcl_AppendToObj(objPtr, lastValidPathEnd, -1);
+ }
+ nextCheckpoint += (len - useLength);
+ path = Tcl_GetStringFromObj(objPtr,&len);
+ Tcl_SetStringObj(pathPtr,path, len);
+ Tcl_DecrRefCount(objPtr);
+ }
+ Tcl_DecrRefCount(tmpPathPtr);
}
- Tcl_DecrRefCount(tmpPathPtr);
+#endif
}
return nextCheckpoint;
}
+