diff options
Diffstat (limited to 'win/tclWinFile.c')
-rw-r--r-- | win/tclWinFile.c | 2954 |
1 files changed, 1145 insertions, 1809 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c index 08413d6..f145e23 100644 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c @@ -1,21 +1,24 @@ -/* +/* * tclWinFile.c -- * - * This file contains temporary wrappers around UNIX file handling - * functions. These wrappers map the UNIX functions to Win32 HANDLE-style - * files, which can be manipulated through the Win32 console redirection - * interfaces. + * This file contains temporary wrappers around UNIX file handling + * functions. These wrappers map the UNIX functions to Win32 HANDLE-style + * files, which can be manipulated through the Win32 console redirection + * interfaces. * * Copyright (c) 1995-1998 Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. */ +#ifndef _WIN64 +/* See [Bug 2935503]: file mtime sets wrong time */ +# define _USE_32BIT_TIME_T +#endif +#include <sys/stat.h> #include "tclWinInt.h" -#include "tclFileSystem.h" #include <winioctl.h> -#include <sys/stat.h> #include <shlobj.h> #include <lmaccess.h> /* For TclpGetUserHome(). */ @@ -24,280 +27,234 @@ * on the proleptic Gregorian calendar) and the Posix epoch (1970-01-01). */ -#define POSIX_EPOCH_AS_FILETIME \ - ((Tcl_WideInt) 116444736 * (Tcl_WideInt) 1000000000) +#define POSIX_EPOCH_AS_FILETIME 116444736000000000 /* - * Declarations for 'link' related information. This information should come - * with VC++ 6.0, but is not in some older SDKs. In any case it is not well - * documented. + * Declarations for 'link' related information. This information + * should come with VC++ 6.0, but is not in some older SDKs. + * In any case it is not well documented. */ - #ifndef IO_REPARSE_TAG_RESERVED_ONE -# define IO_REPARSE_TAG_RESERVED_ONE 0x000000001 +# define IO_REPARSE_TAG_RESERVED_ONE 0x000000001 #endif #ifndef IO_REPARSE_TAG_RESERVED_RANGE -# define IO_REPARSE_TAG_RESERVED_RANGE 0x000000001 +# define IO_REPARSE_TAG_RESERVED_RANGE 0x000000001 #endif #ifndef IO_REPARSE_TAG_VALID_VALUES -# define IO_REPARSE_TAG_VALID_VALUES 0x0E000FFFF +# define IO_REPARSE_TAG_VALID_VALUES 0x0E000FFFF #endif #ifndef IO_REPARSE_TAG_HSM -# define IO_REPARSE_TAG_HSM 0x0C0000004 +# define IO_REPARSE_TAG_HSM 0x0C0000004 #endif #ifndef IO_REPARSE_TAG_NSS -# define IO_REPARSE_TAG_NSS 0x080000005 +# define IO_REPARSE_TAG_NSS 0x080000005 #endif #ifndef IO_REPARSE_TAG_NSSRECOVER -# define IO_REPARSE_TAG_NSSRECOVER 0x080000006 +# define IO_REPARSE_TAG_NSSRECOVER 0x080000006 #endif #ifndef IO_REPARSE_TAG_SIS -# define IO_REPARSE_TAG_SIS 0x080000007 +# define IO_REPARSE_TAG_SIS 0x080000007 #endif #ifndef IO_REPARSE_TAG_DFS -# define IO_REPARSE_TAG_DFS 0x080000008 +# define IO_REPARSE_TAG_DFS 0x080000008 #endif #ifndef IO_REPARSE_TAG_RESERVED_ZERO -# define IO_REPARSE_TAG_RESERVED_ZERO 0x00000000 +# define IO_REPARSE_TAG_RESERVED_ZERO 0x00000000 #endif #ifndef FILE_FLAG_OPEN_REPARSE_POINT -# define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 +# define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000 #endif #ifndef IO_REPARSE_TAG_MOUNT_POINT -# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #endif #ifndef IsReparseTagValid -# define IsReparseTagValid(x) \ - (!((x)&~IO_REPARSE_TAG_VALID_VALUES)&&((x)>IO_REPARSE_TAG_RESERVED_RANGE)) +# define IsReparseTagValid(x) (!((x)&~IO_REPARSE_TAG_VALID_VALUES)&&((x)>IO_REPARSE_TAG_RESERVED_RANGE)) #endif #ifndef IO_REPARSE_TAG_SYMBOLIC_LINK -# define IO_REPARSE_TAG_SYMBOLIC_LINK IO_REPARSE_TAG_RESERVED_ZERO +# define IO_REPARSE_TAG_SYMBOLIC_LINK IO_REPARSE_TAG_RESERVED_ZERO #endif #ifndef FILE_SPECIAL_ACCESS -# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) +# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS) #endif #ifndef FSCTL_SET_REPARSE_POINT -# define FSCTL_SET_REPARSE_POINT \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) -# define FSCTL_GET_REPARSE_POINT \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) -# define FSCTL_DELETE_REPARSE_POINT \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) +# define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #endif #ifndef INVALID_FILE_ATTRIBUTES -#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif -/* - * Maximum reparse buffer info size. The max user defined reparse data is - * 16KB, plus there's a header. +/* + * Maximum reparse buffer info size. The max user defined reparse + * data is 16KB, plus there's a header. */ -#define MAX_REPARSE_SIZE 17000 +#define MAX_REPARSE_SIZE 17000 /* - * Undocumented REPARSE_MOUNTPOINT_HEADER_SIZE structure definition. This is - * found in winnt.h. - * - * IMPORTANT: caution when using this structure, since the actual structures - * used will want to store a full path in the 'PathBuffer' field, but there - * isn't room (there's only a single WCHAR!). Therefore one must artificially - * create a larger space of memory and then cast it to this type. We use the - * 'DUMMY_REPARSE_BUFFER' struct just below to deal with this problem. + * Undocumented REPARSE_MOUNTPOINT_HEADER_SIZE structure definition. + * This is found in winnt.h. + * + * IMPORTANT: caution when using this structure, since the actual + * structures used will want to store a full path in the 'PathBuffer' + * field, but there isn't room (there's only a single WCHAR!). Therefore + * one must artificially create a larger space of memory and then cast it + * to this type. We use the 'DUMMY_REPARSE_BUFFER' struct just below to + * deal with this problem. */ -#define REPARSE_MOUNTPOINT_HEADER_SIZE 8 +#define REPARSE_MOUNTPOINT_HEADER_SIZE 8 #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE typedef struct _REPARSE_DATA_BUFFER { - DWORD ReparseTag; - WORD ReparseDataLength; - WORD Reserved; + DWORD ReparseTag; + WORD ReparseDataLength; + WORD Reserved; union { - struct { - WORD SubstituteNameOffset; - WORD SubstituteNameLength; - WORD PrintNameOffset; - WORD PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - WORD SubstituteNameOffset; - WORD SubstituteNameLength; - WORD PrintNameOffset; - WORD PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - BYTE DataBuffer[1]; - } GenericReparseBuffer; + struct { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + BYTE DataBuffer[1]; + } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER; #endif typedef struct { REPARSE_DATA_BUFFER dummy; - WCHAR dummyBuf[MAX_PATH * 3]; + WCHAR dummyBuf[MAX_PATH*3]; } DUMMY_REPARSE_BUFFER; -#if defined(_MSC_VER) && (_MSC_VER <= 1100) -#undef HAVE_NO_FINDEX_ENUMS +#if defined(_MSC_VER) && ( _MSC_VER <= 1100 ) #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 + FindExInfoStandard, + FindExInfoMaxInfoLevel } FINDEX_INFO_LEVELS; typedef enum _FINDEX_SEARCH_OPS { - FindExSearchNameMatch, - FindExSearchLimitToDirectories, - FindExSearchLimitToDevices, - FindExSearchMaxSearchOp + FindExSearchNameMatch, + FindExSearchLimitToDirectories, + FindExSearchLimitToDevices, + FindExSearchMaxSearchOp } FINDEX_SEARCH_OPS; #endif /* HAVE_NO_FINDEX_ENUMS */ -/* - * Other typedefs required by this code. - */ +/* Other typedefs required by this code */ 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 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 NETAPIBUFFERFREEPROC + (LPVOID Buffer); -typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC( - LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr); +typedef NET_API_STATUS NET_API_FUNCTION NETGETDCNAMEPROC + (LPWSTR servername, LPWSTR domainname, LPBYTE *bufptr); /* - * Declarations for local functions defined in this file: + * Declarations for local procedures defined in this file: */ -static int NativeAccess(const TCHAR *path, int mode); -static int NativeDev(const TCHAR *path); -static int NativeStat(const TCHAR *path, Tcl_StatBuf *statPtr, - int checkLinks); -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); -static int NativeWriteReparse(const TCHAR *LinkDirectory, - REPARSE_DATA_BUFFER *buffer); -static int NativeMatchType(int isDrive, DWORD attr, - const TCHAR *nativeName, Tcl_GlobTypeData *types); -static int WinIsDrive(const char *name, int nameLen); -static int WinIsReserved(const char *path); -static Tcl_Obj * WinReadLink(const TCHAR *LinkSource); -static Tcl_Obj * WinReadLinkDirectory(const TCHAR *LinkDirectory); -static int WinLink(const TCHAR *LinkSource, - const TCHAR *LinkTarget, int linkAction); -static int WinSymLinkDirectory(const TCHAR *LinkDirectory, - const TCHAR *LinkTarget); +static int NativeAccess(CONST TCHAR *path, int mode); +static int NativeStat(CONST TCHAR *path, Tcl_StatBuf *statPtr, int checkLinks); +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); +static int NativeWriteReparse(CONST TCHAR* LinkDirectory, + REPARSE_DATA_BUFFER* buffer); +static int NativeMatchType(int isDrive, DWORD attr, CONST TCHAR* nativeName, + Tcl_GlobTypeData *types); +static int WinIsDrive(CONST char *name, int nameLen); +static int WinIsReserved(CONST char *path); +static Tcl_Obj* WinReadLink(CONST TCHAR* LinkSource); +static Tcl_Obj* WinReadLinkDirectory(CONST TCHAR* LinkDirectory); +static int WinLink(CONST TCHAR* LinkSource, CONST TCHAR* LinkTarget, + int linkAction); +static int WinSymLinkDirectory(CONST TCHAR* LinkDirectory, + CONST TCHAR* LinkTarget); /* *-------------------------------------------------------------------- * - * WinLink -- - * - * Make a link from source to target. + * WinLink * + * Make a link from source to target. *-------------------------------------------------------------------- */ - -static int -WinLink( - const TCHAR *linkSourcePath, - const TCHAR *linkTargetPath, - int linkAction) +static int +WinLink(LinkSource, LinkTarget, linkAction) + CONST TCHAR* LinkSource; + CONST TCHAR* LinkTarget; + int linkAction; { - WCHAR tempFileName[MAX_PATH]; - TCHAR *tempFilePart; - DWORD attr; - - /* - * Get the full path referenced by the target. - */ - - if (!(*tclWinProcs->getFullPathNameProc)(linkTargetPath, MAX_PATH, - tempFileName, &tempFilePart)) { - /* - * Invalid file. - */ - + WCHAR tempFileName[MAX_PATH]; + TCHAR* tempFilePart; + int attr; + + /* Get the full path referenced by the target */ + if (!(*tclWinProcs->getFullPathNameProc)(LinkTarget, + MAX_PATH, tempFileName, &tempFilePart)) { + /* Invalid file */ TclWinConvertError(GetLastError()); return -1; } - /* - * Make sure source file doesn't exist. - */ - - attr = (*tclWinProcs->getFileAttributesProc)(linkSourcePath); - if (attr != INVALID_FILE_ATTRIBUTES) { + /* Make sure source file doesn't exist */ + attr = (*tclWinProcs->getFileAttributesProc)(LinkSource); + if (attr != 0xffffffff) { Tcl_SetErrno(EEXIST); return -1; } - /* - * Get the full path referenced by the source file/directory. - */ - - if (!(*tclWinProcs->getFullPathNameProc)(linkSourcePath, MAX_PATH, - tempFileName, &tempFilePart)) { - /* - * Invalid file. - */ - + /* Get the full path referenced by the directory */ + if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, + MAX_PATH, tempFileName, &tempFilePart)) { + /* Invalid file */ TclWinConvertError(GetLastError()); return -1; } - - /* - * Check the target. - */ - - attr = (*tclWinProcs->getFileAttributesProc)(linkTargetPath); - if (attr == INVALID_FILE_ATTRIBUTES) { - /* - * The target doesn't exist. - */ - + /* Check the target */ + attr = (*tclWinProcs->getFileAttributesProc)(LinkTarget); + if (attr == 0xffffffff) { + /* The target doesn't exist */ TclWinConvertError(GetLastError()); return -1; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { - /* - * It is a file. - */ - + /* 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)) { + if (!(*tclWinProcs->createHardLinkProc)(LinkSource, LinkTarget, NULL)) { TclWinConvertError(GetLastError()); return -1; } return 0; - } else if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { - /* - * Can't symlink files. - */ - + /* Can't symlink files */ Tcl_SetErrno(ENOTDIR); return -1; } else { @@ -305,19 +262,10 @@ WinLink( return -1; } } else { - /* - * We've got a directory. Now check whether what we're trying to do is - * reasonable. - */ - if (linkAction & TCL_CREATE_SYMBOLIC_LINK) { - return WinSymLinkDirectory(linkSourcePath, linkTargetPath); - + return WinSymLinkDirectory(LinkSource, LinkTarget); } else if (linkAction & TCL_CREATE_HARD_LINK) { - /* - * Can't hard link directories. - */ - + /* Can't hard link directories */ Tcl_SetErrno(EISDIR); return -1; } else { @@ -330,213 +278,169 @@ WinLink( /* *-------------------------------------------------------------------- * - * WinReadLink -- - * - * What does 'LinkSource' point to? + * WinReadLink * + * What does 'LinkSource' point to? *-------------------------------------------------------------------- */ - -static Tcl_Obj * -WinReadLink( - const TCHAR *linkSourcePath) +static Tcl_Obj* +WinReadLink(LinkSource) + CONST TCHAR* LinkSource; { - WCHAR tempFileName[MAX_PATH]; - TCHAR *tempFilePart; - DWORD attr; - - /* - * Get the full path referenced by the target. - */ - - if (!(*tclWinProcs->getFullPathNameProc)(linkSourcePath, MAX_PATH, - tempFileName, &tempFilePart)) { - /* - * Invalid file. - */ - + WCHAR tempFileName[MAX_PATH]; + TCHAR* tempFilePart; + int attr; + + /* Get the full path referenced by the target */ + if (!(*tclWinProcs->getFullPathNameProc)(LinkSource, + MAX_PATH, tempFileName, &tempFilePart)) { + /* Invalid file */ TclWinConvertError(GetLastError()); return NULL; } - /* - * Make sure source file does exist. - */ - - attr = (*tclWinProcs->getFileAttributesProc)(linkSourcePath); - if (attr == INVALID_FILE_ATTRIBUTES) { - /* - * The source doesn't exist. - */ - + /* Make sure source file does exist */ + attr = (*tclWinProcs->getFileAttributesProc)(LinkSource); + if (attr == 0xffffffff) { + /* The source doesn't exist */ TclWinConvertError(GetLastError()); return NULL; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { - /* - * It is a file - this is not yet supported. - */ - + /* It is a file - this is not yet supported */ Tcl_SetErrno(ENOTDIR); return NULL; } else { - return WinReadLinkDirectory(linkSourcePath); + return WinReadLinkDirectory(LinkSource); } } /* *-------------------------------------------------------------------- * - * WinSymLinkDirectory -- - * - * This routine creates a NTFS junction, using the undocumented - * FSCTL_SET_REPARSE_POINT structure Win2K uses for mount points and - * junctions. + * WinSymLinkDirectory * - * Assumption that linkTargetPath is a valid, existing directory. - * - * Returns: - * Zero on success. + * This routine creates a NTFS junction, using the undocumented + * FSCTL_SET_REPARSE_POINT structure Win2K uses for mount points + * and junctions. * + * Assumption that LinkTarget is a valid, existing directory. + * + * Returns zero on success. *-------------------------------------------------------------------- */ - -static int -WinSymLinkDirectory( - const TCHAR *linkDirPath, - const TCHAR *linkTargetPath) +static int +WinSymLinkDirectory(LinkDirectory, LinkTarget) + CONST TCHAR* LinkDirectory; + CONST TCHAR* LinkTarget; { DUMMY_REPARSE_BUFFER dummy; - REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy; - int len; - WCHAR nativeTarget[MAX_PATH]; - WCHAR *loop; - - /* - * Make the native target name. - */ - - memcpy(nativeTarget, L"\\??\\", 4 * sizeof(WCHAR)); - memcpy(nativeTarget + 4, linkTargetPath, - sizeof(WCHAR) * (1+wcslen((WCHAR *) linkTargetPath))); + REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; + int len; + WCHAR nativeTarget[MAX_PATH]; + WCHAR *loop; + + /* Make the native target name */ + memcpy((VOID*)nativeTarget, (VOID*)L"\\??\\", 4*sizeof(WCHAR)); + memcpy((VOID*)(nativeTarget + 4), (VOID*)LinkTarget, + sizeof(WCHAR)*(1+wcslen((WCHAR*)LinkTarget))); len = wcslen(nativeTarget); - - /* - * We must have backslashes only. This is VERY IMPORTANT. If we have any - * forward slashes everything appears to work, but the resulting symlink - * is useless! + /* + * We must have backslashes only. This is VERY IMPORTANT. + * If we have any forward slashes everything appears to work, + * but the resulting symlink is useless! */ - for (loop = nativeTarget; *loop != 0; loop++) { - if (*loop == L'/') { - *loop = L'\\'; - } + if (*loop == L'/') *loop = L'\\'; } if ((nativeTarget[len-1] == L'\\') && (nativeTarget[len-2] != L':')) { nativeTarget[len-1] = 0; } - - /* - * Build the reparse info. - */ - + + /* Build the reparse info */ memset(reparseBuffer, 0, sizeof(DUMMY_REPARSE_BUFFER)); reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; - reparseBuffer->MountPointReparseBuffer.SubstituteNameLength = - wcslen(nativeTarget) * sizeof(WCHAR); + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength = + wcslen(nativeTarget) * sizeof(WCHAR); reparseBuffer->Reserved = 0; - reparseBuffer->MountPointReparseBuffer.PrintNameLength = 0; - reparseBuffer->MountPointReparseBuffer.PrintNameOffset = - reparseBuffer->MountPointReparseBuffer.SubstituteNameLength - + sizeof(WCHAR); - memcpy(reparseBuffer->MountPointReparseBuffer.PathBuffer, nativeTarget, - sizeof(WCHAR) - + reparseBuffer->MountPointReparseBuffer.SubstituteNameLength); - reparseBuffer->ReparseDataLength = - reparseBuffer->MountPointReparseBuffer.SubstituteNameLength+12; - - return NativeWriteReparse(linkDirPath, reparseBuffer); + reparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength = 0; + reparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset = + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + + sizeof(WCHAR); + memcpy(reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, nativeTarget, + sizeof(WCHAR) + + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength); + reparseBuffer->ReparseDataLength = + reparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + 12; + + return NativeWriteReparse(LinkDirectory, reparseBuffer); } /* *-------------------------------------------------------------------- * - * TclWinSymLinkCopyDirectory -- - * - * Copy a Windows NTFS junction. This function assumes that LinkOriginal - * exists and is a valid junction point, and that LinkCopy does not - * exist. - * - * Returns: - * Zero on success. + * TclWinSymLinkCopyDirectory * + * Copy a Windows NTFS junction. This function assumes that + * LinkOriginal exists and is a valid junction point, and that + * LinkCopy does not exist. + * + * Returns zero on success. *-------------------------------------------------------------------- */ - -int -TclWinSymLinkCopyDirectory( - const TCHAR *linkOrigPath, /* Existing junction - reparse point */ - const TCHAR *linkCopyPath) /* Will become a duplicate junction */ +int +TclWinSymLinkCopyDirectory(LinkOriginal, LinkCopy) + CONST TCHAR* LinkOriginal; /* Existing junction - reparse point */ + CONST TCHAR* LinkCopy; /* Will become a duplicate junction */ { DUMMY_REPARSE_BUFFER dummy; - REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy; - - if (NativeReadReparse(linkOrigPath, reparseBuffer)) { + REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; + + if (NativeReadReparse(LinkOriginal, reparseBuffer)) { return -1; } - return NativeWriteReparse(linkCopyPath, reparseBuffer); + return NativeWriteReparse(LinkCopy, reparseBuffer); } /* *-------------------------------------------------------------------- * - * TclWinSymLinkDelete -- - * - * Delete a Windows NTFS junction. Once the junction information is - * deleted, the filesystem object becomes an ordinary directory. Unless - * 'linkOnly' is given, that directory is also removed. - * - * Assumption that LinkOriginal is a valid, existing junction. - * - * Returns: - * Zero on success. + * TclWinSymLinkDelete * + * Delete a Windows NTFS junction. Once the junction information + * is deleted, the filesystem object becomes an ordinary directory. + * Unless 'linkOnly' is given, that directory is also removed. + * + * Assumption that LinkOriginal is a valid, existing junction. + * + * Returns zero on success. *-------------------------------------------------------------------- */ - -int -TclWinSymLinkDelete( - const TCHAR *linkOrigPath, - int linkOnly) +int +TclWinSymLinkDelete(LinkOriginal, linkOnly) + CONST TCHAR* LinkOriginal; + int linkOnly; { - /* - * It is a symbolic link - remove it. - */ - + /* It is a symbolic link -- remove it */ DUMMY_REPARSE_BUFFER dummy; - REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy; + REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; HANDLE hFile; DWORD returnedLength; - 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 = (*tclWinProcs->createFileProc)(LinkOriginal, 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, - REPARSE_MOUNTPOINT_HEADER_SIZE,NULL,0,&returnedLength,NULL)) { - /* - * Error setting junction. - */ - + if (!DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, reparseBuffer, + REPARSE_MOUNTPOINT_HEADER_SIZE, + NULL, 0, &returnedLength, NULL)) { + /* Error setting junction */ TclWinConvertError(GetLastError()); CloseHandle(hFile); } else { CloseHandle(hFile); if (!linkOnly) { - (*tclWinProcs->removeDirectoryProc)(linkOrigPath); + (*tclWinProcs->removeDirectoryProc)(LinkOriginal); } return 0; } @@ -547,136 +451,123 @@ TclWinSymLinkDelete( /* *-------------------------------------------------------------------- * - * WinReadLinkDirectory -- - * - * This routine reads a NTFS junction, using the undocumented - * FSCTL_GET_REPARSE_POINT structure Win2K uses for mount points and - * junctions. - * - * Assumption that LinkDirectory is a valid, existing directory. - * - * Returns: - * A Tcl_Obj with refCount of 1 (i.e. owned by the caller), or NULL if - * anything went wrong. + * WinReadLinkDirectory * - * In the future we should enhance this to return a path object rather - * than a string. + * This routine reads a NTFS junction, using the undocumented + * FSCTL_GET_REPARSE_POINT structure Win2K uses for mount points + * and junctions. * + * Assumption that LinkDirectory is a valid, existing directory. + * + * Returns a Tcl_Obj with refCount of 1 (i.e. owned by the caller), + * or NULL if anything went wrong. + * + * In the future we should enhance this to return a path object + * rather than a string. *-------------------------------------------------------------------- */ - -static Tcl_Obj * -WinReadLinkDirectory( - const TCHAR *linkDirPath) +static Tcl_Obj* +WinReadLinkDirectory(LinkDirectory) + CONST TCHAR* LinkDirectory; { - int attr, len, offset; + int attr; DUMMY_REPARSE_BUFFER dummy; - REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER *) &dummy; - Tcl_Obj *retVal; - Tcl_DString ds; - const char *copy; - - attr = (*tclWinProcs->getFileAttributesProc)(linkDirPath); + REPARSE_DATA_BUFFER *reparseBuffer = (REPARSE_DATA_BUFFER*)&dummy; + + attr = (*tclWinProcs->getFileAttributesProc)(LinkDirectory); if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) { - goto invalidError; - } - if (NativeReadReparse(linkDirPath, reparseBuffer)) { + Tcl_SetErrno(EINVAL); return NULL; } - + if (NativeReadReparse(LinkDirectory, reparseBuffer)) { + return NULL; + } + switch (reparseBuffer->ReparseTag) { - case 0x80000000|IO_REPARSE_TAG_SYMBOLIC_LINK: - case IO_REPARSE_TAG_SYMBOLIC_LINK: - case IO_REPARSE_TAG_MOUNT_POINT: - /* - * Certain native path representations on Windows have a special - * prefix to indicate that they are to be treated specially. For - * example extremely long paths, or symlinks, or volumes mounted - * inside directories. - * - * There is an assumption in this code that 'wide' interfaces are - * being used (see tclWin32Dll.c), which is true for the only systems - * which support reparse tags at present. If that changes in the - * future, this code will have to be generalised. - */ - - offset = 0; - if (reparseBuffer->MountPointReparseBuffer.PathBuffer[0] == L'\\') { - /* - * Check whether this is a mounted volume. + case 0x80000000|IO_REPARSE_TAG_SYMBOLIC_LINK: + case IO_REPARSE_TAG_SYMBOLIC_LINK: + case IO_REPARSE_TAG_MOUNT_POINT: { + Tcl_Obj *retVal; + Tcl_DString ds; + CONST char *copy; + int len; + int offset = 0; + + /* + * Certain native path representations on Windows have a + * special prefix to indicate that they are to be treated + * specially. For example extremely long paths, or symlinks, + * or volumes mounted inside directories. + * + * There is an assumption in this code that 'wide' interfaces + * are being used (see tclWin32Dll.c), which is true for the + * only systems which support reparse tags at present. If + * that changes in the future, this code will have to be + * generalised. */ - - if (wcsncmp(reparseBuffer->MountPointReparseBuffer.PathBuffer, - L"\\??\\Volume{",11) == 0) { - char drive; - - /* - * There is some confusion between \??\ and \\?\ which we have - * to fix here. It doesn't seem very well documented. - */ - - reparseBuffer->MountPointReparseBuffer.PathBuffer[1]=L'\\'; - - /* - * Check if a corresponding drive letter exists, and use that - * if it is found - */ - - drive = TclWinDriveLetterForVolMountPoint( - reparseBuffer->MountPointReparseBuffer.PathBuffer); - if (drive != -1) { - char driveSpec[3] = { - '\0', ':', '\0' - }; - - driveSpec[0] = drive; - retVal = Tcl_NewStringObj(driveSpec,2); - Tcl_IncrRefCount(retVal); - return retVal; + if (reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer[0] + == L'\\') { + /* Check whether this is a mounted volume */ + if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, + L"\\??\\Volume{",11) == 0) { + char drive; + /* + * There is some confusion between \??\ and \\?\ which + * we have to fix here. It doesn't seem very well + * documented. + */ + reparseBuffer->SymbolicLinkReparseBuffer + .PathBuffer[1] = L'\\'; + /* + * Check if a corresponding drive letter exists, and + * use that if it is found + */ + drive = TclWinDriveLetterForVolMountPoint(reparseBuffer + ->SymbolicLinkReparseBuffer.PathBuffer); + if (drive != -1) { + char driveSpec[3] = { + drive, ':', '\0' + }; + retVal = Tcl_NewStringObj(driveSpec,2); + Tcl_IncrRefCount(retVal); + return retVal; + } + /* + * This is actually a mounted drive, which doesn't + * exists as a DOS drive letter. This means the path + * isn't actually a link, although we partially treat + * it like one ('file type' will return 'link'), but + * then the link will actually just be treated like + * an ordinary directory. I don't believe any + * serious inconsistency will arise from this, but it + * is something to be aware of. + */ + Tcl_SetErrno(EINVAL); + return NULL; + } else if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer + .PathBuffer, L"\\\\?\\",4) == 0) { + /* Strip off the prefix */ + offset = 4; + } else if (wcsncmp(reparseBuffer->SymbolicLinkReparseBuffer + .PathBuffer, L"\\??\\",4) == 0) { + /* Strip off the prefix */ + offset = 4; } - - /* - * This is actually a mounted drive, which doesn't exists as a - * DOS drive letter. This means the path isn't actually a - * link, although we partially treat it like one ('file type' - * will return 'link'), but then the link will actually just - * be treated like an ordinary directory. I don't believe any - * serious inconsistency will arise from this, but it is - * something to be aware of. - */ - - goto invalidError; - } else if (wcsncmp(reparseBuffer->MountPointReparseBuffer - .PathBuffer, L"\\\\?\\",4) == 0) { - /* - * Strip off the prefix. - */ - - offset = 4; - } else if (wcsncmp(reparseBuffer->MountPointReparseBuffer - .PathBuffer, L"\\??\\",4) == 0) { - /* - * Strip off the prefix. - */ - - offset = 4; } - } - - Tcl_WinTCharToUtf((const char *) - reparseBuffer->MountPointReparseBuffer.PathBuffer, - (int) reparseBuffer->MountPointReparseBuffer + + Tcl_WinTCharToUtf( + (CONST char*)reparseBuffer->SymbolicLinkReparseBuffer.PathBuffer, + (int)reparseBuffer->SymbolicLinkReparseBuffer .SubstituteNameLength, &ds); - - copy = Tcl_DStringValue(&ds)+offset; - len = Tcl_DStringLength(&ds)-offset; - retVal = Tcl_NewStringObj(copy,len); - Tcl_IncrRefCount(retVal); - Tcl_DStringFree(&ds); - return retVal; + + copy = Tcl_DStringValue(&ds)+offset; + len = Tcl_DStringLength(&ds)-offset; + retVal = Tcl_NewStringObj(copy,len); + Tcl_IncrRefCount(retVal); + Tcl_DStringFree(&ds); + return retVal; + } } - - invalidError: Tcl_SetErrno(EINVAL); return NULL; } @@ -684,55 +575,42 @@ WinReadLinkDirectory( /* *-------------------------------------------------------------------- * - * NativeReadReparse -- + * NativeReadReparse * - * Read the junction/reparse information from a given NTFS directory. - * - * Assumption that linkDirPath is a valid, existing directory. - * - * Returns: - * Zero on success. + * Read the junction/reparse information from a given NTFS directory. * + * Assumption that LinkDirectory is a valid, existing directory. + * + * Returns zero on success. *-------------------------------------------------------------------- */ - -static int -NativeReadReparse( - const TCHAR *linkDirPath, /* The junction to read */ - REPARSE_DATA_BUFFER *buffer)/* Pointer to buffer. Cannot be NULL */ +static int +NativeReadReparse(LinkDirectory, buffer) + CONST TCHAR* LinkDirectory; /* The junction to read */ + REPARSE_DATA_BUFFER* buffer; /* Pointer to buffer. Cannot be NULL */ { 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 = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_READ, 0, + NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) { - /* - * Error creating directory. - */ - + /* Error creating directory */ TclWinConvertError(GetLastError()); return -1; } - - /* - * Get the link. - */ - - if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, - sizeof(DUMMY_REPARSE_BUFFER), &returnedLength, NULL)) { - /* - * Error setting junction. - */ - + /* Get the link */ + if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, + 0, buffer, sizeof(DUMMY_REPARSE_BUFFER), + &returnedLength, NULL)) { + /* Error setting junction */ TclWinConvertError(GetLastError()); CloseHandle(hFile); return -1; } CloseHandle(hFile); - + if (!IsReparseTagValid(buffer->ReparseTag)) { Tcl_SetErrno(EINVAL); return -1; @@ -743,69 +621,48 @@ NativeReadReparse( /* *-------------------------------------------------------------------- * - * NativeWriteReparse -- - * - * Write the reparse information for a given directory. - * - * Assumption that LinkDirectory does not exist. + * NativeWriteReparse * + * Write the reparse information for a given directory. + * + * Assumption that LinkDirectory does not exist. *-------------------------------------------------------------------- */ - -static int -NativeWriteReparse( - const TCHAR *linkDirPath, - REPARSE_DATA_BUFFER *buffer) +static int +NativeWriteReparse(LinkDirectory, buffer) + CONST TCHAR* LinkDirectory; + REPARSE_DATA_BUFFER* buffer; { HANDLE hFile; DWORD returnedLength; - - /* - * Create the directory - it must not already exist. - */ - - if ((*tclWinProcs->createDirectoryProc)(linkDirPath, NULL) == 0) { - /* - * Error creating directory. - */ - + + /* Create the directory - it must not already exist */ + if ((*tclWinProcs->createDirectoryProc)(LinkDirectory, NULL) == 0) { + /* Error creating directory */ 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 = (*tclWinProcs->createFileProc)(LinkDirectory, GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) { - /* - * Error creating directory. - */ - + /* Error creating directory */ TclWinConvertError(GetLastError()); return -1; } - - /* - * Set the link. - */ - - if (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, buffer, - (DWORD) buffer->ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE, - NULL, 0, &returnedLength, NULL)) { - /* - * Error setting junction. - */ - + /* Set the link */ + if (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, buffer, + (DWORD) buffer->ReparseDataLength + + REPARSE_MOUNTPOINT_HEADER_SIZE, + NULL, 0, &returnedLength, NULL)) { + /* Error setting junction */ TclWinConvertError(GetLastError()); CloseHandle(hFile); - (*tclWinProcs->removeDirectoryProc)(linkDirPath); + (*tclWinProcs->removeDirectoryProc)(LinkDirectory); return -1; } CloseHandle(hFile); - - /* - * We succeeded. - */ - + /* We succeeded */ return 0; } @@ -814,26 +671,39 @@ NativeWriteReparse( * * TclpFindExecutable -- * - * This function computes the absolute path name of the current - * application. + * This procedure computes the absolute path name of the current + * application, given its argv[0] value. * * Results: - * None. + * A clean UTF string that is the path to the executable. At this + * point we may not know the system encoding, but we convert the + * string value to UTF-8 using core Windows functions. The path name + * contains ASCII string and '/' chars do not conflict with other UTF + * chars. * * Side effects: - * The computed path is stored. + * The variable tclNativeExecutableName gets filled in with the file + * name for the application, if we figured it out. If we couldn't + * figure it out, tclNativeExecutableName is set to NULL. * *--------------------------------------------------------------------------- */ -void -TclpFindExecutable( - const char *argv0) /* The value of the application's argv[0] +char * +TclpFindExecutable(argv0) + CONST char *argv0; /* The value of the application's argv[0] * (native). */ { WCHAR wName[MAX_PATH]; char name[MAX_PATH * TCL_UTF_MAX]; + if (argv0 == NULL) { + return NULL; + } + if (tclNativeExecutableName != NULL) { + return tclNativeExecutableName; + } + /* * Under Windows we ignore argv0, and return the path for the file used to * create this process. @@ -841,17 +711,16 @@ TclpFindExecutable( if (GetModuleFileNameW(NULL, wName, MAX_PATH) == 0) { GetModuleFileNameA(NULL, name, sizeof(name)); - - /* - * Convert to WCHAR to get out of ANSI codepage - */ - - MultiByteToWideChar(CP_ACP, 0, name, -1, wName, MAX_PATH); + } else { + WideCharToMultiByte(CP_UTF8, 0, wName, -1, + name, sizeof(name), NULL, NULL); } - WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, sizeof(name), NULL,NULL); - TclWinNoBackslash(name); - TclSetObjNameOfExecutable(Tcl_NewStringObj(name, -1), NULL); + tclNativeExecutableName = ckalloc((unsigned) (strlen(name) + 1)); + strcpy(tclNativeExecutableName, name); + + TclWinNoBackslash(tclNativeExecutableName); + return tclNativeExecutableName; } /* @@ -859,53 +728,42 @@ TclpFindExecutable( * * TclpMatchInDirectory -- * - * This routine is used by the globbing code to search a directory for - * all files which match a given pattern. + * This routine is used by the globbing code to search a + * directory for all files which match a given pattern. * - * Results: - * The return value is a standard Tcl result indicating whether an error - * occurred in globbing. Errors are left in interp, good results are - * lappended to resultPtr (which must be a valid object). + * Results: + * + * The return value is a standard Tcl result indicating whether an + * error occurred in globbing. Errors are left in interp, good + * results are lappended to resultPtr (which must be a valid object) * * Side effects: * None. * - *---------------------------------------------------------------------- - */ + *---------------------------------------------------------------------- */ int -TclpMatchInDirectory( - Tcl_Interp *interp, /* Interpreter to receive errors. */ - Tcl_Obj *resultPtr, /* List object to lappend results. */ - Tcl_Obj *pathPtr, /* Contains path to directory to search. */ - const char *pattern, /* Pattern to match against. */ - Tcl_GlobTypeData *types) /* Object containing list of acceptable types. +TclpMatchInDirectory(interp, resultPtr, pathPtr, pattern, types) + Tcl_Interp *interp; /* Interpreter to receive errors. */ + Tcl_Obj *resultPtr; /* List object to lappend results. */ + Tcl_Obj *pathPtr; /* Contains path to directory to search. */ + CONST char *pattern; /* Pattern to match against. */ + Tcl_GlobTypeData *types; /* Object containing list of acceptable types. * May be NULL. In particular the directory * flag is very important. */ { - const TCHAR *native; - - if (types != NULL && types->type == TCL_GLOB_TYPE_MOUNT) { - /* - * The native filesystem never adds mounts. - */ - - return TCL_OK; - } + CONST TCHAR *native; if (pattern == NULL || (*pattern == '\0')) { Tcl_Obj *norm = Tcl_FSGetNormalizedPath(NULL, pathPtr); if (norm != NULL) { - /* - * Match a single file directly. - */ - + /* Match a single file directly */ int len; DWORD attr; - const char *str = Tcl_GetStringFromObj(norm,&len); - - native = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); + CONST char *str = Tcl_GetStringFromObj(norm,&len); + native = (CONST TCHAR*) Tcl_FSGetNativePath(pathPtr); + if (tclWinProcs->getFileAttributesExProc == NULL) { attr = (*tclWinProcs->getFileAttributesProc)(native); if (attr == 0xffffffff) { @@ -919,8 +777,8 @@ TclpMatchInDirectory( } attr = data.dwFileAttributes; } - - if (NativeMatchType(WinIsDrive(str,len), attr, native, types)) { + if (NativeMatchType(WinIsDrive(str,len), attr, + native, types)) { Tcl_ListObjAppendElement(interp, resultPtr, pathPtr); } } @@ -929,127 +787,94 @@ TclpMatchInDirectory( DWORD attr; HANDLE handle; WIN32_FIND_DATAT data; - const char *dirName; /* UTF-8 dir name, later with pattern - * appended. */ + CONST char *dirName; int dirLength; int matchSpecialDots; - Tcl_DString ds; /* Native encoding of dir, also used - * temporarily for other things. */ - Tcl_DString dsOrig; /* UTF-8 encoding of dir. */ + Tcl_DString ds; /* native encoding of dir */ + Tcl_DString dsOrig; /* utf-8 encoding of dir */ + Tcl_DString dirString; /* utf-8 encoding of dir with \'s */ Tcl_Obj *fileNamePtr; - char lastChar; /* - * Get the normalized path representation (the main thing is we dont - * want any '~' sequences). + * Convert the path to normalized form since some interfaces only + * accept backslashes. Also, ensure that the directory ends with a + * separator character. */ - fileNamePtr = Tcl_FSGetNormalizedPath(interp, pathPtr); + fileNamePtr = Tcl_FSGetTranslatedPath(interp, pathPtr); if (fileNamePtr == NULL) { return TCL_ERROR; } + Tcl_DStringInit(&dsOrig); + dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength); + Tcl_DStringAppend(&dsOrig, dirName, dirLength); + + Tcl_DStringInit(&dirString); + if (dirLength == 0) { + Tcl_DStringAppend(&dirString, ".\\", 2); + } else { + char *p; + Tcl_DStringAppend(&dirString, dirName, dirLength); + for (p = Tcl_DStringValue(&dirString); *p != '\0'; p++) { + if (*p == '/') { + *p = '\\'; + } + } + p--; + /* Make sure we have a trailing directory delimiter */ + if ((*p != '\\') && (*p != ':')) { + Tcl_DStringAppend(&dirString, "\\", 1); + Tcl_DStringAppend(&dsOrig, "/", 1); + dirLength++; + } + } + dirName = Tcl_DStringValue(&dirString); + Tcl_DecrRefCount(fileNamePtr); + /* - * Verify that the specified path exists and is actually a directory. + * First verify that the specified path is actually a directory. */ - native = Tcl_FSGetNativePath(pathPtr); - if (native == NULL) { - return TCL_OK; - } + native = Tcl_WinUtfToTChar(dirName, Tcl_DStringLength(&dirString), + &ds); attr = (*tclWinProcs->getFileAttributesProc)(native); + Tcl_DStringFree(&ds); if ((attr == 0xffffffff) || ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)) { + Tcl_DStringFree(&dirString); return TCL_OK; } /* - * Build up the directory name for searching, including a trailing - * directory separator. + * We need to check all files in the directory, so append a *.* + * to the path. */ - Tcl_DStringInit(&dsOrig); - dirName = Tcl_GetStringFromObj(fileNamePtr, &dirLength); - Tcl_DStringAppend(&dsOrig, dirName, dirLength); - - lastChar = dirName[dirLength -1]; - if ((lastChar != '\\') && (lastChar != '/') && (lastChar != ':')) { - Tcl_DStringAppend(&dsOrig, "/", 1); - dirLength++; - } - dirName = Tcl_DStringValue(&dsOrig); - - /* - * We need to check all files in the directory, so we append '*.*' to - * the path, unless the pattern we've been given is rather simple, - * when we can use that instead. - */ - - if (strpbrk(pattern, "[]\\") == NULL) { - /* - * The pattern is a simple one containing just '*' and/or '?'. - * This means we can get the OS to help us, by passing it the - * pattern. - */ - - dirName = Tcl_DStringAppend(&dsOrig, pattern, -1); - } else { - dirName = Tcl_DStringAppend(&dsOrig, "*.*", 3); - } - + dirName = Tcl_DStringAppend(&dirString, "*.*", 3); native = Tcl_WinUtfToTChar(dirName, -1, &ds); - if (tclWinProcs->findFirstFileExProc == NULL || (types == NULL) - || (types->type != TCL_GLOB_TYPE_DIR)) { - handle = (*tclWinProcs->findFirstFileProc)(native, &data); - } else { - /* - * We can be more efficient, for pure directory requests. - */ - - handle = (*tclWinProcs->findFirstFileExProc)(native, - FindExInfoStandard, &data, - FindExSearchLimitToDirectories, NULL, 0); - } + handle = (*tclWinProcs->findFirstFileProc)(native, &data); if (handle == INVALID_HANDLE_VALUE) { - DWORD err = GetLastError(); + TclWinConvertError(GetLastError()); Tcl_DStringFree(&ds); - if (err == ERROR_FILE_NOT_FOUND) { - /* - * We used our 'pattern' above, and matched nothing. This - * means we just return TCL_OK, indicating no results found. - */ - - Tcl_DStringFree(&dsOrig); - return TCL_OK; - } - - TclWinConvertError(err); - if (interp != NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "couldn't read directory \"", - Tcl_DStringValue(&dsOrig), "\": ", - Tcl_PosixError(interp), NULL); - } + Tcl_DStringFree(&dirString); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "couldn't read directory \"", + Tcl_DStringValue(&dsOrig), "\": ", + Tcl_PosixError(interp), (char *) NULL); Tcl_DStringFree(&dsOrig); return TCL_ERROR; } Tcl_DStringFree(&ds); /* - * We may use this later, so we must restore it to its length - * including the directory delimiter. - */ - - Tcl_DStringSetLength(&dsOrig, dirLength); - - /* - * Check to see if the pattern should match the special . and - * .. names, referring to the current directory, or the directory - * above. We need a special check for this because paths beginning - * with a dot are not considered hidden on Windows, and so otherwise a - * relative glob like 'glob -join * *' will actually return - * './. ../..' etc. + * Check to see if the pattern should match the special + * . and .. names, referring to the current directory, + * or the directory above. We need a special check for + * this because paths beginning with a dot are not considered + * hidden on Windows, and so otherwise a relative glob like + * 'glob -join * *' will actually return './. ../..' etc. */ if ((pattern[0] == '.') @@ -1060,53 +885,51 @@ TclpMatchInDirectory( } /* - * Now iterate over all of the files in the directory, starting with - * the first one we found. + * Now iterate over all of the files in the directory, starting + * with the first one we found. */ do { - const char *utfname; - int checkDrive = 0, isDrive; + CONST char *utfname; + int checkDrive = 0; + int isDrive; DWORD attr; - + if (tclWinProcs->useWide) { - native = (const TCHAR *) data.w.cFileName; + native = (CONST TCHAR *) data.w.cFileName; attr = data.w.dwFileAttributes; } else { - native = (const TCHAR *) data.a.cFileName; + native = (CONST TCHAR *) data.a.cFileName; attr = data.a.dwFileAttributes; } - + utfname = Tcl_WinTCharToUtf(native, -1, &ds); if (!matchSpecialDots) { - /* - * If it is exactly '.' or '..' then we ignore it. - */ - - if ((utfname[0] == '.') && (utfname[1] == '\0' + /* If it is exactly '.' or '..' then we ignore it */ + if ((utfname[0] == '.') && (utfname[1] == '\0' || (utfname[1] == '.' && utfname[2] == '\0'))) { Tcl_DStringFree(&ds); continue; } } else if (utfname[0] == '.' && utfname[1] == '.' && utfname[2] == '\0') { - /* - * Have to check if this is a drive below, so we can correctly - * match 'hidden' and not hidden files. + /* + * Have to check if this is a drive below, so we can + * correctly match 'hidden' and not hidden files. */ - checkDrive = 1; } - + /* - * Check to see if the file matches the pattern. Note that we are - * ignoring the case sensitivity flag because Windows doesn't - * honor case even if the volume is case sensitive. If the volume - * also doesn't preserve case, then we previously returned the - * lower case form of the name. This didn't seem quite right since - * there are non-case-preserving volumes that actually return - * mixed case. So now we are returning exactly what we get from + * Check to see if the file matches the pattern. Note that + * we are ignoring the case sensitivity flag because Windows + * doesn't honor case even if the volume is case sensitive. + * If the volume also doesn't preserve case, then we + * previously returned the lower case form of the name. This + * didn't seem quite right since there are + * non-case-preserving volumes that actually return mixed + * case. So now we are returning exactly what we get from * the system. */ @@ -1117,7 +940,7 @@ TclpMatchInDirectory( */ if (checkDrive) { - const char *fullname = Tcl_DStringAppend(&dsOrig, utfname, + CONST char *fullname = Tcl_DStringAppend(&dsOrig, utfname, Tcl_DStringLength(&ds)); isDrive = WinIsDrive(fullname, Tcl_DStringLength(&dsOrig)); Tcl_DStringSetLength(&dsOrig, dirLength); @@ -1125,7 +948,7 @@ TclpMatchInDirectory( isDrive = 0; } if (NativeMatchType(isDrive, attr, native, types)) { - Tcl_ListObjAppendElement(interp, resultPtr, + Tcl_ListObjAppendElement(interp, resultPtr, TclNewFSPathObj(pathPtr, utfname, Tcl_DStringLength(&ds))); } @@ -1134,38 +957,33 @@ TclpMatchInDirectory( /* * Free ds here to ensure that native is valid above. */ - Tcl_DStringFree(&ds); } while ((*tclWinProcs->findNextFileProc)(handle, &data) == TRUE); FindClose(handle); + Tcl_DStringFree(&dirString); Tcl_DStringFree(&dsOrig); return TCL_OK; } } -/* - * Does the given path represent a root volume? We need this special case - * because for NTFS root volumes, the getFileAttributesProc returns a 'hidden' - * attribute when it should not. +/* + * Does the given path represent a root volume? We need this special + * case because for NTFS root volumes, the getFileAttributesProc returns + * a 'hidden' attribute when it should not. */ - static int WinIsDrive( - const char *name, /* Name (UTF-8) */ - int len) /* Length of name */ + CONST char *name, /* Name (UTF-8) */ + int len) /* Length of name */ { int remove = 0; - while (len > 4) { - if ((name[len-1] != '.' || name[len-2] != '.') - || (name[len-3] != '/' && name[len-3] != '\\')) { - /* - * We don't have '/..' at the end. - */ - + if ((name[len-1] != '.' || name[len-2] != '.') + || (name[len-3] != '/' && name[len-3] != '\\')) { + /* We don't have '/..' at the end */ if (remove == 0) { - break; + break; } remove--; while (len > 0) { @@ -1175,95 +993,74 @@ WinIsDrive( } } if (len < 4) { - len++; + len++; break; } - } else { - /* - * We do have '/..' - */ - + } else { + /* We do have '/..' */ len -= 3; remove++; - } + } } - if (len < 4) { if (len == 0) { - /* - * Not sure if this is possible, but we pass it on anyway. + /* + * Not sure if this is possible, but we pass it on + * anyway */ } else if (len == 1 && (name[0] == '/' || name[0] == '\\')) { - /* - * Path is pointing to the root volume. - */ - + /* Path is pointing to the root volume */ return 1; - } else if ((name[1] == ':') + } else if ((name[1] == ':') && (len == 2 || (name[2] == '/' || name[2] == '\\'))) { - /* - * Path is of the form 'x:' or 'x:/' or 'x:\' - */ - + /* Path is of the form 'x:' or 'x:/' or 'x:\' */ return 1; } } - return 0; } -/* - * Does the given path represent a reserved window path name? If not return 0, - * if true, return the number of characters of the path that we actually want - * (not any trailing :). +/* + * Does the given path represent a reserved window path name? If not + * return 0, if true, return the number of characters of the path that + * we actually want (not any trailing :). */ - -static int -WinIsReserved( - const char *path) /* Path in UTF-8 */ +static int WinIsReserved( + CONST char *path) /* Path in UTF-8 */ { - if ((path[0] == 'c' || path[0] == 'C') - && (path[1] == 'o' || path[1] == 'O')) { + if ((path[0] == 'c' || path[0] == 'C') + && (path[1] == 'o' || path[1] == 'O')) { if ((path[2] == 'm' || path[2] == 'M') - && path[3] >= '1' && path[3] <= '4') { - /* - * May have match for 'com[1-4]:?', which is a serial port. - */ - + && path[3] >= '1' && path[3] <= '4') { + /* May have match for 'com[1-4]:?', which is a serial port */ if (path[4] == '\0') { return 4; } else if (path [4] == ':' && path[5] == '\0') { return 4; } } else if ((path[2] == 'n' || path[2] == 'N') && path[3] == '\0') { - /* - * Have match for 'con' - */ - + /* Have match for 'con' */ return 3; } - } else if ((path[0] == 'l' || path[0] == 'L') - && (path[1] == 'p' || path[1] == 'P') - && (path[2] == 't' || path[2] == 'T')) { + && (path[1] == 'p' || path[1] == 'P') + && (path[2] == 't' || path[2] == 'T')) { if (path[3] >= '1' && path[3] <= '3') { - /* - * May have match for 'lpt[1-3]:?' - */ - + /* May have match for 'lpt[1-3]:?' */ if (path[4] == '\0') { return 4; } else if (path [4] == ':' && path[5] == '\0') { return 4; } } - - } else if (!strcasecmp(path, "prn") || !strcasecmp(path, "nul") - || !strcasecmp(path, "aux")) { - /* - * Have match for 'prn', 'nul' or 'aux'. - */ - + } else if (stricmp(path, "prn") == 0) { + /* Have match for 'prn' */ + return 3; + } else if (stricmp(path, "nul") == 0) { + /* Have match for 'nul' */ + return 3; + } else if (stricmp(path, "aux") == 0) { + /* Have match for 'aux' */ return 3; } return 0; @@ -1271,108 +1068,102 @@ WinIsReserved( /* *---------------------------------------------------------------------- - * + * * NativeMatchType -- - * - * This function needs a special case for a path which is a root volume, - * because for NTFS root volumes, the getFileAttributesProc returns a - * 'hidden' attribute when it should not. - * - * We never make any calls to a 'get attributes' routine here, since we - * have arranged things so that our caller already knows such - * information. - * + * + * This function needs a special case for a path which is a root + * volume, because for NTFS root volumes, the getFileAttributesProc + * returns a 'hidden' attribute when it should not. + * + * We never make any calls to a 'get attributes' routine here, + * since we have arranged things so that our caller already knows + * such information. + * * Results: - * 0 = file doesn't match - * 1 = file matches - * + * 0 = file doesn't match + * 1 = file matches + * *---------------------------------------------------------------------- */ - -static int +static int NativeMatchType( - int isDrive, /* Is this a drive. */ - DWORD attr, /* We already know the attributes for the - * file. */ - const TCHAR *nativeName, /* Native path to check. */ - Tcl_GlobTypeData *types) /* Type description to match against. */ + int isDrive, /* Is this a drive */ + DWORD attr, /* We already know the attributes + * for the file */ + CONST TCHAR* nativeName, /* Native path to check */ + Tcl_GlobTypeData *types) /* Type description to match against */ { /* - * 'attr' represents the attributes of the file, but we only want to - * retrieve this info if it is absolutely necessary because it is an - * expensive call. Unfortunately, to deal with hidden files properly, we - * must always retrieve it. + * 'attr' represents the attributes of the file, but we only + * want to retrieve this info if it is absolutely necessary + * because it is an expensive call. Unfortunately, to deal + * with hidden files properly, we must always retrieve it. */ if (types == NULL) { - /* - * If invisible, don't return the file. - */ - + /* If invisible, don't return the file */ if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) { return 0; } } else { if (attr & FILE_ATTRIBUTE_HIDDEN && !isDrive) { - /* - * If invisible. - */ - - if ((types->perm == 0) || !(types->perm & TCL_GLOB_PERM_HIDDEN)) { + /* 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 != 0) { - if (((types->perm & TCL_GLOB_PERM_RONLY) && + if ( + ((types->perm & TCL_GLOB_PERM_RONLY) && !(attr & FILE_ATTRIBUTE_READONLY)) || - ((types->perm & TCL_GLOB_PERM_R) && + ((types->perm & TCL_GLOB_PERM_R) && (0 /* File exists => R_OK on Windows */)) || - ((types->perm & TCL_GLOB_PERM_W) && + ((types->perm & TCL_GLOB_PERM_W) && (attr & FILE_ATTRIBUTE_READONLY)) || - ((types->perm & TCL_GLOB_PERM_X) && + ((types->perm & TCL_GLOB_PERM_X) && (!(attr & FILE_ATTRIBUTE_DIRECTORY) - && !NativeIsExec(nativeName)))) { + && !NativeIsExec(nativeName))) + ) { return 0; } } - if ((types->type & TCL_GLOB_TYPE_DIR) - && (attr & FILE_ATTRIBUTE_DIRECTORY)) { - /* - * Quicker test for directory, which is a common case. - */ - + if ((types->type & TCL_GLOB_TYPE_DIR) + && (attr & FILE_ATTRIBUTE_DIRECTORY)) { + /* Quicker test for directory, which is a common case */ return 1; - } else if (types->type != 0) { unsigned short st_mode; int isExec = NativeIsExec(nativeName); - + st_mode = NativeStatMode(attr, 0, isExec); /* * 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)) || + 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)) || + ((types->type & TCL_GLOB_TYPE_FILE) && + S_ISREG(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. - */ + ) { + /* Do nothing -- this file is ok */ } else { #ifdef S_ISLNK if (types->type & TCL_GLOB_TYPE_LINK) { @@ -1384,8 +1175,8 @@ NativeMatchType( #endif return 0; } - } - } + } + } return 1; } @@ -1400,9 +1191,9 @@ NativeMatchType( * Results: * The result is a pointer to a string specifying the user's home * directory, or NULL if the user's home directory could not be - * determined. Storage for the result string is allocated in bufferPtr; - * the caller must call Tcl_DStringFree() when the result is no longer - * needed. + * determined. Storage for the result string is allocated in + * bufferPtr; the caller must call Tcl_DStringFree() when the result + * is no longer needed. * * Side effects: * None. @@ -1411,15 +1202,16 @@ NativeMatchType( */ 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. */ +TclpGetUserHome(name, bufferPtr) + 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; result = NULL; + Tcl_DStringInit(bufferPtr); netapiInst = LoadLibraryA("netapi32.dll"); @@ -1430,17 +1222,17 @@ TclpGetUserHome( netApiBufferFreeProc = (NETAPIBUFFERFREEPROC *) GetProcAddress(netapiInst, "NetApiBufferFree"); - netGetDCNameProc = (NETGETDCNAMEPROC *) + netGetDCNameProc = (NETGETDCNAMEPROC *) GetProcAddress(netapiInst, "NetGetDCName"); - netUserGetInfoProc = (NETUSERGETINFOPROC *) + netUserGetInfoProc = (NETUSERGETINFOPROC *) GetProcAddress(netapiInst, "NetUserGetInfo"); if ((netUserGetInfoProc != NULL) && (netGetDCNameProc != NULL) && (netApiBufferFreeProc != NULL)) { - USER_INFO_1 *uiPtr, **uiPtrPtr = &uiPtr; + USER_INFO_1 *uiPtr; Tcl_DString ds; int nameLen, badDomain; char *domain; - WCHAR *wName, *wHomeDir, *wDomain, **wDomainPtr = &wDomain; + WCHAR *wName, *wHomeDir, *wDomain; WCHAR buf[MAX_PATH]; badDomain = 0; @@ -1450,23 +1242,23 @@ TclpGetUserHome( if (domain != NULL) { Tcl_DStringInit(&ds); wName = Tcl_UtfToUniCharDString(domain + 1, -1, &ds); - badDomain = (netGetDCNameProc)(NULL, wName, - (LPBYTE *) wDomainPtr); + badDomain = (*netGetDCNameProc)(NULL, wName, + (LPBYTE *) &wDomain); 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) { + if ((*netUserGetInfoProc)(wDomain, wName, 1, + (LPBYTE *) &uiPtr) == 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 + /* + * User exists but has no home dir. Return * "{Windows Drive}:/users/default". */ @@ -1487,20 +1279,20 @@ TclpGetUserHome( } if (result == NULL) { /* - * Look in the "Password Lists" section of system.ini for the local - * user. There are also entries in that section that begin with a "*" - * character that are used by Windows for other purposes; ignore user - * names beginning with a "*". + * Look in the "Password Lists" section of system.ini for the + * local user. There are also entries in that section that begin + * with a "*" character that are used by Windows for other + * purposes; ignore user names beginning with a "*". */ char buf[MAX_PATH]; if (name[0] != '*') { - if (GetPrivateProfileStringA("Password Lists", name, "", buf, + if (GetPrivateProfileStringA("Password Lists", name, "", buf, MAX_PATH, "system.ini") > 0) { - /* - * User exists, but there is no such thing as a home directory - * in system.ini. Return "{Windows drive}:/". + /* + * User exists, but there is no such thing as a home + * directory in system.ini. Return "{Windows drive}:/". */ GetWindowsDirectoryA(buf, MAX_PATH); @@ -1520,7 +1312,7 @@ TclpGetUserHome( * * This function replaces the library version of access(), fixing the * following bugs: - * + * * 1. access() returns that all files have execute permission. * * Results: @@ -1534,7 +1326,7 @@ TclpGetUserHome( static int NativeAccess( - const TCHAR *nativePath, /* Path of file to access, native encoding. */ + CONST TCHAR *nativePath, /* Path of file to access (UTF-8). */ int mode) /* Permission setting. */ { DWORD attr; @@ -1602,7 +1394,8 @@ NativeAccess( SID_IDENTIFIER_AUTHORITY samba_unmapped = { 0, 0, 0, 0, 0, 22 }; GENERIC_MAPPING genMap; HANDLE hToken = NULL; - DWORD desiredAccess = 0, grantedAccess = 0; + DWORD desiredAccess = 0; + DWORD grantedAccess = 0; BOOL accessYesNo = FALSE; PRIVILEGE_SET privSet; DWORD privSetSize = sizeof(PRIVILEGE_SET); @@ -1629,12 +1422,12 @@ NativeAccess( * to EACCES - just what we want! */ - TclWinConvertError((DWORD) error); + TclWinConvertError((DWORD)error); return -1; } /* - * Now size contains the size of buffer needed. + * Now size contains the size of buffer needed */ sdPtr = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size); @@ -1644,7 +1437,7 @@ NativeAccess( } /* - * Call GetFileSecurity() for real. + * Call GetFileSecurity() for real */ if (!(*tclWinProcs->getFileSecurityProc)(nativePath, @@ -1679,28 +1472,28 @@ NativeAccess( } /* - * Perform security impersonation of the user and open the resulting - * thread token. + * Perform security impersonation of the user and open the + * resulting thread token. */ if (!(*tclWinProcs->impersonateSelfProc)(SecurityImpersonation)) { /* * Unable to perform security impersonation. */ - + goto accessError; } - if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread(), + if (!(*tclWinProcs->openThreadTokenProc)(GetCurrentThread (), TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken)) { /* * Unable to get current thread's token. */ - + goto accessError; } - + (*tclWinProcs->revertToSelfProc)(); - + /* * Setup desiredAccess according to the access priveleges we are * checking. @@ -1716,12 +1509,12 @@ NativeAccess( desiredAccess |= FILE_GENERIC_EXECUTE; } - memset(&genMap, 0x0, sizeof(GENERIC_MAPPING)); + memset (&genMap, 0x0, sizeof (GENERIC_MAPPING)); genMap.GenericRead = FILE_GENERIC_READ; genMap.GenericWrite = FILE_GENERIC_WRITE; genMap.GenericExecute = FILE_GENERIC_EXECUTE; genMap.GenericAll = FILE_ALL_ACCESS; - + /* * Perform access check using the token. */ @@ -1748,13 +1541,12 @@ NativeAccess( * Clean up. */ - HeapFree(GetProcessHeap(), 0, sdPtr); + HeapFree(GetProcessHeap (), 0, sdPtr); CloseHandle(hToken); if (!accessYesNo) { Tcl_SetErrno(EACCES); return -1; } - } return 0; } @@ -1764,22 +1556,25 @@ NativeAccess( * * NativeIsExec -- * - * Determines if a path is executable. On windows this is simply defined - * by whether the path ends in any of ".exe", ".com", or ".bat" + * Determines if a path is executable. On windows this is + * simply defined by whether the path ends in any of ".exe", + * ".com", or ".bat" * * Results: * 1 = executable, 0 = not. * *---------------------------------------------------------------------- */ - static int -NativeIsExec( - const TCHAR *nativePath) +NativeIsExec(nativePath) + CONST TCHAR *nativePath; { if (tclWinProcs->useWide) { - const WCHAR *path = (const WCHAR *) nativePath; - int len = wcslen(path); + CONST WCHAR *path; + int len; + + path = (CONST WCHAR*)nativePath; + len = wcslen(path); if (len < 5) { return 0; @@ -1792,31 +1587,26 @@ NativeIsExec( /* * 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)) { + 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; + CONST char *p; - /* - * We are only looking for pure ascii. - */ + /* We are only looking for pure ascii */ - p = strrchr((const char *) nativePath, '.'); + 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 ((strcasecmp(p, "exe") == 0) - || (strcasecmp(p, "com") == 0) - || (strcasecmp(p, "bat") == 0)) { + if ((stricmp(p, "exe") == 0) + || (stricmp(p, "com") == 0) + || (stricmp(p, "bat") == 0)) { /* * File that ends with .exe, .com, or .bat is executable. */ @@ -1839,31 +1629,28 @@ NativeIsExec( * See chdir() documentation. * * Side effects: - * See chdir() documentation. + * See chdir() documentation. * *---------------------------------------------------------------------- */ -int -TclpObjChdir( - Tcl_Obj *pathPtr) /* Path to new working directory. */ +int +TclpObjChdir(pathPtr) + Tcl_Obj *pathPtr; /* Path to new working directory. */ { int result; - const TCHAR *nativePath; + CONST TCHAR *nativePath; #ifdef __CYGWIN__ - extern int cygwin_conv_to_posix_path(const char *, char *); + extern int cygwin_conv_to_posix_path + _ANSI_ARGS_((CONST char *, char *)); char posixPath[MAX_PATH+1]; - const char *path; + CONST char *path; Tcl_DString ds; #endif /* __CYGWIN__ */ - nativePath = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); - + nativePath = (CONST TCHAR *) Tcl_FSGetNativePath(pathPtr); #ifdef __CYGWIN__ - /* - * Cygwin chdir only groks POSIX path. - */ - + /* 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); @@ -1885,26 +1672,26 @@ TclpObjChdir( * * TclpReadlink -- * - * This function replaces the library version of readlink(). + * 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. + * 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. + * 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). */ +TclpReadlink(path, linkPtr) + 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; @@ -1912,9 +1699,9 @@ TclpReadlink( Tcl_DString ds; native = Tcl_UtfToExternalDString(NULL, path, -1, &ds); - length = readlink(native, link, sizeof(link)); /* INTL: Native. */ + length = readlink(native, link, sizeof(link)); /* INTL: Native. */ Tcl_DStringFree(&ds); - + if (length < 0) { return NULL; } @@ -1929,16 +1716,15 @@ TclpReadlink( * * TclpGetCwd -- * - * This function replaces the library version of getcwd(). (Obsolete - * function, only retained for old extensions which may call it - * directly). + * This function replaces the library version of getcwd(). * * Results: - * The result is a pointer to a string specifying the current directory, - * or NULL if the current directory could not be determined. If NULL is - * returned, an error message is left in the interp's result. Storage for - * the result string is allocated in bufferPtr; the caller must call - * Tcl_DStringFree() when the result is no longer needed. + * The result is a pointer to a string specifying the current + * directory, or NULL if the current directory could not be + * determined. If NULL is returned, an error message is left in the + * interp's result. Storage for the result string is allocated in + * bufferPtr; the caller must call Tcl_DStringFree() when the result + * is no longer needed. * * Side effects: * None. @@ -1946,11 +1732,11 @@ TclpReadlink( *---------------------------------------------------------------------- */ -const char * -TclpGetCwd( - Tcl_Interp *interp, /* If non-NULL, used for error reporting. */ - Tcl_DString *bufferPtr) /* Uninitialized or free DString filled with - * name of current directory. */ +CONST char * +TclpGetCwd(interp, bufferPtr) + Tcl_Interp *interp; /* If non-NULL, used for error reporting. */ + Tcl_DString *bufferPtr; /* Uninitialized or free DString filled + * with name of current directory. */ { WCHAR buffer[MAX_PATH]; char *p; @@ -1958,8 +1744,9 @@ TclpGetCwd( if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) { TclWinConvertError(GetLastError()); if (interp != NULL) { - Tcl_AppendResult(interp, "error getting working directory name: ", - Tcl_PosixError(interp), NULL); + Tcl_AppendResult(interp, + "error getting working directory name: ", + Tcl_PosixError(interp), (char *) NULL); } return NULL; } @@ -1972,7 +1759,7 @@ TclpGetCwd( WCHAR *native; native = (WCHAR *) buffer; - if ((native[0] != '\0') && (native[1] == ':') + if ((native[0] != '\0') && (native[1] == ':') && (native[2] == '\\') && (native[3] == '\\')) { native += 2; } @@ -1981,7 +1768,7 @@ TclpGetCwd( char *native; native = (char *) buffer; - if ((native[0] != '\0') && (native[1] == ':') + if ((native[0] != '\0') && (native[1] == ':') && (native[2] == '\\') && (native[3] == '\\')) { native += 2; } @@ -1991,7 +1778,7 @@ TclpGetCwd( /* * Convert to forward slashes for easier use in scripts. */ - + for (p = Tcl_DStringValue(bufferPtr); *p != '\0'; p++) { if (*p == '\\') { *p = '/'; @@ -2000,21 +1787,38 @@ TclpGetCwd( return Tcl_DStringValue(bufferPtr); } -int -TclpObjStat( - Tcl_Obj *pathPtr, /* Path of file to stat. */ - Tcl_StatBuf *statPtr) /* Filled with results of stat call. */ +int +TclpObjStat(pathPtr, statPtr) + Tcl_Obj *pathPtr; /* Path of file to stat */ + Tcl_StatBuf *statPtr; /* Filled with results of stat call. */ { +#ifdef OLD_API + Tcl_Obj *transPtr; /* - * Ensure correct file sizes by forcing the OS to write any pending data - * to disk. This is done only for channels which are dirty, i.e. have been - * written to since the last flush here. + * Eliminate file names containing wildcard characters, or subsequent + * call to FindFirstFile() will expand them, matching some other file. */ - TclWinFlushDirtyChannels(); + transPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); + if (transPtr == NULL || (strpbrk(Tcl_GetString(transPtr), "?*") != NULL)) { + if (transPtr != NULL) { + Tcl_DecrRefCount(transPtr); + } + Tcl_SetErrno(ENOENT); + return -1; + } + Tcl_DecrRefCount(transPtr); +#endif + + /* + * Ensure correct file sizes by forcing the OS to write any + * pending data to disk. This is done only for channels which are + * dirty, i.e. have been written to since the last flush here. + */ - return NativeStat((const TCHAR *) Tcl_FSGetNativePath(pathPtr), - statPtr, 0); + TclWinFlushDirtyChannels (); + + return NativeStat((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), statPtr, 0); } /* @@ -2022,8 +1826,8 @@ TclpObjStat( * * NativeStat -- * - * This function replaces the library version of stat(), fixing the - * following bugs: + * This function replaces the library version of stat(), fixing + * the following bugs: * * 1. stat("c:") returns an error. * 2. Borland stat() return time in GMT instead of localtime. @@ -2040,97 +1844,31 @@ TclpObjStat( *---------------------------------------------------------------------- */ -static int -NativeStat( - const TCHAR *nativePath, /* Path of file to stat */ - Tcl_StatBuf *statPtr, /* Filled with results of stat call. */ - int checkLinks) /* If non-zero, behave like 'lstat' */ +static int +NativeStat(nativePath, statPtr, checkLinks) + CONST TCHAR *nativePath; /* Path of file to stat */ + Tcl_StatBuf *statPtr; /* Filled with results of stat call. */ + int checkLinks; /* If non-zero, behave like 'lstat' */ { + Tcl_DString ds; DWORD attr; - int dev, nlink = 1; + WCHAR nativeFullPath[MAX_PATH]; + TCHAR *nativePart; + CONST char *fullPath; + int dev; unsigned short mode; - unsigned int inode = 0; - HANDLE fileHandle; - - /* - * If we can use 'createFile' on this, then we can use the resulting - * fileHandle to read more information (nlink, ino) than we can get from - * other attributes reading APIs. If not, then we try to fall back on the - * 'getFileAttributesExProc', and if that isn't available, then on even - * simpler routines. - */ - - fileHandle = (tclWinProcs->createFileProc)(nativePath, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); - - if (fileHandle != INVALID_HANDLE_VALUE) { - BY_HANDLE_FILE_INFORMATION data; - - if (GetFileInformationByHandle(fileHandle,&data) != TRUE) { - CloseHandle(fileHandle); - Tcl_SetErrno(ENOENT); - return -1; - } - CloseHandle(fileHandle); - - attr = data.dwFileAttributes; - - statPtr->st_size = ((Tcl_WideInt) data.nFileSizeLow) | - (((Tcl_WideInt) data.nFileSizeHigh) << 32); - statPtr->st_atime = ToCTime(data.ftLastAccessTime); - statPtr->st_mtime = ToCTime(data.ftLastWriteTime); - statPtr->st_ctime = ToCTime(data.ftCreationTime); - - /* - * On Unix, for directories, nlink apparently depends on the number of - * files in the directory. We could calculate that, but it would be a - * bit of a performance penalty, I think. Hence we just use what - * Windows gives us, which is the same as Unix for files, at least. - */ - - nlink = data.nNumberOfLinks; - - /* - * Unfortunately our stat definition's inode field (unsigned short) - * will throw away most of the precision we have here, which means we - * can't rely on inode as a unique identifier of a file. We'd really - * like to do something like how we handle 'st_size'. - */ - - inode = data.nFileIndexHigh | data.nFileIndexLow; - } else if (tclWinProcs->getFileAttributesExProc != NULL) { - /* - * Fall back on the less capable routines. This means no nlink or ino. - */ - - WIN32_FILE_ATTRIBUTE_DATA data; - - if ((*tclWinProcs->getFileAttributesExProc)(nativePath, - GetFileExInfoStandard, &data) != TRUE) { - Tcl_SetErrno(ENOENT); - return -1; - } - - attr = data.dwFileAttributes; - - statPtr->st_size = ((Tcl_WideInt) data.nFileSizeLow) | - (((Tcl_WideInt) data.nFileSizeHigh) << 32); - 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. - */ - + + if (tclWinProcs->getFileAttributesExProc == NULL) { + /* + * 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. */ @@ -2141,9 +1879,9 @@ NativeStat( return -1; } - /* - * Make up some fake information for this file. It has the correct - * file attributes and a time of 0. + /* + * Make up some fake information for this file. It has the + * correct file attributes and a time of 0. */ memset(&data, 0, sizeof(data)); @@ -2152,95 +1890,131 @@ NativeStat( FindClose(handle); } + + (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, nativeFullPath, + &nativePart); + + fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds); + + dev = -1; + if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) { + CONST char *p; + DWORD dw; + CONST TCHAR *nativeVol; + Tcl_DString volString; + + p = strchr(fullPath + 2, '\\'); + p = strchr(p + 1, '\\'); + if (p == NULL) { + /* + * Add terminating backslash to fullpath or + * GetVolumeInformation() won't work. + */ + + fullPath = Tcl_DStringAppend(&ds, "\\", 1); + 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); + /* + * GetFullPathName() turns special devices like "NUL" into + * "\\.\NUL", but GetVolumeInformation() returns failure for + * "\\.\NUL". This will cause "NUL" to get a drive number of + * -1, which makes about as much sense as anything since the + * special devices don't live on any drive. + */ + + dev = dw; + Tcl_DStringFree(&volString); + } else if ((fullPath[0] != '\0') && (fullPath[1] == ':')) { + dev = Tcl_UniCharToLower(fullPath[0]) - 'a'; + } + Tcl_DStringFree(&ds); + attr = data.a.dwFileAttributes; - statPtr->st_size = ((Tcl_WideInt) data.a.nFileSizeLow) | - (((Tcl_WideInt) data.a.nFileSizeHigh) << 32); + 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); - mode = NativeStatMode(attr, checkLinks, NativeIsExec(nativePath)); - - statPtr->st_dev = (dev_t) dev; - statPtr->st_ino = inode; - statPtr->st_mode = mode; - statPtr->st_nlink = nlink; - statPtr->st_uid = 0; - statPtr->st_gid = 0; - statPtr->st_rdev = (dev_t) dev; - return 0; -} - -/* - *---------------------------------------------------------------------- - * - * NativeDev -- - * - * Calculate just the 'st_dev' field of a 'stat' structure. - * - *---------------------------------------------------------------------- - */ - -static int -NativeDev( - const TCHAR *nativePath) /* Full path of file to stat */ -{ - int dev; - Tcl_DString ds; - WCHAR nativeFullPath[MAX_PATH]; - TCHAR *nativePart; - const char *fullPath; + } else { + WIN32_FILE_ATTRIBUTE_DATA data; + if((*tclWinProcs->getFileAttributesExProc)(nativePath, + GetFileExInfoStandard, + &data) != TRUE) { + Tcl_SetErrno(ENOENT); + return -1; + } - (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, - nativeFullPath, &nativePart); + + (*tclWinProcs->getFullPathNameProc)(nativePath, MAX_PATH, + nativeFullPath, &nativePart); - fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds); + fullPath = Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds); - if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) { - const char *p; - DWORD dw; - const TCHAR *nativeVol; - Tcl_DString volString; + dev = -1; + if ((fullPath[0] == '\\') && (fullPath[1] == '\\')) { + CONST char *p; + DWORD dw; + CONST TCHAR *nativeVol; + Tcl_DString volString; + + p = strchr(fullPath + 2, '\\'); + p = strchr(p + 1, '\\'); + if (p == NULL) { + /* + * Add terminating backslash to fullpath or + * GetVolumeInformation() won't work. + */ - p = strchr(fullPath + 2, '\\'); - p = strchr(p + 1, '\\'); - if (p == NULL) { + fullPath = Tcl_DStringAppend(&ds, "\\", 1); + 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); /* - * Add terminating backslash to fullpath or GetVolumeInformation() - * won't work. + * GetFullPathName() turns special devices like "NUL" into + * "\\.\NUL", but GetVolumeInformation() returns failure for + * "\\.\NUL". This will cause "NUL" to get a drive number of + * -1, which makes about as much sense as anything since the + * special devices don't live on any drive. */ - fullPath = Tcl_DStringAppend(&ds, "\\", 1); - p = fullPath + Tcl_DStringLength(&ds); - } else { - p++; + dev = dw; + Tcl_DStringFree(&volString); + } else if ((fullPath[0] != '\0') && (fullPath[1] == ':')) { + dev = Tcl_UniCharToLower(fullPath[0]) - 'a'; } - nativeVol = Tcl_WinUtfToTChar(fullPath, p - fullPath, &volString); - dw = (DWORD) -1; - (*tclWinProcs->getVolumeInformationProc)(nativeVol, NULL, 0, &dw, - NULL, NULL, NULL, 0); - - /* - * GetFullPathName() turns special devices like "NUL" into "\\.\NUL", - * but GetVolumeInformation() returns failure for "\\.\NUL". This will - * cause "NUL" to get a drive number of -1, which makes about as much - * sense as anything since the special devices don't live on any - * drive. - */ - - dev = dw; - Tcl_DStringFree(&volString); - } else if ((fullPath[0] != '\0') && (fullPath[1] == ':')) { - dev = Tcl_UniCharToLower(fullPath[0]) - 'a'; - } else { - dev = -1; + Tcl_DStringFree(&ds); + + attr = data.dwFileAttributes; + + statPtr->st_size = ((Tcl_WideInt)data.nFileSizeLow) | + (((Tcl_WideInt)data.nFileSizeHigh) << 32); + statPtr->st_atime = ToCTime(data.ftLastAccessTime); + statPtr->st_mtime = ToCTime(data.ftLastWriteTime); + statPtr->st_ctime = ToCTime(data.ftCreationTime); } - Tcl_DStringFree(&ds); - return dev; + mode = NativeStatMode(attr, checkLinks, NativeIsExec(nativePath)); + + statPtr->st_dev = (dev_t) dev; + statPtr->st_ino = 0; + statPtr->st_mode = mode; + statPtr->st_nlink = 1; + statPtr->st_uid = 0; + statPtr->st_gid = 0; + statPtr->st_rdev = (dev_t) dev; + return 0; } /* @@ -2250,42 +2024,31 @@ NativeDev( * * Calculate just the 'st_mode' field of a 'stat' structure. * - * In many places we don't need the full stat structure, and it's much - * faster just to calculate these pieces, if that's all we need. - * *---------------------------------------------------------------------- */ - static unsigned short -NativeStatMode( - DWORD attr, - int checkLinks, - int isExec) +NativeStatMode(DWORD attr, int checkLinks, int isExec) { int mode; - if (checkLinks && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) { - /* - * It is a link. - */ - + /* It is a link */ mode = S_IFLNK; } else { - mode = (attr & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR|S_IEXEC : S_IFREG; + mode = (attr & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | S_IEXEC : S_IFREG; } - mode |= (attr & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD|S_IWRITE; + mode |= (attr & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; if (isExec) { mode |= S_IEXEC; } - + /* - * Propagate the S_IREAD, S_IWRITE, S_IEXEC bits to the group and other - * positions. + * Propagate the S_IREAD, S_IWRITE, S_IEXEC bits to the group and + * other positions. */ mode |= (mode & (S_IREAD|S_IWRITE|S_IEXEC)) >> 3; mode |= (mode & (S_IREAD|S_IWRITE|S_IEXEC)) >> 6; - return (unsigned short) mode; + return (unsigned short)mode; } /* @@ -2310,8 +2073,8 @@ ToCTime( convertedTime.LowPart = fileTime.dwLowDateTime; convertedTime.HighPart = (LONG) fileTime.dwHighDateTime; - return (time_t) ((convertedTime.QuadPart - - (Tcl_WideInt) POSIX_EPOCH_AS_FILETIME) / (Tcl_WideInt) 10000000); + return (time_t) ((convertedTime.QuadPart + - (Tcl_WideInt) POSIX_EPOCH_AS_FILETIME) / (Tcl_WideInt) 10000000); } /* @@ -2330,7 +2093,7 @@ ToCTime( static void FromCTime( time_t posixTime, - FILETIME *fileTime) /* UTC Time */ + FILETIME* fileTime) /* UTC Time */ { LARGE_INTEGER convertedTime; convertedTime.QuadPart = ((LONGLONG) posixTime) * 10000000 @@ -2339,105 +2102,136 @@ FromCTime( fileTime->dwHighDateTime = convertedTime.HighPart; } +#if 0 /* - *--------------------------------------------------------------------------- + *------------------------------------------------------------------------- * - * TclpGetNativeCwd -- + * TclWinResolveShortcut -- * - * This function replaces the library version of getcwd(). + * Resolve a potential Windows shortcut to get the actual file or + * directory in question. * * Results: - * The input and output are filesystem paths in native form. The result - * is either the given clientData, if the working directory hasn't - * changed, or a new clientData (owned by our caller), giving the new - * native path, or NULL if the current directory could not be determined. - * If NULL is returned, the caller can examine the standard posix error - * codes to determine the cause of the problem. + * Returns 1 if the shortcut could be resolved, or 0 if there was + * an error or if the filename was not a shortcut. + * If bufferPtr did hold the name of a shortcut, it is modified to + * hold the resolved target of the shortcut instead. * * Side effects: - * None. + * Loads and unloads OLE package to determine if filename refers to + * a shortcut. * - *---------------------------------------------------------------------- + *------------------------------------------------------------------------- */ -ClientData -TclpGetNativeCwd( - ClientData clientData) +int +TclWinResolveShortcut(bufferPtr) + Tcl_DString *bufferPtr; /* Holds name of file to resolve. On + * return, holds resolved file name. */ { - WCHAR buffer[MAX_PATH]; - - if ((*tclWinProcs->getCurrentDirectoryProc)(MAX_PATH, buffer) == 0) { - TclWinConvertError(GetLastError()); - return NULL; - } - - if (clientData != NULL) { - if (tclWinProcs->useWide) { - /* - * Unicode representation when running on NT/2K/XP. - */ + HRESULT hres; + IShellLink *psl; + IPersistFile *ppf; + WIN32_FIND_DATA wfd; + WCHAR wpath[MAX_PATH]; + char *path, *ext; + char realFileName[MAX_PATH]; - if (wcscmp((const WCHAR*)clientData, (const WCHAR*)buffer) == 0) { - return clientData; - } - } else { - /* - * ANSI representation when running on 95/98/ME. - */ + /* + * Windows system calls do not automatically resolve + * shortcuts like UNIX automatically will with symbolic links. + */ - if (strcmp((const char*) clientData, (const char*) buffer) == 0) { - return clientData; - } - } + path = Tcl_DStringValue(bufferPtr); + ext = strrchr(path, '.'); + if ((ext == NULL) || (stricmp(ext, ".lnk") != 0)) { + return 0; } - return TclNativeDupInternalRep((ClientData) buffer); + CoInitialize(NULL); + path = Tcl_DStringValue(bufferPtr); + realFileName[0] = '\0'; + hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellLink, &psl); + if (SUCCEEDED(hres)) { + hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf); + if (SUCCEEDED(hres)) { + MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, sizeof(wpath)); + hres = ppf->lpVtbl->Load(ppf, wpath, STGM_READ); + if (SUCCEEDED(hres)) { + hres = psl->lpVtbl->Resolve(psl, NULL, + SLR_ANY_MATCH | SLR_NO_UI); + if (SUCCEEDED(hres)) { + hres = psl->lpVtbl->GetPath(psl, realFileName, MAX_PATH, + &wfd, 0); + } + } + ppf->lpVtbl->Release(ppf); + } + psl->lpVtbl->Release(psl); + } + CoUninitialize(); + + if (realFileName[0] != '\0') { + Tcl_DStringSetLength(bufferPtr, 0); + Tcl_DStringAppend(bufferPtr, realFileName, -1); + return 1; + } + return 0; } +#endif -int -TclpObjAccess( - Tcl_Obj *pathPtr, - int mode) +Tcl_Obj* +TclpObjGetCwd(interp) + Tcl_Interp *interp; { - return NativeAccess((const TCHAR *) Tcl_FSGetNativePath(pathPtr), mode); + Tcl_DString ds; + if (TclpGetCwd(interp, &ds) != NULL) { + Tcl_Obj *cwdPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), -1); + Tcl_IncrRefCount(cwdPtr); + Tcl_DStringFree(&ds); + return cwdPtr; + } else { + return NULL; + } } - -int -TclpObjLstat( - Tcl_Obj *pathPtr, - Tcl_StatBuf *statPtr) + +int +TclpObjAccess(pathPtr, mode) + Tcl_Obj *pathPtr; + int mode; +{ + return NativeAccess((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), mode); +} + +int +TclpObjLstat(pathPtr, statPtr) + Tcl_Obj *pathPtr; + Tcl_StatBuf *statPtr; { /* - * Ensure correct file sizes by forcing the OS to write any pending data - * to disk. This is done only for channels which are dirty, i.e. have been - * written to since the last flush here. + * Ensure correct file sizes by forcing the OS to write any + * pending data to disk. This is done only for channels which are + * dirty, i.e. have been written to since the last flush here. */ - TclWinFlushDirtyChannels(); + TclWinFlushDirtyChannels (); - return NativeStat((const TCHAR *) Tcl_FSGetNativePath(pathPtr), - statPtr, 1); + return NativeStat((CONST TCHAR*) Tcl_FSGetNativePath(pathPtr), statPtr, 1); } - + #ifdef S_IFLNK -Tcl_Obj * -TclpObjLink( - Tcl_Obj *pathPtr, - Tcl_Obj *toPtr, - int linkAction) + +Tcl_Obj* +TclpObjLink(pathPtr, toPtr, linkAction) + Tcl_Obj *pathPtr; + Tcl_Obj *toPtr; + int linkAction; { if (toPtr != NULL) { int res; - TCHAR *LinkTarget; - TCHAR *LinkSource = (TCHAR *) Tcl_FSGetNativePath(pathPtr); - Tcl_Obj *normalizedToPtr = Tcl_FSGetNormalizedPath(NULL, toPtr); - - if (normalizedToPtr == NULL) { - return NULL; - } - - LinkTarget = (TCHAR *) Tcl_FSGetNativePath(normalizedToPtr); - + TCHAR* LinkTarget = (TCHAR*)Tcl_FSGetNativePath(toPtr); + TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr); if (LinkSource == NULL || LinkTarget == NULL) { return NULL; } @@ -2448,65 +2242,60 @@ TclpObjLink( return NULL; } } else { - TCHAR *LinkSource = (TCHAR *) Tcl_FSGetNativePath(pathPtr); - + TCHAR* LinkSource = (TCHAR*)Tcl_FSGetNativePath(pathPtr); if (LinkSource == NULL) { return NULL; } return WinReadLink(LinkSource); } } + #endif + /* *--------------------------------------------------------------------------- * * TclpFilesystemPathType -- * - * This function is part of the native filesystem support, and returns - * the path type of the given path. Returns NTFS or FAT or whatever is - * returned by the 'volume information' proc. + * This function is part of the native filesystem support, and + * returns the path type of the given path. Returns NTFS or FAT + * or whatever is returned by the 'volume information' proc. * * Results: - * NULL at present. + * NULL at present. * * Side effects: * None. * *--------------------------------------------------------------------------- */ - -Tcl_Obj * -TclpFilesystemPathType( - Tcl_Obj *pathPtr) +Tcl_Obj* +TclpFilesystemPathType(pathObjPtr) + Tcl_Obj* pathObjPtr; { #define VOL_BUF_SIZE 32 int found; WCHAR volType[VOL_BUF_SIZE]; - char *firstSeparator; - const char *path; - Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathPtr); - - if (normPath == NULL) { - return NULL; - } + char* firstSeparator; + CONST char *path; + + Tcl_Obj *normPath = Tcl_FSGetNormalizedPath(NULL, pathObjPtr); + if (normPath == NULL) return NULL; path = Tcl_GetString(normPath); - if (path == NULL) { - return NULL; - } - + if (path == NULL) return NULL; + firstSeparator = strchr(path, '/'); if (firstSeparator == NULL) { found = tclWinProcs->getVolumeInformationProc( - Tcl_FSGetNativePath(pathPtr), NULL, 0, NULL, NULL, NULL, - (WCHAR *) volType, VOL_BUF_SIZE); + Tcl_FSGetNativePath(pathObjPtr), NULL, 0, NULL, NULL, + NULL, (WCHAR *)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); + Tcl_FSGetNativePath(driveName), NULL, 0, NULL, NULL, + NULL, (WCHAR *)volType, VOL_BUF_SIZE); Tcl_DecrRefCount(driveName); } @@ -2515,118 +2304,91 @@ TclpFilesystemPathType( } else { Tcl_DString ds; Tcl_Obj *objPtr; - - Tcl_WinTCharToUtf((const char *) volType, -1, &ds); - objPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), - Tcl_DStringLength(&ds)); + + Tcl_WinTCharToUtf((CONST char *)volType, -1, &ds); + objPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds),Tcl_DStringLength(&ds)); Tcl_DStringFree(&ds); return objPtr; } #undef VOL_BUF_SIZE } - -/* - * This define can be turned on to experiment with a different way of - * normalizing paths (using a different Windows API). Unfortunately the new - * path seems to take almost exactly the same amount of time as the old path! - * The primary time taken by normalization is in - * GetFileAttributesEx/FindFirstFile or GetFileAttributesEx/GetLongPathName. - * Conversion to/from native is not a significant factor at all. - * - * Also, since we have to check for symbolic links (reparse points) then we - * have to call GetFileAttributes on each path segment anyway, so there's no - * benefit to doing anything clever there. - */ -/* #define TclNORM_LONG_PATH */ /* *--------------------------------------------------------------------------- * * TclpObjNormalizePath -- * - * This function scans through a path specification and replaces it, in - * place, with a normalized version. This means using the 'longname', and - * expanding any symbolic links contained within the path. + * This function scans through a path specification and replaces it, + * in place, with a normalized version. This means using the + * 'longname', and expanding any symbolic links contained within the + * path. * * Results: - * The new 'nextCheckpoint' value, giving as far as we could understand - * in the path. + * The new 'nextCheckpoint' value, giving as far as we could + * understand in the path. * * Side effects: - * The pathPtr string, which must contain a valid path, is possibly - * modified in place. + * The pathPtr string, which must contain a valid path, is + * possibly modified in place. * *--------------------------------------------------------------------------- */ int -TclpObjNormalizePath( - Tcl_Interp *interp, - Tcl_Obj *pathPtr, - int nextCheckpoint) +TclpObjNormalizePath(interp, pathPtr, nextCheckpoint) + Tcl_Interp *interp; + Tcl_Obj *pathPtr; + int nextCheckpoint; { char *lastValidPathEnd = NULL; - Tcl_DString dsNorm; /* This will hold the normalized string. */ - char *path, *currentPathEndPosition; + /* This will hold the normalized string */ + Tcl_DString dsNorm; + char *path; + char *currentPathEndPosition; Tcl_Obj *temp = NULL; 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. + /* + * 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 == '/') { + if (*currentPathEndPosition == '/') { currentPathEndPosition++; - } - + } while (1) { char cur = *currentPathEndPosition; + if ((cur == '/' || cur == 0) && (path != currentPathEndPosition)) { + /* Reached directory separator, or end of string */ + CONST char *nativePath = Tcl_UtfToExternalDString(NULL, path, + currentPathEndPosition - path, &ds); - if ((cur=='/' || cur==0) && (path != currentPathEndPosition)) { /* - * Reached directory separator, or end of string. + * 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. */ - - const char *nativePath = Tcl_UtfToExternalDString(NULL, path, - currentPathEndPosition - path, &ds); - - /* - * 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. - */ - if (isDrive) { - if (GetFileAttributesA(nativePath) - == INVALID_FILE_ATTRIBUTES) { - /* - * File doesn't exist. - */ - + 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. - */ - + /* Actually it does exist - COM1, etc */ int i; - - for (i=0 ; i<len ; i++) { + for (i=0;i<len;i++) { if (nativePath[i] >= 'a') { - ((char *) nativePath)[i] -= ('a'-'A'); + ((char*)nativePath)[i] -= ('a' - 'A'); } } Tcl_DStringAppend(&dsNorm, nativePath, len); @@ -2637,74 +2399,33 @@ TclpObjNormalizePath( break; } if (nativePath[0] >= 'a') { - ((char *) nativePath)[0] -= ('a' - 'A'); + ((char*)nativePath)[0] -= ('a' - 'A'); } - Tcl_DStringAppend(&dsNorm, nativePath, - Tcl_DStringLength(&ds)); + 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++; + WIN32_FIND_DATA fData; + HANDLE handle; + + handle = FindFirstFileA(nativePath, &fData); + if (handle == INVALID_HANDLE_VALUE) { + if (GetFileAttributesA(nativePath) + == INVALID_FILE_ATTRIBUTES) { + /* File doesn't exist */ + Tcl_DStringFree(&ds); + break; } - } - if (checkDots != NULL) { - int dotLen = currentPathEndPosition-lastValidPathEnd; - - /* - * 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); + /* This is usually the '/' in 'c:/' at end of string */ + Tcl_DStringAppend(&dsNorm,"/", 1); } else { - /* - * Normal path. - */ - - WIN32_FIND_DATA fData; - HANDLE handle; - - handle = FindFirstFileA(nativePath, &fData); - if (handle == INVALID_HANDLE_VALUE) { - if (GetFileAttributesA(nativePath) - == INVALID_FILE_ATTRIBUTES) { - /* - * File doesn't exist. - */ - - Tcl_DStringFree(&ds); - break; - } - - /* - * This is usually the '/' in 'c:/' at end of - * string. - */ - - Tcl_DStringAppend(&dsNorm,"/", 1); + char *nativeName; + if (fData.cFileName[0] != '\0') { + nativeName = fData.cFileName; } 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); + nativeName = fData.cAlternateFileName; } + FindClose(handle); + Tcl_DStringAppend(&dsNorm,"/", 1); + Tcl_DStringAppend(&dsNorm,nativeName,-1); } } Tcl_DStringFree(&ds); @@ -2712,21 +2433,16 @@ TclpObjNormalizePath( 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++; } } else { - /* - * We're on WinNT (or 2000 or XP; something with an NT core). - */ - + /* We're on WinNT or 2000 or XP */ int isDrive = 1; Tcl_DString ds; @@ -2736,42 +2452,28 @@ TclpObjNormalizePath( } 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 */ WIN32_FILE_ATTRIBUTE_DATA data; - const char *nativePath = Tcl_WinUtfToTChar(path, - currentPathEndPosition - path, &ds); - + CONST char *nativePath = Tcl_WinUtfToTChar(path, + currentPathEndPosition - path, &ds); if ((*tclWinProcs->getFileAttributesExProc)(nativePath, - GetFileExInfoStandard, &data) != TRUE) { - /* - * File doesn't exist. - */ - + GetFileExInfoStandard, &data) != TRUE) { + /* File doesn't exist */ if (isDrive) { int len = WinIsReserved(path); - if (len > 0) { - /* - * Actually it does exist - COM1, etc. - */ - + /* Actually it does exist - COM1, etc */ int i; - - for (i=0 ; i<len ; i++) { - WCHAR wc = ((WCHAR *) nativePath)[i]; - + for (i=0;i<len;i++) { + WCHAR wc = ((WCHAR*)nativePath)[i]; if (wc >= L'a') { wc -= (L'a' - L'A'); - ((WCHAR *) nativePath)[i] = wc; + ((WCHAR*)nativePath)[i] = wc; } } Tcl_DStringAppend(&dsNorm, nativePath, - (int)(sizeof(WCHAR) * len)); + sizeof(WCHAR)*len); lastValidPathEnd = currentPathEndPosition; } } @@ -2779,46 +2481,31 @@ TclpObjNormalizePath( 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. + /* + * 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 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){ + if (cur != 0 && !isDrive && (data.dwFileAttributes + & FILE_ATTRIBUTE_REPARSE_POINT)) { Tcl_Obj *to = WinReadLinkDirectory(nativePath); - 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. - */ - - nextCheckpoint = 0; + /* Read the reparse point ok */ + /* Tcl_GetStringFromObj(to, &pathLen); */ + nextCheckpoint = 0; /* pathLen */ Tcl_AppendToObj(to, currentPathEndPosition, -1); - - /* - * Convert link to forward slashes. - */ - + /* Convert link to forward slashes */ for (path = Tcl_GetString(to); *path != 0; path++) { - if (*path == '\\') { - *path = '/'; - } + if (*path == '\\') *path = '/'; } path = Tcl_GetString(to); currentPathEndPosition = path + nextCheckpoint; @@ -2826,11 +2513,7 @@ TclpObjNormalizePath( Tcl_DecrRefCount(temp); } temp = to; - - /* - * Reset variables so we can restart normalization. - */ - + /* Reset variables so we can restart normalization */ isDrive = 1; Tcl_DStringFree(&dsNorm); Tcl_DStringInit(&dsNorm); @@ -2838,25 +2521,21 @@ TclpObjNormalizePath( continue; } } - -#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 + * 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]; + WCHAR drive = ((WCHAR*)nativePath)[0]; if (drive >= L'a') { - drive -= (L'a' - L'A'); - ((WCHAR *) nativePath)[0] = drive; + drive -= (L'a' - L'A'); + ((WCHAR*)nativePath)[0] = drive; } - Tcl_DStringAppend(&dsNorm, nativePath, - Tcl_DStringLength(&ds)); + Tcl_DStringAppend(&dsNorm,nativePath,Tcl_DStringLength(&ds)); } else { char *checkDots = NULL; - + if (lastValidPathEnd[1] == '.') { checkDots = lastValidPathEnd + 1; while (checkDots < currentPathEndPosition) { @@ -2868,132 +2547,86 @@ TclpObjNormalizePath( } } if (checkDots != NULL) { - int dotLen = currentPathEndPosition-lastValidPathEnd; - - /* - * 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. + int dotLen = currentPathEndPosition - lastValidPathEnd; + /* + * 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 *) - ((WCHAR*)(nativePath + Tcl_DStringLength(&ds)) - - dotLen), (int)(dotLen * sizeof(WCHAR))); + Tcl_DStringAppend(&dsNorm, + (TCHAR*)((WCHAR*)(nativePath + + Tcl_DStringLength(&ds)) + - dotLen), + (int)(dotLen * sizeof(WCHAR))); } else { - /* - * Normal path. - */ - + /* Normal path */ WIN32_FIND_DATAW fData; HANDLE handle; - - handle = FindFirstFileW((WCHAR *) nativePath, &fData); + + 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)); + /* 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, - (int) (wcslen(nativeName)*sizeof(WCHAR))); + Tcl_DStringAppend(&dsNorm,(CONST char*)L"/", + sizeof(WCHAR)); + Tcl_DStringAppend(&dsNorm,(TCHAR*)nativeName, + (int) (wcslen(nativeName)*sizeof(WCHAR))); } } } -#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. + /* + * If we get here, we've got past one directory + * delimiter, so we know it is no longer a drive */ - isDrive = 0; } currentPathEndPosition++; } - -#ifdef TclNORM_LONG_PATH - /* - * Convert the entire known path to long form. - */ - - if (1) { - WCHAR wpath[MAX_PATH]; - const char *nativePath = - Tcl_WinUtfToTChar(path, lastValidPathEnd - path, &ds); - DWORD wpathlen = (*tclWinProcs->getLongPathNameProc)( - nativePath, (TCHAR *) wpath, MAX_PATH); - - /* - * We have to make the drive letter uppercase. - */ - - if (wpath[0] >= L'a') { - wpath[0] -= (L'a' - L'A'); - } - Tcl_DStringAppend(&dsNorm, (TCHAR*)wpath, wpathlen*sizeof(WCHAR)); - Tcl_DStringFree(&ds); - } -#endif } - - /* - * Common code path for all Windows platforms. - */ - + /* Common code path for all Windows platforms */ nextCheckpoint = currentPathEndPosition - path; if (lastValidPathEnd != NULL) { - /* - * 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. + /* + * 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); + Tcl_WinTCharToUtf(Tcl_DStringValue(&dsNorm), + Tcl_DStringLength(&dsNorm), &dsTemp); nextCheckpoint = Tcl_DStringLength(&dsTemp); if (*lastValidPathEnd != 0) { - /* - * Not the end of the string. - */ - + /* Not the end of the string */ int len; char *path; Tcl_Obj *tmpPathPtr; - - tmpPathPtr = Tcl_NewStringObj(Tcl_DStringValue(&dsTemp), - nextCheckpoint); + 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. - */ - + /* End of string was reached above */ Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&dsTemp), - nextCheckpoint); + nextCheckpoint); } Tcl_DStringFree(&dsTemp); } @@ -3007,298 +2640,8 @@ TclpObjNormalizePath( if (temp != NULL) { Tcl_DecrRefCount(temp); } - return nextCheckpoint; -} - -/* - *--------------------------------------------------------------------------- - * - * TclWinVolumeRelativeNormalize -- - * - * Only Windows has volume-relative paths. These paths are rather rare, - * but it is nice if Tcl can handle them. It is much better if we can - * handle them here, rather than in the native fs code, because we really - * need to have a real absolute path just below. - * - * We do not let this block compile on non-Windows platforms because the - * test suite's manual forcing of tclPlatform can otherwise cause this - * code path to be executed, causing various errors because - * volume-relative paths really do not exist. - * - * Results: - * A valid normalized path. - * - * Side effects: - * None. - * - *--------------------------------------------------------------------------- - */ - -Tcl_Obj * -TclWinVolumeRelativeNormalize( - Tcl_Interp *interp, - const char *path, - Tcl_Obj **useThisCwdPtr) -{ - Tcl_Obj *absolutePath, *useThisCwd; - - useThisCwd = Tcl_FSGetCwd(interp); - if (useThisCwd == NULL) { - return NULL; - } - - if (path[0] == '/') { - /* - * Path of form /foo/bar which is a path in the root directory of the - * current volume. - */ - - const char *drive = Tcl_GetString(useThisCwd); - - absolutePath = Tcl_NewStringObj(drive,2); - Tcl_AppendToObj(absolutePath, path, -1); - Tcl_IncrRefCount(absolutePath); - /* - * We have a refCount on the cwd. - */ - } else { - /* - * Path of form C:foo/bar, but this only makes sense if the cwd is - * also on drive C. - */ - - int cwdLen; - const char *drive = - Tcl_GetStringFromObj(useThisCwd, &cwdLen); - char drive_cur = path[0]; - - if (drive_cur >= 'a') { - drive_cur -= ('a' - 'A'); - } - if (drive[0] == drive_cur) { - absolutePath = Tcl_DuplicateObj(useThisCwd); - - /* - * We have a refCount on the cwd, which we will release later. - */ - - if (drive[cwdLen-1] != '/' && (path[2] != '\0')) { - /* - * Only add a trailing '/' if needed, which is if there isn't - * one already, and if we are going to be adding some more - * characters. - */ - - Tcl_AppendToObj(absolutePath, "/", 1); - } - } else { - Tcl_DecrRefCount(useThisCwd); - useThisCwd = NULL; - - /* - * The path is not in the current drive, but is volume-relative. - * The way Tcl 8.3 handles this is that it treats such a path as - * relative to the root of the drive. We therefore behave the same - * here. This behaviour is, however, different to that of the - * windows command-line. If we want to fix this at some point in - * the future (at the expense of a behaviour change to Tcl), we - * could use the '_dgetdcwd' Win32 API to get the drive's cwd. - */ - - absolutePath = Tcl_NewStringObj(path, 2); - Tcl_AppendToObj(absolutePath, "/", 1); - } - Tcl_IncrRefCount(absolutePath); - Tcl_AppendToObj(absolutePath, path+2, -1); - } - *useThisCwdPtr = useThisCwd; - return absolutePath; -} - -/* - *--------------------------------------------------------------------------- - * - * TclpNativeToNormalized -- - * - * Convert native format to a normalized path object, with refCount of - * zero. - * - * Currently assumes all native paths are actually normalized already, so - * if the path given is not normalized this will actually just convert to - * a valid string path, but not necessarily a normalized one. - * - * Results: - * A valid normalized path. - * - * Side effects: - * None. - * - *--------------------------------------------------------------------------- - */ - -Tcl_Obj * -TclpNativeToNormalized( - ClientData clientData) -{ - Tcl_DString ds; - Tcl_Obj *objPtr; - int len; - char *copy, *p; - - Tcl_WinTCharToUtf((const char *) clientData, -1, &ds); - copy = Tcl_DStringValue(&ds); - len = Tcl_DStringLength(&ds); - - /* - * Certain native path representations on Windows have this special prefix - * to indicate that they are to be treated specially. For example - * extremely long paths, or symlinks. - */ - - if (*copy == '\\') { - if (0 == strncmp(copy,"\\??\\",4)) { - copy += 4; - len -= 4; - } else if (0 == strncmp(copy,"\\\\?\\",4)) { - copy += 4; - len -= 4; - } - } - - /* - * Ensure we are using forward slashes only. - */ - - for (p = copy; *p != '\0'; p++) { - if (*p == '\\') { - *p = '/'; - } - } - - objPtr = Tcl_NewStringObj(copy,len); - Tcl_DStringFree(&ds); - - return objPtr; -} - -/* - *--------------------------------------------------------------------------- - * - * TclNativeCreateNativeRep -- - * - * Create a native representation for the given path. - * - * Results: - * The nativePath representation. - * - * Side effects: - * Memory will be allocated. The path may need to be normalized. - * - *--------------------------------------------------------------------------- - */ - -ClientData -TclNativeCreateNativeRep( - Tcl_Obj *pathPtr) -{ - char *nativePathPtr, *str; - Tcl_DString ds; - Tcl_Obj *validPathPtr; - int len; - - if (TclFSCwdIsNative()) { - /* - * The cwd is native, which means we can use the translated path - * without worrying about normalization (this will also usually be - * shorter so the utf-to-external conversion will be somewhat faster). - */ - - validPathPtr = Tcl_FSGetTranslatedPath(NULL, pathPtr); - if (validPathPtr == NULL) { - return NULL; - } - } else { - /* - * Make sure the normalized path is set. - */ - - validPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr); - if (validPathPtr == NULL) { - return NULL; - } - Tcl_IncrRefCount(validPathPtr); - } - - 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 = '\\'; - } - } - } - Tcl_WinUtfToTChar(str, len, &ds); - if (tclWinProcs->useWide) { - len = Tcl_DStringLength(&ds) + sizeof(WCHAR); - } else { - len = Tcl_DStringLength(&ds) + sizeof(char); - } - Tcl_DecrRefCount(validPathPtr); - nativePathPtr = ckalloc((unsigned) len); - memcpy(nativePathPtr, Tcl_DStringValue(&ds), (size_t) len); - - Tcl_DStringFree(&ds); - return (ClientData) nativePathPtr; -} - -/* - *--------------------------------------------------------------------------- - * - * TclNativeDupInternalRep -- - * - * Duplicate the native representation. - * - * Results: - * The copied native representation, or NULL if it is not possible to - * copy the representation. - * - * Side effects: - * Memory allocation for the copy. - * - *--------------------------------------------------------------------------- - */ - -ClientData -TclNativeDupInternalRep( - ClientData clientData) -{ - char *copy; - size_t len; - - if (clientData == NULL) { - 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); - } - - copy = (char *) ckalloc(len); - memcpy(copy, clientData, len); - return (ClientData) copy; + return nextCheckpoint; } /* @@ -3325,7 +2668,7 @@ TclpUtime( { int res = 0; HANDLE fileHandle; - const TCHAR *native; + CONST TCHAR *native; DWORD attr = 0; DWORD flags = FILE_ATTRIBUTE_NORMAL; FILETIME lastAccessTime, lastModTime; @@ -3333,7 +2676,7 @@ TclpUtime( FromCTime(tval->actime, &lastAccessTime); FromCTime(tval->modtime, &lastModTime); - native = (const TCHAR *) Tcl_FSGetNativePath(pathPtr); + native = (CONST TCHAR *)Tcl_FSGetNativePath(pathPtr); attr = (*tclWinProcs->getFileAttributesProc)(native); @@ -3346,8 +2689,9 @@ TclpUtime( * savings complications that utime gets wrong. */ - fileHandle = (tclWinProcs->createFileProc)(native, FILE_WRITE_ATTRIBUTES, - 0, NULL, OPEN_EXISTING, flags, NULL); + fileHandle = (tclWinProcs->createFileProc) ( + native, FILE_WRITE_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, flags, NULL); if (fileHandle == INVALID_HANDLE_VALUE || !SetFileTime(fileHandle, NULL, &lastAccessTime, &lastModTime)) { @@ -3359,11 +2703,3 @@ TclpUtime( } return res; } - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |