os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTest.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.
sl@0
     1
/* 
sl@0
     2
 * tclWinTest.c --
sl@0
     3
 *
sl@0
     4
 *	Contains commands for platform specific tests on Windows.
sl@0
     5
 *
sl@0
     6
 * Copyright (c) 1996 Sun Microsystems, Inc.
sl@0
     7
 *
sl@0
     8
 * See the file "license.terms" for information on usage and redistribution
sl@0
     9
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    10
 *
sl@0
    11
 * RCS: @(#) $Id: tclWinTest.c,v 1.8.2.6 2006/03/27 23:30:54 patthoyts Exp $
sl@0
    12
 */
sl@0
    13
sl@0
    14
#define USE_COMPAT_CONST
sl@0
    15
#include "tclWinInt.h"
sl@0
    16
sl@0
    17
/*
sl@0
    18
 * For TestplatformChmod on Windows
sl@0
    19
 */
sl@0
    20
#ifdef __WIN32__
sl@0
    21
#include <aclapi.h>
sl@0
    22
#endif
sl@0
    23
sl@0
    24
/*
sl@0
    25
 * MinGW 3.4.2 does not define this.
sl@0
    26
 */
sl@0
    27
#ifndef INHERITED_ACE
sl@0
    28
#define INHERITED_ACE (0x10)
sl@0
    29
#endif
sl@0
    30
sl@0
    31
/*
sl@0
    32
 * Forward declarations of procedures defined later in this file:
sl@0
    33
 */
sl@0
    34
int		TclplatformtestInit _ANSI_ARGS_((Tcl_Interp *interp));
sl@0
    35
static int	TesteventloopCmd _ANSI_ARGS_((ClientData dummy,
sl@0
    36
	Tcl_Interp *interp, int argc, CONST84 char **argv));
sl@0
    37
static int	TestvolumetypeCmd _ANSI_ARGS_((ClientData dummy,
sl@0
    38
	Tcl_Interp *interp, int objc,
sl@0
    39
	Tcl_Obj *CONST objv[]));
sl@0
    40
static int      TestwinclockCmd _ANSI_ARGS_(( ClientData dummy,
sl@0
    41
					      Tcl_Interp* interp,
sl@0
    42
					      int objc,
sl@0
    43
					      Tcl_Obj *CONST objv[] ));
sl@0
    44
static int      TestwinsleepCmd _ANSI_ARGS_(( ClientData dummy,
sl@0
    45
					      Tcl_Interp* interp,
sl@0
    46
					      int objc,
sl@0
    47
					      Tcl_Obj *CONST objv[] ));
sl@0
    48
static Tcl_ObjCmdProc TestExceptionCmd;
sl@0
    49
static int	TestwincpuidCmd _ANSI_ARGS_(( ClientData dummy,
sl@0
    50
					      Tcl_Interp* interp,
sl@0
    51
					      int objc,
sl@0
    52
					      Tcl_Obj *CONST objv[] ));
sl@0
    53
static int	TestplatformChmod _ANSI_ARGS_((CONST char *nativePath, 
sl@0
    54
						 int pmode));
sl@0
    55
static int	TestchmodCmd _ANSI_ARGS_((ClientData dummy,
sl@0
    56
		  Tcl_Interp *interp, int argc, CONST84 char **argv));
sl@0
    57
sl@0
    58

sl@0
    59
/*
sl@0
    60
 *----------------------------------------------------------------------
sl@0
    61
 *
sl@0
    62
 * TclplatformtestInit --
sl@0
    63
 *
sl@0
    64
 *	Defines commands that test platform specific functionality for
sl@0
    65
 *	Windows platforms.
sl@0
    66
 *
sl@0
    67
 * Results:
sl@0
    68
 *	A standard Tcl result.
sl@0
    69
 *
sl@0
    70
 * Side effects:
sl@0
    71
 *	Defines new commands.
sl@0
    72
 *
sl@0
    73
 *----------------------------------------------------------------------
sl@0
    74
 */
sl@0
    75
sl@0
    76
int
sl@0
    77
TclplatformtestInit(interp)
sl@0
    78
    Tcl_Interp *interp;		/* Interpreter to add commands to. */
sl@0
    79
{
sl@0
    80
    /*
sl@0
    81
     * Add commands for platform specific tests for Windows here.
sl@0
    82
     */
sl@0
    83
sl@0
    84
    Tcl_CreateCommand(interp, "testchmod", TestchmodCmd,
sl@0
    85
		      (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
sl@0
    86
    Tcl_CreateCommand(interp, "testeventloop", TesteventloopCmd,
sl@0
    87
		      (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
sl@0
    88
    Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd,
sl@0
    89
			 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
sl@0
    90
    Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd,
sl@0
    91
			 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
sl@0
    92
    Tcl_CreateObjCommand(interp, "testwincpuid", TestwincpuidCmd,
sl@0
    93
			 (ClientData) 0, (Tcl_CmdDeleteProc*) NULL );
sl@0
    94
    Tcl_CreateObjCommand(interp, "testwinsleep", TestwinsleepCmd,
sl@0
    95
			 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL );
sl@0
    96
    Tcl_CreateObjCommand(interp, "testexcept", TestExceptionCmd,
sl@0
    97
			 (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
sl@0
    98
    return TCL_OK;
sl@0
    99
}
sl@0
   100

sl@0
   101
/*
sl@0
   102
 *----------------------------------------------------------------------
sl@0
   103
 *
sl@0
   104
 * TesteventloopCmd --
sl@0
   105
 *
sl@0
   106
 *	This procedure implements the "testeventloop" command. It is
sl@0
   107
 *	used to test the Tcl notifier from an "external" event loop
sl@0
   108
 *	(i.e. not Tcl_DoOneEvent()).
sl@0
   109
 *
sl@0
   110
 * Results:
sl@0
   111
 *	A standard Tcl result.
sl@0
   112
 *
sl@0
   113
 * Side effects:
sl@0
   114
 *	None.
sl@0
   115
 *
sl@0
   116
 *----------------------------------------------------------------------
sl@0
   117
 */
sl@0
   118
sl@0
   119
static int
sl@0
   120
TesteventloopCmd(clientData, interp, argc, argv)
sl@0
   121
    ClientData clientData;		/* Not used. */
sl@0
   122
    Tcl_Interp *interp;			/* Current interpreter. */
sl@0
   123
    int argc;				/* Number of arguments. */
sl@0
   124
    CONST84 char **argv;		/* Argument strings. */
sl@0
   125
{
sl@0
   126
    static int *framePtr = NULL; /* Pointer to integer on stack frame of
sl@0
   127
				  * innermost invocation of the "wait"
sl@0
   128
				  * subcommand. */
sl@0
   129
sl@0
   130
   if (argc < 2) {
sl@0
   131
	Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0],
sl@0
   132
                " option ... \"", (char *) NULL);
sl@0
   133
        return TCL_ERROR;
sl@0
   134
    }
sl@0
   135
    if (strcmp(argv[1], "done") == 0) {
sl@0
   136
	*framePtr = 1;
sl@0
   137
    } else if (strcmp(argv[1], "wait") == 0) {
sl@0
   138
	int *oldFramePtr;
sl@0
   139
	int done;
sl@0
   140
	MSG msg;
sl@0
   141
	int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
sl@0
   142
sl@0
   143
	/*
sl@0
   144
	 * Save the old stack frame pointer and set up the current frame.
sl@0
   145
	 */
sl@0
   146
sl@0
   147
	oldFramePtr = framePtr;
sl@0
   148
	framePtr = &done;
sl@0
   149
sl@0
   150
	/*
sl@0
   151
	 * Enter a standard Windows event loop until the flag changes.
sl@0
   152
	 * Note that we do not explicitly call Tcl_ServiceEvent().
sl@0
   153
	 */
sl@0
   154
sl@0
   155
	done = 0;
sl@0
   156
	while (!done) {
sl@0
   157
	    if (!GetMessage(&msg, NULL, 0, 0)) {
sl@0
   158
		/*
sl@0
   159
		 * The application is exiting, so repost the quit message
sl@0
   160
		 * and start unwinding.
sl@0
   161
		 */
sl@0
   162
sl@0
   163
		PostQuitMessage((int)msg.wParam);
sl@0
   164
		break;
sl@0
   165
	    }
sl@0
   166
	    TranslateMessage(&msg);
sl@0
   167
	    DispatchMessage(&msg);
sl@0
   168
	}
sl@0
   169
	(void) Tcl_SetServiceMode(oldMode);
sl@0
   170
	framePtr = oldFramePtr;
sl@0
   171
    } else {
sl@0
   172
	Tcl_AppendResult(interp, "bad option \"", argv[1],
sl@0
   173
		"\": must be done or wait", (char *) NULL);
sl@0
   174
	return TCL_ERROR;
sl@0
   175
    }
sl@0
   176
    return TCL_OK;
sl@0
   177
}
sl@0
   178

sl@0
   179
/*
sl@0
   180
 *----------------------------------------------------------------------
sl@0
   181
 *
sl@0
   182
 * Testvolumetype --
sl@0
   183
 *
sl@0
   184
 *	This procedure implements the "testvolumetype" command. It is
sl@0
   185
 *	used to check the volume type (FAT, NTFS) of a volume.
sl@0
   186
 *
sl@0
   187
 * Results:
sl@0
   188
 *	A standard Tcl result.
sl@0
   189
 *
sl@0
   190
 * Side effects:
sl@0
   191
 *	None.
sl@0
   192
 *
sl@0
   193
 *----------------------------------------------------------------------
sl@0
   194
 */
sl@0
   195
sl@0
   196
static int
sl@0
   197
TestvolumetypeCmd(clientData, interp, objc, objv)
sl@0
   198
    ClientData clientData;		/* Not used. */
sl@0
   199
    Tcl_Interp *interp;			/* Current interpreter. */
sl@0
   200
    int objc;				/* Number of arguments. */
sl@0
   201
    Tcl_Obj *CONST objv[];		/* Argument objects. */
sl@0
   202
{
sl@0
   203
#define VOL_BUF_SIZE 32
sl@0
   204
    int found;
sl@0
   205
    char volType[VOL_BUF_SIZE];
sl@0
   206
    char *path;
sl@0
   207
sl@0
   208
    if (objc > 2) {
sl@0
   209
	Tcl_WrongNumArgs(interp, 1, objv, "?name?");
sl@0
   210
        return TCL_ERROR;
sl@0
   211
    }
sl@0
   212
    if (objc == 2) {
sl@0
   213
	/*
sl@0
   214
	 * path has to be really a proper volume, but we don't
sl@0
   215
	 * get query APIs for that until NT5
sl@0
   216
	 */
sl@0
   217
	path = Tcl_GetString(objv[1]);
sl@0
   218
    } else {
sl@0
   219
	path = NULL;
sl@0
   220
    }
sl@0
   221
    found = GetVolumeInformationA(path, NULL, 0, NULL, NULL, 
sl@0
   222
	    NULL, volType, VOL_BUF_SIZE);
sl@0
   223
sl@0
   224
    if (found == 0) {
sl@0
   225
	Tcl_AppendResult(interp, "could not get volume type for \"",
sl@0
   226
		(path?path:""), "\"", (char *) NULL);
sl@0
   227
	TclWinConvertError(GetLastError());
sl@0
   228
	return TCL_ERROR;
sl@0
   229
    }
sl@0
   230
    Tcl_SetResult(interp, volType, TCL_VOLATILE);
sl@0
   231
    return TCL_OK;
sl@0
   232
#undef VOL_BUF_SIZE
sl@0
   233
}
sl@0
   234

sl@0
   235
/*
sl@0
   236
 *----------------------------------------------------------------------
sl@0
   237
 *
sl@0
   238
 * TestwinclockCmd --
sl@0
   239
 *
sl@0
   240
 *	Command that returns the seconds and microseconds portions of
sl@0
   241
 *	the system clock and of the Tcl clock so that they can be
sl@0
   242
 *	compared to validate that the Tcl clock is staying in sync.
sl@0
   243
 *
sl@0
   244
 * Usage:
sl@0
   245
 *	testclock
sl@0
   246
 *
sl@0
   247
 * Parameters:
sl@0
   248
 *	None.
sl@0
   249
 *
sl@0
   250
 * Results:
sl@0
   251
 *	Returns a standard Tcl result comprising a four-element list:
sl@0
   252
 *	the seconds and microseconds portions of the system clock,
sl@0
   253
 *	and the seconds and microseconds portions of the Tcl clock.
sl@0
   254
 *
sl@0
   255
 * Side effects:
sl@0
   256
 *	None.
sl@0
   257
 *
sl@0
   258
 *----------------------------------------------------------------------
sl@0
   259
 */
sl@0
   260
sl@0
   261
static int
sl@0
   262
TestwinclockCmd( ClientData dummy,
sl@0
   263
				/* Unused */
sl@0
   264
		 Tcl_Interp* interp,
sl@0
   265
				/* Tcl interpreter */
sl@0
   266
		 int objc,
sl@0
   267
				/* Argument count */
sl@0
   268
		 Tcl_Obj *CONST objv[] )
sl@0
   269
				/* Argument vector */
sl@0
   270
{
sl@0
   271
    CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
sl@0
   272
				/* The Posix epoch, expressed as a
sl@0
   273
				 * Windows FILETIME */
sl@0
   274
    Tcl_Time tclTime;		/* Tcl clock */
sl@0
   275
    FILETIME sysTime;		/* System clock */
sl@0
   276
    Tcl_Obj* result;		/* Result of the command */
sl@0
   277
    LARGE_INTEGER t1, t2;
sl@0
   278
    LARGE_INTEGER p1, p2;
sl@0
   279
sl@0
   280
    if ( objc != 1 ) {
sl@0
   281
	Tcl_WrongNumArgs( interp, 1, objv, "" );
sl@0
   282
	return TCL_ERROR;
sl@0
   283
    }
sl@0
   284
sl@0
   285
    QueryPerformanceCounter( &p1 );
sl@0
   286
sl@0
   287
    Tcl_GetTime( &tclTime );
sl@0
   288
    GetSystemTimeAsFileTime( &sysTime );
sl@0
   289
    t1.LowPart = posixEpoch.dwLowDateTime;
sl@0
   290
    t1.HighPart = posixEpoch.dwHighDateTime;
sl@0
   291
    t2.LowPart = sysTime.dwLowDateTime;
sl@0
   292
    t2.HighPart = sysTime.dwHighDateTime;
sl@0
   293
    t2.QuadPart -= t1.QuadPart;
sl@0
   294
sl@0
   295
    QueryPerformanceCounter( &p2 );
sl@0
   296
sl@0
   297
    result = Tcl_NewObj();
sl@0
   298
    Tcl_ListObjAppendElement
sl@0
   299
	( interp, result, Tcl_NewIntObj( (int) (t2.QuadPart / 10000000 ) ) );
sl@0
   300
    Tcl_ListObjAppendElement
sl@0
   301
	( interp, result,
sl@0
   302
	  Tcl_NewIntObj( (int) ( (t2.QuadPart / 10 ) % 1000000 ) ) );
sl@0
   303
    Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.sec ) );
sl@0
   304
    Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.usec ) );
sl@0
   305
sl@0
   306
    Tcl_ListObjAppendElement( interp, result, Tcl_NewWideIntObj( p1.QuadPart ) );
sl@0
   307
    Tcl_ListObjAppendElement( interp, result, Tcl_NewWideIntObj( p2.QuadPart ) );
sl@0
   308
sl@0
   309
    Tcl_SetObjResult( interp, result );
sl@0
   310
sl@0
   311
    return TCL_OK;
sl@0
   312
}
sl@0
   313

sl@0
   314
/*
sl@0
   315
 *----------------------------------------------------------------------
sl@0
   316
 *
sl@0
   317
 * TestwincpuidCmd --
sl@0
   318
 *
sl@0
   319
 *	Retrieves CPU ID information.
sl@0
   320
 *
sl@0
   321
 * Usage:
sl@0
   322
 *	testwincpuid <eax>
sl@0
   323
 *
sl@0
   324
 * Parameters:
sl@0
   325
 *	eax - The value to pass in the EAX register to a CPUID instruction.
sl@0
   326
 *
sl@0
   327
 * Results:
sl@0
   328
 *	Returns a four-element list containing the values from the
sl@0
   329
 *	EAX, EBX, ECX and EDX registers returned from the CPUID instruction.
sl@0
   330
 *
sl@0
   331
 * Side effects:
sl@0
   332
 *	None.
sl@0
   333
 *
sl@0
   334
 *----------------------------------------------------------------------
sl@0
   335
 */
sl@0
   336
sl@0
   337
static int
sl@0
   338
TestwincpuidCmd( ClientData dummy,
sl@0
   339
		 Tcl_Interp* interp, /* Tcl interpreter */
sl@0
   340
		 int objc,	/* Parameter count */
sl@0
   341
		 Tcl_Obj *CONST * objv ) /* Parameter vector */
sl@0
   342
{
sl@0
   343
    int status;
sl@0
   344
    int index;
sl@0
   345
    unsigned int regs[4];
sl@0
   346
    Tcl_Obj * regsObjs[4];
sl@0
   347
    int i;
sl@0
   348
sl@0
   349
    if ( objc != 2 ) {
sl@0
   350
	Tcl_WrongNumArgs( interp, 1, objv, "eax" );
sl@0
   351
	return TCL_ERROR;
sl@0
   352
    }
sl@0
   353
    if ( Tcl_GetIntFromObj( interp, objv[1], &index ) != TCL_OK ) {
sl@0
   354
	return TCL_ERROR;
sl@0
   355
    }
sl@0
   356
    status = TclWinCPUID( (unsigned int) index, regs );
sl@0
   357
    if ( status != TCL_OK ) {
sl@0
   358
	Tcl_SetObjResult( interp, Tcl_NewStringObj( "operation not available", 
sl@0
   359
						    -1 ) );
sl@0
   360
	return status;
sl@0
   361
    }
sl@0
   362
    for ( i = 0; i < 4; ++i ) {
sl@0
   363
	regsObjs[i] = Tcl_NewIntObj( (int) regs[i] );
sl@0
   364
    }
sl@0
   365
    Tcl_SetObjResult( interp, Tcl_NewListObj( 4, regsObjs ) );
sl@0
   366
    return TCL_OK;
sl@0
   367
       
sl@0
   368
}
sl@0
   369

sl@0
   370
/*
sl@0
   371
 *----------------------------------------------------------------------
sl@0
   372
 *
sl@0
   373
 * TestwinsleepCmd --
sl@0
   374
 *
sl@0
   375
 *	Causes this process to wait for the given number of milliseconds
sl@0
   376
 *	by means of a direct call to Sleep.
sl@0
   377
 *
sl@0
   378
 * Usage:
sl@0
   379
 *	testwinsleep <n>
sl@0
   380
 *
sl@0
   381
 * Parameters:
sl@0
   382
 *	n - the number of milliseconds to sleep
sl@0
   383
 *
sl@0
   384
 * Results:
sl@0
   385
 *	None.
sl@0
   386
 *
sl@0
   387
 * Side effects:
sl@0
   388
 *	Sleeps for the requisite number of milliseconds.
sl@0
   389
 *
sl@0
   390
 *----------------------------------------------------------------------
sl@0
   391
 */
sl@0
   392
sl@0
   393
static int
sl@0
   394
TestwinsleepCmd( ClientData clientData,
sl@0
   395
				/* Unused */
sl@0
   396
		 Tcl_Interp* interp,
sl@0
   397
				/* Tcl interpreter */
sl@0
   398
		 int objc,
sl@0
   399
				/* Parameter count */
sl@0
   400
		 Tcl_Obj * CONST * objv )
sl@0
   401
				/* Parameter vector */
sl@0
   402
{
sl@0
   403
    int ms;
sl@0
   404
    if ( objc != 2 ) {
sl@0
   405
	Tcl_WrongNumArgs( interp, 1, objv, "ms" );
sl@0
   406
	return TCL_ERROR;
sl@0
   407
    }
sl@0
   408
    if ( Tcl_GetIntFromObj( interp, objv[1], &ms ) != TCL_OK ) {
sl@0
   409
	return TCL_ERROR;
sl@0
   410
    }
sl@0
   411
    Sleep( (DWORD) ms );
sl@0
   412
    return TCL_OK;
sl@0
   413
}
sl@0
   414

sl@0
   415
/*
sl@0
   416
 *----------------------------------------------------------------------
sl@0
   417
 *
sl@0
   418
 * TestExceptionCmd --
sl@0
   419
 *
sl@0
   420
 *	Causes this process to end with the named exception. Used for
sl@0
   421
 *	testing Tcl_WaitPid().
sl@0
   422
 *
sl@0
   423
 * Usage:
sl@0
   424
 *	testexcept <type>
sl@0
   425
 *
sl@0
   426
 * Parameters:
sl@0
   427
 *	Type of exception.
sl@0
   428
 *
sl@0
   429
 * Results:
sl@0
   430
 *	None, this process closes now and doesn't return.
sl@0
   431
 *
sl@0
   432
 * Side effects:
sl@0
   433
 *	This Tcl process closes, hard... Bang!
sl@0
   434
 *
sl@0
   435
 *----------------------------------------------------------------------
sl@0
   436
 */
sl@0
   437
sl@0
   438
static int
sl@0
   439
TestExceptionCmd(
sl@0
   440
    ClientData dummy,			/* Unused */
sl@0
   441
    Tcl_Interp* interp,			/* Tcl interpreter */
sl@0
   442
    int objc,				/* Argument count */
sl@0
   443
    Tcl_Obj *CONST objv[])		/* Argument vector */
sl@0
   444
{
sl@0
   445
    static char *cmds[] = {
sl@0
   446
	    "access_violation",
sl@0
   447
	    "datatype_misalignment",
sl@0
   448
	    "array_bounds",
sl@0
   449
	    "float_denormal",
sl@0
   450
	    "float_divbyzero",
sl@0
   451
	    "float_inexact",
sl@0
   452
	    "float_invalidop",
sl@0
   453
	    "float_overflow",
sl@0
   454
	    "float_stack",
sl@0
   455
	    "float_underflow",
sl@0
   456
	    "int_divbyzero",
sl@0
   457
	    "int_overflow",
sl@0
   458
	    "private_instruction",
sl@0
   459
	    "inpageerror",
sl@0
   460
	    "illegal_instruction",
sl@0
   461
	    "noncontinue",
sl@0
   462
	    "stack_overflow",
sl@0
   463
	    "invalid_disp",
sl@0
   464
	    "guard_page",
sl@0
   465
	    "invalid_handle",
sl@0
   466
	    "ctrl+c",
sl@0
   467
	    NULL
sl@0
   468
    };
sl@0
   469
    static DWORD exceptions[] = {
sl@0
   470
	    EXCEPTION_ACCESS_VIOLATION,
sl@0
   471
	    EXCEPTION_DATATYPE_MISALIGNMENT,
sl@0
   472
	    EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
sl@0
   473
	    EXCEPTION_FLT_DENORMAL_OPERAND,
sl@0
   474
	    EXCEPTION_FLT_DIVIDE_BY_ZERO,
sl@0
   475
	    EXCEPTION_FLT_INEXACT_RESULT,
sl@0
   476
	    EXCEPTION_FLT_INVALID_OPERATION,
sl@0
   477
	    EXCEPTION_FLT_OVERFLOW,
sl@0
   478
	    EXCEPTION_FLT_STACK_CHECK,
sl@0
   479
	    EXCEPTION_FLT_UNDERFLOW,
sl@0
   480
	    EXCEPTION_INT_DIVIDE_BY_ZERO,
sl@0
   481
	    EXCEPTION_INT_OVERFLOW,
sl@0
   482
	    EXCEPTION_PRIV_INSTRUCTION,
sl@0
   483
	    EXCEPTION_IN_PAGE_ERROR,
sl@0
   484
	    EXCEPTION_ILLEGAL_INSTRUCTION,
sl@0
   485
	    EXCEPTION_NONCONTINUABLE_EXCEPTION,
sl@0
   486
	    EXCEPTION_STACK_OVERFLOW,
sl@0
   487
	    EXCEPTION_INVALID_DISPOSITION,
sl@0
   488
	    EXCEPTION_GUARD_PAGE,
sl@0
   489
	    EXCEPTION_INVALID_HANDLE,
sl@0
   490
	    CONTROL_C_EXIT
sl@0
   491
    };
sl@0
   492
    int cmd;
sl@0
   493
sl@0
   494
    if ( objc != 2 ) {
sl@0
   495
	Tcl_WrongNumArgs(interp, 0, objv, "<type-of-exception>");
sl@0
   496
	return TCL_ERROR;
sl@0
   497
    }
sl@0
   498
    if (Tcl_GetIndexFromObj(interp, objv[1], cmds, "command", 0,
sl@0
   499
	    &cmd) != TCL_OK) {
sl@0
   500
	return TCL_ERROR;
sl@0
   501
    }
sl@0
   502
sl@0
   503
    /*
sl@0
   504
     * Make sure the GPF dialog doesn't popup.
sl@0
   505
     */
sl@0
   506
sl@0
   507
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
sl@0
   508
sl@0
   509
    /*
sl@0
   510
     * As Tcl does not handle structured exceptions, this falls all the way
sl@0
   511
     * back up the instruction stack to the C run-time portion that called
sl@0
   512
     * main() where the process will now be terminated with this exception
sl@0
   513
     * code by the default handler the C run-time provides.
sl@0
   514
     */
sl@0
   515
sl@0
   516
    /* SMASH! */
sl@0
   517
    RaiseException(exceptions[cmd], EXCEPTION_NONCONTINUABLE, 0, NULL);
sl@0
   518
sl@0
   519
    /* NOTREACHED */
sl@0
   520
    return TCL_OK;
sl@0
   521
}
sl@0
   522

sl@0
   523
static int 
sl@0
   524
TestplatformChmod(CONST char *nativePath, int pmode)
sl@0
   525
{
sl@0
   526
    SID_IDENTIFIER_AUTHORITY userSidAuthority =
sl@0
   527
    { SECURITY_WORLD_SID_AUTHORITY };
sl@0
   528
sl@0
   529
    typedef DWORD (WINAPI *getSidLengthRequiredDef) ( UCHAR );
sl@0
   530
    typedef BOOL (WINAPI *initializeSidDef) ( PSID,
sl@0
   531
    PSID_IDENTIFIER_AUTHORITY, BYTE );
sl@0
   532
    typedef PDWORD (WINAPI *getSidSubAuthorityDef) ( PSID, DWORD );
sl@0
   533
sl@0
   534
    static getSidLengthRequiredDef getSidLengthRequiredProc;
sl@0
   535
    static initializeSidDef initializeSidProc;
sl@0
   536
    static getSidSubAuthorityDef getSidSubAuthorityProc;
sl@0
   537
    static const SECURITY_INFORMATION infoBits = OWNER_SECURITY_INFORMATION 
sl@0
   538
      | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
sl@0
   539
    static const DWORD readOnlyMask = FILE_DELETE_CHILD | FILE_ADD_FILE 
sl@0
   540
      | FILE_ADD_SUBDIRECTORY | FILE_WRITE_EA |  FILE_APPEND_DATA 
sl@0
   541
      | FILE_WRITE_DATA | DELETE;
sl@0
   542
sl@0
   543
    BYTE *secDesc = 0;
sl@0
   544
    DWORD secDescLen;
sl@0
   545
sl@0
   546
    const BOOL set_readOnly = !(pmode & 0222);
sl@0
   547
    BOOL acl_readOnly_found = FALSE;
sl@0
   548
sl@0
   549
    ACL_SIZE_INFORMATION ACLSize;
sl@0
   550
    BOOL curAclPresent, curAclDefaulted;
sl@0
   551
    PACL curAcl; 
sl@0
   552
    PACL newAcl = 0;
sl@0
   553
    DWORD newAclSize;
sl@0
   554
sl@0
   555
    WORD j;
sl@0
   556
  
sl@0
   557
    SID *userSid = 0;
sl@0
   558
    TCHAR *userDomain = NULL;
sl@0
   559
sl@0
   560
    DWORD attr;
sl@0
   561
sl@0
   562
    int res = 0;
sl@0
   563
sl@0
   564
    /*
sl@0
   565
     * One time initialization, dynamically load Windows NT features
sl@0
   566
     */
sl@0
   567
    typedef DWORD (WINAPI *setNamedSecurityInfoADef)( IN LPSTR,
sl@0
   568
      IN SE_OBJECT_TYPE, IN SECURITY_INFORMATION, IN PSID, IN PSID,
sl@0
   569
      IN PACL, IN PACL );
sl@0
   570
    typedef BOOL (WINAPI *getAceDef) (PACL, DWORD, LPVOID *);
sl@0
   571
    typedef BOOL (WINAPI *addAceDef) ( PACL, DWORD, DWORD, LPVOID, DWORD );
sl@0
   572
    typedef BOOL (WINAPI *equalSidDef) ( PSID, PSID );
sl@0
   573
    typedef BOOL (WINAPI *addAccessDeniedAceDef) ( PACL, DWORD, DWORD, PSID );
sl@0
   574
    typedef BOOL (WINAPI *initializeAclDef) ( PACL, DWORD, DWORD );
sl@0
   575
    typedef DWORD (WINAPI *getLengthSidDef) ( PSID );
sl@0
   576
    typedef BOOL (WINAPI *getAclInformationDef) (PACL, LPVOID, DWORD, 
sl@0
   577
      ACL_INFORMATION_CLASS );
sl@0
   578
    typedef BOOL (WINAPI *getSecurityDescriptorDaclDef) (PSECURITY_DESCRIPTOR,
sl@0
   579
      LPBOOL, PACL *, LPBOOL );
sl@0
   580
    typedef BOOL (WINAPI *lookupAccountNameADef) ( LPCSTR, LPCSTR, PSID, 
sl@0
   581
      PDWORD, LPSTR, LPDWORD, PSID_NAME_USE );
sl@0
   582
    typedef BOOL (WINAPI *getFileSecurityADef) ( LPCSTR, SECURITY_INFORMATION,
sl@0
   583
      PSECURITY_DESCRIPTOR, DWORD, LPDWORD );
sl@0
   584
sl@0
   585
    static setNamedSecurityInfoADef setNamedSecurityInfoProc;
sl@0
   586
    static getAceDef getAceProc;
sl@0
   587
    static addAceDef addAceProc;
sl@0
   588
    static equalSidDef equalSidProc;
sl@0
   589
    static addAccessDeniedAceDef addAccessDeniedAceProc;
sl@0
   590
    static initializeAclDef initializeAclProc;
sl@0
   591
    static getLengthSidDef getLengthSidProc;
sl@0
   592
    static getAclInformationDef getAclInformationProc;
sl@0
   593
    static getSecurityDescriptorDaclDef getSecurityDescriptorDaclProc;
sl@0
   594
    static lookupAccountNameADef lookupAccountNameProc; 
sl@0
   595
    static getFileSecurityADef getFileSecurityProc;
sl@0
   596
sl@0
   597
    static int initialized = 0;
sl@0
   598
    if (!initialized) {
sl@0
   599
	TCL_DECLARE_MUTEX(initializeMutex)
sl@0
   600
	Tcl_MutexLock(&initializeMutex);
sl@0
   601
	if (!initialized) {
sl@0
   602
	    HINSTANCE hInstance = LoadLibrary("Advapi32");
sl@0
   603
	    if (hInstance != NULL) {
sl@0
   604
		setNamedSecurityInfoProc = (setNamedSecurityInfoADef)
sl@0
   605
		  GetProcAddress(hInstance, "SetNamedSecurityInfoA");
sl@0
   606
		getFileSecurityProc = (getFileSecurityADef)
sl@0
   607
		  GetProcAddress(hInstance, "GetFileSecurityA");
sl@0
   608
		getAceProc = (getAceDef)
sl@0
   609
		  GetProcAddress(hInstance, "GetAce");
sl@0
   610
		addAceProc = (addAceDef)
sl@0
   611
		  GetProcAddress(hInstance, "AddAce");
sl@0
   612
		equalSidProc = (equalSidDef)
sl@0
   613
		  GetProcAddress(hInstance, "EqualSid");
sl@0
   614
		addAccessDeniedAceProc = (addAccessDeniedAceDef)
sl@0
   615
		  GetProcAddress(hInstance, "AddAccessDeniedAce");
sl@0
   616
		initializeAclProc = (initializeAclDef)
sl@0
   617
		  GetProcAddress(hInstance, "InitializeAcl");
sl@0
   618
		getLengthSidProc = (getLengthSidDef)
sl@0
   619
		  GetProcAddress(hInstance, "GetLengthSid");
sl@0
   620
		getAclInformationProc = (getAclInformationDef)
sl@0
   621
		  GetProcAddress(hInstance, "GetAclInformation");
sl@0
   622
		getSecurityDescriptorDaclProc = (getSecurityDescriptorDaclDef)
sl@0
   623
		  GetProcAddress(hInstance, "GetSecurityDescriptorDacl");
sl@0
   624
		lookupAccountNameProc = (lookupAccountNameADef)
sl@0
   625
		  GetProcAddress(hInstance, "LookupAccountNameA");
sl@0
   626
		getSidLengthRequiredProc = (getSidLengthRequiredDef)
sl@0
   627
		  GetProcAddress(hInstance, "GetSidLengthRequired");
sl@0
   628
		initializeSidProc = (initializeSidDef)
sl@0
   629
		  GetProcAddress(hInstance, "InitializeSid");
sl@0
   630
		getSidSubAuthorityProc = (getSidSubAuthorityDef)
sl@0
   631
		  GetProcAddress(hInstance, "GetSidSubAuthority");
sl@0
   632
		if (setNamedSecurityInfoProc && getAceProc
sl@0
   633
		  && addAceProc && equalSidProc && addAccessDeniedAceProc
sl@0
   634
		  && initializeAclProc && getLengthSidProc
sl@0
   635
		  && getAclInformationProc && getSecurityDescriptorDaclProc
sl@0
   636
		  && lookupAccountNameProc && getFileSecurityProc
sl@0
   637
		  && getSidLengthRequiredProc && initializeSidProc
sl@0
   638
		  && getSidSubAuthorityProc)
sl@0
   639
		    initialized = 1;
sl@0
   640
	    }
sl@0
   641
	    if (!initialized)
sl@0
   642
		initialized = -1;
sl@0
   643
	}
sl@0
   644
	Tcl_MutexUnlock(&initializeMutex);
sl@0
   645
    }
sl@0
   646
sl@0
   647
    /* Process the chmod request */
sl@0
   648
    attr = GetFileAttributes(nativePath);
sl@0
   649
sl@0
   650
    /* nativePath not found */
sl@0
   651
    if (attr == 0xffffffff) {
sl@0
   652
	res = -1;
sl@0
   653
	goto done;
sl@0
   654
    }
sl@0
   655
sl@0
   656
    /* If no ACL API is present or nativePath is not a directory, 
sl@0
   657
     * there is no special handling 
sl@0
   658
     */
sl@0
   659
    if (initialized < 0 || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
sl@0
   660
	goto done;
sl@0
   661
    }
sl@0
   662
    
sl@0
   663
    /* Set the result to error, if the ACL change is successful it will 
sl@0
   664
     *  be reset to 0 
sl@0
   665
     */
sl@0
   666
    res = -1;
sl@0
   667
sl@0
   668
    /*
sl@0
   669
     * Read the security descriptor for the directory. Note the
sl@0
   670
     * first call obtains the size of the security descriptor.
sl@0
   671
     */
sl@0
   672
    if (!getFileSecurityProc(nativePath, infoBits, NULL, 0, &secDescLen)) {
sl@0
   673
	if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
sl@0
   674
	    DWORD secDescLen2 = 0;
sl@0
   675
	    secDesc = (BYTE *) ckalloc(secDescLen);
sl@0
   676
	    if (!getFileSecurityProc(nativePath, infoBits,
sl@0
   677
				     (PSECURITY_DESCRIPTOR)secDesc, 
sl@0
   678
				     secDescLen, &secDescLen2) 
sl@0
   679
		|| (secDescLen < secDescLen2)) {
sl@0
   680
		goto done;
sl@0
   681
	    }
sl@0
   682
	} else {
sl@0
   683
	    goto done;
sl@0
   684
	}
sl@0
   685
    }
sl@0
   686
sl@0
   687
    /* Get the World SID */
sl@0
   688
    userSid = (SID*) ckalloc(getSidLengthRequiredProc((UCHAR)1));
sl@0
   689
    initializeSidProc( userSid, &userSidAuthority, (BYTE)1);
sl@0
   690
    *(getSidSubAuthorityProc( userSid, 0)) = SECURITY_WORLD_RID;
sl@0
   691
sl@0
   692
    /* If curAclPresent == false then curAcl and curAclDefaulted not valid */
sl@0
   693
    if (!getSecurityDescriptorDaclProc(secDesc, &curAclPresent, 
sl@0
   694
				       &curAcl, &curAclDefaulted))
sl@0
   695
	goto done;
sl@0
   696
sl@0
   697
    if (!curAclPresent || !curAcl) {
sl@0
   698
	ACLSize.AclBytesInUse = 0;
sl@0
   699
	ACLSize.AceCount = 0;
sl@0
   700
    } else if (!getAclInformationProc(curAcl, &ACLSize, sizeof(ACLSize), 
sl@0
   701
      AclSizeInformation))
sl@0
   702
	goto done;
sl@0
   703
sl@0
   704
    /* Allocate memory for the new ACL */
sl@0
   705
    newAclSize = ACLSize.AclBytesInUse + sizeof (ACCESS_DENIED_ACE) 
sl@0
   706
      + getLengthSidProc(userSid) - sizeof (DWORD);
sl@0
   707
    newAcl = (ACL *) ckalloc (newAclSize);
sl@0
   708
  
sl@0
   709
    /* Initialize the new ACL */
sl@0
   710
    if(!initializeAclProc(newAcl, newAclSize, ACL_REVISION)) {
sl@0
   711
	goto done;
sl@0
   712
    }
sl@0
   713
    
sl@0
   714
    /* Add denied to make readonly, this will be known as a "read-only tag" */
sl@0
   715
    if (set_readOnly && !addAccessDeniedAceProc(newAcl, ACL_REVISION, 
sl@0
   716
      readOnlyMask, userSid)) {
sl@0
   717
	goto done;
sl@0
   718
    }
sl@0
   719
      
sl@0
   720
    acl_readOnly_found = FALSE;
sl@0
   721
    for (j = 0; j < ACLSize.AceCount; j++) {
sl@0
   722
	PACL *pACE2;
sl@0
   723
	ACE_HEADER *phACE2;
sl@0
   724
	if (! getAceProc (curAcl, j, (LPVOID*) &pACE2)) {
sl@0
   725
	    goto done;
sl@0
   726
	}
sl@0
   727
	
sl@0
   728
	phACE2 = ((ACE_HEADER *) pACE2);
sl@0
   729
sl@0
   730
	/* Do NOT propagate inherited ACEs */
sl@0
   731
	if (phACE2->AceFlags & INHERITED_ACE) {
sl@0
   732
	    continue;
sl@0
   733
	}
sl@0
   734
	
sl@0
   735
	/* Skip the "read-only tag" restriction (either added above, or it
sl@0
   736
	 * is being removed) 
sl@0
   737
	 */
sl@0
   738
	if (phACE2->AceType == ACCESS_DENIED_ACE_TYPE) {
sl@0
   739
	    ACCESS_DENIED_ACE *pACEd = (ACCESS_DENIED_ACE *)phACE2;
sl@0
   740
	    if (pACEd->Mask == readOnlyMask && equalSidProc(userSid, 
sl@0
   741
	      (PSID)&(pACEd->SidStart))) {
sl@0
   742
		acl_readOnly_found = TRUE;
sl@0
   743
		continue;
sl@0
   744
	    }
sl@0
   745
	}
sl@0
   746
sl@0
   747
	/* Copy the current ACE from the old to the new ACL */
sl@0
   748
	if(! addAceProc (newAcl, ACL_REVISION, MAXDWORD, pACE2, 
sl@0
   749
	  ((PACE_HEADER) pACE2)->AceSize)) {
sl@0
   750
	    goto done;
sl@0
   751
	}
sl@0
   752
    }
sl@0
   753
sl@0
   754
    /* Apply the new ACL */
sl@0
   755
    if (set_readOnly == acl_readOnly_found
sl@0
   756
	|| setNamedSecurityInfoProc((LPSTR)nativePath, SE_FILE_OBJECT, 
sl@0
   757
	     DACL_SECURITY_INFORMATION, NULL, NULL, newAcl, NULL)
sl@0
   758
	   == ERROR_SUCCESS ) {
sl@0
   759
	res = 0;
sl@0
   760
    }
sl@0
   761
sl@0
   762
 done:
sl@0
   763
    if (secDesc) ckfree(secDesc);
sl@0
   764
    if (newAcl) ckfree((char *)newAcl);
sl@0
   765
    if (userSid) ckfree((char *)userSid);
sl@0
   766
    if (userDomain) ckfree(userDomain);
sl@0
   767
sl@0
   768
    if (res != 0)
sl@0
   769
	return res;
sl@0
   770
    
sl@0
   771
    /* Run normal chmod command */
sl@0
   772
    return chmod(nativePath, pmode);
sl@0
   773
}
sl@0
   774

sl@0
   775
/*
sl@0
   776
 *---------------------------------------------------------------------------
sl@0
   777
 *
sl@0
   778
 * TestchmodCmd --
sl@0
   779
 *
sl@0
   780
 *	Implements the "testchmod" cmd.  Used when testing "file" command.
sl@0
   781
 *	The only attribute used by the Windows platform is the user write
sl@0
   782
 *	flag; if this is not set, the file is made read-only.  Otehrwise, the
sl@0
   783
 *	file is made read-write.
sl@0
   784
 *
sl@0
   785
 * Results:
sl@0
   786
 *	A standard Tcl result.
sl@0
   787
 *
sl@0
   788
 * Side effects:
sl@0
   789
 *	Changes permissions of specified files.
sl@0
   790
 *
sl@0
   791
 *---------------------------------------------------------------------------
sl@0
   792
 */
sl@0
   793
sl@0
   794
static int
sl@0
   795
TestchmodCmd(dummy, interp, argc, argv)
sl@0
   796
    ClientData dummy;			/* Not used. */
sl@0
   797
    Tcl_Interp *interp;			/* Current interpreter. */
sl@0
   798
    int argc;				/* Number of arguments. */
sl@0
   799
    CONST84 char **argv;		/* Argument strings. */
sl@0
   800
{
sl@0
   801
    int i, mode;
sl@0
   802
    char *rest;
sl@0
   803
sl@0
   804
    if (argc < 2) {
sl@0
   805
	usage:
sl@0
   806
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   807
		" mode file ?file ...?", NULL);
sl@0
   808
	return TCL_ERROR;
sl@0
   809
    }
sl@0
   810
sl@0
   811
    mode = (int) strtol(argv[1], &rest, 8);
sl@0
   812
    if ((rest == argv[1]) || (*rest != '\0')) {
sl@0
   813
	goto usage;
sl@0
   814
    }
sl@0
   815
sl@0
   816
    for (i = 2; i < argc; i++) {
sl@0
   817
	Tcl_DString buffer;
sl@0
   818
	CONST char *translated;
sl@0
   819
sl@0
   820
	translated = Tcl_TranslateFileName(interp, argv[i], &buffer);
sl@0
   821
	if (translated == NULL) {
sl@0
   822
	    return TCL_ERROR;
sl@0
   823
	}
sl@0
   824
	if (TestplatformChmod(translated, mode) != 0) {
sl@0
   825
	    Tcl_AppendResult(interp, translated, ": ", Tcl_PosixError(interp),
sl@0
   826
		    NULL);
sl@0
   827
	    return TCL_ERROR;
sl@0
   828
	}
sl@0
   829
	Tcl_DStringFree(&buffer);
sl@0
   830
    }
sl@0
   831
    return TCL_OK;
sl@0
   832
}