1/* 2 * tclWin32Dll.c -- 3 * 4 * This file contains the DLL entry point and other low-level bit bashing 5 * code that needs inline assembly. 6 * 7 * Copyright (c) 1995-1996 Sun Microsystems, Inc. 8 * Copyright (c) 1998-2000 Scriptics Corporation. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tclWin32Dll.c,v 1.54.2.3 2010/09/08 15:42:13 dgp Exp $ 14 */ 15 16#include "tclWinInt.h" 17 18#ifndef TCL_NO_STACK_CHECK 19/* 20 * The following functions implement stack depth checking 21 */ 22typedef struct ThreadSpecificData { 23 int *stackBound; /* The current stack boundary */ 24} ThreadSpecificData; 25static Tcl_ThreadDataKey dataKey; 26#endif /* TCL_NO_STACK_CHECK */ 27 28/* 29 * The following data structures are used when loading the thunking library 30 * for execing child processes under Win32s. 31 */ 32 33typedef DWORD (WINAPI UT32PROC)(LPVOID lpBuff, DWORD dwUserDefined, 34 LPVOID *lpTranslationList); 35 36typedef BOOL (WINAPI UTREGISTER)(HANDLE hModule, LPCSTR SixteenBitDLL, 37 LPCSTR InitName, LPCSTR ProcName, UT32PROC **ThirtyTwoBitThunk, 38 FARPROC UT32Callback, LPVOID Buff); 39 40typedef VOID (WINAPI UTUNREGISTER)(HANDLE hModule); 41 42/* 43 * The following variables keep track of information about this DLL on a 44 * per-instance basis. Each time this DLL is loaded, it gets its own new data 45 * segment with its own copy of all static and global information. 46 */ 47 48static HINSTANCE hInstance; /* HINSTANCE of this DLL. */ 49static int platformId; /* Running under NT, or 95/98? */ 50 51#ifdef HAVE_NO_SEH 52/* 53 * Unlike Borland and Microsoft, we don't register exception handlers by 54 * pushing registration records onto the runtime stack. Instead, we register 55 * them by creating an EXCEPTION_REGISTRATION within the activation record. 56 */ 57 58typedef struct EXCEPTION_REGISTRATION { 59 struct EXCEPTION_REGISTRATION *link; 60 EXCEPTION_DISPOSITION (*handler)( 61 struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*); 62 void *ebp; 63 void *esp; 64 int status; 65} EXCEPTION_REGISTRATION; 66#endif 67 68/* 69 * VC++ 5.x has no 'cpuid' assembler instruction, so we must emulate it 70 */ 71 72#if defined(_MSC_VER) && (_MSC_VER <= 1100) 73#define cpuid __asm __emit 0fh __asm __emit 0a2h 74#endif 75 76/* 77 * The following function tables are used to dispatch to either the 78 * wide-character or multi-byte versions of the operating system calls, 79 * depending on whether the Unicode calls are available. 80 */ 81 82static TclWinProcs asciiProcs = { 83 0, 84 85 (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBA, 86 (TCHAR *(WINAPI *)(TCHAR *)) CharLowerA, 87 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileA, 88 (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryA, 89 (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, 90 DWORD, DWORD, HANDLE)) CreateFileA, 91 (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, 92 LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, 93 LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessA, 94 (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileA, 95 (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileA, 96 (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileA, 97 (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameA, 98 (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryA, 99 (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesA, 100 (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, 101 TCHAR **)) GetFullPathNameA, 102 (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameA, 103 (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameA, 104 (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, 105 WCHAR *)) GetTempFileNameA, 106 (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathA, 107 (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, 108 WCHAR *, DWORD)) GetVolumeInformationA, 109 (HINSTANCE (WINAPI *)(CONST TCHAR *, HANDLE, DWORD)) LoadLibraryExA, 110 (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyA, 111 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileA, 112 (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryA, 113 (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, 114 WCHAR *, TCHAR **)) SearchPathA, 115 (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryA, 116 (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesA, 117 118 /* 119 * The three NULL function pointers will only be set when 120 * Tcl_FindExecutable is called. If you don't ever call that function, the 121 * application will crash whenever WinTcl tries to call functions through 122 * these null pointers. That is not a bug in Tcl - Tcl_FindExecutable is 123 * mandatory in recent Tcl releases. 124 */ 125 126 NULL, 127 NULL, 128 /* deleted (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _utime, */ 129 NULL, 130 NULL, 131 /* getLongPathNameProc */ 132 NULL, 133 /* Security SDK - not available on 95,98,ME */ 134 NULL, NULL, NULL, NULL, NULL, NULL, 135 /* ReadConsole and WriteConsole */ 136 (BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleA, 137 (BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleA, 138 (BOOL (WINAPI *)(LPTSTR, LPDWORD)) GetUserNameA 139}; 140 141static TclWinProcs unicodeProcs = { 142 1, 143 144 (BOOL (WINAPI *)(CONST TCHAR *, LPDCB)) BuildCommDCBW, 145 (TCHAR *(WINAPI *)(TCHAR *)) CharLowerW, 146 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *, BOOL)) CopyFileW, 147 (BOOL (WINAPI *)(CONST TCHAR *, LPSECURITY_ATTRIBUTES)) CreateDirectoryW, 148 (HANDLE (WINAPI *)(CONST TCHAR *, DWORD, DWORD, SECURITY_ATTRIBUTES *, 149 DWORD, DWORD, HANDLE)) CreateFileW, 150 (BOOL (WINAPI *)(CONST TCHAR *, TCHAR *, LPSECURITY_ATTRIBUTES, 151 LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, CONST TCHAR *, 152 LPSTARTUPINFOA, LPPROCESS_INFORMATION)) CreateProcessW, 153 (BOOL (WINAPI *)(CONST TCHAR *)) DeleteFileW, 154 (HANDLE (WINAPI *)(CONST TCHAR *, WIN32_FIND_DATAT *)) FindFirstFileW, 155 (BOOL (WINAPI *)(HANDLE, WIN32_FIND_DATAT *)) FindNextFileW, 156 (BOOL (WINAPI *)(WCHAR *, LPDWORD)) GetComputerNameW, 157 (DWORD (WINAPI *)(DWORD, WCHAR *)) GetCurrentDirectoryW, 158 (DWORD (WINAPI *)(CONST TCHAR *)) GetFileAttributesW, 159 (DWORD (WINAPI *)(CONST TCHAR *, DWORD nBufferLength, WCHAR *, 160 TCHAR **)) GetFullPathNameW, 161 (DWORD (WINAPI *)(HMODULE, WCHAR *, int)) GetModuleFileNameW, 162 (DWORD (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD)) GetShortPathNameW, 163 (UINT (WINAPI *)(CONST TCHAR *, CONST TCHAR *, UINT uUnique, 164 WCHAR *)) GetTempFileNameW, 165 (DWORD (WINAPI *)(DWORD, WCHAR *)) GetTempPathW, 166 (BOOL (WINAPI *)(CONST TCHAR *, WCHAR *, DWORD, LPDWORD, LPDWORD, LPDWORD, 167 WCHAR *, DWORD)) GetVolumeInformationW, 168 (HINSTANCE (WINAPI *)(CONST TCHAR *, HANDLE, DWORD)) LoadLibraryExW, 169 (TCHAR (WINAPI *)(WCHAR *, CONST TCHAR *)) lstrcpyW, 170 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR *)) MoveFileW, 171 (BOOL (WINAPI *)(CONST TCHAR *)) RemoveDirectoryW, 172 (DWORD (WINAPI *)(CONST TCHAR *, CONST TCHAR *, CONST TCHAR *, DWORD, 173 WCHAR *, TCHAR **)) SearchPathW, 174 (BOOL (WINAPI *)(CONST TCHAR *)) SetCurrentDirectoryW, 175 (BOOL (WINAPI *)(CONST TCHAR *, DWORD)) SetFileAttributesW, 176 177 /* 178 * The three NULL function pointers will only be set when 179 * Tcl_FindExecutable is called. If you don't ever call that function, the 180 * application will crash whenever WinTcl tries to call functions through 181 * these null pointers. That is not a bug in Tcl - Tcl_FindExecutable is 182 * mandatory in recent Tcl releases. 183 */ 184 185 NULL, 186 NULL, 187 /* deleted (int (__cdecl*)(CONST TCHAR *, struct _utimbuf *)) _wutime, */ 188 NULL, 189 NULL, 190 /* getLongPathNameProc */ 191 NULL, 192 /* Security SDK - will be filled in on NT,XP,2000,2003 */ 193 NULL, NULL, NULL, NULL, NULL, NULL, 194 /* ReadConsole and WriteConsole */ 195 (BOOL (WINAPI *)(HANDLE, LPVOID, DWORD, LPDWORD, LPVOID)) ReadConsoleW, 196 (BOOL (WINAPI *)(HANDLE, const VOID*, DWORD, LPDWORD, LPVOID)) WriteConsoleW, 197 (BOOL (WINAPI *)(LPTSTR, LPDWORD)) GetUserNameW 198}; 199 200TclWinProcs *tclWinProcs; 201static Tcl_Encoding tclWinTCharEncoding; 202 203#ifdef HAVE_NO_SEH 204/* 205 * Need to add noinline flag to DllMain declaration so that gcc -O3 does not 206 * inline asm code into DllEntryPoint and cause a compile time error because 207 * of redefined local labels. 208 */ 209 210BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, 211 LPVOID reserved) __attribute__ ((noinline)); 212#else 213/* 214 * The following declaration is for the VC++ DLL entry point. 215 */ 216 217BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, 218 LPVOID reserved); 219#endif /* HAVE_NO_SEH */ 220 221/* 222 * The following structure and linked list is to allow us to map between 223 * volume mount points and drive letters on the fly (no Win API exists for 224 * this). 225 */ 226 227typedef struct MountPointMap { 228 CONST WCHAR *volumeName; /* Native wide string volume name. */ 229 char driveLetter; /* Drive letter corresponding to the volume 230 * name. */ 231 struct MountPointMap *nextPtr; 232 /* Pointer to next structure in list, or 233 * NULL. */ 234} MountPointMap; 235 236/* 237 * This is the head of the linked list, which is protected by the mutex which 238 * follows, for thread-enabled builds. 239 */ 240 241MountPointMap *driveLetterLookup = NULL; 242TCL_DECLARE_MUTEX(mountPointMap) 243 244/* 245 * We will need this below. 246 */ 247 248extern Tcl_FSDupInternalRepProc TclNativeDupInternalRep; 249 250#ifdef __WIN32__ 251#ifndef STATIC_BUILD 252 253/* 254 *---------------------------------------------------------------------- 255 * 256 * DllEntryPoint -- 257 * 258 * This wrapper function is used by Borland to invoke the initialization 259 * code for Tcl. It simply calls the DllMain routine. 260 * 261 * Results: 262 * See DllMain. 263 * 264 * Side effects: 265 * See DllMain. 266 * 267 *---------------------------------------------------------------------- 268 */ 269 270BOOL APIENTRY 271DllEntryPoint( 272 HINSTANCE hInst, /* Library instance handle. */ 273 DWORD reason, /* Reason this function is being called. */ 274 LPVOID reserved) /* Not used. */ 275{ 276 return DllMain(hInst, reason, reserved); 277} 278 279/* 280 *---------------------------------------------------------------------- 281 * 282 * DllMain -- 283 * 284 * This routine is called by the VC++ C run time library init code, or 285 * the DllEntryPoint routine. It is responsible for initializing various 286 * dynamically loaded libraries. 287 * 288 * Results: 289 * TRUE on sucess, FALSE on failure. 290 * 291 * Side effects: 292 * Establishes 32-to-16 bit thunk and initializes sockets library. This 293 * might call some sycronization functions, but MSDN documentation 294 * states: "Waiting on synchronization objects in DllMain can cause a 295 * deadlock." 296 * 297 *---------------------------------------------------------------------- 298 */ 299 300BOOL APIENTRY 301DllMain( 302 HINSTANCE hInst, /* Library instance handle. */ 303 DWORD reason, /* Reason this function is being called. */ 304 LPVOID reserved) /* Not used. */ 305{ 306#ifdef HAVE_NO_SEH 307 EXCEPTION_REGISTRATION registration; 308#endif 309 310 switch (reason) { 311 case DLL_PROCESS_ATTACH: 312 DisableThreadLibraryCalls(hInst); 313 TclWinInit(hInst); 314 return TRUE; 315 316 case DLL_PROCESS_DETACH: 317 /* 318 * Protect the call to Tcl_Finalize. The OS could be unloading us from 319 * an exception handler and the state of the stack might be unstable. 320 */ 321 322#if defined(HAVE_NO_SEH) && !defined(_WIN64) 323 __asm__ __volatile__ ( 324 325 /* 326 * Construct an EXCEPTION_REGISTRATION to protect the call to 327 * Tcl_Finalize 328 */ 329 330 "leal %[registration], %%edx" "\n\t" 331 "movl %%fs:0, %%eax" "\n\t" 332 "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ 333 "leal 1f, %%eax" "\n\t" 334 "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ 335 "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ 336 "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ 337 "movl %[error], 0x10(%%edx)" "\n\t" /* status */ 338 339 /* 340 * Link the EXCEPTION_REGISTRATION on the chain 341 */ 342 343 "movl %%edx, %%fs:0" "\n\t" 344 345 /* 346 * Call Tcl_Finalize 347 */ 348 349 "call _Tcl_Finalize" "\n\t" 350 351 /* 352 * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION 353 * and store a TCL_OK status 354 */ 355 356 "movl %%fs:0, %%edx" "\n\t" 357 "movl %[ok], %%eax" "\n\t" 358 "movl %%eax, 0x10(%%edx)" "\n\t" 359 "jmp 2f" "\n" 360 361 /* 362 * Come here on an exception. Get the EXCEPTION_REGISTRATION that 363 * we previously put on the chain. 364 */ 365 366 "1:" "\t" 367 "movl %%fs:0, %%edx" "\n\t" 368 "movl 0x8(%%edx), %%edx" "\n" 369 370 371 /* 372 * Come here however we exited. Restore context from the 373 * EXCEPTION_REGISTRATION in case the stack is unbalanced. 374 */ 375 376 "2:" "\t" 377 "movl 0xc(%%edx), %%esp" "\n\t" 378 "movl 0x8(%%edx), %%ebp" "\n\t" 379 "movl 0x0(%%edx), %%eax" "\n\t" 380 "movl %%eax, %%fs:0" "\n\t" 381 382 : 383 /* No outputs */ 384 : 385 [registration] "m" (registration), 386 [ok] "i" (TCL_OK), 387 [error] "i" (TCL_ERROR) 388 : 389 "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" 390 ); 391 392#else 393#ifndef HAVE_NO_SEH 394 __try { 395#endif 396 Tcl_Finalize(); 397#ifndef HAVE_NO_SEH 398 } __except (EXCEPTION_EXECUTE_HANDLER) { 399 /* empty handler body. */ 400 } 401#endif 402#endif 403 404 break; 405 } 406 407 return TRUE; 408} 409#endif /* !STATIC_BUILD */ 410#endif /* __WIN32__ */ 411 412/* 413 *---------------------------------------------------------------------- 414 * 415 * TclWinGetTclInstance -- 416 * 417 * Retrieves the global library instance handle. 418 * 419 * Results: 420 * Returns the global library instance handle. 421 * 422 * Side effects: 423 * None. 424 * 425 *---------------------------------------------------------------------- 426 */ 427 428HINSTANCE 429TclWinGetTclInstance(void) 430{ 431 return hInstance; 432} 433 434/* 435 *---------------------------------------------------------------------- 436 * 437 * TclWinInit -- 438 * 439 * This function initializes the internal state of the tcl library. 440 * 441 * Results: 442 * None. 443 * 444 * Side effects: 445 * Initializes the tclPlatformId variable. 446 * 447 *---------------------------------------------------------------------- 448 */ 449 450void 451TclWinInit( 452 HINSTANCE hInst) /* Library instance handle. */ 453{ 454 OSVERSIONINFO os; 455 456 hInstance = hInst; 457 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 458 GetVersionEx(&os); 459 platformId = os.dwPlatformId; 460 461 /* 462 * We no longer support Win32s, so just in case someone manages to get a 463 * runtime there, make sure they know that. 464 */ 465 466 if (platformId == VER_PLATFORM_WIN32s) { 467 Tcl_Panic("Win32s is not a supported platform"); 468 } 469 470 tclWinProcs = &asciiProcs; 471} 472 473/* 474 *---------------------------------------------------------------------- 475 * 476 * TclWinGetPlatformId -- 477 * 478 * Determines whether running under NT, 95, or Win32s, to allow runtime 479 * conditional code. 480 * 481 * Results: 482 * The return value is one of: 483 * VER_PLATFORM_WIN32s Win32s on Windows 3.1. (not supported) 484 * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95, 98, ME. 485 * VER_PLATFORM_WIN32_NT Win32 on Windows NT, 2000, XP 486 * 487 * Side effects: 488 * None. 489 * 490 *---------------------------------------------------------------------- 491 */ 492 493int 494TclWinGetPlatformId(void) 495{ 496 return platformId; 497} 498 499/* 500 *------------------------------------------------------------------------- 501 * 502 * TclWinNoBackslash -- 503 * 504 * We're always iterating through a string in Windows, changing the 505 * backslashes to slashes for use in Tcl. 506 * 507 * Results: 508 * All backslashes in given string are changed to slashes. 509 * 510 * Side effects: 511 * None. 512 * 513 *------------------------------------------------------------------------- 514 */ 515 516char * 517TclWinNoBackslash( 518 char *path) /* String to change. */ 519{ 520 char *p; 521 522 for (p = path; *p != '\0'; p++) { 523 if (*p == '\\') { 524 *p = '/'; 525 } 526 } 527 return path; 528} 529 530/* 531 *---------------------------------------------------------------------- 532 * 533 * TclpGetStackParams -- 534 * 535 * Determine the stack params for the current thread: in which 536 * direction does the stack grow, and what is the stack lower (resp. 537 * upper) bound for safe invocation of a new command? This is used to 538 * cache the values needed for an efficient computation of 539 * TclpCheckStackSpace() when the interp is known. 540 * 541 * Results: 542 * Returns 1 if the stack grows down, in which case a stack lower bound 543 * is stored at stackBoundPtr. If the stack grows up, 0 is returned and 544 * an upper bound is stored at stackBoundPtr. If a bound cannot be 545 * determined NULL is stored at stackBoundPtr. 546 * 547 *---------------------------------------------------------------------- 548 */ 549 550#ifndef TCL_NO_STACK_CHECK 551int 552TclpGetCStackParams( 553 int **stackBoundPtr) 554{ 555 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); 556 SYSTEM_INFO si; /* The system information, used to 557 * determine the page size */ 558 MEMORY_BASIC_INFORMATION mbi; 559 /* The information about the memory 560 * area in which the stack resides */ 561 562 if (!tsdPtr->stackBound 563 || ((UINT_PTR)&tsdPtr < (UINT_PTR)tsdPtr->stackBound)) { 564 565 /* 566 * Either we haven't determined the stack bound in this thread, 567 * or else we've overflowed the bound that we previously 568 * determined. We need to find a new stack bound from 569 * Windows. 570 */ 571 572 GetSystemInfo(&si); 573 if (VirtualQuery((LPCVOID) &tsdPtr, &mbi, sizeof(mbi)) == 0) { 574 575 /* For some reason, the system didn't let us query the 576 * stack size. Nevertheless, we got here and haven't 577 * blown up yet. Don't update the calculated stack bound. 578 * If there is no calculated stack bound yet, set it to 579 * the base of the current page of stack. */ 580 581 if (!tsdPtr->stackBound) { 582 tsdPtr->stackBound = 583 (int*) ((UINT_PTR)(&tsdPtr) 584 & ~ (UINT_PTR)(si.dwPageSize - 1)); 585 } 586 587 } else { 588 589 /* The allocation base of the stack segment has to be advanced 590 * by one page (to allow for the guard page maintained in the 591 * C runtime) and then by TCL_WIN_STACK_THRESHOLD (to allow 592 * for the amount of stack that Tcl needs). 593 */ 594 595 tsdPtr->stackBound = 596 (int*) ((UINT_PTR)(mbi.AllocationBase) 597 + (UINT_PTR)(si.dwPageSize) 598 + TCL_WIN_STACK_THRESHOLD); 599 } 600 } 601 *stackBoundPtr = tsdPtr->stackBound; 602 return 1; 603} 604#endif 605 606 607/* 608 *--------------------------------------------------------------------------- 609 * 610 * TclWinSetInterfaces -- 611 * 612 * A helper proc that allows the test library to change the tclWinProcs 613 * structure to dispatch to either the wide-character or multi-byte 614 * versions of the operating system calls, depending on whether Unicode 615 * is the system encoding. 616 * 617 * As well as this, we can also try to load in some additional procs 618 * which may/may not be present depending on the current Windows version 619 * (e.g. Win95 will not have the procs below). 620 * 621 * Results: 622 * None. 623 * 624 * Side effects: 625 * None. 626 * 627 *--------------------------------------------------------------------------- 628 */ 629 630void 631TclWinSetInterfaces( 632 int wide) /* Non-zero to use wide interfaces, 0 633 * otherwise. */ 634{ 635 Tcl_FreeEncoding(tclWinTCharEncoding); 636 637 if (wide) { 638 tclWinProcs = &unicodeProcs; 639 tclWinTCharEncoding = Tcl_GetEncoding(NULL, "unicode"); 640 if (tclWinProcs->getFileAttributesExProc == NULL) { 641 HINSTANCE hInstance = LoadLibraryA("kernel32"); 642 if (hInstance != NULL) { 643 tclWinProcs->getFileAttributesExProc = 644 (BOOL (WINAPI *)(CONST TCHAR *, GET_FILEEX_INFO_LEVELS, 645 LPVOID)) GetProcAddress(hInstance, 646 "GetFileAttributesExW"); 647 tclWinProcs->createHardLinkProc = 648 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, 649 LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, 650 "CreateHardLinkW"); 651 tclWinProcs->findFirstFileExProc = 652 (HANDLE (WINAPI *)(CONST TCHAR*, UINT, LPVOID, UINT, 653 LPVOID, DWORD)) GetProcAddress(hInstance, 654 "FindFirstFileExW"); 655 tclWinProcs->getVolumeNameForVMPProc = 656 (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, 657 DWORD)) GetProcAddress(hInstance, 658 "GetVolumeNameForVolumeMountPointW"); 659 tclWinProcs->getLongPathNameProc = 660 (DWORD (WINAPI *)(CONST TCHAR*, TCHAR*, 661 DWORD)) GetProcAddress(hInstance, "GetLongPathNameW"); 662 FreeLibrary(hInstance); 663 } 664 hInstance = LoadLibraryA("advapi32"); 665 if (hInstance != NULL) { 666 tclWinProcs->getFileSecurityProc = (BOOL (WINAPI *)( 667 LPCTSTR lpFileName, 668 SECURITY_INFORMATION RequestedInformation, 669 PSECURITY_DESCRIPTOR pSecurityDescriptor, 670 DWORD nLength, LPDWORD lpnLengthNeeded)) 671 GetProcAddress(hInstance, "GetFileSecurityW"); 672 tclWinProcs->impersonateSelfProc = (BOOL (WINAPI *) ( 673 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)) 674 GetProcAddress(hInstance, "ImpersonateSelf"); 675 tclWinProcs->openThreadTokenProc = (BOOL (WINAPI *) ( 676 HANDLE ThreadHandle, DWORD DesiredAccess, 677 BOOL OpenAsSelf, PHANDLE TokenHandle)) 678 GetProcAddress(hInstance, "OpenThreadToken"); 679 tclWinProcs->revertToSelfProc = (BOOL (WINAPI *) (void)) 680 GetProcAddress(hInstance, "RevertToSelf"); 681 tclWinProcs->mapGenericMaskProc = (VOID (WINAPI *) ( 682 PDWORD AccessMask, PGENERIC_MAPPING GenericMapping)) 683 GetProcAddress(hInstance, "MapGenericMask"); 684 tclWinProcs->accessCheckProc = (BOOL (WINAPI *)( 685 PSECURITY_DESCRIPTOR pSecurityDescriptor, 686 HANDLE ClientToken, DWORD DesiredAccess, 687 PGENERIC_MAPPING GenericMapping, 688 PPRIVILEGE_SET PrivilegeSet, 689 LPDWORD PrivilegeSetLength, LPDWORD GrantedAccess, 690 LPBOOL AccessStatus)) GetProcAddress(hInstance, 691 "AccessCheck"); 692 FreeLibrary(hInstance); 693 } 694 } 695 } else { 696 tclWinProcs = &asciiProcs; 697 tclWinTCharEncoding = NULL; 698 if (tclWinProcs->getFileAttributesExProc == NULL) { 699 HINSTANCE hInstance = LoadLibraryA("kernel32"); 700 if (hInstance != NULL) { 701 tclWinProcs->getFileAttributesExProc = 702 (BOOL (WINAPI *)(CONST TCHAR *, GET_FILEEX_INFO_LEVELS, 703 LPVOID)) GetProcAddress(hInstance, 704 "GetFileAttributesExA"); 705 tclWinProcs->createHardLinkProc = 706 (BOOL (WINAPI *)(CONST TCHAR *, CONST TCHAR*, 707 LPSECURITY_ATTRIBUTES)) GetProcAddress(hInstance, 708 "CreateHardLinkA"); 709 tclWinProcs->findFirstFileExProc = NULL; 710 tclWinProcs->getLongPathNameProc = NULL; 711 /* 712 * The 'findFirstFileExProc' function exists on some of 713 * 95/98/ME, but it seems not to work as anticipated. 714 * Therefore we don't set this function pointer. The relevant 715 * code will fall back on a slower approach using the normal 716 * findFirstFileProc. 717 * 718 * (HANDLE (WINAPI *)(CONST TCHAR*, UINT, 719 * LPVOID, UINT, LPVOID, DWORD)) GetProcAddress(hInstance, 720 * "FindFirstFileExA"); 721 */ 722 tclWinProcs->getVolumeNameForVMPProc = 723 (BOOL (WINAPI *)(CONST TCHAR*, TCHAR*, 724 DWORD)) GetProcAddress(hInstance, 725 "GetVolumeNameForVolumeMountPointA"); 726 FreeLibrary(hInstance); 727 } 728 } 729 } 730} 731 732/* 733 *--------------------------------------------------------------------------- 734 * 735 * TclWinResetInterfaceEncodings -- 736 * 737 * Called during finalization to free up any encodings we use. The 738 * tclWinProcs-> look up table is still ok to use after this call, 739 * provided no encoding conversion is required. 740 * 741 * We also clean up any memory allocated in our mount point map which is 742 * used to follow certain kinds of symlinks. That code should never be 743 * used once encodings are taken down. 744 * 745 * Results: 746 * None. 747 * 748 * Side effects: 749 * None. 750 * 751 *--------------------------------------------------------------------------- 752 */ 753 754void 755TclWinResetInterfaceEncodings(void) 756{ 757 MountPointMap *dlIter, *dlIter2; 758 if (tclWinTCharEncoding != NULL) { 759 Tcl_FreeEncoding(tclWinTCharEncoding); 760 tclWinTCharEncoding = NULL; 761 } 762 763 /* 764 * Clean up the mount point map. 765 */ 766 767 Tcl_MutexLock(&mountPointMap); 768 dlIter = driveLetterLookup; 769 while (dlIter != NULL) { 770 dlIter2 = dlIter->nextPtr; 771 ckfree((char*)dlIter->volumeName); 772 ckfree((char*)dlIter); 773 dlIter = dlIter2; 774 } 775 Tcl_MutexUnlock(&mountPointMap); 776} 777 778/* 779 *--------------------------------------------------------------------------- 780 * 781 * TclWinResetInterfaces -- 782 * 783 * Called during finalization to reset us to a safe state for reuse. 784 * After this call, it is best not to use the tclWinProcs-> look up table 785 * since it is likely to be different to what is expected. 786 * 787 * Results: 788 * None. 789 * 790 * Side effects: 791 * None. 792 * 793 *--------------------------------------------------------------------------- 794 */ 795void 796TclWinResetInterfaces(void) 797{ 798 tclWinProcs = &asciiProcs; 799} 800 801/* 802 *-------------------------------------------------------------------- 803 * 804 * TclWinDriveLetterForVolMountPoint 805 * 806 * Unfortunately, Windows provides no easy way at all to get hold of the 807 * drive letter for a volume mount point, but we need that information to 808 * understand paths correctly. So, we have to build an associated array 809 * to find these correctly, and allow quick and easy lookup from volume 810 * mount points to drive letters. 811 * 812 * We assume here that we are running on a system for which the wide 813 * character interfaces are used, which is valid for Win 2000 and WinXP 814 * which are the only systems on which this function will ever be called. 815 * 816 * Result: 817 * The drive letter, or -1 if no drive letter corresponds to the given 818 * mount point. 819 * 820 *-------------------------------------------------------------------- 821 */ 822 823char 824TclWinDriveLetterForVolMountPoint( 825 CONST WCHAR *mountPoint) 826{ 827 MountPointMap *dlIter, *dlPtr2; 828 WCHAR Target[55]; /* Target of mount at mount point */ 829 WCHAR drive[4] = { L'A', L':', L'\\', L'\0' }; 830 831 /* 832 * Detect the volume mounted there. Unfortunately, there is no simple way 833 * to map a unique volume name to a DOS drive letter. So, we have to build 834 * an associative array. 835 */ 836 837 Tcl_MutexLock(&mountPointMap); 838 dlIter = driveLetterLookup; 839 while (dlIter != NULL) { 840 if (wcscmp(dlIter->volumeName, mountPoint) == 0) { 841 /* 842 * We need to check whether this information is still valid, since 843 * either the user or various programs could have adjusted the 844 * mount points on the fly. 845 */ 846 847 drive[0] = L'A' + (dlIter->driveLetter - 'A'); 848 849 /* 850 * Try to read the volume mount point and see where it points. 851 */ 852 853 if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, 854 (TCHAR*)Target, 55) != 0) { 855 if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { 856 /* 857 * Nothing has changed. 858 */ 859 860 Tcl_MutexUnlock(&mountPointMap); 861 return dlIter->driveLetter; 862 } 863 } 864 865 /* 866 * If we reach here, unfortunately, this mount point is no longer 867 * valid at all. 868 */ 869 870 if (driveLetterLookup == dlIter) { 871 dlPtr2 = dlIter; 872 driveLetterLookup = dlIter->nextPtr; 873 } else { 874 for (dlPtr2 = driveLetterLookup; 875 dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) { 876 if (dlPtr2->nextPtr == dlIter) { 877 dlPtr2->nextPtr = dlIter->nextPtr; 878 dlPtr2 = dlIter; 879 break; 880 } 881 } 882 } 883 884 /* 885 * Now dlPtr2 points to the structure to free. 886 */ 887 888 ckfree((char*)dlPtr2->volumeName); 889 ckfree((char*)dlPtr2); 890 891 /* 892 * Restart the loop - we could try to be clever and continue half 893 * way through, but the logic is a bit messy, so it's cleanest 894 * just to restart. 895 */ 896 897 dlIter = driveLetterLookup; 898 continue; 899 } 900 dlIter = dlIter->nextPtr; 901 } 902 903 /* 904 * We couldn't find it, so we must iterate over the letters. 905 */ 906 907 for (drive[0] = L'A'; drive[0] <= L'Z'; drive[0]++) { 908 /* 909 * Try to read the volume mount point and see where it points. 910 */ 911 912 if ((*tclWinProcs->getVolumeNameForVMPProc)((TCHAR*)drive, 913 (TCHAR*)Target, 55) != 0) { 914 int alreadyStored = 0; 915 916 for (dlIter = driveLetterLookup; dlIter != NULL; 917 dlIter = dlIter->nextPtr) { 918 if (wcscmp((WCHAR*)dlIter->volumeName, Target) == 0) { 919 alreadyStored = 1; 920 break; 921 } 922 } 923 if (!alreadyStored) { 924 dlPtr2 = (MountPointMap *) ckalloc(sizeof(MountPointMap)); 925 dlPtr2->volumeName = TclNativeDupInternalRep(Target); 926 dlPtr2->driveLetter = 'A' + (drive[0] - L'A'); 927 dlPtr2->nextPtr = driveLetterLookup; 928 driveLetterLookup = dlPtr2; 929 } 930 } 931 } 932 933 /* 934 * Try again. 935 */ 936 937 for (dlIter = driveLetterLookup; dlIter != NULL; 938 dlIter = dlIter->nextPtr) { 939 if (wcscmp(dlIter->volumeName, mountPoint) == 0) { 940 Tcl_MutexUnlock(&mountPointMap); 941 return dlIter->driveLetter; 942 } 943 } 944 945 /* 946 * The volume doesn't appear to correspond to a drive letter - we remember 947 * that fact and store '-1' so we don't have to look it up each time. 948 */ 949 950 dlPtr2 = (MountPointMap*) ckalloc(sizeof(MountPointMap)); 951 dlPtr2->volumeName = TclNativeDupInternalRep((ClientData)mountPoint); 952 dlPtr2->driveLetter = -1; 953 dlPtr2->nextPtr = driveLetterLookup; 954 driveLetterLookup = dlPtr2; 955 Tcl_MutexUnlock(&mountPointMap); 956 return -1; 957} 958 959/* 960 *--------------------------------------------------------------------------- 961 * 962 * Tcl_WinUtfToTChar, Tcl_WinTCharToUtf -- 963 * 964 * Convert between UTF-8 and Unicode when running Windows NT or the 965 * current ANSI code page when running Windows 95. 966 * 967 * On Mac, Unix, and Windows 95, all strings exchanged between Tcl and 968 * the OS are "char" oriented. We need only one Tcl_Encoding to convert 969 * between UTF-8 and the system's native encoding. We use NULL to 970 * represent that encoding. 971 * 972 * On NT, some strings exchanged between Tcl and the OS are "char" 973 * oriented, while others are in Unicode. We need two Tcl_Encoding APIs 974 * depending on whether we are targeting a "char" or Unicode interface. 975 * 976 * Calling Tcl_UtfToExternal() or Tcl_ExternalToUtf() with an encoding of 977 * NULL should always used to convert between UTF-8 and the system's 978 * "char" oriented encoding. The following two functions are used in 979 * Windows-specific code to convert between UTF-8 and Unicode strings 980 * (NT) or "char" strings(95). This saves you the trouble of writing the 981 * following type of fragment over and over: 982 * 983 * if (running NT) { 984 * encoding <- Tcl_GetEncoding("unicode"); 985 * nativeBuffer <- UtfToExternal(encoding, utfBuffer); 986 * Tcl_FreeEncoding(encoding); 987 * } else { 988 * nativeBuffer <- UtfToExternal(NULL, utfBuffer); 989 * } 990 * 991 * By convention, in Windows a TCHAR is a character in the ANSI code page 992 * on Windows 95, a Unicode character on Windows NT. If you plan on 993 * targeting a Unicode interfaces when running on NT and a "char" 994 * oriented interface while running on 95, these functions should be 995 * used. If you plan on targetting the same "char" oriented function on 996 * both 95 and NT, use Tcl_UtfToExternal() with an encoding of NULL. 997 * 998 * Results: 999 * The result is a pointer to the string in the desired target encoding. 1000 * Storage for the result string is allocated in dsPtr; the caller must 1001 * call Tcl_DStringFree() when the result is no longer needed. 1002 * 1003 * Side effects: 1004 * None. 1005 * 1006 *--------------------------------------------------------------------------- 1007 */ 1008 1009TCHAR * 1010Tcl_WinUtfToTChar( 1011 CONST char *string, /* Source string in UTF-8. */ 1012 int len, /* Source string length in bytes, or < 0 for 1013 * strlen(). */ 1014 Tcl_DString *dsPtr) /* Uninitialized or free DString in which the 1015 * converted string is stored. */ 1016{ 1017 return (TCHAR *) Tcl_UtfToExternalDString(tclWinTCharEncoding, 1018 string, len, dsPtr); 1019} 1020 1021char * 1022Tcl_WinTCharToUtf( 1023 CONST TCHAR *string, /* Source string in Unicode when running NT, 1024 * ANSI when running 95. */ 1025 int len, /* Source string length in bytes, or < 0 for 1026 * platform-specific string length. */ 1027 Tcl_DString *dsPtr) /* Uninitialized or free DString in which the 1028 * converted string is stored. */ 1029{ 1030 return Tcl_ExternalToUtfDString(tclWinTCharEncoding, 1031 (CONST char *) string, len, dsPtr); 1032} 1033 1034/* 1035 *------------------------------------------------------------------------ 1036 * 1037 * TclWinCPUID -- 1038 * 1039 * Get CPU ID information on an Intel box under Windows 1040 * 1041 * Results: 1042 * Returns TCL_OK if successful, TCL_ERROR if CPUID is not supported or 1043 * fails. 1044 * 1045 * Side effects: 1046 * If successful, stores EAX, EBX, ECX and EDX registers after the CPUID 1047 * instruction in the four integers designated by 'regsPtr' 1048 * 1049 *---------------------------------------------------------------------- 1050 */ 1051 1052int 1053TclWinCPUID( 1054 unsigned int index, /* Which CPUID value to retrieve. */ 1055 unsigned int *regsPtr) /* Registers after the CPUID. */ 1056{ 1057#ifdef HAVE_NO_SEH 1058 EXCEPTION_REGISTRATION registration; 1059#endif 1060 int status = TCL_ERROR; 1061 1062#if defined(__GNUC__) && !defined(_WIN64) 1063 /* 1064 * Execute the CPUID instruction with the given index, and store results 1065 * off 'regPtr'. 1066 */ 1067 1068 __asm__ __volatile__( 1069 /* 1070 * Construct an EXCEPTION_REGISTRATION to protect the CPUID 1071 * instruction (early 486's don't have CPUID) 1072 */ 1073 1074 "leal %[registration], %%edx" "\n\t" 1075 "movl %%fs:0, %%eax" "\n\t" 1076 "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ 1077 "leal 1f, %%eax" "\n\t" 1078 "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ 1079 "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ 1080 "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ 1081 "movl %[error], 0x10(%%edx)" "\n\t" /* status */ 1082 1083 /* 1084 * Link the EXCEPTION_REGISTRATION on the chain 1085 */ 1086 1087 "movl %%edx, %%fs:0" "\n\t" 1088 1089 /* 1090 * Do the CPUID instruction, and save the results in the 'regsPtr' 1091 * area. 1092 */ 1093 1094 "movl %[rptr], %%edi" "\n\t" 1095 "movl %[index], %%eax" "\n\t" 1096 "cpuid" "\n\t" 1097 "movl %%eax, 0x0(%%edi)" "\n\t" 1098 "movl %%ebx, 0x4(%%edi)" "\n\t" 1099 "movl %%ecx, 0x8(%%edi)" "\n\t" 1100 "movl %%edx, 0xc(%%edi)" "\n\t" 1101 1102 /* 1103 * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION and 1104 * store a TCL_OK status. 1105 */ 1106 1107 "movl %%fs:0, %%edx" "\n\t" 1108 "movl %[ok], %%eax" "\n\t" 1109 "movl %%eax, 0x10(%%edx)" "\n\t" 1110 "jmp 2f" "\n" 1111 1112 /* 1113 * Come here on an exception. Get the EXCEPTION_REGISTRATION that we 1114 * previously put on the chain. 1115 */ 1116 1117 "1:" "\t" 1118 "movl %%fs:0, %%edx" "\n\t" 1119 "movl 0x8(%%edx), %%edx" "\n\t" 1120 1121 /* 1122 * Come here however we exited. Restore context from the 1123 * EXCEPTION_REGISTRATION in case the stack is unbalanced. 1124 */ 1125 1126 "2:" "\t" 1127 "movl 0xc(%%edx), %%esp" "\n\t" 1128 "movl 0x8(%%edx), %%ebp" "\n\t" 1129 "movl 0x0(%%edx), %%eax" "\n\t" 1130 "movl %%eax, %%fs:0" "\n\t" 1131 1132 : 1133 /* No outputs */ 1134 : 1135 [index] "m" (index), 1136 [rptr] "m" (regsPtr), 1137 [registration] "m" (registration), 1138 [ok] "i" (TCL_OK), 1139 [error] "i" (TCL_ERROR) 1140 : 1141 "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 1142 status = registration.status; 1143 1144#elif defined(_MSC_VER) && !defined(_WIN64) 1145 /* 1146 * Define a structure in the stack frame to hold the registers. 1147 */ 1148 1149 struct { 1150 DWORD dw0; 1151 DWORD dw1; 1152 DWORD dw2; 1153 DWORD dw3; 1154 } regs; 1155 regs.dw0 = index; 1156 1157 /* 1158 * Execute the CPUID instruction and save regs in the stack frame. 1159 */ 1160 1161 _try { 1162 _asm { 1163 push ebx 1164 push ecx 1165 push edx 1166 mov eax, regs.dw0 1167 cpuid 1168 mov regs.dw0, eax 1169 mov regs.dw1, ebx 1170 mov regs.dw2, ecx 1171 mov regs.dw3, edx 1172 pop edx 1173 pop ecx 1174 pop ebx 1175 } 1176 1177 /* 1178 * Copy regs back out to the caller. 1179 */ 1180 1181 regsPtr[0] = regs.dw0; 1182 regsPtr[1] = regs.dw1; 1183 regsPtr[2] = regs.dw2; 1184 regsPtr[3] = regs.dw3; 1185 1186 status = TCL_OK; 1187 } __except(EXCEPTION_EXECUTE_HANDLER) { 1188 /* do nothing */ 1189 } 1190 1191#else 1192 /* 1193 * Don't know how to do assembly code for this compiler and/or 1194 * architecture. 1195 */ 1196#endif 1197 return status; 1198} 1199 1200/* 1201 * Local Variables: 1202 * mode: c 1203 * c-basic-offset: 4 1204 * fill-column: 78 1205 * End: 1206 */ 1207