diff options
Diffstat (limited to 'win/tclWin32Dll.c')
-rw-r--r-- | win/tclWin32Dll.c | 193 |
1 files changed, 190 insertions, 3 deletions
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c index 5b939f9..ded9c6f 100644 --- a/win/tclWin32Dll.c +++ b/win/tclWin32Dll.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWin32Dll.c,v 1.24 2003/02/04 17:06:52 vincentdarley Exp $ + * RCS: @(#) $Id: tclWin32Dll.c,v 1.25 2003/04/11 16:00:08 vincentdarley Exp $ */ #include "tclWinInt.h" @@ -88,7 +88,7 @@ static TclWinProcs asciiProcs = { (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryA, (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesA, /* - * These two function pointers will only be set when + * The three NULL function pointers will only be set when * Tcl_FindExecutable is called. If you don't ever call that * function, the application will crash whenever WinTcl tries to call * functions through these null pointers. That is not a bug in Tcl @@ -97,6 +97,8 @@ static TclWinProcs asciiProcs = { NULL, NULL, (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _utime, + NULL, + NULL, }; static TclWinProcs unicodeProcs = { @@ -135,7 +137,7 @@ static TclWinProcs unicodeProcs = { (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryW, (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesW, /* - * These two function pointers will only be set when + * The three NULL function pointers will only be set when * Tcl_FindExecutable is called. If you don't ever call that * function, the application will crash whenever WinTcl tries to call * functions through these null pointers. That is not a bug in Tcl @@ -144,6 +146,8 @@ static TclWinProcs unicodeProcs = { NULL, NULL, (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _wutime, + NULL, + NULL, }; TclWinProcs *tclWinProcs; @@ -156,6 +160,28 @@ static Tcl_Encoding tclWinTCharEncoding; BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved); +/* + * The following structure and linked list is to allow us to map between + * volume mount points and drive letters on the fly (no Win API exists + * for this). + */ +typedef struct MountPointMap { + CONST WCHAR* volumeName; /* Native wide string volume name */ + char driveLetter; /* Drive letter corresponding to + * the volume name. */ + struct MountPointMap* nextPtr; /* Pointer to next structure in list, + * or NULL */ +} MountPointMap; + +/* + * This is the head of the linked list, which is protected by the + * mutex which follows, for thread-enabled builds. + */ +MountPointMap *driveLetterLookup = NULL; +TCL_DECLARE_MUTEX(mountPointMap) + +/* We will need this below */ +extern Tcl_FSDupInternalRepProc NativeDupInternalRep; #ifdef __WIN32__ #ifndef STATIC_BUILD @@ -531,6 +557,14 @@ TclWinSetInterfaces( (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, "CreateHardLinkW"); + tclWinProcs->findFirstFileExProc = + (HANDLE (WINAPI *)(CONST TCHAR*, UINT, + LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance, + "FindFirstFileExW"); + tclWinProcs->getVolumeNameForVMPProc = + (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, + DWORD)) GetProcAddress(hInstance, + "GetVolumeNameForVolumeMountPointW"); FreeLibrary(hInstance); } } @@ -547,6 +581,14 @@ TclWinSetInterfaces( (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, "CreateHardLinkA"); + tclWinProcs->findFirstFileExProc = + (HANDLE (WINAPI *)(CONST TCHAR*, UINT, + LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance, + "FindFirstFileExA"); + tclWinProcs->getVolumeNameForVMPProc = + (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, + DWORD)) GetProcAddress(hInstance, + "GetVolumeNameForVolumeMountPointA"); FreeLibrary(hInstance); } } @@ -562,6 +604,11 @@ TclWinSetInterfaces( * The tclWinProcs-> look up table is still ok to use after * this call, provided no encoding conversion is required. * + * We also clean up any memory allocated in our mount point + * map which is used to follow certain kinds of symlinks. + * That code should never be used once encodings are taken + * down. + * * Results: * None. * @@ -573,10 +620,21 @@ TclWinSetInterfaces( void TclWinResetInterfaceEncodings() { + MountPointMap *dlIter, *dlIter2; if (tclWinTCharEncoding != NULL) { Tcl_FreeEncoding(tclWinTCharEncoding); tclWinTCharEncoding = NULL; } + /* Clean up the mount point map */ + Tcl_MutexLock(&mountPointMap); + dlIter = driveLetterLookup; + while (dlIter != NULL) { + dlIter2 = dlIter->nextPtr; + ckfree((char*)dlIter->volumeName); + ckfree((char*)dlIter); + dlIter = dlIter2; + } + Tcl_MutexUnlock(&mountPointMap); } /* @@ -603,6 +661,135 @@ TclWinResetInterfaces() } /* + *-------------------------------------------------------------------- + * + * TclWinDriveLetterForVolMountPoint + * + * Unfortunately, Windows provides no easy way at all to get hold + * of the drive letter for a volume mount point, but we need that + * information to understand paths correctly. So, we have to + * build an associated array to find these correctly, and allow + * quick and easy lookup from volume mount points to drive letters. + * + * We assume here that we are running on a system for which the wide + * character interfaces are used, which is valid for Win 2000 and WinXP + * which are the only systems on which this function will ever be called. + * + * Result: the drive letter, or -1 if no drive letter corresponds to + * the given mount point. + * + *-------------------------------------------------------------------- + */ +char +TclWinDriveLetterForVolMountPoint(CONST WCHAR *mountPoint) +{ + MountPointMap *dlIter, *dlPtr2; + WCHAR Target[55]; /* Target of mount at mount point */ + WCHAR drive[4] = { L'A', L':', L'\\', L'\0' }; + + /* + * Detect the volume mounted there. Unfortunately, there is no + * simple way to map a unique volume name to a DOS drive letter. + * So, we have to build an associative array. + */ + + Tcl_MutexLock(&mountPointMap); + dlIter = driveLetterLookup; + while (dlIter != NULL) { + if (wcscmp(dlIter->volumeName, mountPoint) == 0) { + /* + * We need to check whether this information is + * still valid, since either the user or various + * programs could have adjusted the mount points on + * the fly. + */ + drive[0] = L'A' + (dlIter->driveLetter - 'A'); + /* Try to read the volume mount point and see where it points */ + if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, + (TCHAR*)Target, 55) != 0) { + if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { + /* Nothing has changed */ + Tcl_MutexUnlock(&mountPointMap); + return dlIter->driveLetter; + } + } + /* + * If we reach here, unfortunately, this mount point is + * no longer valid at all + */ + if (driveLetterLookup == dlIter) { + dlPtr2 = dlIter; + driveLetterLookup = dlIter->nextPtr; + } else { + for (dlPtr2 = driveLetterLookup; + dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) { + if (dlPtr2->nextPtr == dlIter) { + dlPtr2->nextPtr = dlIter->nextPtr; + dlPtr2 = dlIter; + break; + } + } + } + /* Now dlPtr2 points to the structure to free */ + ckfree((char*)dlPtr2->volumeName); + ckfree((char*)dlPtr2); + /* + * Restart the loop --- we could try to be clever + * and continue half way through, but the logic is a + * bit messy, so it's cleanest just to restart + */ + dlIter = driveLetterLookup; + continue; + } + dlIter = dlIter->nextPtr; + } + + /* We couldn't find it, so we must iterate over the letters */ + + for (drive[0] = L'A'; drive[0] <= L'Z'; drive[0]++) { + /* Try to read the volume mount point and see where it points */ + if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, + (TCHAR*)Target, 55) != 0) { + int alreadyStored = 0; + for (dlIter = driveLetterLookup; dlIter != NULL; + dlIter = dlIter->nextPtr) { + if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { + alreadyStored = 1; + break; + } + } + if (!alreadyStored) { + dlPtr2 = (MountPointMap*) ckalloc(sizeof(MountPointMap)); + dlPtr2->volumeName = NativeDupInternalRep(Target); + dlPtr2->driveLetter = 'A' + (drive[0] - L'A'); + dlPtr2->nextPtr = driveLetterLookup; + driveLetterLookup = dlPtr2; + } + } + } + /* Try again */ + for (dlIter = driveLetterLookup; dlIter != NULL; + dlIter = dlIter->nextPtr) { + if (wcscmp(dlIter->volumeName, mountPoint) == 0) { + Tcl_MutexUnlock(&mountPointMap); + return dlIter->driveLetter; + } + } + /* + * The volume doesn't appear to correspond to a drive letter -- we + * remember that fact and store '-1' so we don't have to look it + * up each time. + */ + dlPtr2 = (MountPointMap*) ckalloc(sizeof(MountPointMap)); + dlPtr2->volumeName = NativeDupInternalRep((ClientData)mountPoint); + dlPtr2->driveLetter = -1; + dlPtr2->nextPtr = driveLetterLookup; + driveLetterLookup = dlPtr2; + Tcl_MutexUnlock(&mountPointMap); + return -1; +} + +/* *--------------------------------------------------------------------------- * * Tcl_WinUtfToTChar, Tcl_WinTCharToUtf -- |