os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTest.c
Update contrib.
4 * Contains commands for platform specific tests on Windows.
6 * Copyright (c) 1996 Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * RCS: @(#) $Id: tclWinTest.c,v 1.8.2.6 2006/03/27 23:30:54 patthoyts Exp $
14 #define USE_COMPAT_CONST
15 #include "tclWinInt.h"
18 * For TestplatformChmod on Windows
25 * MinGW 3.4.2 does not define this.
28 #define INHERITED_ACE (0x10)
32 * Forward declarations of procedures defined later in this file:
34 int TclplatformtestInit _ANSI_ARGS_((Tcl_Interp *interp));
35 static int TesteventloopCmd _ANSI_ARGS_((ClientData dummy,
36 Tcl_Interp *interp, int argc, CONST84 char **argv));
37 static int TestvolumetypeCmd _ANSI_ARGS_((ClientData dummy,
38 Tcl_Interp *interp, int objc,
39 Tcl_Obj *CONST objv[]));
40 static int TestwinclockCmd _ANSI_ARGS_(( ClientData dummy,
43 Tcl_Obj *CONST objv[] ));
44 static int TestwinsleepCmd _ANSI_ARGS_(( ClientData dummy,
47 Tcl_Obj *CONST objv[] ));
48 static Tcl_ObjCmdProc TestExceptionCmd;
49 static int TestwincpuidCmd _ANSI_ARGS_(( ClientData dummy,
52 Tcl_Obj *CONST objv[] ));
53 static int TestplatformChmod _ANSI_ARGS_((CONST char *nativePath,
55 static int TestchmodCmd _ANSI_ARGS_((ClientData dummy,
56 Tcl_Interp *interp, int argc, CONST84 char **argv));
60 *----------------------------------------------------------------------
62 * TclplatformtestInit --
64 * Defines commands that test platform specific functionality for
68 * A standard Tcl result.
71 * Defines new commands.
73 *----------------------------------------------------------------------
77 TclplatformtestInit(interp)
78 Tcl_Interp *interp; /* Interpreter to add commands to. */
81 * Add commands for platform specific tests for Windows here.
84 Tcl_CreateCommand(interp, "testchmod", TestchmodCmd,
85 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
86 Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd,
87 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
88 Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd,
89 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
90 Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd,
91 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
92 Tcl_CreateObjCommand(interp, "testwincpuid", TestwincpuidCmd,
93 (ClientData) 0, (Tcl_CmdDeleteProc*) NULL );
94 Tcl_CreateObjCommand(interp, "testwinsleep", TestwinsleepCmd,
95 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL );
96 Tcl_CreateObjCommand(interp, "testexcept", TestExceptionCmd,
97 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
102 *----------------------------------------------------------------------
104 * TesteventloopCmd --
106 * This procedure implements the "testeventloop" command. It is
107 * used to test the Tcl notifier from an "external" event loop
108 * (i.e. not Tcl_DoOneEvent()).
111 * A standard Tcl result.
116 *----------------------------------------------------------------------
120 TesteventloopCmd(clientData, interp, argc, argv)
121 ClientData clientData; /* Not used. */
122 Tcl_Interp *interp; /* Current interpreter. */
123 int argc; /* Number of arguments. */
124 CONST84 char **argv; /* Argument strings. */
126 static int *framePtr = NULL; /* Pointer to integer on stack frame of
127 * innermost invocation of the "wait"
131 Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
132 " option ... \"", (char *) NULL);
135 if (strcmp(argv[1], "done") == 0) {
137 } else if (strcmp(argv[1], "wait") == 0) {
141 int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
144 * Save the old stack frame pointer and set up the current frame.
147 oldFramePtr = framePtr;
151 * Enter a standard Windows event loop until the flag changes.
152 * Note that we do not explicitly call Tcl_ServiceEvent().
157 if (!GetMessage(&msg, NULL, 0, 0)) {
159 * The application is exiting, so repost the quit message
160 * and start unwinding.
163 PostQuitMessage((int)msg.wParam);
166 TranslateMessage(&msg);
167 DispatchMessage(&msg);
169 (void) Tcl_SetServiceMode(oldMode);
170 framePtr = oldFramePtr;
172 Tcl_AppendResult(interp, "bad option \"", argv[1],
173 "\": must be done or wait", (char *) NULL);
180 *----------------------------------------------------------------------
184 * This procedure implements the "testvolumetype" command. It is
185 * used to check the volume type (FAT, NTFS) of a volume.
188 * A standard Tcl result.
193 *----------------------------------------------------------------------
197 TestvolumetypeCmd(clientData, interp, objc, objv)
198 ClientData clientData; /* Not used. */
199 Tcl_Interp *interp; /* Current interpreter. */
200 int objc; /* Number of arguments. */
201 Tcl_Obj *CONST objv[]; /* Argument objects. */
203 #define VOL_BUF_SIZE 32
205 char volType[VOL_BUF_SIZE];
209 Tcl_WrongNumArgs(interp, 1, objv, "?name?");
214 * path has to be really a proper volume, but we don't
215 * get query APIs for that until NT5
217 path = Tcl_GetString(objv[1]);
221 found = GetVolumeInformationA(path, NULL, 0, NULL, NULL,
222 NULL, volType, VOL_BUF_SIZE);
225 Tcl_AppendResult(interp, "could not get volume type for \"",
226 (path?path:""), "\"", (char *) NULL);
227 TclWinConvertError(GetLastError());
230 Tcl_SetResult(interp, volType, TCL_VOLATILE);
236 *----------------------------------------------------------------------
240 * Command that returns the seconds and microseconds portions of
241 * the system clock and of the Tcl clock so that they can be
242 * compared to validate that the Tcl clock is staying in sync.
251 * Returns a standard Tcl result comprising a four-element list:
252 * the seconds and microseconds portions of the system clock,
253 * and the seconds and microseconds portions of the Tcl clock.
258 *----------------------------------------------------------------------
262 TestwinclockCmd( ClientData dummy,
265 /* Tcl interpreter */
268 Tcl_Obj *CONST objv[] )
269 /* Argument vector */
271 CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
272 /* The Posix epoch, expressed as a
273 * Windows FILETIME */
274 Tcl_Time tclTime; /* Tcl clock */
275 FILETIME sysTime; /* System clock */
276 Tcl_Obj* result; /* Result of the command */
277 LARGE_INTEGER t1, t2;
278 LARGE_INTEGER p1, p2;
281 Tcl_WrongNumArgs( interp, 1, objv, "" );
285 QueryPerformanceCounter( &p1 );
287 Tcl_GetTime( &tclTime );
288 GetSystemTimeAsFileTime( &sysTime );
289 t1.LowPart = posixEpoch.dwLowDateTime;
290 t1.HighPart = posixEpoch.dwHighDateTime;
291 t2.LowPart = sysTime.dwLowDateTime;
292 t2.HighPart = sysTime.dwHighDateTime;
293 t2.QuadPart -= t1.QuadPart;
295 QueryPerformanceCounter( &p2 );
297 result = Tcl_NewObj();
298 Tcl_ListObjAppendElement
299 ( interp, result, Tcl_NewIntObj( (int) (t2.QuadPart / 10000000 ) ) );
300 Tcl_ListObjAppendElement
302 Tcl_NewIntObj( (int) ( (t2.QuadPart / 10 ) % 1000000 ) ) );
303 Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.sec ) );
304 Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.usec ) );
306 Tcl_ListObjAppendElement( interp, result, Tcl_NewWideIntObj( p1.QuadPart ) );
307 Tcl_ListObjAppendElement( interp, result, Tcl_NewWideIntObj( p2.QuadPart ) );
309 Tcl_SetObjResult( interp, result );
315 *----------------------------------------------------------------------
319 * Retrieves CPU ID information.
325 * eax - The value to pass in the EAX register to a CPUID instruction.
328 * Returns a four-element list containing the values from the
329 * EAX, EBX, ECX and EDX registers returned from the CPUID instruction.
334 *----------------------------------------------------------------------
338 TestwincpuidCmd( ClientData dummy,
339 Tcl_Interp* interp, /* Tcl interpreter */
340 int objc, /* Parameter count */
341 Tcl_Obj *CONST * objv ) /* Parameter vector */
345 unsigned int regs[4];
346 Tcl_Obj * regsObjs[4];
350 Tcl_WrongNumArgs( interp, 1, objv, "eax" );
353 if ( Tcl_GetIntFromObj( interp, objv[1], &index ) != TCL_OK ) {
356 status = TclWinCPUID( (unsigned int) index, regs );
357 if ( status != TCL_OK ) {
358 Tcl_SetObjResult( interp, Tcl_NewStringObj( "operation not available",
362 for ( i = 0; i < 4; ++i ) {
363 regsObjs[i] = Tcl_NewIntObj( (int) regs[i] );
365 Tcl_SetObjResult( interp, Tcl_NewListObj( 4, regsObjs ) );
371 *----------------------------------------------------------------------
375 * Causes this process to wait for the given number of milliseconds
376 * by means of a direct call to Sleep.
382 * n - the number of milliseconds to sleep
388 * Sleeps for the requisite number of milliseconds.
390 *----------------------------------------------------------------------
394 TestwinsleepCmd( ClientData clientData,
397 /* Tcl interpreter */
399 /* Parameter count */
400 Tcl_Obj * CONST * objv )
401 /* Parameter vector */
405 Tcl_WrongNumArgs( interp, 1, objv, "ms" );
408 if ( Tcl_GetIntFromObj( interp, objv[1], &ms ) != TCL_OK ) {
416 *----------------------------------------------------------------------
418 * TestExceptionCmd --
420 * Causes this process to end with the named exception. Used for
421 * testing Tcl_WaitPid().
430 * None, this process closes now and doesn't return.
433 * This Tcl process closes, hard... Bang!
435 *----------------------------------------------------------------------
440 ClientData dummy, /* Unused */
441 Tcl_Interp* interp, /* Tcl interpreter */
442 int objc, /* Argument count */
443 Tcl_Obj *CONST objv[]) /* Argument vector */
445 static char *cmds[] = {
447 "datatype_misalignment",
458 "private_instruction",
460 "illegal_instruction",
469 static DWORD exceptions[] = {
470 EXCEPTION_ACCESS_VIOLATION,
471 EXCEPTION_DATATYPE_MISALIGNMENT,
472 EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
473 EXCEPTION_FLT_DENORMAL_OPERAND,
474 EXCEPTION_FLT_DIVIDE_BY_ZERO,
475 EXCEPTION_FLT_INEXACT_RESULT,
476 EXCEPTION_FLT_INVALID_OPERATION,
477 EXCEPTION_FLT_OVERFLOW,
478 EXCEPTION_FLT_STACK_CHECK,
479 EXCEPTION_FLT_UNDERFLOW,
480 EXCEPTION_INT_DIVIDE_BY_ZERO,
481 EXCEPTION_INT_OVERFLOW,
482 EXCEPTION_PRIV_INSTRUCTION,
483 EXCEPTION_IN_PAGE_ERROR,
484 EXCEPTION_ILLEGAL_INSTRUCTION,
485 EXCEPTION_NONCONTINUABLE_EXCEPTION,
486 EXCEPTION_STACK_OVERFLOW,
487 EXCEPTION_INVALID_DISPOSITION,
488 EXCEPTION_GUARD_PAGE,
489 EXCEPTION_INVALID_HANDLE,
495 Tcl_WrongNumArgs(interp, 0, objv, "<type-of-exception>");
498 if (Tcl_GetIndexFromObj(interp, objv[1], cmds, "command", 0,
504 * Make sure the GPF dialog doesn't popup.
507 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
510 * As Tcl does not handle structured exceptions, this falls all the way
511 * back up the instruction stack to the C run-time portion that called
512 * main() where the process will now be terminated with this exception
513 * code by the default handler the C run-time provides.
517 RaiseException(exceptions[cmd], EXCEPTION_NONCONTINUABLE, 0, NULL);
524 TestplatformChmod(CONST char *nativePath, int pmode)
526 SID_IDENTIFIER_AUTHORITY userSidAuthority =
527 { SECURITY_WORLD_SID_AUTHORITY };
529 typedef DWORD (WINAPI *getSidLengthRequiredDef) ( UCHAR );
530 typedef BOOL (WINAPI *initializeSidDef) ( PSID,
531 PSID_IDENTIFIER_AUTHORITY, BYTE );
532 typedef PDWORD (WINAPI *getSidSubAuthorityDef) ( PSID, DWORD );
534 static getSidLengthRequiredDef getSidLengthRequiredProc;
535 static initializeSidDef initializeSidProc;
536 static getSidSubAuthorityDef getSidSubAuthorityProc;
537 static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION
538 | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
539 static const DWORD readOnlyMask = FILE_DELETE_CHILD | FILE_ADD_FILE
540 | FILE_ADD_SUBDIRECTORY | FILE_WRITE_EA | FILE_APPEND_DATA
541 | FILE_WRITE_DATA | DELETE;
546 const BOOL set_readOnly = !(pmode & 0222);
547 BOOL acl_readOnly_found = FALSE;
549 ACL_SIZE_INFORMATION ACLSize;
550 BOOL curAclPresent, curAclDefaulted;
558 TCHAR *userDomain = NULL;
565 * One time initialization, dynamically load Windows NT features
567 typedef DWORD (WINAPI *setNamedSecurityInfoADef)( IN LPSTR,
568 IN SE_OBJECT_TYPE, IN SECURITY_INFORMATION, IN PSID, IN PSID,
570 typedef BOOL (WINAPI *getAceDef) (PACL, DWORD, LPVOID *);
571 typedef BOOL (WINAPI *addAceDef) ( PACL, DWORD, DWORD, LPVOID, DWORD );
572 typedef BOOL (WINAPI *equalSidDef) ( PSID, PSID );
573 typedef BOOL (WINAPI *addAccessDeniedAceDef) ( PACL, DWORD, DWORD, PSID );
574 typedef BOOL (WINAPI *initializeAclDef) ( PACL, DWORD, DWORD );
575 typedef DWORD (WINAPI *getLengthSidDef) ( PSID );
576 typedef BOOL (WINAPI *getAclInformationDef) (PACL, LPVOID, DWORD,
577 ACL_INFORMATION_CLASS );
578 typedef BOOL (WINAPI *getSecurityDescriptorDaclDef) (PSECURITY_DESCRIPTOR,
579 LPBOOL, PACL *, LPBOOL );
580 typedef BOOL (WINAPI *lookupAccountNameADef) ( LPCSTR, LPCSTR, PSID,
581 PDWORD, LPSTR, LPDWORD, PSID_NAME_USE );
582 typedef BOOL (WINAPI *getFileSecurityADef) ( LPCSTR, SECURITY_INFORMATION,
583 PSECURITY_DESCRIPTOR, DWORD, LPDWORD );
585 static setNamedSecurityInfoADef setNamedSecurityInfoProc;
586 static getAceDef getAceProc;
587 static addAceDef addAceProc;
588 static equalSidDef equalSidProc;
589 static addAccessDeniedAceDef addAccessDeniedAceProc;
590 static initializeAclDef initializeAclProc;
591 static getLengthSidDef getLengthSidProc;
592 static getAclInformationDef getAclInformationProc;
593 static getSecurityDescriptorDaclDef getSecurityDescriptorDaclProc;
594 static lookupAccountNameADef lookupAccountNameProc;
595 static getFileSecurityADef getFileSecurityProc;
597 static int initialized = 0;
599 TCL_DECLARE_MUTEX(initializeMutex)
600 Tcl_MutexLock(&initializeMutex);
602 HINSTANCE hInstance = LoadLibrary("Advapi32");
603 if (hInstance != NULL) {
604 setNamedSecurityInfoProc = (setNamedSecurityInfoADef)
605 GetProcAddress(hInstance, "SetNamedSecurityInfoA");
606 getFileSecurityProc = (getFileSecurityADef)
607 GetProcAddress(hInstance, "GetFileSecurityA");
608 getAceProc = (getAceDef)
609 GetProcAddress(hInstance, "GetAce");
610 addAceProc = (addAceDef)
611 GetProcAddress(hInstance, "AddAce");
612 equalSidProc = (equalSidDef)
613 GetProcAddress(hInstance, "EqualSid");
614 addAccessDeniedAceProc = (addAccessDeniedAceDef)
615 GetProcAddress(hInstance, "AddAccessDeniedAce");
616 initializeAclProc = (initializeAclDef)
617 GetProcAddress(hInstance, "InitializeAcl");
618 getLengthSidProc = (getLengthSidDef)
619 GetProcAddress(hInstance, "GetLengthSid");
620 getAclInformationProc = (getAclInformationDef)
621 GetProcAddress(hInstance, "GetAclInformation");
622 getSecurityDescriptorDaclProc = (getSecurityDescriptorDaclDef)
623 GetProcAddress(hInstance, "GetSecurityDescriptorDacl");
624 lookupAccountNameProc = (lookupAccountNameADef)
625 GetProcAddress(hInstance, "LookupAccountNameA");
626 getSidLengthRequiredProc = (getSidLengthRequiredDef)
627 GetProcAddress(hInstance, "GetSidLengthRequired");
628 initializeSidProc = (initializeSidDef)
629 GetProcAddress(hInstance, "InitializeSid");
630 getSidSubAuthorityProc = (getSidSubAuthorityDef)
631 GetProcAddress(hInstance, "GetSidSubAuthority");
632 if (setNamedSecurityInfoProc && getAceProc
633 && addAceProc && equalSidProc && addAccessDeniedAceProc
634 && initializeAclProc && getLengthSidProc
635 && getAclInformationProc && getSecurityDescriptorDaclProc
636 && lookupAccountNameProc && getFileSecurityProc
637 && getSidLengthRequiredProc && initializeSidProc
638 && getSidSubAuthorityProc)
644 Tcl_MutexUnlock(&initializeMutex);
647 /* Process the chmod request */
648 attr = GetFileAttributes(nativePath);
650 /* nativePath not found */
651 if (attr == 0xffffffff) {
656 /* If no ACL API is present or nativePath is not a directory,
657 * there is no special handling
659 if (initialized < 0 || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
663 /* Set the result to error, if the ACL change is successful it will
669 * Read the security descriptor for the directory. Note the
670 * first call obtains the size of the security descriptor.
672 if (!getFileSecurityProc(nativePath, infoBits, NULL, 0, &secDescLen)) {
673 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
674 DWORD secDescLen2 = 0;
675 secDesc = (BYTE *) ckalloc(secDescLen);
676 if (!getFileSecurityProc(nativePath, infoBits,
677 (PSECURITY_DESCRIPTOR)secDesc,
678 secDescLen, &secDescLen2)
679 || (secDescLen < secDescLen2)) {
687 /* Get the World SID */
688 userSid = (SID*) ckalloc(getSidLengthRequiredProc((UCHAR)1));
689 initializeSidProc( userSid, &userSidAuthority, (BYTE)1);
690 *(getSidSubAuthorityProc( userSid, 0)) = SECURITY_WORLD_RID;
692 /* If curAclPresent == false then curAcl and curAclDefaulted not valid */
693 if (!getSecurityDescriptorDaclProc(secDesc, &curAclPresent,
694 &curAcl, &curAclDefaulted))
697 if (!curAclPresent || !curAcl) {
698 ACLSize.AclBytesInUse = 0;
699 ACLSize.AceCount = 0;
700 } else if (!getAclInformationProc(curAcl, &ACLSize, sizeof(ACLSize),
704 /* Allocate memory for the new ACL */
705 newAclSize = ACLSize.AclBytesInUse + sizeof (ACCESS_DENIED_ACE)
706 + getLengthSidProc(userSid) - sizeof (DWORD);
707 newAcl = (ACL *) ckalloc (newAclSize);
709 /* Initialize the new ACL */
710 if(!initializeAclProc(newAcl, newAclSize, ACL_REVISION)) {
714 /* Add denied to make readonly, this will be known as a "read-only tag" */
715 if (set_readOnly && !addAccessDeniedAceProc(newAcl, ACL_REVISION,
716 readOnlyMask, userSid)) {
720 acl_readOnly_found = FALSE;
721 for (j = 0; j < ACLSize.AceCount; j++) {
724 if (! getAceProc (curAcl, j, (LPVOID*) &pACE2)) {
728 phACE2 = ((ACE_HEADER *) pACE2);
730 /* Do NOT propagate inherited ACEs */
731 if (phACE2->AceFlags & INHERITED_ACE) {
735 /* Skip the "read-only tag" restriction (either added above, or it
738 if (phACE2->AceType == ACCESS_DENIED_ACE_TYPE) {
739 ACCESS_DENIED_ACE *pACEd = (ACCESS_DENIED_ACE *)phACE2;
740 if (pACEd->Mask == readOnlyMask && equalSidProc(userSid,
741 (PSID)&(pACEd->SidStart))) {
742 acl_readOnly_found = TRUE;
747 /* Copy the current ACE from the old to the new ACL */
748 if(! addAceProc (newAcl, ACL_REVISION, MAXDWORD, pACE2,
749 ((PACE_HEADER) pACE2)->AceSize)) {
754 /* Apply the new ACL */
755 if (set_readOnly == acl_readOnly_found
756 || setNamedSecurityInfoProc((LPSTR)nativePath, SE_FILE_OBJECT,
757 DACL_SECURITY_INFORMATION, NULL, NULL, newAcl, NULL)
763 if (secDesc) ckfree(secDesc);
764 if (newAcl) ckfree((char *)newAcl);
765 if (userSid) ckfree((char *)userSid);
766 if (userDomain) ckfree(userDomain);
771 /* Run normal chmod command */
772 return chmod(nativePath, pmode);
776 *---------------------------------------------------------------------------
780 * Implements the "testchmod" cmd. Used when testing "file" command.
781 * The only attribute used by the Windows platform is the user write
782 * flag; if this is not set, the file is made read-only. Otehrwise, the
783 * file is made read-write.
786 * A standard Tcl result.
789 * Changes permissions of specified files.
791 *---------------------------------------------------------------------------
795 TestchmodCmd(dummy, interp, argc, argv)
796 ClientData dummy; /* Not used. */
797 Tcl_Interp *interp; /* Current interpreter. */
798 int argc; /* Number of arguments. */
799 CONST84 char **argv; /* Argument strings. */
806 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
807 " mode file ?file ...?", NULL);
811 mode = (int) strtol(argv[1], &rest, 8);
812 if ((rest == argv[1]) || (*rest != '\0')) {
816 for (i = 2; i < argc; i++) {
818 CONST char *translated;
820 translated = Tcl_TranslateFileName(interp, argv[i], &buffer);
821 if (translated == NULL) {
824 if (TestplatformChmod(translated, mode) != 0) {
825 Tcl_AppendResult(interp, translated, ": ", Tcl_PosixError(interp),
829 Tcl_DStringFree(&buffer);