diff options
Diffstat (limited to 'win/tclWinFile.c')
| -rw-r--r-- | win/tclWinFile.c | 1491 | 
1 files changed, 619 insertions, 872 deletions
| diff --git a/win/tclWinFile.c b/win/tclWinFile.c index 61e73c6..5761eeb 100644 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c @@ -10,18 +10,13 @@   *   * 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.95.2.4 2010/04/19 07:40:41 nijtmans Exp $   */ -/* #define _WIN32_WINNT	0x0500 */ -  #include "tclWinInt.h"  #include "tclFileSystem.h"  #include <winioctl.h> -#include <sys/stat.h>  #include <shlobj.h> -#include <lmaccess.h>		/* For TclpGetUserHome(). */ +#include <lm.h>		/* For TclpGetUserHome(). */  /*   * The number of 100-ns intervals between the Windows system epoch (1601-01-01 @@ -145,28 +140,6 @@ typedef struct {      WCHAR dummyBuf[MAX_PATH * 3];  } DUMMY_REPARSE_BUFFER; -#if defined(_MSC_VER) && (_MSC_VER <= 1100) -#undef	HAVE_NO_FINDEX_ENUMS -#define HAVE_NO_FINDEX_ENUMS -#elif !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0400) -#undef	HAVE_NO_FINDEX_ENUMS -#define HAVE_NO_FINDEX_ENUMS -#endif - -#ifdef HAVE_NO_FINDEX_ENUMS -/* These two aren't in VC++ 5.2 headers */ -typedef enum _FINDEX_INFO_LEVELS { -    FindExInfoStandard, -    FindExInfoMaxInfoLevel -} FINDEX_INFO_LEVELS; -typedef enum _FINDEX_SEARCH_OPS { -    FindExSearchNameMatch, -    FindExSearchLimitToDirectories, -    FindExSearchLimitToDevices, -    FindExSearchMaxSearchOp -} FINDEX_SEARCH_OPS; -#endif /* HAVE_NO_FINDEX_ENUMS */ -  /*   * Other typedefs required by this code.   */ @@ -174,14 +147,6 @@ typedef enum _FINDEX_SEARCH_OPS {  static time_t		ToCTime(FILETIME fileTime);  static void		FromCTime(time_t posixTime, FILETIME *fileTime); -typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC( -	LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr); - -typedef NET_API_STATUS NET_API_FUNCTION NETAPIBUFFERFREEPROC(LPVOID Buffer); - -typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC( -	LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr); -  /*   * Declarations for local functions defined in this file:   */ @@ -194,7 +159,7 @@ static unsigned short	NativeStatMode(DWORD attr, int checkLinks,  			    int isExec);  static int		NativeIsExec(const TCHAR *path);  static int		NativeReadReparse(const TCHAR *LinkDirectory, -			    REPARSE_DATA_BUFFER *buffer); +			    REPARSE_DATA_BUFFER *buffer, DWORD desiredAccess);  static int		NativeWriteReparse(const TCHAR *LinkDirectory,  			    REPARSE_DATA_BUFFER *buffer);  static int		NativeMatchType(int isDrive, DWORD attr, @@ -207,6 +172,7 @@ static int		WinLink(const TCHAR *LinkSource,  			    const TCHAR *LinkTarget, int linkAction);  static int		WinSymLinkDirectory(const TCHAR *LinkDirectory,  			    const TCHAR *LinkTarget); +MODULE_SCOPE void	tclWinDebugPanic(const char *format, ...);  /*   *-------------------------------------------------------------------- @@ -224,7 +190,7 @@ WinLink(      const TCHAR *linkTargetPath,      int linkAction)  { -    WCHAR tempFileName[MAX_PATH]; +    TCHAR tempFileName[MAX_PATH];      TCHAR *tempFilePart;      DWORD attr; @@ -232,8 +198,8 @@ WinLink(       * Get the full path referenced by the target.       */ -    if (!(*tclWinProcs->getFullPathNameProc)(linkTargetPath, MAX_PATH, -	    tempFileName, &tempFilePart)) { +    if (!GetFullPathName(linkTargetPath, MAX_PATH, tempFileName, +	    &tempFilePart)) {  	/*  	 * Invalid file.  	 */ @@ -246,7 +212,7 @@ WinLink(       * Make sure source file doesn't exist.       */ -    attr = (*tclWinProcs->getFileAttributesProc)(linkSourcePath); +    attr = GetFileAttributes(linkSourcePath);      if (attr != INVALID_FILE_ATTRIBUTES) {  	Tcl_SetErrno(EEXIST);  	return -1; @@ -256,8 +222,8 @@ WinLink(       * Get the full path referenced by the source file/directory.       */ -    if (!(*tclWinProcs->getFullPathNameProc)(linkSourcePath, MAX_PATH, -	    tempFileName, &tempFilePart)) { +    if (!GetFullPathName(linkSourcePath, MAX_PATH, tempFileName, +	    &tempFilePart)) {  	/*  	 * Invalid file.  	 */ @@ -270,43 +236,36 @@ WinLink(       * Check the target.       */ -    attr = (*tclWinProcs->getFileAttributesProc)(linkTargetPath); +    attr = GetFileAttributes(linkTargetPath);      if (attr == INVALID_FILE_ATTRIBUTES) {  	/*  	 * The target doesn't exist.  	 */  	TclWinConvertError(GetLastError()); -	return -1; -      } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {  	/*  	 * It is a file.  	 */ -	if (tclWinProcs->createHardLinkProc == NULL) { -	    Tcl_SetErrno(ENOTDIR); -	    return -1; -	} -  	if (linkAction & TCL_CREATE_HARD_LINK) { -	    if (!(*tclWinProcs->createHardLinkProc)(linkSourcePath, -		    linkTargetPath, NULL)) { -		TclWinConvertError(GetLastError()); -		return -1; +	    if (CreateHardLink(linkSourcePath, linkTargetPath, NULL)) { +		/* +		 * Success! +		 */ + +		return 0;  	    } -	    return 0; +	    TclWinConvertError(GetLastError());  	} else if (linkAction & TCL_CREATE_SYMBOLIC_LINK) {  	    /*  	     * Can't symlink files.  	     */  	    Tcl_SetErrno(ENOTDIR); -	    return -1;  	} else {  	    Tcl_SetErrno(ENODEV); -	    return -1;  	}      } else {  	/* @@ -323,12 +282,11 @@ WinLink(  	     */  	    Tcl_SetErrno(EISDIR); -	    return -1;  	} else {  	    Tcl_SetErrno(ENODEV); -	    return -1;  	}      } +    return -1;  }  /* @@ -345,7 +303,7 @@ static Tcl_Obj *  WinReadLink(      const TCHAR *linkSourcePath)  { -    WCHAR tempFileName[MAX_PATH]; +    TCHAR tempFileName[MAX_PATH];      TCHAR *tempFilePart;      DWORD attr; @@ -353,8 +311,8 @@ WinReadLink(       * Get the full path referenced by the target.       */ -    if (!(*tclWinProcs->getFullPathNameProc)(linkSourcePath, MAX_PATH, -	    tempFileName, &tempFilePart)) { +    if (!GetFullPathName(linkSourcePath, MAX_PATH, tempFileName, +	    &tempFilePart)) {  	/*  	 * Invalid file.  	 */ @@ -367,7 +325,7 @@ WinReadLink(       * Make sure source file does exist.       */ -    attr = (*tclWinProcs->getFileAttributesProc)(linkSourcePath); +    attr = GetFileAttributes(linkSourcePath);      if (attr == INVALID_FILE_ATTRIBUTES) {  	/*  	 * The source doesn't exist. @@ -383,9 +341,9 @@ WinReadLink(  	Tcl_SetErrno(ENOTDIR);  	return NULL; -    } else { -	return WinReadLinkDirectory(linkSourcePath);      } + +    return WinReadLinkDirectory(linkSourcePath);  }  /* @@ -485,7 +443,7 @@ TclWinSymLinkCopyDirectory(      DUMMY_REPARSE_BUFFER dummy;      REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy; -    if (NativeReadReparse(linkOrigPath, reparseBuffer)) { +    if (NativeReadReparse(linkOrigPath, reparseBuffer, GENERIC_READ)) {  	return -1;      }      return NativeWriteReparse(linkCopyPath, reparseBuffer); @@ -524,9 +482,8 @@ TclWinSymLinkDelete(      memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER));      reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; -    hFile = (*tclWinProcs->createFileProc)(linkOrigPath, GENERIC_WRITE, 0, -	    NULL, OPEN_EXISTING, -	    FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); +    hFile = CreateFile(linkOrigPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, +	    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);      if (hFile != INVALID_HANDLE_VALUE) {  	if (!DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, reparseBuffer, @@ -540,7 +497,7 @@ TclWinSymLinkDelete(  	} else {  	    CloseHandle(hFile);  	    if (!linkOnly) { -		(*tclWinProcs->removeDirectoryProc)(linkOrigPath); +		RemoveDirectory(linkOrigPath);  	    }  	    return 0;  	} @@ -580,11 +537,11 @@ WinReadLinkDirectory(      Tcl_DString ds;      const char *copy; -    attr = (*tclWinProcs->getFileAttributesProc)(linkDirPath); +    attr = GetFileAttributes(linkDirPath);      if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {  	goto invalidError;      } -    if (NativeReadReparse(linkDirPath, reparseBuffer)) { +    if (NativeReadReparse(linkDirPath, reparseBuffer, 0)) {  	return NULL;      } @@ -605,6 +562,7 @@ WinReadLinkDirectory(  	 */  	offset = 0; +#ifdef UNICODE  	if (reparseBuffer->MountPointReparseBuffer.PathBuffer[0] == L'\\') {  	    /*  	     * Check whether this is a mounted volume. @@ -666,8 +624,9 @@ WinReadLinkDirectory(  		offset = 4;  	    }  	} +#endif /* UNICODE */ -	Tcl_WinTCharToUtf((const char *) +	Tcl_WinTCharToUtf((const TCHAR *)  		reparseBuffer->MountPointReparseBuffer.PathBuffer,  		(int) reparseBuffer->MountPointReparseBuffer  		.SubstituteNameLength, &ds); @@ -703,14 +662,14 @@ WinReadLinkDirectory(  static int  NativeReadReparse(      const TCHAR *linkDirPath,	/* The junction to read */ -    REPARSE_DATA_BUFFER *buffer)/* Pointer to buffer. Cannot be NULL */ +    REPARSE_DATA_BUFFER *buffer,/* Pointer to buffer. Cannot be NULL */ +    DWORD desiredAccess)  {      HANDLE hFile;      DWORD returnedLength; -    hFile = (*tclWinProcs->createFileProc)(linkDirPath, GENERIC_READ, 0, -	    NULL, OPEN_EXISTING, -	    FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); +    hFile = CreateFile(linkDirPath, desiredAccess, 0, NULL, OPEN_EXISTING, +	    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);      if (hFile == INVALID_HANDLE_VALUE) {  	/* @@ -768,7 +727,7 @@ NativeWriteReparse(       * Create the directory - it must not already exist.       */ -    if ((*tclWinProcs->createDirectoryProc)(linkDirPath, NULL) == 0) { +    if (CreateDirectory(linkDirPath, NULL) == 0) {  	/*  	 * Error creating directory.  	 */ @@ -776,9 +735,9 @@ NativeWriteReparse(  	TclWinConvertError(GetLastError());  	return -1;      } -    hFile = (*tclWinProcs->createFileProc)(linkDirPath, GENERIC_WRITE, 0, -	    NULL, OPEN_EXISTING, -	    FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); +    hFile = CreateFile(linkDirPath, GENERIC_WRITE, 0, NULL, +	    OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT +	    | FILE_FLAG_BACKUP_SEMANTICS, NULL);      if (hFile == INVALID_HANDLE_VALUE) {  	/*  	 * Error creating directory. @@ -801,7 +760,7 @@ NativeWriteReparse(  	TclWinConvertError(GetLastError());  	CloseHandle(hFile); -	(*tclWinProcs->removeDirectoryProc)(linkDirPath); +	RemoveDirectory(linkDirPath);  	return -1;      }      CloseHandle(hFile); @@ -814,6 +773,65 @@ NativeWriteReparse(  }  /* + *---------------------------------------------------------------------- + * + * tclWinDebugPanic -- + * + *	Display a message. If a debugger is present, present it directly to + *	the debugger, otherwise use a MessageBox. + * + * Results: + *	None. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +void +tclWinDebugPanic( +    const char *format, ...) +{ +#define TCL_MAX_WARN_LEN 1024 +    va_list argList; +    char buf[TCL_MAX_WARN_LEN * TCL_UTF_MAX]; +    WCHAR msgString[TCL_MAX_WARN_LEN]; + +    va_start(argList, format); +    vsnprintf(buf, sizeof(buf), format, argList); + +    msgString[TCL_MAX_WARN_LEN-1] = L'\0'; +    MultiByteToWideChar(CP_UTF8, 0, buf, -1, msgString, TCL_MAX_WARN_LEN); + +    /* +     * Truncate MessageBox string if it is too long to not overflow the screen +     * and cause possible oversized window error. +     */ + +    if (msgString[TCL_MAX_WARN_LEN-1] != L'\0') { +	memcpy(msgString + (TCL_MAX_WARN_LEN - 5), L" ...", 5 * sizeof(WCHAR)); +    } +    if (IsDebuggerPresent()) { +	OutputDebugStringW(msgString); +    } else { +	MessageBeep(MB_ICONEXCLAMATION); +	MessageBoxW(NULL, msgString, L"Fatal Error", +		MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); +    } +#if defined(__GNUC__) +    __builtin_trap(); +#elif defined(_WIN64) +    __debugbreak(); +#elif defined(_MSC_VER) +    _asm {int 3} +#else +    DebugBreak(); +#endif +    abort(); +} + +/*   *---------------------------------------------------------------------------   *   * TclpFindExecutable -- @@ -832,28 +850,33 @@ NativeWriteReparse(  void  TclpFindExecutable( -    const char *argv0)		/* The value of the application's argv[0] -				 * (native). */ +    const char *argv0)		/* If NULL, install PanicMessageBox, otherwise +				 * ignore. */  {      WCHAR wName[MAX_PATH];      char name[MAX_PATH * TCL_UTF_MAX];      /*       * Under Windows we ignore argv0, and return the path for the file used to -     * create this process. +     * create this process. Only if it is NULL, install a new panic handler.       */ -    if (GetModuleFileNameW(NULL, wName, MAX_PATH) == 0) { -	GetModuleFileNameA(NULL, name, sizeof(name)); +    if (argv0 == NULL) { +	Tcl_SetPanicProc(tclWinDebugPanic); +    } -	/* -	 * Convert to WCHAR to get out of ANSI codepage -	 */ +#ifdef UNICODE +    GetModuleFileNameW(NULL, wName, MAX_PATH); +#else +    GetModuleFileNameA(NULL, name, sizeof(name)); -	MultiByteToWideChar(CP_ACP, 0, name, -1, wName, MAX_PATH); -    } +    /* +     * Convert to WCHAR to get out of ANSI codepage +     */ -    WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, sizeof(name), NULL,NULL); +    MultiByteToWideChar(CP_ACP, 0, name, -1, wName, MAX_PATH); +#endif +    WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, sizeof(name), NULL, NULL);      TclWinNoBackslash(name);      TclSetObjNameOfExecutable(Tcl_NewStringObj(name, -1), NULL);  } @@ -899,6 +922,7 @@ TclpMatchInDirectory(      if (pattern == NULL || (*pattern == '\0')) {  	Tcl_Obj *norm = Tcl_FSGetNormalizedPath(NULL, pathPtr); +  	if (norm != NULL) {  	    /*  	     * Match a single file directly. @@ -906,23 +930,16 @@ TclpMatchInDirectory(  	    int len;  	    DWORD attr; +	    WIN32_FILE_ATTRIBUTE_DATA data;  	    const char *str = Tcl_GetStringFromObj(norm,&len); -	    native = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); +	    native = Tcl_FSGetNativePath(pathPtr); -	    if (tclWinProcs->getFileAttributesExProc == NULL) { -		attr = (*tclWinProcs->getFileAttributesProc)(native); -		if (attr == 0xffffffff) { -		    return TCL_OK; -		} -	    } else { -		WIN32_FILE_ATTRIBUTE_DATA data; -		if ((*tclWinProcs->getFileAttributesExProc)(native, -			GetFileExInfoStandard, &data) != TRUE) { -		    return TCL_OK; -		} -		attr = data.dwFileAttributes; +	    if (GetFileAttributesEx(native, +		    GetFileExInfoStandard, &data) != TRUE) { +		return TCL_OK;  	    } +	    attr = data.dwFileAttributes;  	    if (NativeMatchType(WinIsDrive(str,len), attr, native, types)) {  		Tcl_ListObjAppendElement(interp, resultPtr, pathPtr); @@ -932,7 +949,7 @@ TclpMatchInDirectory(      } else {  	DWORD attr;  	HANDLE handle; -	WIN32_FIND_DATAT data; +	WIN32_FIND_DATA data;  	const char *dirName;	/* UTF-8 dir name, later with pattern  				 * appended. */  	int dirLength; @@ -961,9 +978,10 @@ TclpMatchInDirectory(  	if (native == NULL) {  	    return TCL_OK;  	} -	attr = (*tclWinProcs->getFileAttributesProc)(native); +	attr = GetFileAttributes(native); -	if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) { +	if ((attr == INVALID_FILE_ATTRIBUTES) +	    || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) {  	    return TCL_OK;  	} @@ -978,7 +996,7 @@ TclpMatchInDirectory(  	lastChar = dirName[dirLength -1];  	if ((lastChar != '\\') && (lastChar != '/') && (lastChar != ':')) { -	    Tcl_DStringAppend(&dsOrig, "/", 1); +	    TclDStringAppendLiteral(&dsOrig, "/");  	    dirLength++;  	}  	dirName = Tcl_DStringValue(&dsOrig); @@ -998,25 +1016,25 @@ TclpMatchInDirectory(  	    dirName = Tcl_DStringAppend(&dsOrig, pattern, -1);  	} else { -	    dirName = Tcl_DStringAppend(&dsOrig, "*.*", 3); +	    dirName = TclDStringAppendLiteral(&dsOrig, "*.*");  	}  	native = Tcl_WinUtfToTChar(dirName, -1, &ds); -	if (tclWinProcs->findFirstFileExProc == NULL || (types == NULL) -		|| (types->type != TCL_GLOB_TYPE_DIR)) { -	    handle = (*tclWinProcs->findFirstFileProc)(native, &data); +	if ((types == NULL) || (types->type != TCL_GLOB_TYPE_DIR)) { +	    handle = FindFirstFile(native, &data);  	} else {  	    /*  	     * We can be more efficient, for pure directory requests.  	     */ -	    handle = (*tclWinProcs->findFirstFileExProc)(native, +	    handle = FindFirstFileEx(native,  		    FindExInfoStandard, &data,  		    FindExSearchLimitToDirectories, NULL, 0);  	}  	if (handle == INVALID_HANDLE_VALUE) {  	    DWORD err = GetLastError(); +  	    Tcl_DStringFree(&ds);  	    if (err == ERROR_FILE_NOT_FOUND) {  		/* @@ -1030,10 +1048,9 @@ TclpMatchInDirectory(  	    TclWinConvertError(err);  	    if (interp != NULL) { -		Tcl_ResetResult(interp); -		Tcl_AppendResult(interp, "couldn't read directory \"", -			Tcl_DStringValue(&dsOrig), "\": ", -			Tcl_PosixError(interp), NULL); +		Tcl_SetObjResult(interp, Tcl_ObjPrintf( +			"couldn't read directory \"%s\": %s", +			Tcl_DStringValue(&dsOrig), Tcl_PosixError(interp)));  	    }  	    Tcl_DStringFree(&dsOrig);  	    return TCL_ERROR; @@ -1073,14 +1090,8 @@ TclpMatchInDirectory(  	    int checkDrive = 0, isDrive;  	    DWORD attr; -	    if (tclWinProcs->useWide) { -		native = (const TCHAR *) data.w.cFileName; -		attr = data.w.dwFileAttributes; -	    } else { -		native = (const TCHAR *) data.a.cFileName; -		attr = data.a.dwFileAttributes; -	    } - +	    native = data.cFileName; +	    attr = data.dwFileAttributes;  	    utfname = Tcl_WinTCharToUtf(native, -1, &ds);  	    if (!matchSpecialDots) { @@ -1123,6 +1134,7 @@ TclpMatchInDirectory(  		if (checkDrive) {  		    const char *fullname = Tcl_DStringAppend(&dsOrig, utfname,  			    Tcl_DStringLength(&ds)); +  		    isDrive = WinIsDrive(fullname, Tcl_DStringLength(&dsOrig));  		    Tcl_DStringSetLength(&dsOrig, dirLength);  		} else { @@ -1140,7 +1152,7 @@ TclpMatchInDirectory(  	     */  	    Tcl_DStringFree(&ds); -	} while ((*tclWinProcs->findNextFileProc)(handle, &data) == TRUE); +	} while (FindNextFile(handle, &data) == TRUE);  	FindClose(handle);  	Tcl_DStringFree(&dsOrig); @@ -1282,7 +1294,7 @@ WinIsReserved(   *	because for NTFS root volumes, the getFileAttributesProc returns a   *	'hidden' attribute when it should not.   * - *	We never make any calss to a 'get attributes' routine here, since we + *	We never make any calls to a 'get attributes' routine here, since we   *	have arranged things so that our caller already knows such   *	information.   * @@ -1313,81 +1325,80 @@ NativeMatchType(  	 * If invisible, don't return the file.  	 */ -	if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) { +	return !(attr & FILE_ATTRIBUTE_HIDDEN && !isDrive); +    } + +    if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) { +	/* +	 * If invisible. +	 */ + +	if ((types->perm == 0) || !(types->perm & TCL_GLOB_PERM_HIDDEN)) {  	    return 0;  	}      } else { -	if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) { -	    /* -	     * If invisible. -	     */ - -	    if ((types->perm == 0) || !(types->perm & TCL_GLOB_PERM_HIDDEN)) { -		return 0; -	    } -	} else { -	    /* -	     * Visible. -	     */ +	/* +	 * Visible. +	 */ -	    if (types->perm & TCL_GLOB_PERM_HIDDEN) { -		return 0; -	    } +	if (types->perm & TCL_GLOB_PERM_HIDDEN) { +	    return 0;  	} +    } -	if (types->perm != 0) { -	    if (((types->perm & TCL_GLOB_PERM_RONLY) && -			!(attr & FILE_ATTRIBUTE_READONLY)) || -		    ((types->perm & TCL_GLOB_PERM_R) && -			(0 /* File exists => R_OK on Windows */)) || -		    ((types->perm & TCL_GLOB_PERM_W) && -			(attr & FILE_ATTRIBUTE_READONLY)) || -		    ((types->perm & TCL_GLOB_PERM_X) && -			(!(attr & FILE_ATTRIBUTE_DIRECTORY) -			 && !NativeIsExec(nativeName)))) { -		return 0; -	    } +    if (types->perm != 0) { +	if (((types->perm & TCL_GLOB_PERM_RONLY) && +		    !(attr & FILE_ATTRIBUTE_READONLY)) || +		((types->perm & TCL_GLOB_PERM_R) && +		    (0 /* File exists => R_OK on Windows */)) || +		((types->perm & TCL_GLOB_PERM_W) && +		    (attr & FILE_ATTRIBUTE_READONLY)) || +		((types->perm & TCL_GLOB_PERM_X) && +		    (!(attr & FILE_ATTRIBUTE_DIRECTORY) +		    && !NativeIsExec(nativeName)))) { +	    return 0;  	} -	if ((types->type & TCL_GLOB_TYPE_DIR) -		&& (attr & FILE_ATTRIBUTE_DIRECTORY)) { -	    /* -	     * Quicker test for directory, which is a common case. -	     */ +    } -	    return 1; +    if ((types->type & TCL_GLOB_TYPE_DIR) +	    && (attr & FILE_ATTRIBUTE_DIRECTORY)) { +	/* +	 * Quicker test for directory, which is a common case. +	 */ -	} else if (types->type != 0) { -	    unsigned short st_mode; -	    int isExec = NativeIsExec(nativeName); +	return 1; -	    st_mode = NativeStatMode(attr, 0, isExec); +    } else if (types->type != 0) { +	unsigned short st_mode; +	int isExec = NativeIsExec(nativeName); -	    /* -	     * In order bcdpfls as in 'find -t' -	     */ +	st_mode = NativeStatMode(attr, 0, isExec); -	    if (((types->type&TCL_GLOB_TYPE_BLOCK)    && S_ISBLK(st_mode)) || -		    ((types->type&TCL_GLOB_TYPE_CHAR) && S_ISCHR(st_mode)) || -		    ((types->type&TCL_GLOB_TYPE_DIR)  && S_ISDIR(st_mode)) || -		    ((types->type&TCL_GLOB_TYPE_PIPE) && S_ISFIFO(st_mode)) || +	/* +	 * In order bcdpfls as in 'find -t' +	 */ + +	if (((types->type&TCL_GLOB_TYPE_BLOCK)    && S_ISBLK(st_mode)) || +		((types->type&TCL_GLOB_TYPE_CHAR) && S_ISCHR(st_mode)) || +		((types->type&TCL_GLOB_TYPE_DIR)  && S_ISDIR(st_mode)) || +		((types->type&TCL_GLOB_TYPE_PIPE) && S_ISFIFO(st_mode)) ||  #ifdef S_ISSOCK -		    ((types->type&TCL_GLOB_TYPE_SOCK) && S_ISSOCK(st_mode)) || +		((types->type&TCL_GLOB_TYPE_SOCK) && S_ISSOCK(st_mode)) ||  #endif -		    ((types->type&TCL_GLOB_TYPE_FILE) && S_ISREG(st_mode))) { -		/* -		 * Do nothing - this file is ok. -		 */ -	    } else { +		((types->type&TCL_GLOB_TYPE_FILE) && S_ISREG(st_mode))) { +	    /* +	     * Do nothing - this file is ok. +	     */ +	} else {  #ifdef S_ISLNK -		if (types->type & TCL_GLOB_TYPE_LINK) { -		    st_mode = NativeStatMode(attr, 1, isExec); -		    if (S_ISLNK(st_mode)) { -			return 1; -		    } +	    if (types->type & TCL_GLOB_TYPE_LINK) { +		st_mode = NativeStatMode(attr, 1, isExec); +		if (S_ISLNK(st_mode)) { +		    return 1;  		} -#endif -		return 0;  	    } +#endif /* S_ISLNK */ +	    return 0;  	}      }      return 1; @@ -1414,80 +1425,56 @@ NativeMatchType(   *----------------------------------------------------------------------   */ -char * +const char *  TclpGetUserHome(      const char *name,		/* User name for desired home directory. */      Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with  				 * name of user's home directory. */  { -    char *result; -    HINSTANCE netapiInst; +    const char *result = NULL; +    USER_INFO_1 *uiPtr, **uiPtrPtr = &uiPtr; +    Tcl_DString ds; +    int nameLen = -1; +    int badDomain = 0; +    char *domain; +    WCHAR *wName, *wHomeDir, *wDomain, **wDomainPtr = &wDomain; +    WCHAR buf[MAX_PATH]; -    result = NULL;      Tcl_DStringInit(bufferPtr); +    wDomain = NULL; +    domain = strchr(name, '@'); +    if (domain != NULL) { +	Tcl_DStringInit(&ds); +	wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds); +	badDomain = NetGetDCName(NULL, wName, (LPBYTE *) wDomainPtr); +	Tcl_DStringFree(&ds); +	nameLen = domain - name; +    } +    if (badDomain == 0) { +	Tcl_DStringInit(&ds); +	wName = Tcl_UtfToUniCharDString(name, nameLen, &ds); +	if (NetUserGetInfo(wDomain, wName, 1, (LPBYTE *) uiPtrPtr) == 0) { +	    wHomeDir = uiPtr->usri1_home_dir; +	    if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) { +		Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir), +			bufferPtr); +	    } else { +		/* +		 * User exists but has no home dir. Return +		 * "{Windows Drive}:/users/default". +		 */ -    netapiInst = LoadLibraryA("netapi32.dll"); -    if (netapiInst != NULL) { -	NETAPIBUFFERFREEPROC *netApiBufferFreeProc; -	NETGETDCNAMEPROC *netGetDCNameProc; -	NETUSERGETINFOPROC *netUserGetInfoProc; - -	netApiBufferFreeProc = (NETAPIBUFFERFREEPROC *) -		GetProcAddress(netapiInst, "NetApiBufferFree"); -	netGetDCNameProc = (NETGETDCNAMEPROC *) -		GetProcAddress(netapiInst, "NetGetDCName"); -	netUserGetInfoProc = (NETUSERGETINFOPROC *) -		GetProcAddress(netapiInst, "NetUserGetInfo"); -	if ((netUserGetInfoProc != NULL) && (netGetDCNameProc != NULL) -		&& (netApiBufferFreeProc != NULL)) { -	    USER_INFO_1 *uiPtr, **uiPtrPtr = &uiPtr; -	    Tcl_DString ds; -	    int nameLen, badDomain; -	    char *domain; -	    WCHAR *wName, *wHomeDir, *wDomain, **wDomainPtr = &wDomain; -	    WCHAR buf[MAX_PATH]; - -	    badDomain = 0; -	    nameLen = -1; -	    wDomain = NULL; -	    domain = strchr(name, '@'); -	    if (domain != NULL) { -		Tcl_DStringInit(&ds); -		wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds); -		badDomain = (netGetDCNameProc)(NULL, wName, -			(LPBYTE *) wDomainPtr); -		Tcl_DStringFree(&ds); -		nameLen = domain - name; -	    } -	    if (badDomain == 0) { -		Tcl_DStringInit(&ds); -		wName = Tcl_UtfToUniCharDString(name, nameLen, &ds); -		if ((netUserGetInfoProc)(wDomain, wName, 1, -			(LPBYTE *) uiPtrPtr) == 0) { -		    wHomeDir = uiPtr->usri1_home_dir; -		    if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) { -			Tcl_UniCharToUtfDString(wHomeDir, lstrlenW(wHomeDir), -				bufferPtr); -		    } else { -			/* -			 * User exists but has no home dir. Return -			 * "{Windows Drive}:/users/default". -			 */ - -			GetWindowsDirectoryW(buf, MAX_PATH); -			Tcl_UniCharToUtfDString(buf, 2, bufferPtr); -			Tcl_DStringAppend(bufferPtr, "/users/default", -1); -		    } -		    result = Tcl_DStringValue(bufferPtr); -		    (*netApiBufferFreeProc)((void *) uiPtr); -		} -		Tcl_DStringFree(&ds); -	    } -	    if (wDomain != NULL) { -		(*netApiBufferFreeProc)((void *) wDomain); +		GetWindowsDirectoryW(buf, MAX_PATH); +		Tcl_UniCharToUtfDString(buf, 2, bufferPtr); +		TclDStringAppendLiteral(bufferPtr, "/users/default");  	    } +	    result = Tcl_DStringValue(bufferPtr); +	    NetApiBufferFree((void *) uiPtr);  	} -	FreeLibrary(netapiInst); +	Tcl_DStringFree(&ds); +    } +    if (wDomain != NULL) { +	NetApiBufferFree((void *) wDomain);      }      if (result == NULL) {  	/* @@ -1543,25 +1530,39 @@ NativeAccess(  {      DWORD attr; -    attr = (*tclWinProcs->getFileAttributesProc)(nativePath); +    attr = GetFileAttributes(nativePath); -    if (attr == 0xffffffff) { +    if (attr == INVALID_FILE_ATTRIBUTES) {  	/* -	 * File doesn't exist. +	 * File might not exist.  	 */ -	TclWinConvertError(GetLastError()); -	return -1; +	DWORD lasterror = GetLastError(); +	if (lasterror != ERROR_SHARING_VIOLATION) { +	    TclWinConvertError(lasterror); +	    return -1; +	} +    } + +    if (mode == F_OK) { +	/* +	 * File exists, nothing else to check. +	 */ + +	return 0;      }      if ((mode & W_OK) -	    && (tclWinProcs->getFileSecurityProc == NULL) -	    && (attr & FILE_ATTRIBUTE_READONLY)) { +	&& (attr & FILE_ATTRIBUTE_READONLY) +	&& !(attr & FILE_ATTRIBUTE_DIRECTORY)) {  	/* -	 * We don't have the advanced 'getFileSecurityProc', and our -	 * attributes say the file is not writable. If we do have -	 * 'getFileSecurityProc', we'll do a more robust XP-related check -	 * below. +	 * The attributes say the file is not writable.	 If the file is a +	 * regular file (i.e., not a directory), then the file is not +	 * writable, full stop.	 For directories, the read-only bit is +	 * (mostly) ignored by Windows, so we can't ascertain anything about +	 * directory access from the attrib data.  However, if we have the +	 * advanced 'getFileSecurityProc', then more robust ACL checks +	 * will be done below.  	 */  	Tcl_SetErrno(EACCES); @@ -1585,15 +1586,15 @@ NativeAccess(       * we have a more complex permissions structure so we try to check that.       * The code below is remarkably complex for such a simple thing as finding       * what permissions the OS has set for a file. -     * -     * If we are simply checking for file existence, then we don't need all -     * these complications (which are really quite slow: with this code 'file -     * readable' is 5-6 times slower than 'file exists').       */ -    if ((mode != F_OK) && (tclWinProcs->getFileSecurityProc != NULL)) { +#ifdef UNICODE +    {  	SECURITY_DESCRIPTOR *sdPtr = NULL;  	unsigned long size; +	PSID pSid = 0; +	BOOL SidDefaulted; +	SID_IDENTIFIER_AUTHORITY samba_unmapped = {{0, 0, 0, 0, 0, 22}};  	GENERIC_MAPPING genMap;  	HANDLE hToken = NULL;  	DWORD desiredAccess = 0, grantedAccess = 0; @@ -1603,13 +1604,14 @@ NativeAccess(  	int error;  	/* -	 * First find out how big the buffer needs to be +	 * First find out how big the buffer needs to be.  	 */  	size = 0; -	(*tclWinProcs->getFileSecurityProc)(nativePath, +	GetFileSecurity(nativePath,  		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION -		| DACL_SECURITY_INFORMATION, 0, 0, &size); +		| DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, +		0, 0, &size);  	/*  	 * Should have failed with ERROR_INSUFFICIENT_BUFFER @@ -1640,9 +1642,10 @@ NativeAccess(  	 * Call GetFileSecurity() for real.  	 */ -	if (!(*tclWinProcs->getFileSecurityProc)(nativePath, +	if (!GetFileSecurity(nativePath,  		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION -		| DACL_SECURITY_INFORMATION, sdPtr, size, &size)) { +		| DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, +		sdPtr, size, &size)) {  	    /*  	     * Error getting owner SD  	     */ @@ -1651,18 +1654,38 @@ NativeAccess(  	}  	/* +	 * As of Samba 3.0.23 (10-Jul-2006), unmapped users and groups are +	 * assigned to SID domains S-1-22-1 and S-1-22-2, where "22" is the +	 * top-level authority.	 If the file owner and group is unmapped then +	 * the ACL access check below will only test against world access, +	 * which is likely to be more restrictive than the actual access +	 * restrictions.  Since the ACL tests are more likely wrong than +	 * right, skip them.  Moreover, the unix owner access permissions are +	 * usually mapped to the Windows attributes, so if the user is the +	 * file owner then the attrib checks above are correct (as far as they +	 * go). +	 */ + +	if(!GetSecurityDescriptorOwner(sdPtr,&pSid,&SidDefaulted) || +	   memcmp(GetSidIdentifierAuthority(pSid),&samba_unmapped, +		  sizeof(SID_IDENTIFIER_AUTHORITY))==0) { +	    HeapFree(GetProcessHeap(), 0, sdPtr); +	    return 0; /* Attrib tests say access allowed. */ +	} + +	/*  	 * Perform security impersonation of the user and open the resulting  	 * thread token.  	 */ -	if (!(*tclWinProcs->impersonateSelfProc)(SecurityImpersonation)) { +	if (!ImpersonateSelf(SecurityImpersonation)) {  	    /*  	     * Unable to perform security impersonation.  	     */  	    goto accessError;  	} -	if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread(), +	if (!OpenThreadToken(GetCurrentThread(),  		TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) {  	    /*  	     * Unable to get current thread's token. @@ -1671,7 +1694,7 @@ NativeAccess(  	    goto accessError;  	} -	(*tclWinProcs->revertToSelfProc)(); +	RevertToSelf();  	/*  	 * Setup desiredAccess according to the access priveleges we are @@ -1698,7 +1721,7 @@ NativeAccess(  	 * Perform access check using the token.  	 */ -	if (!(*tclWinProcs->accessCheckProc)(sdPtr, hToken, desiredAccess, +	if (!AccessCheck(sdPtr, hToken, desiredAccess,  		&genMap, &privSet, &privSetSize, &grantedAccess,  		&accessYesNo)) {  	    /* @@ -1727,18 +1750,8 @@ NativeAccess(  	    return -1;  	} -	/* -	 * For directories the above checks are ok. For files, though, we must -	 * still check the 'attr' value. -	 */ - -	if ((mode & W_OK) -		&& !(attr & FILE_ATTRIBUTE_DIRECTORY) -		&& (attr & FILE_ATTRIBUTE_READONLY)) { -	    Tcl_SetErrno(EACCES); -	    return -1; -	}      } +#endif /* !UNICODE */      return 0;  } @@ -1758,55 +1771,22 @@ NativeAccess(  static int  NativeIsExec( -    const TCHAR *nativePath) +    const TCHAR *path)  { -    if (tclWinProcs->useWide) { -	const WCHAR *path = (const WCHAR *) nativePath; -	int len = wcslen(path); +    int len = _tcslen(path); -	if (len < 5) { -	    return 0; -	} - -	if (path[len-4] != L'.') { -	    return 0; -	} - -	/* -	 * Use wide-char case-insensitive comparison -	 */ - -	if ((_wcsicmp(path+len-3, L"exe") == 0) -		|| (_wcsicmp(path+len-3, L"com") == 0) -		|| (_wcsicmp(path+len-3, L"bat") == 0)) { -	    return 1; -	} -    } else { -	const char *p; - -	/* -	 * We are only looking for pure ascii. -	 */ - -	p = strrchr((const char *) nativePath, '.'); -	if (p != NULL) { -	    p++; - -	    /* -	     * Note: in the old code, stat considered '.pif' files as -	     * executable, whereas access did not. -	     */ +    if (len < 5) { +	return 0; +    } -	    if ((strcasecmp(p, "exe") == 0) -		    || (strcasecmp(p, "com") == 0) -		    || (strcasecmp(p, "bat") == 0)) { -		/* -		 * File that ends with .exe, .com, or .bat is executable. -		 */ +    if (path[len-4] != '.') { +	return 0; +    } -		return 1; -	    } -	} +    if ((_tcsicmp(path+len-3, TEXT("exe")) == 0) +	    || (_tcsicmp(path+len-3, TEXT("com")) == 0) +	    || (_tcsicmp(path+len-3, TEXT("bat")) == 0)) { +	return 1;      }      return 0;  } @@ -1833,27 +1813,13 @@ TclpObjChdir(  {      int result;      const TCHAR *nativePath; -#ifdef __CYGWIN__ -    extern int cygwin_conv_to_posix_path(const char *, char *); -    char posixPath[MAX_PATH+1]; -    const char *path; -    Tcl_DString ds; -#endif /* __CYGWIN__ */ -    nativePath = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); +    nativePath = Tcl_FSGetNativePath(pathPtr); -#ifdef __CYGWIN__ -    /* -     * Cygwin chdir only groks POSIX path. -     */ - -    path = Tcl_WinTCharToUtf(nativePath, -1, &ds); -    cygwin_conv_to_posix_path(path, posixPath); -    result = (chdir(posixPath) == 0 ? 1 : 0); -    Tcl_DStringFree(&ds); -#else /* __CYGWIN__ */ -    result = (*tclWinProcs->setCurrentDirectoryProc)(nativePath); -#endif /* __CYGWIN__ */ +    if (!nativePath) { +	return -1; +    } +    result = SetCurrentDirectory(nativePath);      if (result == 0) {  	TclWinConvertError(GetLastError()); @@ -1862,51 +1828,6 @@ TclpObjChdir(      return 0;  } -#ifdef __CYGWIN__ -/* - *--------------------------------------------------------------------------- - * - * TclpReadlink -- - * - *	This function replaces the library version of readlink(). - * - * Results: - *	The result is a pointer to a string specifying the contents of the - *	symbolic link given by 'path', or NULL if the symbolic link could not - *	be read. Storage for the result string is allocated in bufferPtr; the - *	caller must call Tcl_DStringFree() when the result is no longer - *	needed. - * - * Side effects: - *	See readlink() documentation. - * - *--------------------------------------------------------------------------- - */ - -char * -TclpReadlink( -    const char *path,		/* Path of file to readlink (UTF-8). */ -    Tcl_DString *linkPtr)	/* Uninitialized or free DString filled with -				 * contents of link (UTF-8). */ -{ -    char link[MAXPATHLEN]; -    int length; -    char *native; -    Tcl_DString ds; - -    native = Tcl_UtfToExternalDString(NULL, path, -1, &ds); -    length = readlink(native, link, sizeof(link));	/* INTL: Native. */ -    Tcl_DStringFree(&ds); - -    if (length < 0) { -	return NULL; -    } - -    Tcl_ExternalToUtfDString(NULL, link, length, linkPtr); -    return Tcl_DStringValue(linkPtr); -} -#endif /* __CYGWIN__ */ -  /*   *----------------------------------------------------------------------   * @@ -1935,14 +1856,16 @@ TclpGetCwd(      Tcl_DString *bufferPtr)	/* Uninitialized or free DString filled with  				 * name of current directory. */  { -    WCHAR buffer[MAX_PATH]; +    TCHAR buffer[MAX_PATH];      char *p; +    WCHAR *native; -    if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) { +    if (GetCurrentDirectory(MAX_PATH, buffer) == 0) {  	TclWinConvertError(GetLastError());  	if (interp != NULL) { -	    Tcl_AppendResult(interp, "error getting working directory name: ", -		    Tcl_PosixError(interp), NULL); +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "error getting working directory name: %s", +		    Tcl_PosixError(interp)));  	}  	return NULL;      } @@ -1951,25 +1874,12 @@ TclpGetCwd(       * Watch for the weird Windows c:\\UNC syntax.       */ -    if (tclWinProcs->useWide) { -	WCHAR *native; - -	native = (WCHAR *) buffer; -	if ((native[0] != '\0') && (native[1] == ':') -		&& (native[2] == '\\') && (native[3] == '\\')) { -	    native += 2; -	} -	Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr); -    } else { -	char *native; - -	native = (char *) buffer; -	if ((native[0] != '\0') && (native[1] == ':') -		&& (native[2] == '\\') && (native[3] == '\\')) { -	    native += 2; -	} -	Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr); +    native = (WCHAR *) buffer; +    if ((native[0] != '\0') && (native[1] == ':') +	    && (native[2] == '\\') && (native[3] == '\\')) { +	native += 2;      } +    Tcl_WinTCharToUtf((TCHAR *) native, -1, bufferPtr);      /*       * Convert to forward slashes for easier use in scripts. @@ -1996,8 +1906,7 @@ TclpObjStat(      TclWinFlushDirtyChannels(); -    return NativeStat((const TCHAR *) Tcl_FSGetNativePath(pathPtr), -	    statPtr, 0); +    return NativeStat(Tcl_FSGetNativePath(pathPtr), statPtr, 0);  }  /* @@ -2043,7 +1952,7 @@ NativeStat(       * simpler routines.       */ -    fileHandle = (tclWinProcs->createFileProc)(nativePath, GENERIC_READ, +    fileHandle = CreateFile(nativePath, GENERIC_READ,  	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,  	    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); @@ -2082,17 +1991,30 @@ NativeStat(  	 */  	inode = data.nFileIndexHigh | data.nFileIndexLow; -    } else if (tclWinProcs->getFileAttributesExProc != NULL) { +    } else {  	/*  	 * Fall back on the less capable routines. This means no nlink or ino.  	 */  	WIN32_FILE_ATTRIBUTE_DATA data; -	if ((*tclWinProcs->getFileAttributesExProc)(nativePath, +	if (GetFileAttributesEx(nativePath,  		GetFileExInfoStandard, &data) != TRUE) { -	    Tcl_SetErrno(ENOENT); -	    return -1; +	    HANDLE hFind; +	    WIN32_FIND_DATA ffd; +	    DWORD lasterror = GetLastError(); + +	    if (lasterror != ERROR_SHARING_VIOLATION) { +		TclWinConvertError(lasterror); +		return -1; +		} +	    hFind = FindFirstFile(nativePath, &ffd); +	    if (hFind == INVALID_HANDLE_VALUE) { +		TclWinConvertError(GetLastError()); +		return -1; +	    } +	    memcpy(&data, &ffd, sizeof(data)); +	    FindClose(hFind);  	}  	attr = data.dwFileAttributes; @@ -2102,46 +2024,6 @@ NativeStat(  	statPtr->st_atime = ToCTime(data.ftLastAccessTime);  	statPtr->st_mtime = ToCTime(data.ftLastWriteTime);  	statPtr->st_ctime = ToCTime(data.ftCreationTime); -    } else { -	/* -	 * We don't have the faster attributes proc, so we're probably running -	 * on Win95. -	 */ - -	WIN32_FIND_DATAT data; -	HANDLE handle; - -	handle = (*tclWinProcs->findFirstFileProc)(nativePath, &data); -	if (handle == INVALID_HANDLE_VALUE) { -	    /* -	     * FindFirstFile() doesn't work on root directories, so call -	     * GetFileAttributes() to see if the specified file exists. -	     */ - -	    attr = (*tclWinProcs->getFileAttributesProc)(nativePath); -	    if (attr == INVALID_FILE_ATTRIBUTES) { -		Tcl_SetErrno(ENOENT); -		return -1; -	    } - -	    /* -	     * Make up some fake information for this file. It has the correct -	     * file attributes and a time of 0. -	     */ - -	    memset(&data, 0, sizeof(data)); -	    data.a.dwFileAttributes = attr; -	} else { -	    FindClose(handle); -	} - -	attr = data.a.dwFileAttributes; - -	statPtr->st_size = ((Tcl_WideInt) data.a.nFileSizeLow) | -		(((Tcl_WideInt) data.a.nFileSizeHigh) << 32); -	statPtr->st_atime = ToCTime(data.a.ftLastAccessTime); -	statPtr->st_mtime = ToCTime(data.a.ftLastWriteTime); -	statPtr->st_ctime = ToCTime(data.a.ftCreationTime);      }      dev = NativeDev(nativePath); @@ -2173,14 +2055,12 @@ NativeDev(  {      int dev;      Tcl_DString ds; -    WCHAR nativeFullPath[MAX_PATH]; +    TCHAR nativeFullPath[MAX_PATH];      TCHAR *nativePart;      const char *fullPath; -    (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, -	    nativeFullPath, &nativePart); - -    fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds); +    GetFullPathName(nativePath, MAX_PATH, nativeFullPath, &nativePart); +    fullPath = Tcl_WinTCharToUtf(nativeFullPath, -1, &ds);      if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) {  	const char *p; @@ -2196,15 +2076,14 @@ NativeDev(  	     * won't work.  	     */ -	    fullPath = Tcl_DStringAppend(&ds, "\\", 1); +	    fullPath = TclDStringAppendLiteral(&ds, "\\");  	    p = fullPath + Tcl_DStringLength(&ds);  	} else {  	    p++;  	}  	nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString);  	dw = (DWORD) -1; -	(*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw, -		NULL, NULL, NULL, 0); +	GetVolumeInformation(nativeVol, NULL, 0, &dw, NULL, NULL, NULL, 0);  	/*  	 * GetFullPathName() turns special devices like "NUL" into "\\.\NUL", @@ -2266,8 +2145,8 @@ NativeStatMode(       * positions.       */ -    mode |= (mode & 0x0700) >> 3; -    mode |= (mode & 0x0700) >> 6; +    mode |= (mode & (S_IREAD|S_IWRITE|S_IEXEC)) >> 3; +    mode |= (mode & (S_IREAD|S_IWRITE|S_IEXEC)) >> 6;      return (unsigned short) mode;  } @@ -2316,8 +2195,9 @@ FromCTime(      FILETIME *fileTime)		/* UTC Time */  {      LARGE_INTEGER convertedTime; +      convertedTime.QuadPart = ((LONGLONG) posixTime) * 10000000 -	+ POSIX_EPOCH_AS_FILETIME; +	    + POSIX_EPOCH_AS_FILETIME;      fileTime->dwLowDateTime = convertedTime.LowPart;      fileTime->dwHighDateTime = convertedTime.HighPart;  } @@ -2347,34 +2227,20 @@ ClientData  TclpGetNativeCwd(      ClientData clientData)  { -    WCHAR buffer[MAX_PATH]; +    TCHAR buffer[MAX_PATH]; -    if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) { +    if (GetCurrentDirectory(MAX_PATH, buffer) == 0) {  	TclWinConvertError(GetLastError());  	return NULL;      }      if (clientData != NULL) { -	if (tclWinProcs->useWide) { -	    /* -	     * Unicode representation when running on NT/2K/XP. -	     */ - -	    if (wcscmp((const WCHAR*)clientData, (const WCHAR*)buffer) == 0) { -		return clientData; -	    } -	} else { -	    /* -	     * ANSI representation when running on 95/98/ME. -	     */ - -	    if (strcmp((const char*) clientData, (const char*) buffer) == 0) { -		return clientData; -	    } +	if (_tcscmp((const TCHAR*)clientData, buffer) == 0) { +	    return clientData;  	}      } -    return TclNativeDupInternalRep((ClientData) buffer); +    return TclNativeDupInternalRep(buffer);  }  int @@ -2382,7 +2248,7 @@ TclpObjAccess(      Tcl_Obj *pathPtr,      int mode)  { -    return NativeAccess((const TCHAR *) Tcl_FSGetNativePath(pathPtr), mode); +    return NativeAccess(Tcl_FSGetNativePath(pathPtr), mode);  }  int @@ -2398,8 +2264,7 @@ TclpObjLstat(      TclWinFlushDirtyChannels(); -    return NativeStat((const TCHAR *) Tcl_FSGetNativePath(pathPtr), -	    statPtr, 1); +    return NativeStat(Tcl_FSGetNativePath(pathPtr), statPtr, 1);  }  #ifdef S_IFLNK @@ -2411,15 +2276,15 @@ TclpObjLink(  {      if (toPtr != NULL) {  	int res; -	TCHAR *LinkTarget; -	TCHAR *LinkSource = (TCHAR *) Tcl_FSGetNativePath(pathPtr); +	const TCHAR *LinkTarget; +	const TCHAR *LinkSource = Tcl_FSGetNativePath(pathPtr);  	Tcl_Obj *normalizedToPtr = Tcl_FSGetNormalizedPath(NULL, toPtr);  	if (normalizedToPtr == NULL) {  	    return NULL;  	} -	LinkTarget = (TCHAR *) Tcl_FSGetNativePath(normalizedToPtr); +	LinkTarget = Tcl_FSGetNativePath(normalizedToPtr);  	if (LinkSource == NULL || LinkTarget == NULL) {  	    return NULL; @@ -2431,7 +2296,7 @@ TclpObjLink(  	    return NULL;  	}      } else { -	TCHAR *LinkSource = (TCHAR *) Tcl_FSGetNativePath(pathPtr); +	const TCHAR *LinkSource = Tcl_FSGetNativePath(pathPtr);  	if (LinkSource == NULL) {  	    return NULL; @@ -2439,7 +2304,7 @@ TclpObjLink(  	return WinReadLink(LinkSource);      }  } -#endif +#endif /* S_IFLNK */  /*   *--------------------------------------------------------------------------- @@ -2465,7 +2330,7 @@ TclpFilesystemPathType(  {  #define VOL_BUF_SIZE 32      int found; -    WCHAR volType[VOL_BUF_SIZE]; +    TCHAR volType[VOL_BUF_SIZE];      char *firstSeparator;      const char *path;      Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); @@ -2480,16 +2345,14 @@ TclpFilesystemPathType(      firstSeparator = strchr(path, '/');      if (firstSeparator == NULL) { -	found = tclWinProcs->getVolumeInformationProc( -		Tcl_FSGetNativePath(pathPtr), NULL, 0, NULL, NULL, NULL, -		(WCHAR *) volType, VOL_BUF_SIZE); +	found = GetVolumeInformation(Tcl_FSGetNativePath(pathPtr), +		NULL, 0, NULL, NULL, NULL, volType, VOL_BUF_SIZE);      } else {  	Tcl_Obj *driveName = Tcl_NewStringObj(path, firstSeparator - path+1);  	Tcl_IncrRefCount(driveName); -	found = tclWinProcs->getVolumeInformationProc( -		Tcl_FSGetNativePath(driveName), NULL, 0, NULL, NULL, NULL, -		(WCHAR *) volType, VOL_BUF_SIZE); +	found = GetVolumeInformation(Tcl_FSGetNativePath(driveName), +		NULL, 0, NULL, NULL, NULL, volType, VOL_BUF_SIZE);  	Tcl_DecrRefCount(driveName);      } @@ -2497,13 +2360,9 @@ TclpFilesystemPathType(  	return NULL;      } else {  	Tcl_DString ds; -	Tcl_Obj *objPtr; -	Tcl_WinTCharToUtf((const char *) volType, -1, &ds); -	objPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -		Tcl_DStringLength(&ds)); -	Tcl_DStringFree(&ds); -	return objPtr; +	Tcl_WinTCharToUtf(volType, -1, &ds); +	return TclDStringToObj(&ds);      }  #undef VOL_BUF_SIZE  } @@ -2553,365 +2412,219 @@ TclpObjNormalizePath(      Tcl_DString dsNorm;		/* This will hold the normalized string. */      char *path, *currentPathEndPosition;      Tcl_Obj *temp = NULL; +    int isDrive = 1; +    Tcl_DString ds;		/* Some workspace. */      Tcl_DStringInit(&dsNorm);      path = Tcl_GetString(pathPtr); -    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) { -	/* -	 * We're on Win95, 98 or ME. There are two assumptions in this block -	 * of code. First that the native (NULL) encoding is basically ascii, -	 * and second that symbolic links are not possible. Both of these -	 * assumptions appear to be true of these operating systems. -	 */ - -	int isDrive = 1; -	Tcl_DString ds; - -	currentPathEndPosition = path + nextCheckpoint; -	if (*currentPathEndPosition == '/') { -	    currentPathEndPosition++; -	} - -	while (1) { -	    char cur = *currentPathEndPosition; +    currentPathEndPosition = path + nextCheckpoint; +    if (*currentPathEndPosition == '/') { +	currentPathEndPosition++; +    } +    while (1) { +	char cur = *currentPathEndPosition; -	    if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) { -		/* -		 * Reached directory separator, or end of string. -		 */ +	if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) { +	    /* +	     * Reached directory separator, or end of string. +	     */ -		const char *nativePath = Tcl_UtfToExternalDString(NULL, path, -			currentPathEndPosition - path, &ds); +	    WIN32_FILE_ATTRIBUTE_DATA data; +	    const TCHAR *nativePath = Tcl_WinUtfToTChar(path, +		    currentPathEndPosition - path, &ds); +	    if (GetFileAttributesEx(nativePath, +		    GetFileExInfoStandard, &data) != TRUE) {  		/* -		 * 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 the file exists. +		 * File doesn't exist.  		 */  		if (isDrive) { -		    if (GetFileAttributesA(nativePath) -			    == INVALID_FILE_ATTRIBUTES) { -			/* -			 * File doesn't exist. -			 */ - -			if (isDrive) { -			    int len = WinIsReserved(path); - -			    if (len > 0) { -				/* -				 * Actually it does exist - COM1, etc. -				 */ - -				int i; - -				for (i=0 ; i<len ; i++) { -				    if (nativePath[i] >= 'a') { -					((char *) nativePath)[i] -= ('a'-'A'); -				    } -				} -				Tcl_DStringAppend(&dsNorm, nativePath, len); -				lastValidPathEnd = currentPathEndPosition; -			    } -			} -			Tcl_DStringFree(&ds); -			break; -		    } -		    if (nativePath[0] >= 'a') { -			((char *) nativePath)[0] -= ('a' - 'A'); -		    } -		    Tcl_DStringAppend(&dsNorm, nativePath, -			    Tcl_DStringLength(&ds)); -		} else { -		    char *checkDots = NULL; - -		    if (lastValidPathEnd[1] == '.') { -			checkDots = lastValidPathEnd + 1; -			while (checkDots < currentPathEndPosition) { -			    if (*checkDots != '.') { -				checkDots = NULL; -				break; -			    } -			    checkDots++; -			} -		    } -		    if (checkDots != NULL) { -			int dotLen = currentPathEndPosition-lastValidPathEnd; +		    int len = WinIsReserved(path); +		    if (len > 0) {  			/* -			 * Path is just dots. We shouldn't really ever see a -			 * path like that. However, to be nice we at least -			 * don't mangle the path - we just add the dots as a -			 * path segment and continue -			 */ - -			Tcl_DStringAppend(&dsNorm, (TCHAR *) -				(nativePath + Tcl_DStringLength(&ds)-dotLen), -				dotLen); -		    } else { -			/* -			 * Normal path. +			 * Actually it does exist - COM1, etc.  			 */ -			WIN32_FIND_DATA fData; -			HANDLE handle; +			int i; -			handle = FindFirstFileA(nativePath, &fData); -			if (handle == INVALID_HANDLE_VALUE) { -			    if (GetFileAttributesA(nativePath) -				    == INVALID_FILE_ATTRIBUTES) { -				/* -				 * File doesn't exist. -				 */ +			for (i=0 ; i<len ; i++) { +			    WCHAR wc = ((WCHAR *) nativePath)[i]; -				Tcl_DStringFree(&ds); -				break; +			    if (wc >= L'a') { +				wc -= (L'a' - L'A'); +				((WCHAR *) nativePath)[i] = wc;  			    } - -			    /* -			     * This is usually the '/' in 'c:/' at end of -			     * string. -			     */ - -			    Tcl_DStringAppend(&dsNorm,"/", 1); -			} else { -			    char *nativeName; - -			    if (fData.cFileName[0] != '\0') { -				nativeName = fData.cFileName; -			    } else { -				nativeName = fData.cAlternateFileName; -			    } -			    FindClose(handle); -			    Tcl_DStringAppend(&dsNorm,"/", 1); -			    Tcl_DStringAppend(&dsNorm,nativeName,-1);  			} +			Tcl_DStringAppend(&dsNorm, +				(const char *)nativePath, +				(int)(sizeof(WCHAR) * len)); +			lastValidPathEnd = currentPathEndPosition; +		    } else if (nextCheckpoint == 0) { +			/* Path starts with a drive designation +			 * that's not actually on the system. +			 * We still must normalize up past the +			 * first separator.  [Bug 3603434] */ +			currentPathEndPosition++;  		    }  		}  		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; +		break;  	    } -	    currentPathEndPosition++; -	} -    } else { -	/* -	 * We're on WinNT (or 2000 or XP; something with an NT core). -	 */ -	int isDrive = 1; -	Tcl_DString ds; - -	currentPathEndPosition = path + nextCheckpoint; -	if (*currentPathEndPosition == '/') { -	    currentPathEndPosition++; -	} -	while (1) { -	    char cur = *currentPathEndPosition; +	    /* +	     * 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. +	     */ -	    if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) { -		/* -		 * Reached directory separator, or end of string. -		 */ +	    /* +	     * 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. +	     */ -		WIN32_FILE_ATTRIBUTE_DATA data; -		const char *nativePath = Tcl_WinUtfToTChar(path, -			currentPathEndPosition - path, &ds); +	    if (cur != 0 && !isDrive && +		    data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ +		Tcl_Obj *to = WinReadLinkDirectory(nativePath); -		if ((*tclWinProcs->getFileAttributesExProc)(nativePath, -			GetFileExInfoStandard, &data) != TRUE) { +		if (to != NULL) {  		    /* -		     * File doesn't exist. +		     * Read the reparse point ok. Now, reparse points need +		     * not be normalized, otherwise we could use: +		     * +		     * Tcl_GetStringFromObj(to, &pathLen); +		     * nextCheckpoint = pathLen; +		     * +		     * So, instead we have to start from the beginning.  		     */ -		    if (isDrive) { -			int len = WinIsReserved(path); - -			if (len > 0) { -			    /* -			     * Actually it does exist - COM1, etc. -			     */ +		    nextCheckpoint = 0; +		    Tcl_AppendToObj(to, currentPathEndPosition, -1); -			    int i; - -			    for (i=0 ; i<len ; i++) { -				WCHAR wc = ((WCHAR *) nativePath)[i]; +		    /* +		     * Convert link to forward slashes. +		     */ -				if (wc >= L'a') { -				    wc -= (L'a' - L'A'); -				    ((WCHAR *) nativePath)[i] = wc; -				} -			    } -			    Tcl_DStringAppend(&dsNorm, nativePath, -				    (int)(sizeof(WCHAR) * len)); -			    lastValidPathEnd = currentPathEndPosition; +		    for (path = Tcl_GetString(to); *path != 0; path++) { +			if (*path == '\\') { +			    *path = '/';  			}  		    } -		    Tcl_DStringFree(&ds); -		    break; -		} - -		/* -		 * 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. -		 */ +		    path = Tcl_GetString(to); +		    currentPathEndPosition = path + nextCheckpoint; +		    if (temp != NULL) { +			Tcl_DecrRefCount(temp); +		    } +		    temp = to; -		if (cur != 0 && !isDrive && -			data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT){ -		    Tcl_Obj *to = WinReadLinkDirectory(nativePath); +		    /* +		     * Reset variables so we can restart normalization. +		     */ -		    if (to != NULL) { -			/* -			 * Read the reparse point ok. Now, reparse points need -			 * not be normalized, otherwise we could use: -			 * -			 * Tcl_GetStringFromObj(to, &pathLen); -			 * nextCheckpoint = pathLen -			 * -			 * So, instead we have to start from the beginning. -			 */ +		    isDrive = 1; +		    Tcl_DStringFree(&dsNorm); +		    Tcl_DStringFree(&ds); +		    continue; +		} +	    } -			nextCheckpoint = 0; -			Tcl_AppendToObj(to, currentPathEndPosition, -1); +#ifndef TclNORM_LONG_PATH +	    /* +	     * Now we convert the tail of the current path to its 'long +	     * form', and append it to 'dsNorm' which holds the current +	     * normalized path +	     */ -			/* -			 * Convert link to forward slashes. -			 */ +	    if (isDrive) { +		WCHAR drive = ((WCHAR *) nativePath)[0]; -			for (path = Tcl_GetString(to); *path != 0; path++) { -			    if (*path == '\\') { -				*path = '/'; -			    } -			} -			path = Tcl_GetString(to); -			currentPathEndPosition = path + nextCheckpoint; -			if (temp != NULL) { -			    Tcl_DecrRefCount(temp); +		if (drive >= L'a') { +		    drive -= (L'a' - L'A'); +		    ((WCHAR *) nativePath)[0] = drive; +		} +		Tcl_DStringAppend(&dsNorm, (const char *)nativePath, +			Tcl_DStringLength(&ds)); +	    } else { +		char *checkDots = NULL; + +		if (lastValidPathEnd[1] == '.') { +		    checkDots = lastValidPathEnd + 1; +		    while (checkDots < currentPathEndPosition) { +			if (*checkDots != '.') { +			    checkDots = NULL; +			    break;  			} -			temp = to; - -			/* -			 * Reset variables so we can restart normalization. -			 */ - -			isDrive = 1; -			Tcl_DStringFree(&dsNorm); -			Tcl_DStringInit(&dsNorm); -			Tcl_DStringFree(&ds); -			continue; +			checkDots++;  		    }  		} +		if (checkDots != NULL) { +		    int dotLen = currentPathEndPosition-lastValidPathEnd; -#ifndef TclNORM_LONG_PATH -		/* -		 * Now we convert the tail of the current path to its 'long -		 * form', and append it to 'dsNorm' which holds the current -		 * normalized path -		 */ +		    /* +		     * Path is just dots. We shouldn't really ever see a +		     * path like that. However, to be nice we at least +		     * don't mangle the path - we just add the dots as a +		     * path segment and continue. +		     */ -		if (isDrive) { -		    WCHAR drive = ((WCHAR *) nativePath)[0]; -		    if (drive >= L'a') { -			drive -= (L'a' - L'A'); -			((WCHAR *) nativePath)[0] = drive; -		    } -		    Tcl_DStringAppend(&dsNorm, nativePath, -			    Tcl_DStringLength(&ds)); +		    Tcl_DStringAppend(&dsNorm, ((const char *)nativePath) +			    + Tcl_DStringLength(&ds) +			    - (dotLen * sizeof(TCHAR)), +			    (int)(dotLen * sizeof(TCHAR)));  		} else { -		    char *checkDots = NULL; - -		    if (lastValidPathEnd[1] == '.') { -			checkDots = lastValidPathEnd + 1; -			while (checkDots < currentPathEndPosition) { -			    if (*checkDots != '.') { -				checkDots = NULL; -				break; -			    } -			    checkDots++; -			} -		    } -		    if (checkDots != NULL) { -			int dotLen = currentPathEndPosition-lastValidPathEnd; +		    /* +		     * Normal path. +		     */ -			/* -			 * Path is just dots. We shouldn't really ever see a -			 * path like that. However, to be nice we at least -			 * don't mangle the path - we just add the dots as a -			 * path segment and continue. -			 */ +		    WIN32_FIND_DATAW fData; +		    HANDLE handle; -			Tcl_DStringAppend(&dsNorm, (TCHAR *) -				((WCHAR*)(nativePath + Tcl_DStringLength(&ds)) -				- dotLen), (int)(dotLen * sizeof(WCHAR))); -		    } else { +		    handle = FindFirstFileW((WCHAR *) nativePath, &fData); +		    if (handle == INVALID_HANDLE_VALUE) {  			/* -			 * Normal path. +			 * This is usually the '/' in 'c:/' at end of +			 * string.  			 */ -			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; -			    Tcl_DStringAppend(&dsNorm, (const char *) L"/", -				    sizeof(WCHAR)); +			if (fData.cFileName[0] != '\0') { +			    nativeName = fData.cFileName;  			} 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, -				    (int) (wcslen(nativeName)*sizeof(WCHAR))); +			    nativeName = fData.cAlternateFileName;  			} +			FindClose(handle); +			Tcl_DStringAppend(&dsNorm, (const char *) L"/", +				sizeof(WCHAR)); +			Tcl_DStringAppend(&dsNorm, +				(const char *) nativeName, +				(int) (wcslen(nativeName)*sizeof(WCHAR)));  		    }  		} -#endif -		Tcl_DStringFree(&ds); -		lastValidPathEnd = currentPathEndPosition; -		if (cur == 0) { -		    break; -		} +	    } +#endif /* !TclNORM_LONG_PATH */ +	    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. -		 */ +	    /* +	     * If we get here, we've got past one directory delimiter, so +	     * we know it is no longer a drive. +	     */ -		isDrive = 0; -	    } -	    currentPathEndPosition++; +	    isDrive = 0;  	} +	currentPathEndPosition++;  #ifdef TclNORM_LONG_PATH  	/* @@ -2920,10 +2633,10 @@ TclpObjNormalizePath(  	if (1) {  	    WCHAR wpath[MAX_PATH]; -	    const char *nativePath = +	    const TCHAR *nativePath =  		    Tcl_WinUtfToTChar(path, lastValidPathEnd - path, &ds); -	    DWORD wpathlen = (*tclWinProcs->getLongPathNameProc)( -		    nativePath, (TCHAR *) wpath, MAX_PATH); +	    DWORD wpathlen = GetLongPathNameProc(nativePath, +		    (TCHAR *) wpath, MAX_PATH);  	    /*  	     * We have to make the drive letter uppercase. @@ -2932,10 +2645,11 @@ TclpObjNormalizePath(  	    if (wpath[0] >= L'a') {  		wpath[0] -= (L'a' - L'A');  	    } -	    Tcl_DStringAppend(&dsNorm, (TCHAR*)wpath, wpathlen*sizeof(WCHAR)); +	    Tcl_DStringAppend(&dsNorm, (const char *) wpath, +		    wpathlen * sizeof(WCHAR));  	    Tcl_DStringFree(&ds);  	} -#endif +#endif /* TclNORM_LONG_PATH */      }      /* @@ -2950,11 +2664,9 @@ TclpObjNormalizePath(  	 * 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); +	Tcl_WinTCharToUtf((const TCHAR *) Tcl_DStringValue(&dsNorm), +		Tcl_DStringLength(&dsNorm), &ds); +	nextCheckpoint = Tcl_DStringLength(&ds);  	if (*lastValidPathEnd != 0) {  	    /*  	     * Not the end of the string. @@ -2964,7 +2676,7 @@ TclpObjNormalizePath(  	    char *path;  	    Tcl_Obj *tmpPathPtr; -	    tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsTemp), +	    tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),  		    nextCheckpoint);  	    Tcl_AppendToObj(tmpPathPtr, lastValidPathEnd, -1);  	    path = Tcl_GetStringFromObj(tmpPathPtr, &len); @@ -2975,10 +2687,9 @@ TclpObjNormalizePath(  	     * End of string was reached above.  	     */ -	    Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&dsTemp), -		    nextCheckpoint); +	    Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds), nextCheckpoint);  	} -	Tcl_DStringFree(&dsTemp); +	Tcl_DStringFree(&ds);      }      Tcl_DStringFree(&dsNorm); @@ -2990,6 +2701,7 @@ TclpObjNormalizePath(      if (temp != NULL) {  	Tcl_DecrRefCount(temp);      } +      return nextCheckpoint;  } @@ -3129,7 +2841,7 @@ TclpNativeToNormalized(      int len;      char *copy, *p; -    Tcl_WinTCharToUtf((const char *) clientData, -1, &ds); +    Tcl_WinTCharToUtf((const TCHAR *) clientData, -1, &ds);      copy = Tcl_DStringValue(&ds);      len = Tcl_DStringLength(&ds); @@ -3185,10 +2897,11 @@ ClientData  TclNativeCreateNativeRep(      Tcl_Obj *pathPtr)  { -    char *nativePathPtr, *str; -    Tcl_DString ds; +    WCHAR *nativePathPtr; +    const char *str;      Tcl_Obj *validPathPtr;      int len; +    WCHAR *wp;      if (TclFSCwdIsNative()) {  	/* @@ -3214,27 +2927,73 @@ TclNativeCreateNativeRep(      }      str = Tcl_GetStringFromObj(validPathPtr, &len); -    if (str[0] == '/' && str[1] == '/' && str[2] == '?' && str[3] == '/') { -	char *p; -	for (p = str; p && *p; ++p) { -	    if (*p == '/') { -		*p = '\\'; -	    } -	} +    if (strlen(str)!=len) { +	/* String contains NUL-bytes. This is invalid. */ +	return 0;      } -    Tcl_WinUtfToTChar(str, len, &ds); -    if (tclWinProcs->useWide) { -	len = Tcl_DStringLength(&ds) + sizeof(WCHAR); -    } else { -	len = Tcl_DStringLength(&ds) + sizeof(char); +    /* Let MultiByteToWideChar check for other invalid sequences, like +     * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames */ +    len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, 0, 0); +    if (len==0) { +	return 0;      } -    Tcl_DecrRefCount(validPathPtr); -    nativePathPtr = ckalloc((unsigned) len); -    memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len); - -    Tcl_DStringFree(&ds); -    return (ClientData) nativePathPtr; +    /* Overallocate 6 chars, making some room for extended paths */ +    wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) ); +    if (nativePathPtr==0) { +      return 0; +    } +    MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len); +    /* +    ** If path starts with "//?/" or "\\?\" (extended path), translate +    ** any slashes to backslashes but leave the '?' intact +    */ +    if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/') +	    && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) { +	wp[0] = wp[1] = wp[3] = '\\'; +	str += 4; +	wp += 4; +    } +    /* +    ** If there is no "\\?\" prefix but there is a drive or UNC +    ** path prefix and the path is larger than MAX_PATH chars, +    ** no Win32 API function can handle that unless it is +    ** prefixed with the extended path prefix. See: +    ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> +    **/ +    if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z')) +	    && str[1]==':' && (str[2]=='\\' || str[2]=='/')) { +	if (wp==nativePathPtr && len>MAX_PATH) { +	    memmove(wp+4, wp, len*sizeof(WCHAR)); +	    memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR)); +	    wp += 4; +	} +	/* +	 ** If (remainder of) path starts with "<drive>:/" or "<drive>:\", +	 ** leave the ':' intact but translate the backslash to a slash. +	 */ +	wp[2] = '\\'; +	wp += 3; +    } else if (wp==nativePathPtr && len>MAX_PATH +	    && (str[0]=='\\' || str[0]=='/') +	    && (str[1]=='\\' || str[1]=='/') && str[2]!='?') { +	memmove(wp+6, wp, len*sizeof(WCHAR)); +	memcpy(wp, L"\\\\?\\UNC", 7*sizeof(WCHAR)); +	wp += 7; +    } +    /* +    ** In the remainder of the path, translate invalid characters to +    ** characters in the Unicode private use area. +    */ +    while (*wp != '\0') { +	if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) { +	    *wp |= 0xF000; +	} else if (*wp == '/') { +	    *wp = '\\'; +	} +	++wp; +    } +    return nativePathPtr;  }  /* @@ -3265,23 +3024,11 @@ TclNativeDupInternalRep(  	return NULL;      } -    if (tclWinProcs->useWide) { -	/* -	 * Unicode representation when running on NT/2K/XP. -	 */ - -	len = sizeof(WCHAR) * (wcslen((const WCHAR *) clientData) + 1); -    } else { -	/* -	 * ANSI representation when running on 95/98/ME. -	 */ - -	len = sizeof(char) * (strlen((const char *) clientData) + 1); -    } +    len = sizeof(TCHAR) * (_tcslen((const TCHAR *) clientData) + 1); -    copy = (char *) ckalloc(len); +    copy = ckalloc(len);      memcpy(copy, clientData, len); -    return (ClientData) copy; +    return copy;  }  /* @@ -3316,9 +3063,9 @@ TclpUtime(      FromCTime(tval->actime, &lastAccessTime);      FromCTime(tval->modtime, &lastModTime); -    native = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); +    native = Tcl_FSGetNativePath(pathPtr); -    attr = (*tclWinProcs->getFileAttributesProc)(native); +    attr = GetFileAttributes(native);      if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) {  	flags = FILE_FLAG_BACKUP_SEMANTICS; @@ -3329,8 +3076,8 @@ TclpUtime(       * savings complications that utime gets wrong.       */ -    fileHandle = (tclWinProcs->createFileProc)(native, FILE_WRITE_ATTRIBUTES, -	    0, NULL, OPEN_EXISTING, flags, NULL); +    fileHandle = CreateFile(native, FILE_WRITE_ATTRIBUTES, 0, NULL, +	    OPEN_EXISTING, flags, NULL);      if (fileHandle == INVALID_HANDLE_VALUE ||  	    !SetFileTime(fileHandle, NULL, &lastAccessTime, &lastModTime)) { | 
