diff options
Diffstat (limited to 'win/tclWin32Dll.c')
| -rw-r--r-- | win/tclWin32Dll.c | 879 |
1 files changed, 544 insertions, 335 deletions
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c index 349e770..688fa8d 100644 --- a/win/tclWin32Dll.c +++ b/win/tclWin32Dll.c @@ -1,146 +1,84 @@ -/* - +/* * tclWin32Dll.c -- * - * This file contains the DLL entry point which sets up the 32-to-16-bit - * thunking code for SynchSpawn if the library is running under Win32s. + * This file contains the DLL entry point and other low-level bit bashing + * code that needs inline assembly. * * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 Scriptics Corporation. * - * 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.7 1999/04/23 01:57:23 stanton Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclWinInt.h" +#if defined(HAVE_INTRIN_H) +# include <intrin.h> +#endif /* - * The following data structures are used when loading the thunking - * library for execing child processes under Win32s. + * The following variables keep track of information about this DLL on a + * per-instance basis. Each time this DLL is loaded, it gets its own new data + * segment with its own copy of all static and global information. */ -typedef DWORD (WINAPI UT32PROC)(LPVOID lpBuff, DWORD dwUserDefined, - LPVOID *lpTranslationList); +static HINSTANCE hInstance; /* HINSTANCE of this DLL. */ +static int platformId; /* Running under NT, or 95/98? */ + +/* + * VC++ 5.x has no 'cpuid' assembler instruction, so we must emulate it + */ -typedef BOOL (WINAPI UTREGISTER)(HANDLE hModule, LPCSTR SixteenBitDLL, - LPCSTR InitName, LPCSTR ProcName, UT32PROC **ThirtyTwoBitThunk, - FARPROC UT32Callback, LPVOID Buff); +#if defined(_MSC_VER) && (_MSC_VER <= 1100) +#define cpuid __asm __emit 0fh __asm __emit 0a2h +#endif -typedef VOID (WINAPI UTUNREGISTER)(HANDLE hModule); +static Tcl_Encoding winTCharEncoding = NULL; -/* - * The following variables keep track of information about this DLL - * on a per-instance basis. Each time this DLL is loaded, it gets its own - * new data segment with its own copy of all static and global information. +/* + * The following declaration is for the VC++ DLL entry point. */ -static HINSTANCE hInstance; /* HINSTANCE of this DLL. */ -static int platformId; /* Running under NT, 95, or Win32s? */ +BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, + LPVOID reserved); /* - * The following function tables are used to dispatch to either the - * wide-character or multi-byte versions of the operating system calls, - * depending on whether the Unicode calls are available. + * 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). */ -static TclWinProcs asciiProcs = { - 0, - - (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBA, - (TCHAR *(WINAPI *)(TCHAR *)) CharLowerA, - (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileA, - (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryA, - (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, - DWORD, DWORD, HANDLE)) CreateFileA, - (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, - LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, - LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessA, - (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileA, - (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileA, - (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileA, - (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameA, - (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryA, - (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesA, - (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, - TCHAR **)) GetFullPathNameA, - (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameA, - (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameA, - (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, - WCHAR *)) GetTempFileNameA, - (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathA, - (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, - WCHAR *, DWORD)) GetVolumeInformationA, - (HINSTANCE (WINAPI *)(CONST TCHAR *)) LoadLibraryA, - (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyA, - (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileA, - (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryA, - (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, - WCHAR *, TCHAR **)) SearchPathA, - (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryA, - (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesA, -}; - -static TclWinProcs unicodeProcs = { - 1, - - (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBW, - (TCHAR *(WINAPI *)(TCHAR *)) CharLowerW, - (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileW, - (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryW, - (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, - DWORD, DWORD, HANDLE)) CreateFileW, - (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, - LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, - LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessW, - (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileW, - (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileW, - (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileW, - (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameW, - (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryW, - (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesW, - (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, - TCHAR **)) GetFullPathNameW, - (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameW, - (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameW, - (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, - WCHAR *)) GetTempFileNameW, - (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathW, - (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, - WCHAR *, DWORD)) GetVolumeInformationW, - (HINSTANCE (WINAPI *)(CONST TCHAR *)) LoadLibraryW, - (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyW, - (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileW, - (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryW, - (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, - WCHAR *, TCHAR **)) SearchPathW, - (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryW, - (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesW, -}; - -TclWinProcs *tclWinProcs; -static Tcl_Encoding tclWinTCharEncoding; +typedef struct MountPointMap { + const TCHAR *volumeName; /* Native wide string volume name. */ + TCHAR driveLetter; /* Drive letter corresponding to the volume + * name. */ + struct MountPointMap *nextPtr; + /* Pointer to next structure in list, or + * NULL. */ +} MountPointMap; /* - * The following declaration is for the VC++ DLL entry point. + * This is the head of the linked list, which is protected by the mutex which + * follows, for thread-enabled builds. */ -BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, - LPVOID reserved); +MountPointMap *driveLetterLookup = NULL; +TCL_DECLARE_MUTEX(mountPointMap) +/* + * We will need this below. + */ -#ifdef __WIN32__ +#ifdef _WIN32 #ifndef STATIC_BUILD - /* *---------------------------------------------------------------------- * * DllEntryPoint -- * - * This wrapper function is used by Borland to invoke the - * initialization code for Tcl. It simply calls the DllMain - * routine. + * This wrapper function is used by Borland to invoke the initialization + * code for Tcl. It simply calls the DllMain routine. * * Results: * See DllMain. @@ -152,10 +90,10 @@ BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, */ BOOL APIENTRY -DllEntryPoint(hInst, reason, reserved) - HINSTANCE hInst; /* Library instance handle. */ - DWORD reason; /* Reason this function is being called. */ - LPVOID reserved; /* Not used. */ +DllEntryPoint( + HINSTANCE hInst, /* Library instance handle. */ + DWORD reason, /* Reason this function is being called. */ + LPVOID reserved) /* Not used. */ { return DllMain(hInst, reason, reserved); } @@ -165,133 +103,41 @@ DllEntryPoint(hInst, reason, reserved) * * DllMain -- * - * This routine is called by the VC++ C run time library init - * code, or the DllEntryPoint routine. It is responsible for - * initializing various dynamically loaded libraries. + * This routine is called by the VC++ C run time library init code, or + * the DllEntryPoint routine. It is responsible for initializing various + * dynamically loaded libraries. * * Results: * TRUE on sucess, FALSE on failure. * * Side effects: - * Establishes 32-to-16 bit thunk and initializes sockets library. + * Initializes most rudimentary Windows bits. * *---------------------------------------------------------------------- */ + BOOL APIENTRY -DllMain(hInst, reason, reserved) - HINSTANCE hInst; /* Library instance handle. */ - DWORD reason; /* Reason this function is being called. */ - LPVOID reserved; /* Not used. */ +DllMain( + HINSTANCE hInst, /* Library instance handle. */ + DWORD reason, /* Reason this function is being called. */ + LPVOID reserved) /* Not used. */ { switch (reason) { case DLL_PROCESS_ATTACH: - if (hInstance != NULL) { - /* - * Prevents DLL from being loaded multiple times under Win32s, - * since all copies of the DLL share the same data segment and - * Tcl isn't set up to handle that. Under NT or 95, each time - * the DLL is loaded, it gets its own private copy of the data - * segment. - */ - - return FALSE; - } - + DisableThreadLibraryCalls(hInst); TclWinInit(hInst); return TRUE; - case DLL_PROCESS_DETACH: - if (hInst == hInstance) { - Tcl_Finalize(); - } - break; - } - - return TRUE; -} - -#endif /* !STATIC_BUILD */ -#endif /* __WIN32__ */ - -/* - *---------------------------------------------------------------------- - * - * TclWinSynchSpawn -- - * - * 32-bit entry point to the 16-bit SynchSpawn code. - * - * Results: - * 1 on success, 0 on failure. - * - * Side effects: - * Spawns a command and waits for it to complete. - * - *---------------------------------------------------------------------- - */ -int -TclWinSynchSpawn(void *args, int type, void **trans, Tcl_Pid *pidPtr) -{ - HINSTANCE hKernel; - UTREGISTER *utRegisterProc; - UTUNREGISTER *utUnRegisterProc; - UT32PROC *ut32Proc; - char buffer[] = "TCL16xx.DLL"; - int result; - - hKernel = LoadLibraryA("kernel32.dll"); - if (hKernel == NULL) { - return 0; - } - - /* - * Load the Universal Thunking routines from kernel32.dll. - */ - - utRegisterProc = (UTREGISTER *) GetProcAddress(hKernel, "UTRegister"); - utUnRegisterProc = (UTUNREGISTER *) GetProcAddress(hKernel, "UTUnRegister"); - if ((utRegisterProc == NULL) || (utUnRegisterProc == NULL)) { - result = 0; - goto done; - } - - /* - * Construct the complete name of tcl16xx.dll. - */ - - buffer[5] = '0' + TCL_MAJOR_VERSION; - buffer[6] = '0' + TCL_MINOR_VERSION; - - /* - * Register the Tcl thunk. - */ - - if ((*utRegisterProc)(hInstance, buffer, NULL, "UTProc", &ut32Proc, - NULL, NULL) == FALSE) { - result = 0; - goto done; - } - if (ut32Proc != NULL) { /* - * Invoke the thunk. + * DLL_PROCESS_DETACH is unnecessary as the user should call + * Tcl_Finalize explicitly before unloading Tcl. */ - - *pidPtr = 0; - (*ut32Proc)(args, type, trans); - result = 1; - } else { - /* - * The 16-bit thunking DLL wasn't found. Return error code that - * indicates this problem. - */ - - result = 0; } - (*utUnRegisterProc)(hInstance); - done: - FreeLibrary(hKernel); - return result; + return TRUE; } +#endif /* !STATIC_BUILD */ +#endif /* _WIN32 */ /* *---------------------------------------------------------------------- @@ -310,7 +156,7 @@ TclWinSynchSpawn(void *args, int type, void **trans, Tcl_Pid *pidPtr) */ HINSTANCE -TclWinGetTclInstance() +TclWinGetTclInstance(void) { return hInstance; } @@ -326,39 +172,35 @@ TclWinGetTclInstance() * None. * * Side effects: - * Initializes the 16-bit thunking library, and the tclPlatformId - * variable. + * Initializes the tclPlatformId variable. * *---------------------------------------------------------------------- */ void -TclWinInit(hInst) - HINSTANCE hInst; /* Library instance handle. */ +TclWinInit( + HINSTANCE hInst) /* Library instance handle. */ { - OSVERSIONINFO os; + OSVERSIONINFOW os; hInstance = hInst; - os.dwOSVersionInfoSize = sizeof(os); - GetVersionEx(&os); + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&os); platformId = os.dwPlatformId; /* - * The following code stops Windows 3.x from automatically putting - * up Sharing Violation dialogs, e.g, when someone tries to - * access a file that is locked or a drive with no disk in it. - * Tcl already returns the appropriate error to the caller, and they - * can decide to put up their own dialog in response to that failure. - * - * Under 95 and NT, the system doesn't automatically put up dialogs - * when the above operations fail. + * We no longer support Win32s or Win9x, so just in case someone manages + * to get a runtime there, make sure they know that. */ if (platformId == VER_PLATFORM_WIN32s) { - SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS); + Tcl_Panic("Win32s is not a supported platform"); + } + if (platformId == VER_PLATFORM_WIN32_WINDOWS) { + Tcl_Panic("Windows 9x is not a supported platform"); } - tclWinProcs = &asciiProcs; + TclWinResetInterfaces(); } /* @@ -366,14 +208,15 @@ TclWinInit(hInst) * * TclWinGetPlatformId -- * - * Determines whether running under NT, 95, or Win32s, to allow - * runtime conditional code. + * Determines whether running under NT, 95, or Win32s, to allow runtime + * conditional code. * * Results: * The return value is one of: - * VER_PLATFORM_WIN32s Win32s on Windows 3.1. - * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95. - * VER_PLATFORM_WIN32_NT Win32 on Windows NT + * VER_PLATFORM_WIN32s Win32s on Windows 3.1 (not supported) + * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95, 98, ME (not supported) + * VER_PLATFORM_WIN32_NT Win32 on Windows NT, 2000, XP + * VER_PLATFORM_WIN32_CE Win32 on Windows CE * * Side effects: * None. @@ -381,8 +224,8 @@ TclWinInit(hInst) *---------------------------------------------------------------------- */ -int -TclWinGetPlatformId() +int +TclWinGetPlatformId(void) { return platformId; } @@ -419,73 +262,76 @@ TclWinNoBackslash( } /* - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- * - * TclpCheckStackSpace -- + * TclpSetInterfaces -- * - * Detect if we are about to blow the stack. Called before an - * evaluation can happen when nesting depth is checked. + * A helper proc that initializes winTCharEncoding. * * Results: - * 1 if there is enough stack space to continue; 0 if not. + * None. * * Side effects: * None. * - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- */ -int -TclpCheckStackSpace() +void +TclpSetInterfaces(void) { - /* - * We can recurse only if there is at least TCL_WIN_STACK_THRESHOLD - * bytes of stack space left. alloca() is cheap on windows; basically - * it just subtracts from the stack pointer causing the OS to throw an - * exception if the stack pointer is set below the bottom of the stack. - */ - - __try { - alloca(TCL_WIN_STACK_THRESHOLD); - return 1; - } __except (1) {} - - return 0; + TclWinResetInterfaces(); + winTCharEncoding = Tcl_GetEncoding(NULL, "unicode"); } - /* - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- + * + * TclWinEncodingsCleanup -- * - * TclWinGetPlatform -- + * Called during finalization to free up any encodings we use. * - * This is a kludge that allows the test library to get access - * the internal tclPlatform variable. + * 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: - * Returns a pointer to the tclPlatform variable. + * None. * * Side effects: * None. * - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- */ -TclPlatformType * -TclWinGetPlatform() +void +TclWinEncodingsCleanup(void) { - return &tclPlatform; + MountPointMap *dlIter, *dlIter2; + + TclWinResetInterfaces(); + + /* + * Clean up the mount point map. + */ + + Tcl_MutexLock(&mountPointMap); + dlIter = driveLetterLookup; + while (dlIter != NULL) { + dlIter2 = dlIter->nextPtr; + ckfree(dlIter->volumeName); + ckfree(dlIter); + dlIter = dlIter2; + } + Tcl_MutexUnlock(&mountPointMap); } /* *--------------------------------------------------------------------------- * - * TclWinSetInterfaces -- + * TclWinResetInterfaces -- * - * A helper proc that allows the test library to change the - * tclWinProcs structure to dispatch to either the wide-character - * or multi-byte versions of the operating system calls, depending - * on whether Unicode is the system encoding. + * Called during finalization to reset us to a safe state for reuse. * * Results: * None. @@ -495,21 +341,171 @@ TclWinGetPlatform() * *--------------------------------------------------------------------------- */ - void -TclWinSetInterfaces( - int wide) /* Non-zero to use wide interfaces, 0 - * otherwise. */ +TclWinResetInterfaces(void) { - Tcl_FreeEncoding(tclWinTCharEncoding); - - if (wide) { - tclWinProcs = &unicodeProcs; - tclWinTCharEncoding = Tcl_GetEncoding(NULL, "unicode"); - } else { - tclWinProcs = &asciiProcs; - tclWinTCharEncoding = NULL; + if (winTCharEncoding != NULL) { + Tcl_FreeEncoding(winTCharEncoding); + winTCharEncoding = NULL; + } +} + +/* + *-------------------------------------------------------------------- + * + * 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 TCHAR *mountPoint) +{ + MountPointMap *dlIter, *dlPtr2; + TCHAR Target[55]; /* Target of mount at mount point */ + TCHAR drive[4] = TEXT("A:\\"); + + /* + * 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 (_tcscmp(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] = (TCHAR) dlIter->driveLetter; + + /* + * Try to read the volume mount point and see where it points. + */ + + if (GetVolumeNameForVolumeMountPoint(drive, + Target, 55) != 0) { + if (_tcscmp(dlIter->volumeName, Target) == 0) { + /* + * Nothing has changed. + */ + + Tcl_MutexUnlock(&mountPointMap); + return (char) 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(dlPtr2->volumeName); + ckfree(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 (GetVolumeNameForVolumeMountPoint(drive, + Target, 55) != 0) { + int alreadyStored = 0; + + for (dlIter = driveLetterLookup; dlIter != NULL; + dlIter = dlIter->nextPtr) { + if (_tcscmp(dlIter->volumeName, Target) == 0) { + alreadyStored = 1; + break; + } + } + if (!alreadyStored) { + dlPtr2 = ckalloc(sizeof(MountPointMap)); + dlPtr2->volumeName = TclNativeDupInternalRep(Target); + dlPtr2->driveLetter = (char) drive[0]; + dlPtr2->nextPtr = driveLetterLookup; + driveLetterLookup = dlPtr2; + } + } } + + /* + * Try again. + */ + + for (dlIter = driveLetterLookup; dlIter != NULL; + dlIter = dlIter->nextPtr) { + if (_tcscmp(dlIter->volumeName, mountPoint) == 0) { + Tcl_MutexUnlock(&mountPointMap); + return (char) 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 = ckalloc(sizeof(MountPointMap)); + dlPtr2->volumeName = TclNativeDupInternalRep((ClientData) mountPoint); + dlPtr2->driveLetter = -1; + dlPtr2->nextPtr = driveLetterLookup; + driveLetterLookup = dlPtr2; + Tcl_MutexUnlock(&mountPointMap); + return -1; } /* @@ -517,26 +513,24 @@ TclWinSetInterfaces( * * Tcl_WinUtfToTChar, Tcl_WinTCharToUtf -- * - * Convert between UTF-8 and Unicode when running Windows NT or - * the current ANSI code page when running Windows 95. + * Convert between UTF-8 and Unicode when running Windows NT or the + * current ANSI code page when running Windows 95. * - * On Mac, Unix, and Windows 95, all strings exchanged between Tcl - * and the OS are "char" oriented. We need only one Tcl_Encoding to - * convert between UTF-8 and the system's native encoding. We use - * NULL to represent that encoding. + * On Mac, Unix, and Windows 95, all strings exchanged between Tcl and + * the OS are "char" oriented. We need only one Tcl_Encoding to convert + * between UTF-8 and the system's native encoding. We use NULL to + * represent that encoding. * * On NT, some strings exchanged between Tcl and the OS are "char" - * oriented, while others are in Unicode. We need two Tcl_Encoding - * APIs depending on whether we are targeting a "char" or Unicode - * interface. - * - * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an - * encoding of NULL should always used to convert between UTF-8 - * and the system's "char" oriented encoding. The following two - * functions are used in Windows-specific code to convert between - * UTF-8 and Unicode strings (NT) or "char" strings(95). This saves - * you the trouble of writing the following type of fragment over and - * over: + * oriented, while others are in Unicode. We need two Tcl_Encoding APIs + * depending on whether we are targeting a "char" or Unicode interface. + * + * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an encoding of + * NULL should always used to convert between UTF-8 and the system's + * "char" oriented encoding. The following two functions are used in + * Windows-specific code to convert between UTF-8 and Unicode strings + * (NT) or "char" strings(95). This saves you the trouble of writing the + * following type of fragment over and over: * * if (running NT) { * encoding <- Tcl_GetEncoding("unicode"); @@ -546,19 +540,17 @@ TclWinSetInterfaces( * nativeBuffer <- UtfToExternal(NULL, utfBuffer); * } * - * By convention, in Windows a TCHAR is a character in the ANSI code - * page on Windows 95, a Unicode character on Windows NT. If you - * plan on targeting a Unicode interfaces when running on NT and a - * "char" oriented interface while running on 95, these functions - * should be used. If you plan on targetting the same "char" - * oriented function on both 95 and NT, use Tcl_UtfToExternal() - * with an encoding of NULL. + * By convention, in Windows a TCHAR is a character in the ANSI code page + * on Windows 95, a Unicode character on Windows NT. If you plan on + * targeting a Unicode interfaces when running on NT and a "char" + * oriented interface while running on 95, these functions should be + * used. If you plan on targetting the same "char" oriented function on + * both 95 and NT, use Tcl_UtfToExternal() with an encoding of NULL. * * Results: - * The result is a pointer to the string in the desired target - * encoding. Storage for the result string is allocated in - * dsPtr; the caller must call Tcl_DStringFree() when the result - * is no longer needed. + * The result is a pointer to the string in the desired target encoding. + * Storage for the result string is allocated in dsPtr; the caller must + * call Tcl_DStringFree() when the result is no longer needed. * * Side effects: * None. @@ -567,26 +559,243 @@ TclWinSetInterfaces( */ TCHAR * -Tcl_WinUtfToTChar(string, len, dsPtr) - CONST char *string; /* Source string in UTF-8. */ - int len; /* Source string length in bytes, or < 0 for +Tcl_WinUtfToTChar( + const char *string, /* Source string in UTF-8. */ + int len, /* Source string length in bytes, or < 0 for * strlen(). */ - Tcl_DString *dsPtr; /* Uninitialized or free DString in which - * the converted string is stored. */ + Tcl_DString *dsPtr) /* Uninitialized or free DString in which the + * converted string is stored. */ { - return (TCHAR *) Tcl_UtfToExternalDString(tclWinTCharEncoding, + return (TCHAR *) Tcl_UtfToExternalDString(winTCharEncoding, string, len, dsPtr); } char * -Tcl_WinTCharToUtf(string, len, dsPtr) - CONST TCHAR *string; /* Source string in Unicode when running - * NT, ANSI when running 95. */ - int len; /* Source string length in bytes, or < 0 for +Tcl_WinTCharToUtf( + const TCHAR *string, /* Source string in Unicode when running NT, + * ANSI when running 95. */ + int len, /* Source string length in bytes, or < 0 for * platform-specific string length. */ - Tcl_DString *dsPtr; /* Uninitialized or free DString in which - * the converted string is stored. */ + Tcl_DString *dsPtr) /* Uninitialized or free DString in which the + * converted string is stored. */ { - return Tcl_ExternalToUtfDString(tclWinTCharEncoding, - (CONST char *) string, len, dsPtr); + return Tcl_ExternalToUtfDString(winTCharEncoding, + (const char *) string, len, dsPtr); } + +/* + *------------------------------------------------------------------------ + * + * TclWinCPUID -- + * + * Get CPU ID information on an Intel box under Windows + * + * Results: + * Returns TCL_OK if successful, TCL_ERROR if CPUID is not supported or + * fails. + * + * Side effects: + * If successful, stores EAX, EBX, ECX and EDX registers after the CPUID + * instruction in the four integers designated by 'regsPtr' + * + *---------------------------------------------------------------------- + */ + +int +TclWinCPUID( + unsigned int index, /* Which CPUID value to retrieve. */ + unsigned int *regsPtr) /* Registers after the CPUID. */ +{ + int status = TCL_ERROR; + +#if defined(HAVE_INTRIN_H) && defined(_WIN64) + + __cpuid(regsPtr, index); + status = TCL_OK; + +#elif defined(__GNUC__) +# if defined(_WIN64) + /* + * Execute the CPUID instruction with the given index, and store results + * off 'regPtr'. + */ + + __asm__ __volatile__( + /* + * Do the CPUID instruction, and save the results in the 'regsPtr' + * area. + */ + + "movl %[rptr], %%edi" "\n\t" + "movl %[index], %%eax" "\n\t" + "cpuid" "\n\t" + "movl %%eax, 0x0(%%edi)" "\n\t" + "movl %%ebx, 0x4(%%edi)" "\n\t" + "movl %%ecx, 0x8(%%edi)" "\n\t" + "movl %%edx, 0xc(%%edi)" "\n\t" + + : + /* No outputs */ + : + [index] "m" (index), + [rptr] "m" (regsPtr) + : + "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); + status = TCL_OK; + +# else + + TCLEXCEPTION_REGISTRATION registration; + + /* + * Execute the CPUID instruction with the given index, and store results + * off 'regPtr'. + */ + + __asm__ __volatile__( + /* + * Construct an TCLEXCEPTION_REGISTRATION to protect the CPUID + * instruction (early 486's don't have CPUID) + */ + + "leal %[registration], %%edx" "\n\t" + "movl %%fs:0, %%eax" "\n\t" + "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ + "leal 1f, %%eax" "\n\t" + "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ + "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ + "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ + "movl %[error], 0x10(%%edx)" "\n\t" /* status */ + + /* + * Link the TCLEXCEPTION_REGISTRATION on the chain + */ + + "movl %%edx, %%fs:0" "\n\t" + + /* + * Do the CPUID instruction, and save the results in the 'regsPtr' + * area. + */ + + "movl %[rptr], %%edi" "\n\t" + "movl %[index], %%eax" "\n\t" + "cpuid" "\n\t" + "movl %%eax, 0x0(%%edi)" "\n\t" + "movl %%ebx, 0x4(%%edi)" "\n\t" + "movl %%ecx, 0x8(%%edi)" "\n\t" + "movl %%edx, 0xc(%%edi)" "\n\t" + + /* + * Come here on a normal exit. Recover the TCLEXCEPTION_REGISTRATION and + * store a TCL_OK status. + */ + + "movl %%fs:0, %%edx" "\n\t" + "movl %[ok], %%eax" "\n\t" + "movl %%eax, 0x10(%%edx)" "\n\t" + "jmp 2f" "\n" + + /* + * Come here on an exception. Get the TCLEXCEPTION_REGISTRATION that we + * previously put on the chain. + */ + + "1:" "\t" + "movl %%fs:0, %%edx" "\n\t" + "movl 0x8(%%edx), %%edx" "\n\t" + + /* + * Come here however we exited. Restore context from the + * TCLEXCEPTION_REGISTRATION in case the stack is unbalanced. + */ + + "2:" "\t" + "movl 0xc(%%edx), %%esp" "\n\t" + "movl 0x8(%%edx), %%ebp" "\n\t" + "movl 0x0(%%edx), %%eax" "\n\t" + "movl %%eax, %%fs:0" "\n\t" + + : + /* No outputs */ + : + [index] "m" (index), + [rptr] "m" (regsPtr), + [registration] "m" (registration), + [ok] "i" (TCL_OK), + [error] "i" (TCL_ERROR) + : + "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); + status = registration.status; + +# endif /* !_WIN64 */ +#elif defined(_MSC_VER) +# if defined(_WIN64) + + __cpuid(regsPtr, index); + status = TCL_OK; + +# else + /* + * Define a structure in the stack frame to hold the registers. + */ + + struct { + DWORD dw0; + DWORD dw1; + DWORD dw2; + DWORD dw3; + } regs; + regs.dw0 = index; + + /* + * Execute the CPUID instruction and save regs in the stack frame. + */ + + _try { + _asm { + push ebx + push ecx + push edx + mov eax, regs.dw0 + cpuid + mov regs.dw0, eax + mov regs.dw1, ebx + mov regs.dw2, ecx + mov regs.dw3, edx + pop edx + pop ecx + pop ebx + } + + /* + * Copy regs back out to the caller. + */ + + regsPtr[0] = regs.dw0; + regsPtr[1] = regs.dw1; + regsPtr[2] = regs.dw2; + regsPtr[3] = regs.dw3; + + status = TCL_OK; + } __except(EXCEPTION_EXECUTE_HANDLER) { + /* do nothing */ + } + +# endif +#else + /* + * Don't know how to do assembly code for this compiler and/or + * architecture. + */ +#endif + return status; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |
