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 |
}
|