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