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