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