summaryrefslogtreecommitdiffstats
path: root/win/tclWinFile.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r--win/tclWinFile.c297
1 files changed, 184 insertions, 113 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index b12fb2a..8c34b24 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWinFile.c,v 1.32 2002/06/13 10:43:41 vincentdarley Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.33 2002/06/21 14:22:29 vincentdarley Exp $
*/
//#define _WIN32_WINNT 0x0500
@@ -162,7 +162,7 @@ static int WinIsDrive(CONST char *name, int nameLen);
static Tcl_Obj* WinReadLink(CONST TCHAR* LinkSource);
static Tcl_Obj* WinReadLinkDirectory(CONST TCHAR* LinkDirectory);
static int WinLink(CONST TCHAR* LinkSource, CONST TCHAR* LinkTarget,
- int linkType);
+ int linkAction);
static int WinSymLinkDirectory(CONST TCHAR* LinkDirectory,
CONST TCHAR* LinkTarget);
extern Tcl_Filesystem nativeFilesystem;
@@ -177,10 +177,10 @@ extern Tcl_Filesystem nativeFilesystem;
*--------------------------------------------------------------------
*/
static int
-WinLink(LinkSource, LinkTarget, linkType)
+WinLink(LinkSource, LinkTarget, linkAction)
CONST TCHAR* LinkSource;
CONST TCHAR* LinkTarget;
- int linkType;
+ int linkAction;
{
WCHAR tempFileName[MAX_PATH];
TCHAR* tempFilePart;
@@ -220,21 +220,31 @@ WinLink(LinkSource, LinkTarget, linkType)
Tcl_SetErrno(ENOTDIR);
return -1;
}
- if (linkType == 1) {
+ if (linkAction & TCL_CREATE_HARD_LINK) {
+ if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) {
+ TclWinConvertError(GetLastError());
+ return -1;
+ }
+ return 0;
+ } else if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
/* Can't symlink files */
+ Tcl_SetErrno(ENOTDIR);
return -1;
- }
- if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) {
- TclWinConvertError(GetLastError());
+ } else {
+ Tcl_SetErrno(ENODEV);
return -1;
}
- return 0;
} else {
- if (linkType == 2) {
+ if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {
+ return WinSymLinkDirectory(LinkSource, LinkTarget);
+ } else if (linkAction & TCL_CREATE_HARD_LINK) {
/* Can't hard link directories */
+ Tcl_SetErrno(EISDIR);
+ return -1;
+ } else {
+ Tcl_SetErrno(ENODEV);
return -1;
}
- return WinSymLinkDirectory(LinkSource, LinkTarget);
}
}
@@ -1855,10 +1865,10 @@ TclpObjLstat(pathPtr, statPtr)
#ifdef S_IFLNK
Tcl_Obj*
-TclpObjLink(pathPtr, toPtr, linkType)
+TclpObjLink(pathPtr, toPtr, linkAction)
Tcl_Obj *pathPtr;
Tcl_Obj *toPtr;
- int linkType;
+ int linkAction;
{
if (toPtr != NULL) {
int res;
@@ -1867,9 +1877,7 @@ TclpObjLink(pathPtr, toPtr, linkType)
if (LinkSource == NULL || LinkTarget == NULL) {
return NULL;
}
- /* We don't recognise these codes */
- if (linkType < 0 || linkType > 2) return NULL;
- res = WinLink(LinkSource, LinkTarget, linkType);
+ res = WinLink(LinkSource, LinkTarget, linkAction);
if (res == 0) {
return toPtr;
} else {
@@ -1948,11 +1956,11 @@ TclpFilesystemPathType(pathObjPtr)
}
+#if 0
/*
* 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);
@@ -2023,15 +2031,24 @@ void WinGetLongPathName(CONST TCHAR* pszOriginal, Tcl_DString *dsPtr) {
#endif
+/*
+ * We have two different implementations of file normalization which
+ * can be turned on or off here. They should both agree for all files,
+ * and timings show the 'TCLWIN_NEW_NORM' version is about 10% faster.
+ */
+#define TCLWIN_NEW_NORM
/*
*---------------------------------------------------------------------------
*
* TclpObjNormalizePath --
*
- * This function scans through a path specification and replaces
- * it, in place, with a normalized version. On windows this
- * means using the 'longname'.
+ * This function scans through a path specification and replaces it,
+ * in place, with a normalized version. On Windows NT/2000/XP this
+ * means using the 'longname', and expanding any symbolic links
+ * contained within the path. On Win95/98/ME it means using the
+ * short form of the name (because the APIs to get at the long form
+ * are much too slow).
*
* Results:
* The new 'nextCheckpoint' value, giving as far as we could
@@ -2100,9 +2117,10 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
*lastValidPathEnd = '\0';
}
/*
- * 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'.
+ * 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'.
*/
/* Copy over the valid part of the path and find its length */
@@ -2129,115 +2147,165 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
} else {
/* We're on WinNT or 2000 or XP */
CONST char *nativePath;
-#if 0
- /*
- * We don't use this simpler version, because the speed
- * increase does not seem significant at present and the version
- * below is thoroughly debugged.
- */
- 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;
Tcl_Obj *temp = NULL;
WIN32_FILE_ATTRIBUTE_DATA data;
+ int isDrive = 1;
+#ifdef TCLWIN_NEW_NORM
+ /* This will hold the normalized string */
+ Tcl_DString dsNorm;
+ Tcl_DStringInit(&dsNorm);
+#endif
nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
- /*
- * We currently don't use this because we have to check
- * each path component for reparse points.
- */
- if (0 && (*tclWinProcs->getFileAttributesExProc)(nativePath,
- GetFileExInfoStandard,
- &data) == TRUE) {
- currentPathEndPosition = path + pathLen;
- nextCheckpoint = pathLen;
- lastValidPathEnd = currentPathEndPosition;
- Tcl_DStringFree(&ds);
- } else {
- int isDrive = 1;
- 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);
+ 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;
+ }
- /* File does exist if we get here */
-
- /*
- * Check for symlinks, except at last component
- * of path (we don't follow final symlinks)
- */
- if (cur != 0 && !isDrive && (data.dwFileAttributes
- & FILE_ATTRIBUTE_REPARSE_POINT)) {
- Tcl_Obj *to = WinReadLinkDirectory(nativePath);
- if (to != NULL) {
- /* Read the reparse point ok */
- Tcl_GetStringFromObj(to, &pathLen);
- nextCheckpoint = pathLen;
- Tcl_AppendToObj(to, currentPathEndPosition, -1);
- path = Tcl_GetString(to);
- currentPathEndPosition = path + nextCheckpoint;
- if (temp != NULL) {
- Tcl_DecrRefCount(temp);
- }
- temp = to;
+ /*
+ * File 'nativePath' does exist if we get here. We
+ * now want to check if it is a symlink and otherwise
+ * continue with the rest of the path.
+ */
+
+ /*
+ * Check for symlinks, except at last component
+ * of path (we don't follow final symlinks). Also
+ * a drive (C:/) for example, may sometimes have
+ * the reparse flag set for some reason I don't
+ * understand. We therefore don't perform this
+ * check for drives.
+ */
+ if (cur != 0 && !isDrive && (data.dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ Tcl_Obj *to = WinReadLinkDirectory(nativePath);
+ if (to != NULL) {
+ /* Read the reparse point ok */
+ Tcl_GetStringFromObj(to, &pathLen);
+ nextCheckpoint = 0; /* pathLen */
+ Tcl_AppendToObj(to, currentPathEndPosition, -1);
+ /* Convert link to forward slashes */
+ for (path = Tcl_GetString(to); *path != 0; path++) {
+ if (*path == '\\') *path = '/';
+ }
+ path = Tcl_GetString(to);
+ currentPathEndPosition = path + nextCheckpoint;
+ if (temp != NULL) {
+ Tcl_DecrRefCount(temp);
}
+ temp = to;
+ /* Reset variables so we can restart normalization */
+ isDrive = 1;
+#ifdef TCLWIN_NEW_NORM
+ Tcl_DStringFree(&dsNorm);
+ Tcl_DStringInit(&dsNorm);
+#endif
+ Tcl_DStringFree(&ds);
+ continue;
}
-
- Tcl_DStringFree(&ds);
- lastValidPathEnd = currentPathEndPosition;
- if (0) {
- WIN32_FIND_DATAT fdata;
- CONST TCHAR *nativeName;
- (*tclWinProcs->findFirstFileProc)(nativePath, &fdata);
- nativeName = (TCHAR *) fdata.w.cAlternateFileName;
- if (fdata.w.cFileName[0] != '\0') {
- nativeName = (TCHAR *) fdata.w.cFileName;
- }
+ }
+#ifdef TCLWIN_NEW_NORM
+ /*
+ * Now we convert the tail of the current path to its
+ * 'long form', and append it to 'dsNorm' which holds
+ * the current normalized path
+ */
+ if (isDrive) {
+ WCHAR drive = ((WCHAR*)nativePath)[0];
+ if (drive >= L'a') {
+ drive -= (L'a' - L'A');
+ ((WCHAR*)nativePath)[0] = drive;
}
- if (cur == 0) {
- break;
+ Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds));
+ } else {
+ WIN32_FIND_DATAW fData;
+ HANDLE handle;
+
+ handle = FindFirstFileW((WCHAR*)nativePath, &fData);
+ if (handle == INVALID_HANDLE_VALUE) {
+ /* This is usually the '/' in 'c:/' at end of string */
+ Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
+ sizeof(WCHAR));
+ } else {
+ WCHAR *nativeName;
+ if (fData.cFileName[0] != '\0') {
+ nativeName = fData.cFileName;
+ } else {
+ nativeName = fData.cAlternateFileName;
+ }
+ FindClose(handle);
+ Tcl_DStringAppend(&dsNorm,(CONST char*)L"/",
+ sizeof(WCHAR));
+ Tcl_DStringAppend(&dsNorm,(TCHAR*)nativeName,
+ wcslen(nativeName)*sizeof(WCHAR));
}
- /*
- * If we get here, we've got past one directory
- * delimiter, so we know it is no longer a drive
- */
- isDrive = 0;
}
- currentPathEndPosition++;
+#endif
+ Tcl_DStringFree(&ds);
+ lastValidPathEnd = currentPathEndPosition;
+ if (cur == 0) {
+ break;
+ }
+ /*
+ * If we get here, we've got past one directory
+ * delimiter, so we know it is no longer a drive
+ */
+ isDrive = 0;
}
- nextCheckpoint = currentPathEndPosition - path;
+ currentPathEndPosition++;
}
+ nextCheckpoint = currentPathEndPosition - path;
+
if (lastValidPathEnd != NULL) {
- Tcl_Obj *tmpPathPtr;
+#ifdef TCLWIN_NEW_NORM
+ /*
+ * Concatenate the normalized string in dsNorm with the
+ * tail of the path which we didn't recognise. The
+ * string in dsNorm is in the native encoding, so we
+ * have to convert it to Utf.
+ */
+ Tcl_DString dsTemp;
+ Tcl_WinTCharToUtf(Tcl_DStringValue(&dsNorm),
+ Tcl_DStringLength(&dsNorm), &dsTemp);
+ nextCheckpoint = Tcl_DStringLength(&dsTemp);
+ if (*lastValidPathEnd != 0) {
+ /* Not the end of the string */
+ int len;
+ char *path;
+ Tcl_Obj *tmpPathPtr;
+ tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsTemp),
+ nextCheckpoint);
+ Tcl_AppendToObj(tmpPathPtr, lastValidPathEnd, -1);
+ path = Tcl_GetStringFromObj(tmpPathPtr, &len);
+ Tcl_SetStringObj(pathPtr, path, len);
+ Tcl_DecrRefCount(tmpPathPtr);
+ } else {
+ /* End of string was reached above */
+ Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&dsTemp),
+ nextCheckpoint);
+ }
+ Tcl_DStringFree(&dsTemp);
+#else
/*
* The leading end of the path description was acceptable to
- * us. We therefore convert it to its long form, and return
+ * us. We therefore convert it to its long form (which is
+ * used by Tcl as a unique normalized form), and return
* that.
*/
- Tcl_Obj* objPtr = NULL;
int endOfString;
+ Tcl_Obj *tmpPathPtr;
+ Tcl_Obj* objPtr = NULL;
int useLength = lastValidPathEnd - path;
if (*lastValidPathEnd == 0) {
tmpPathPtr = Tcl_NewStringObj(path, useLength);
@@ -2269,7 +2337,10 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
Tcl_DecrRefCount(objPtr);
}
Tcl_DecrRefCount(tmpPathPtr);
+#endif
}
+#ifdef TCLWIN_NEW_NORM
+ Tcl_DStringFree(&dsNorm);
#endif
}
return nextCheckpoint;