Parent Directory
|
Revision Log
|
Patch
revision 67 by dashley, Mon Oct 31 00:57:34 2016 UTC | revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
1 | /* $Header$ */ | /* $Header$ */ |
2 | /* | /* |
3 | * tclWinPipe.c -- | * tclWinPipe.c -- |
4 | * | * |
5 | * This file implements the Windows-specific exec pipeline functions, | * This file implements the Windows-specific exec pipeline functions, |
6 | * the "pipe" channel driver, and the "pid" Tcl command. | * the "pipe" channel driver, and the "pid" Tcl command. |
7 | * | * |
8 | * Copyright (c) 1996-1997 by Sun Microsystems, Inc. | * Copyright (c) 1996-1997 by Sun Microsystems, Inc. |
9 | * | * |
10 | * See the file "license.terms" for information on usage and redistribution | * See the file "license.terms" for information on usage and redistribution |
11 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
12 | * | * |
13 | * RCS: @(#) $Id: tclwinpipe.c,v 1.1.1.1 2001/06/13 04:49:50 dtashley Exp $ | * RCS: @(#) $Id: tclwinpipe.c,v 1.1.1.1 2001/06/13 04:49:50 dtashley Exp $ |
14 | */ | */ |
15 | ||
16 | #include "tclWinInt.h" | #include "tclWinInt.h" |
17 | ||
18 | #include <dos.h> | #include <dos.h> |
19 | #include <fcntl.h> | #include <fcntl.h> |
20 | #include <io.h> | #include <io.h> |
21 | #include <sys/stat.h> | #include <sys/stat.h> |
22 | ||
23 | /* | /* |
24 | * The following variable is used to tell whether this module has been | * The following variable is used to tell whether this module has been |
25 | * initialized. | * initialized. |
26 | */ | */ |
27 | ||
28 | static int initialized = 0; | static int initialized = 0; |
29 | ||
30 | /* | /* |
31 | * The pipeMutex locks around access to the initialized and procList variables, | * The pipeMutex locks around access to the initialized and procList variables, |
32 | * and it is used to protect background threads from being terminated while | * and it is used to protect background threads from being terminated while |
33 | * they are using APIs that hold locks. | * they are using APIs that hold locks. |
34 | */ | */ |
35 | ||
36 | TCL_DECLARE_MUTEX(pipeMutex) | TCL_DECLARE_MUTEX(pipeMutex) |
37 | ||
38 | /* | /* |
39 | * The following defines identify the various types of applications that | * The following defines identify the various types of applications that |
40 | * run under windows. There is special case code for the various types. | * run under windows. There is special case code for the various types. |
41 | */ | */ |
42 | ||
43 | #define APPL_NONE 0 | #define APPL_NONE 0 |
44 | #define APPL_DOS 1 | #define APPL_DOS 1 |
45 | #define APPL_WIN3X 2 | #define APPL_WIN3X 2 |
46 | #define APPL_WIN32 3 | #define APPL_WIN32 3 |
47 | ||
48 | /* | /* |
49 | * The following constants and structures are used to encapsulate the state | * The following constants and structures are used to encapsulate the state |
50 | * of various types of files used in a pipeline. | * of various types of files used in a pipeline. |
51 | * This used to have a 1 && 2 that supported Win32s. | * This used to have a 1 && 2 that supported Win32s. |
52 | */ | */ |
53 | ||
54 | #define WIN_FILE 3 /* Basic Win32 file. */ | #define WIN_FILE 3 /* Basic Win32 file. */ |
55 | ||
56 | /* | /* |
57 | * This structure encapsulates the common state associated with all file | * This structure encapsulates the common state associated with all file |
58 | * types used in a pipeline. | * types used in a pipeline. |
59 | */ | */ |
60 | ||
61 | typedef struct WinFile { | typedef struct WinFile { |
62 | int type; /* One of the file types defined above. */ | int type; /* One of the file types defined above. */ |
63 | HANDLE handle; /* Open file handle. */ | HANDLE handle; /* Open file handle. */ |
64 | } WinFile; | } WinFile; |
65 | ||
66 | /* | /* |
67 | * This list is used to map from pids to process handles. | * This list is used to map from pids to process handles. |
68 | */ | */ |
69 | ||
70 | typedef struct ProcInfo { | typedef struct ProcInfo { |
71 | HANDLE hProcess; | HANDLE hProcess; |
72 | DWORD dwProcessId; | DWORD dwProcessId; |
73 | struct ProcInfo *nextPtr; | struct ProcInfo *nextPtr; |
74 | } ProcInfo; | } ProcInfo; |
75 | ||
76 | static ProcInfo *procList; | static ProcInfo *procList; |
77 | ||
78 | /* | /* |
79 | * Bit masks used in the flags field of the PipeInfo structure below. | * Bit masks used in the flags field of the PipeInfo structure below. |
80 | */ | */ |
81 | ||
82 | #define PIPE_PENDING (1<<0) /* Message is pending in the queue. */ | #define PIPE_PENDING (1<<0) /* Message is pending in the queue. */ |
83 | #define PIPE_ASYNC (1<<1) /* Channel is non-blocking. */ | #define PIPE_ASYNC (1<<1) /* Channel is non-blocking. */ |
84 | ||
85 | /* | /* |
86 | * Bit masks used in the sharedFlags field of the PipeInfo structure below. | * Bit masks used in the sharedFlags field of the PipeInfo structure below. |
87 | */ | */ |
88 | ||
89 | #define PIPE_EOF (1<<2) /* Pipe has reached EOF. */ | #define PIPE_EOF (1<<2) /* Pipe has reached EOF. */ |
90 | #define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */ | #define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */ |
91 | ||
92 | /* | /* |
93 | * This structure describes per-instance data for a pipe based channel. | * This structure describes per-instance data for a pipe based channel. |
94 | */ | */ |
95 | ||
96 | typedef struct PipeInfo { | typedef struct PipeInfo { |
97 | struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */ | struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */ |
98 | Tcl_Channel channel; /* Pointer to channel structure. */ | Tcl_Channel channel; /* Pointer to channel structure. */ |
99 | int validMask; /* OR'ed combination of TCL_READABLE, | int validMask; /* OR'ed combination of TCL_READABLE, |
100 | * TCL_WRITABLE, or TCL_EXCEPTION: indicates | * TCL_WRITABLE, or TCL_EXCEPTION: indicates |
101 | * which operations are valid on the file. */ | * which operations are valid on the file. */ |
102 | int watchMask; /* OR'ed combination of TCL_READABLE, | int watchMask; /* OR'ed combination of TCL_READABLE, |
103 | * TCL_WRITABLE, or TCL_EXCEPTION: indicates | * TCL_WRITABLE, or TCL_EXCEPTION: indicates |
104 | * which events should be reported. */ | * which events should be reported. */ |
105 | int flags; /* State flags, see above for a list. */ | int flags; /* State flags, see above for a list. */ |
106 | TclFile readFile; /* Output from pipe. */ | TclFile readFile; /* Output from pipe. */ |
107 | TclFile writeFile; /* Input from pipe. */ | TclFile writeFile; /* Input from pipe. */ |
108 | TclFile errorFile; /* Error output from pipe. */ | TclFile errorFile; /* Error output from pipe. */ |
109 | int numPids; /* Number of processes attached to pipe. */ | int numPids; /* Number of processes attached to pipe. */ |
110 | Tcl_Pid *pidPtr; /* Pids of attached processes. */ | Tcl_Pid *pidPtr; /* Pids of attached processes. */ |
111 | Tcl_ThreadId threadId; /* Thread to which events should be reported. | Tcl_ThreadId threadId; /* Thread to which events should be reported. |
112 | * This value is used by the reader/writer | * This value is used by the reader/writer |
113 | * threads. */ | * threads. */ |
114 | HANDLE writeThread; /* Handle to writer thread. */ | HANDLE writeThread; /* Handle to writer thread. */ |
115 | HANDLE readThread; /* Handle to reader thread. */ | HANDLE readThread; /* Handle to reader thread. */ |
116 | HANDLE writable; /* Manual-reset event to signal when the | HANDLE writable; /* Manual-reset event to signal when the |
117 | * writer thread has finished waiting for | * writer thread has finished waiting for |
118 | * the current buffer to be written. */ | * the current buffer to be written. */ |
119 | HANDLE readable; /* Manual-reset event to signal when the | HANDLE readable; /* Manual-reset event to signal when the |
120 | * reader thread has finished waiting for | * reader thread has finished waiting for |
121 | * input. */ | * input. */ |
122 | HANDLE startWriter; /* Auto-reset event used by the main thread to | HANDLE startWriter; /* Auto-reset event used by the main thread to |
123 | * signal when the writer thread should attempt | * signal when the writer thread should attempt |
124 | * to write to the pipe. */ | * to write to the pipe. */ |
125 | HANDLE startReader; /* Auto-reset event used by the main thread to | HANDLE startReader; /* Auto-reset event used by the main thread to |
126 | * signal when the reader thread should attempt | * signal when the reader thread should attempt |
127 | * to read from the pipe. */ | * to read from the pipe. */ |
128 | DWORD writeError; /* An error caused by the last background | DWORD writeError; /* An error caused by the last background |
129 | * write. Set to 0 if no error has been | * write. Set to 0 if no error has been |
130 | * detected. This word is shared with the | * detected. This word is shared with the |
131 | * writer thread so access must be | * writer thread so access must be |
132 | * synchronized with the writable object. | * synchronized with the writable object. |
133 | */ | */ |
134 | char *writeBuf; /* Current background output buffer. | char *writeBuf; /* Current background output buffer. |
135 | * Access is synchronized with the writable | * Access is synchronized with the writable |
136 | * object. */ | * object. */ |
137 | int writeBufLen; /* Size of write buffer. Access is | int writeBufLen; /* Size of write buffer. Access is |
138 | * synchronized with the writable | * synchronized with the writable |
139 | * object. */ | * object. */ |
140 | int toWrite; /* Current amount to be written. Access is | int toWrite; /* Current amount to be written. Access is |
141 | * synchronized with the writable object. */ | * synchronized with the writable object. */ |
142 | int readFlags; /* Flags that are shared with the reader | int readFlags; /* Flags that are shared with the reader |
143 | * thread. Access is synchronized with the | * thread. Access is synchronized with the |
144 | * readable object. */ | * readable object. */ |
145 | char extraByte; /* Buffer for extra character consumed by | char extraByte; /* Buffer for extra character consumed by |
146 | * reader thread. This byte is shared with | * reader thread. This byte is shared with |
147 | * the reader thread so access must be | * the reader thread so access must be |
148 | * synchronized with the readable object. */ | * synchronized with the readable object. */ |
149 | } PipeInfo; | } PipeInfo; |
150 | ||
151 | typedef struct ThreadSpecificData { | typedef struct ThreadSpecificData { |
152 | /* | /* |
153 | * The following pointer refers to the head of the list of pipes | * The following pointer refers to the head of the list of pipes |
154 | * that are being watched for file events. | * that are being watched for file events. |
155 | */ | */ |
156 | ||
157 | PipeInfo *firstPipePtr; | PipeInfo *firstPipePtr; |
158 | } ThreadSpecificData; | } ThreadSpecificData; |
159 | ||
160 | static Tcl_ThreadDataKey dataKey; | static Tcl_ThreadDataKey dataKey; |
161 | ||
162 | /* | /* |
163 | * The following structure is what is added to the Tcl event queue when | * The following structure is what is added to the Tcl event queue when |
164 | * pipe events are generated. | * pipe events are generated. |
165 | */ | */ |
166 | ||
167 | typedef struct PipeEvent { | typedef struct PipeEvent { |
168 | Tcl_Event header; /* Information that is standard for | Tcl_Event header; /* Information that is standard for |
169 | * all events. */ | * all events. */ |
170 | PipeInfo *infoPtr; /* Pointer to pipe info structure. Note | PipeInfo *infoPtr; /* Pointer to pipe info structure. Note |
171 | * that we still have to verify that the | * that we still have to verify that the |
172 | * pipe exists before dereferencing this | * pipe exists before dereferencing this |
173 | * pointer. */ | * pointer. */ |
174 | } PipeEvent; | } PipeEvent; |
175 | ||
176 | /* | /* |
177 | * Declarations for functions used only in this file. | * Declarations for functions used only in this file. |
178 | */ | */ |
179 | ||
180 | static int ApplicationType(Tcl_Interp *interp, | static int ApplicationType(Tcl_Interp *interp, |
181 | const char *fileName, char *fullName); | const char *fileName, char *fullName); |
182 | static void BuildCommandLine(const char *executable, int argc, | static void BuildCommandLine(const char *executable, int argc, |
183 | char **argv, Tcl_DString *linePtr); | char **argv, Tcl_DString *linePtr); |
184 | static BOOL HasConsole(void); | static BOOL HasConsole(void); |
185 | static int PipeBlockModeProc(ClientData instanceData, int mode); | static int PipeBlockModeProc(ClientData instanceData, int mode); |
186 | static void PipeCheckProc(ClientData clientData, int flags); | static void PipeCheckProc(ClientData clientData, int flags); |
187 | static int PipeClose2Proc(ClientData instanceData, | static int PipeClose2Proc(ClientData instanceData, |
188 | Tcl_Interp *interp, int flags); | Tcl_Interp *interp, int flags); |
189 | static int PipeEventProc(Tcl_Event *evPtr, int flags); | static int PipeEventProc(Tcl_Event *evPtr, int flags); |
190 | static void PipeExitHandler(ClientData clientData); | static void PipeExitHandler(ClientData clientData); |
191 | static int PipeGetHandleProc(ClientData instanceData, | static int PipeGetHandleProc(ClientData instanceData, |
192 | int direction, ClientData *handlePtr); | int direction, ClientData *handlePtr); |
193 | static void PipeInit(void); | static void PipeInit(void); |
194 | static int PipeInputProc(ClientData instanceData, char *buf, | static int PipeInputProc(ClientData instanceData, char *buf, |
195 | int toRead, int *errorCode); | int toRead, int *errorCode); |
196 | static int PipeOutputProc(ClientData instanceData, char *buf, | static int PipeOutputProc(ClientData instanceData, char *buf, |
197 | int toWrite, int *errorCode); | int toWrite, int *errorCode); |
198 | static DWORD WINAPI PipeReaderThread(LPVOID arg); | static DWORD WINAPI PipeReaderThread(LPVOID arg); |
199 | static void PipeSetupProc(ClientData clientData, int flags); | static void PipeSetupProc(ClientData clientData, int flags); |
200 | static void PipeWatchProc(ClientData instanceData, int mask); | static void PipeWatchProc(ClientData instanceData, int mask); |
201 | static DWORD WINAPI PipeWriterThread(LPVOID arg); | static DWORD WINAPI PipeWriterThread(LPVOID arg); |
202 | static void ProcExitHandler(ClientData clientData); | static void ProcExitHandler(ClientData clientData); |
203 | static int TempFileName(WCHAR name[MAX_PATH]); | static int TempFileName(WCHAR name[MAX_PATH]); |
204 | static int WaitForRead(PipeInfo *infoPtr, int blocking); | static int WaitForRead(PipeInfo *infoPtr, int blocking); |
205 | ||
206 | /* | /* |
207 | * This structure describes the channel type structure for command pipe | * This structure describes the channel type structure for command pipe |
208 | * based IO. | * based IO. |
209 | */ | */ |
210 | ||
211 | static Tcl_ChannelType pipeChannelType = { | static Tcl_ChannelType pipeChannelType = { |
212 | "pipe", /* Type name. */ | "pipe", /* Type name. */ |
213 | PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ | PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ |
214 | TCL_CLOSE2PROC, /* Close proc. */ | TCL_CLOSE2PROC, /* Close proc. */ |
215 | PipeInputProc, /* Input proc. */ | PipeInputProc, /* Input proc. */ |
216 | PipeOutputProc, /* Output proc. */ | PipeOutputProc, /* Output proc. */ |
217 | NULL, /* Seek proc. */ | NULL, /* Seek proc. */ |
218 | NULL, /* Set option proc. */ | NULL, /* Set option proc. */ |
219 | NULL, /* Get option proc. */ | NULL, /* Get option proc. */ |
220 | PipeWatchProc, /* Set up notifier to watch the channel. */ | PipeWatchProc, /* Set up notifier to watch the channel. */ |
221 | PipeGetHandleProc, /* Get an OS handle from channel. */ | PipeGetHandleProc, /* Get an OS handle from channel. */ |
222 | PipeClose2Proc | PipeClose2Proc |
223 | }; | }; |
224 | ||
225 | /* | /* |
226 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
227 | * | * |
228 | * PipeInit -- | * PipeInit -- |
229 | * | * |
230 | * This function initializes the static variables for this file. | * This function initializes the static variables for this file. |
231 | * | * |
232 | * Results: | * Results: |
233 | * None. | * None. |
234 | * | * |
235 | * Side effects: | * Side effects: |
236 | * Creates a new event source. | * Creates a new event source. |
237 | * | * |
238 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
239 | */ | */ |
240 | ||
241 | static void | static void |
242 | PipeInit() | PipeInit() |
243 | { | { |
244 | ThreadSpecificData *tsdPtr; | ThreadSpecificData *tsdPtr; |
245 | ||
246 | /* | /* |
247 | * Check the initialized flag first, then check again in the mutex. | * Check the initialized flag first, then check again in the mutex. |
248 | * This is a speed enhancement. | * This is a speed enhancement. |
249 | */ | */ |
250 | ||
251 | if (!initialized) { | if (!initialized) { |
252 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
253 | if (!initialized) { | if (!initialized) { |
254 | initialized = 1; | initialized = 1; |
255 | procList = NULL; | procList = NULL; |
256 | Tcl_CreateExitHandler(ProcExitHandler, NULL); | Tcl_CreateExitHandler(ProcExitHandler, NULL); |
257 | } | } |
258 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
259 | } | } |
260 | ||
261 | tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); | tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); |
262 | if (tsdPtr == NULL) { | if (tsdPtr == NULL) { |
263 | tsdPtr = TCL_TSD_INIT(&dataKey); | tsdPtr = TCL_TSD_INIT(&dataKey); |
264 | tsdPtr->firstPipePtr = NULL; | tsdPtr->firstPipePtr = NULL; |
265 | Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL); | Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL); |
266 | Tcl_CreateThreadExitHandler(PipeExitHandler, NULL); | Tcl_CreateThreadExitHandler(PipeExitHandler, NULL); |
267 | } | } |
268 | } | } |
269 | ||
270 | /* | /* |
271 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
272 | * | * |
273 | * PipeExitHandler -- | * PipeExitHandler -- |
274 | * | * |
275 | * This function is called to cleanup the pipe module before | * This function is called to cleanup the pipe module before |
276 | * Tcl is unloaded. | * Tcl is unloaded. |
277 | * | * |
278 | * Results: | * Results: |
279 | * None. | * None. |
280 | * | * |
281 | * Side effects: | * Side effects: |
282 | * Removes the pipe event source. | * Removes the pipe event source. |
283 | * | * |
284 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
285 | */ | */ |
286 | ||
287 | static void | static void |
288 | PipeExitHandler( | PipeExitHandler( |
289 | ClientData clientData) /* Old window proc */ | ClientData clientData) /* Old window proc */ |
290 | { | { |
291 | Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL); | Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL); |
292 | } | } |
293 | ||
294 | /* | /* |
295 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
296 | * | * |
297 | * ProcExitHandler -- | * ProcExitHandler -- |
298 | * | * |
299 | * This function is called to cleanup the process list before | * This function is called to cleanup the process list before |
300 | * Tcl is unloaded. | * Tcl is unloaded. |
301 | * | * |
302 | * Results: | * Results: |
303 | * None. | * None. |
304 | * | * |
305 | * Side effects: | * Side effects: |
306 | * Resets the process list. | * Resets the process list. |
307 | * | * |
308 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
309 | */ | */ |
310 | ||
311 | static void | static void |
312 | ProcExitHandler( | ProcExitHandler( |
313 | ClientData clientData) /* Old window proc */ | ClientData clientData) /* Old window proc */ |
314 | { | { |
315 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
316 | initialized = 0; | initialized = 0; |
317 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
318 | } | } |
319 | ||
320 | /* | /* |
321 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
322 | * | * |
323 | * PipeSetupProc -- | * PipeSetupProc -- |
324 | * | * |
325 | * This procedure is invoked before Tcl_DoOneEvent blocks waiting | * This procedure is invoked before Tcl_DoOneEvent blocks waiting |
326 | * for an event. | * for an event. |
327 | * | * |
328 | * Results: | * Results: |
329 | * None. | * None. |
330 | * | * |
331 | * Side effects: | * Side effects: |
332 | * Adjusts the block time if needed. | * Adjusts the block time if needed. |
333 | * | * |
334 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
335 | */ | */ |
336 | ||
337 | void | void |
338 | PipeSetupProc( | PipeSetupProc( |
339 | ClientData data, /* Not used. */ | ClientData data, /* Not used. */ |
340 | int flags) /* Event flags as passed to Tcl_DoOneEvent. */ | int flags) /* Event flags as passed to Tcl_DoOneEvent. */ |
341 | { | { |
342 | PipeInfo *infoPtr; | PipeInfo *infoPtr; |
343 | Tcl_Time blockTime = { 0, 0 }; | Tcl_Time blockTime = { 0, 0 }; |
344 | int block = 1; | int block = 1; |
345 | WinFile *filePtr; | WinFile *filePtr; |
346 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
347 | ||
348 | if (!(flags & TCL_FILE_EVENTS)) { | if (!(flags & TCL_FILE_EVENTS)) { |
349 | return; | return; |
350 | } | } |
351 | ||
352 | /* | /* |
353 | * Look to see if any events are already pending. If they are, poll. | * Look to see if any events are already pending. If they are, poll. |
354 | */ | */ |
355 | ||
356 | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; |
357 | infoPtr = infoPtr->nextPtr) { | infoPtr = infoPtr->nextPtr) { |
358 | if (infoPtr->watchMask & TCL_WRITABLE) { | if (infoPtr->watchMask & TCL_WRITABLE) { |
359 | filePtr = (WinFile*) infoPtr->writeFile; | filePtr = (WinFile*) infoPtr->writeFile; |
360 | if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { | if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) { |
361 | block = 0; | block = 0; |
362 | } | } |
363 | } | } |
364 | if (infoPtr->watchMask & TCL_READABLE) { | if (infoPtr->watchMask & TCL_READABLE) { |
365 | filePtr = (WinFile*) infoPtr->readFile; | filePtr = (WinFile*) infoPtr->readFile; |
366 | if (WaitForRead(infoPtr, 0) >= 0) { | if (WaitForRead(infoPtr, 0) >= 0) { |
367 | block = 0; | block = 0; |
368 | } | } |
369 | } | } |
370 | } | } |
371 | if (!block) { | if (!block) { |
372 | Tcl_SetMaxBlockTime(&blockTime); | Tcl_SetMaxBlockTime(&blockTime); |
373 | } | } |
374 | } | } |
375 | ||
376 | /* | /* |
377 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
378 | * | * |
379 | * PipeCheckProc -- | * PipeCheckProc -- |
380 | * | * |
381 | * This procedure is called by Tcl_DoOneEvent to check the pipe | * This procedure is called by Tcl_DoOneEvent to check the pipe |
382 | * event source for events. | * event source for events. |
383 | * | * |
384 | * Results: | * Results: |
385 | * None. | * None. |
386 | * | * |
387 | * Side effects: | * Side effects: |
388 | * May queue an event. | * May queue an event. |
389 | * | * |
390 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
391 | */ | */ |
392 | ||
393 | static void | static void |
394 | PipeCheckProc( | PipeCheckProc( |
395 | ClientData data, /* Not used. */ | ClientData data, /* Not used. */ |
396 | int flags) /* Event flags as passed to Tcl_DoOneEvent. */ | int flags) /* Event flags as passed to Tcl_DoOneEvent. */ |
397 | { | { |
398 | PipeInfo *infoPtr; | PipeInfo *infoPtr; |
399 | PipeEvent *evPtr; | PipeEvent *evPtr; |
400 | WinFile *filePtr; | WinFile *filePtr; |
401 | int needEvent; | int needEvent; |
402 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
403 | ||
404 | if (!(flags & TCL_FILE_EVENTS)) { | if (!(flags & TCL_FILE_EVENTS)) { |
405 | return; | return; |
406 | } | } |
407 | ||
408 | /* | /* |
409 | * Queue events for any ready pipes that don't already have events | * Queue events for any ready pipes that don't already have events |
410 | * queued. | * queued. |
411 | */ | */ |
412 | ||
413 | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; |
414 | infoPtr = infoPtr->nextPtr) { | infoPtr = infoPtr->nextPtr) { |
415 | if (infoPtr->flags & PIPE_PENDING) { | if (infoPtr->flags & PIPE_PENDING) { |
416 | continue; | continue; |
417 | } | } |
418 | ||
419 | /* | /* |
420 | * Queue an event if the pipe is signaled for reading or writing. | * Queue an event if the pipe is signaled for reading or writing. |
421 | */ | */ |
422 | ||
423 | needEvent = 0; | needEvent = 0; |
424 | filePtr = (WinFile*) infoPtr->writeFile; | filePtr = (WinFile*) infoPtr->writeFile; |
425 | if ((infoPtr->watchMask & TCL_WRITABLE) && | if ((infoPtr->watchMask & TCL_WRITABLE) && |
426 | (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { | (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { |
427 | needEvent = 1; | needEvent = 1; |
428 | } | } |
429 | ||
430 | filePtr = (WinFile*) infoPtr->readFile; | filePtr = (WinFile*) infoPtr->readFile; |
431 | if ((infoPtr->watchMask & TCL_READABLE) && | if ((infoPtr->watchMask & TCL_READABLE) && |
432 | (WaitForRead(infoPtr, 0) >= 0)) { | (WaitForRead(infoPtr, 0) >= 0)) { |
433 | needEvent = 1; | needEvent = 1; |
434 | } | } |
435 | ||
436 | if (needEvent) { | if (needEvent) { |
437 | infoPtr->flags |= PIPE_PENDING; | infoPtr->flags |= PIPE_PENDING; |
438 | evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); | evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent)); |
439 | evPtr->header.proc = PipeEventProc; | evPtr->header.proc = PipeEventProc; |
440 | evPtr->infoPtr = infoPtr; | evPtr->infoPtr = infoPtr; |
441 | Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); | Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); |
442 | } | } |
443 | } | } |
444 | } | } |
445 | ||
446 | /* | /* |
447 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
448 | * | * |
449 | * TclWinMakeFile -- | * TclWinMakeFile -- |
450 | * | * |
451 | * This function constructs a new TclFile from a given data and | * This function constructs a new TclFile from a given data and |
452 | * type value. | * type value. |
453 | * | * |
454 | * Results: | * Results: |
455 | * Returns a newly allocated WinFile as a TclFile. | * Returns a newly allocated WinFile as a TclFile. |
456 | * | * |
457 | * Side effects: | * Side effects: |
458 | * None. | * None. |
459 | * | * |
460 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
461 | */ | */ |
462 | ||
463 | TclFile | TclFile |
464 | TclWinMakeFile( | TclWinMakeFile( |
465 | HANDLE handle) /* Type-specific data. */ | HANDLE handle) /* Type-specific data. */ |
466 | { | { |
467 | WinFile *filePtr; | WinFile *filePtr; |
468 | ||
469 | filePtr = (WinFile *) ckalloc(sizeof(WinFile)); | filePtr = (WinFile *) ckalloc(sizeof(WinFile)); |
470 | filePtr->type = WIN_FILE; | filePtr->type = WIN_FILE; |
471 | filePtr->handle = handle; | filePtr->handle = handle; |
472 | ||
473 | return (TclFile)filePtr; | return (TclFile)filePtr; |
474 | } | } |
475 | ||
476 | /* | /* |
477 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
478 | * | * |
479 | * TempFileName -- | * TempFileName -- |
480 | * | * |
481 | * Gets a temporary file name and deals with the fact that the | * Gets a temporary file name and deals with the fact that the |
482 | * temporary file path provided by Windows may not actually exist | * temporary file path provided by Windows may not actually exist |
483 | * if the TMP or TEMP environment variables refer to a | * if the TMP or TEMP environment variables refer to a |
484 | * non-existent directory. | * non-existent directory. |
485 | * | * |
486 | * Results: | * Results: |
487 | * 0 if error, non-zero otherwise. If non-zero is returned, the | * 0 if error, non-zero otherwise. If non-zero is returned, the |
488 | * name buffer will be filled with a name that can be used to | * name buffer will be filled with a name that can be used to |
489 | * construct a temporary file. | * construct a temporary file. |
490 | * | * |
491 | * Side effects: | * Side effects: |
492 | * None. | * None. |
493 | * | * |
494 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
495 | */ | */ |
496 | ||
497 | static int | static int |
498 | TempFileName(name) | TempFileName(name) |
499 | WCHAR name[MAX_PATH]; /* Buffer in which name for temporary | WCHAR name[MAX_PATH]; /* Buffer in which name for temporary |
500 | * file gets stored. */ | * file gets stored. */ |
501 | { | { |
502 | TCHAR *prefix; | TCHAR *prefix; |
503 | ||
504 | prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL"; | prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL"; |
505 | if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) { | if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) { |
506 | if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, | if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, |
507 | name) != 0) { | name) != 0) { |
508 | return 1; | return 1; |
509 | } | } |
510 | } | } |
511 | if (tclWinProcs->useWide) { | if (tclWinProcs->useWide) { |
512 | ((WCHAR *) name)[0] = '.'; | ((WCHAR *) name)[0] = '.'; |
513 | ((WCHAR *) name)[1] = '\0'; | ((WCHAR *) name)[1] = '\0'; |
514 | } else { | } else { |
515 | ((char *) name)[0] = '.'; | ((char *) name)[0] = '.'; |
516 | ((char *) name)[1] = '\0'; | ((char *) name)[1] = '\0'; |
517 | } | } |
518 | return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, | return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0, |
519 | name); | name); |
520 | } | } |
521 | ||
522 | /* | /* |
523 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
524 | * | * |
525 | * TclpMakeFile -- | * TclpMakeFile -- |
526 | * | * |
527 | * Make a TclFile from a channel. | * Make a TclFile from a channel. |
528 | * | * |
529 | * Results: | * Results: |
530 | * Returns a new TclFile or NULL on failure. | * Returns a new TclFile or NULL on failure. |
531 | * | * |
532 | * Side effects: | * Side effects: |
533 | * None. | * None. |
534 | * | * |
535 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
536 | */ | */ |
537 | ||
538 | TclFile | TclFile |
539 | TclpMakeFile(channel, direction) | TclpMakeFile(channel, direction) |
540 | Tcl_Channel channel; /* Channel to get file from. */ | Tcl_Channel channel; /* Channel to get file from. */ |
541 | int direction; /* Either TCL_READABLE or TCL_WRITABLE. */ | int direction; /* Either TCL_READABLE or TCL_WRITABLE. */ |
542 | { | { |
543 | HANDLE handle; | HANDLE handle; |
544 | ||
545 | if (Tcl_GetChannelHandle(channel, direction, | if (Tcl_GetChannelHandle(channel, direction, |
546 | (ClientData *) &handle) == TCL_OK) { | (ClientData *) &handle) == TCL_OK) { |
547 | return TclWinMakeFile(handle); | return TclWinMakeFile(handle); |
548 | } else { | } else { |
549 | return (TclFile) NULL; | return (TclFile) NULL; |
550 | } | } |
551 | } | } |
552 | ||
553 | /* | /* |
554 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
555 | * | * |
556 | * TclpOpenFile -- | * TclpOpenFile -- |
557 | * | * |
558 | * This function opens files for use in a pipeline. | * This function opens files for use in a pipeline. |
559 | * | * |
560 | * Results: | * Results: |
561 | * Returns a newly allocated TclFile structure containing the | * Returns a newly allocated TclFile structure containing the |
562 | * file handle. | * file handle. |
563 | * | * |
564 | * Side effects: | * Side effects: |
565 | * None. | * None. |
566 | * | * |
567 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
568 | */ | */ |
569 | ||
570 | TclFile | TclFile |
571 | TclpOpenFile(path, mode) | TclpOpenFile(path, mode) |
572 | CONST char *path; /* The name of the file to open. */ | CONST char *path; /* The name of the file to open. */ |
573 | int mode; /* In what mode to open the file? */ | int mode; /* In what mode to open the file? */ |
574 | { | { |
575 | HANDLE handle; | HANDLE handle; |
576 | DWORD accessMode, createMode, shareMode, flags; | DWORD accessMode, createMode, shareMode, flags; |
577 | Tcl_DString ds; | Tcl_DString ds; |
578 | TCHAR *nativePath; | TCHAR *nativePath; |
579 | ||
580 | /* | /* |
581 | * Map the access bits to the NT access mode. | * Map the access bits to the NT access mode. |
582 | */ | */ |
583 | ||
584 | switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { | switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { |
585 | case O_RDONLY: | case O_RDONLY: |
586 | accessMode = GENERIC_READ; | accessMode = GENERIC_READ; |
587 | break; | break; |
588 | case O_WRONLY: | case O_WRONLY: |
589 | accessMode = GENERIC_WRITE; | accessMode = GENERIC_WRITE; |
590 | break; | break; |
591 | case O_RDWR: | case O_RDWR: |
592 | accessMode = (GENERIC_READ | GENERIC_WRITE); | accessMode = (GENERIC_READ | GENERIC_WRITE); |
593 | break; | break; |
594 | default: | default: |
595 | TclWinConvertError(ERROR_INVALID_FUNCTION); | TclWinConvertError(ERROR_INVALID_FUNCTION); |
596 | return NULL; | return NULL; |
597 | } | } |
598 | ||
599 | /* | /* |
600 | * Map the creation flags to the NT create mode. | * Map the creation flags to the NT create mode. |
601 | */ | */ |
602 | ||
603 | switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) { | switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) { |
604 | case (O_CREAT | O_EXCL): | case (O_CREAT | O_EXCL): |
605 | case (O_CREAT | O_EXCL | O_TRUNC): | case (O_CREAT | O_EXCL | O_TRUNC): |
606 | createMode = CREATE_NEW; | createMode = CREATE_NEW; |
607 | break; | break; |
608 | case (O_CREAT | O_TRUNC): | case (O_CREAT | O_TRUNC): |
609 | createMode = CREATE_ALWAYS; | createMode = CREATE_ALWAYS; |
610 | break; | break; |
611 | case O_CREAT: | case O_CREAT: |
612 | createMode = OPEN_ALWAYS; | createMode = OPEN_ALWAYS; |
613 | break; | break; |
614 | case O_TRUNC: | case O_TRUNC: |
615 | case (O_TRUNC | O_EXCL): | case (O_TRUNC | O_EXCL): |
616 | createMode = TRUNCATE_EXISTING; | createMode = TRUNCATE_EXISTING; |
617 | break; | break; |
618 | default: | default: |
619 | createMode = OPEN_EXISTING; | createMode = OPEN_EXISTING; |
620 | break; | break; |
621 | } | } |
622 | ||
623 | nativePath = Tcl_WinUtfToTChar(path, -1, &ds); | nativePath = Tcl_WinUtfToTChar(path, -1, &ds); |
624 | ||
625 | /* | /* |
626 | * If the file is not being created, use the existing file attributes. | * If the file is not being created, use the existing file attributes. |
627 | */ | */ |
628 | ||
629 | flags = 0; | flags = 0; |
630 | if (!(mode & O_CREAT)) { | if (!(mode & O_CREAT)) { |
631 | flags = (*tclWinProcs->getFileAttributesProc)(nativePath); | flags = (*tclWinProcs->getFileAttributesProc)(nativePath); |
632 | if (flags == 0xFFFFFFFF) { | if (flags == 0xFFFFFFFF) { |
633 | flags = 0; | flags = 0; |
634 | } | } |
635 | } | } |
636 | ||
637 | /* | /* |
638 | * Set up the file sharing mode. We want to allow simultaneous access. | * Set up the file sharing mode. We want to allow simultaneous access. |
639 | */ | */ |
640 | ||
641 | shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; | shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; |
642 | ||
643 | /* | /* |
644 | * Now we get to create the file. | * Now we get to create the file. |
645 | */ | */ |
646 | ||
647 | handle = (*tclWinProcs->createFileProc)(nativePath, accessMode, | handle = (*tclWinProcs->createFileProc)(nativePath, accessMode, |
648 | shareMode, NULL, createMode, flags, NULL); | shareMode, NULL, createMode, flags, NULL); |
649 | Tcl_DStringFree(&ds); | Tcl_DStringFree(&ds); |
650 | ||
651 | if (handle == INVALID_HANDLE_VALUE) { | if (handle == INVALID_HANDLE_VALUE) { |
652 | DWORD err; | DWORD err; |
653 | ||
654 | err = GetLastError(); | err = GetLastError(); |
655 | if ((err & 0xffffL) == ERROR_OPEN_FAILED) { | if ((err & 0xffffL) == ERROR_OPEN_FAILED) { |
656 | err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND; | err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND; |
657 | } | } |
658 | TclWinConvertError(err); | TclWinConvertError(err); |
659 | return NULL; | return NULL; |
660 | } | } |
661 | ||
662 | /* | /* |
663 | * Seek to the end of file if we are writing. | * Seek to the end of file if we are writing. |
664 | */ | */ |
665 | ||
666 | if (mode & O_WRONLY) { | if (mode & O_WRONLY) { |
667 | SetFilePointer(handle, 0, NULL, FILE_END); | SetFilePointer(handle, 0, NULL, FILE_END); |
668 | } | } |
669 | ||
670 | return TclWinMakeFile(handle); | return TclWinMakeFile(handle); |
671 | } | } |
672 | ||
673 | /* | /* |
674 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
675 | * | * |
676 | * TclpCreateTempFile -- | * TclpCreateTempFile -- |
677 | * | * |
678 | * This function opens a unique file with the property that it | * This function opens a unique file with the property that it |
679 | * will be deleted when its file handle is closed. The temporary | * will be deleted when its file handle is closed. The temporary |
680 | * file is created in the system temporary directory. | * file is created in the system temporary directory. |
681 | * | * |
682 | * Results: | * Results: |
683 | * Returns a valid TclFile, or NULL on failure. | * Returns a valid TclFile, or NULL on failure. |
684 | * | * |
685 | * Side effects: | * Side effects: |
686 | * Creates a new temporary file. | * Creates a new temporary file. |
687 | * | * |
688 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
689 | */ | */ |
690 | ||
691 | TclFile | TclFile |
692 | TclpCreateTempFile(contents) | TclpCreateTempFile(contents) |
693 | CONST char *contents; /* String to write into temp file, or NULL. */ | CONST char *contents; /* String to write into temp file, or NULL. */ |
694 | { | { |
695 | WCHAR name[MAX_PATH]; | WCHAR name[MAX_PATH]; |
696 | CONST char *native; | CONST char *native; |
697 | Tcl_DString dstring; | Tcl_DString dstring; |
698 | HANDLE handle; | HANDLE handle; |
699 | ||
700 | if (TempFileName(name) == 0) { | if (TempFileName(name) == 0) { |
701 | return NULL; | return NULL; |
702 | } | } |
703 | ||
704 | handle = (*tclWinProcs->createFileProc)((TCHAR *) name, | handle = (*tclWinProcs->createFileProc)((TCHAR *) name, |
705 | GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, | GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, |
706 | FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); | FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL); |
707 | if (handle == INVALID_HANDLE_VALUE) { | if (handle == INVALID_HANDLE_VALUE) { |
708 | goto error; | goto error; |
709 | } | } |
710 | ||
711 | /* | /* |
712 | * Write the file out, doing line translations on the way. | * Write the file out, doing line translations on the way. |
713 | */ | */ |
714 | ||
715 | if (contents != NULL) { | if (contents != NULL) { |
716 | DWORD result, length; | DWORD result, length; |
717 | CONST char *p; | CONST char *p; |
718 | ||
719 | /* | /* |
720 | * Convert the contents from UTF to native encoding | * Convert the contents from UTF to native encoding |
721 | */ | */ |
722 | native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); | native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); |
723 | ||
724 | for (p = native; *p != '\0'; p++) { | for (p = native; *p != '\0'; p++) { |
725 | if (*p == '\n') { | if (*p == '\n') { |
726 | length = p - native; | length = p - native; |
727 | if (length > 0) { | if (length > 0) { |
728 | if (!WriteFile(handle, native, length, &result, NULL)) { | if (!WriteFile(handle, native, length, &result, NULL)) { |
729 | goto error; | goto error; |
730 | } | } |
731 | } | } |
732 | if (!WriteFile(handle, "\r\n", 2, &result, NULL)) { | if (!WriteFile(handle, "\r\n", 2, &result, NULL)) { |
733 | goto error; | goto error; |
734 | } | } |
735 | native = p+1; | native = p+1; |
736 | } | } |
737 | } | } |
738 | length = p - native; | length = p - native; |
739 | if (length > 0) { | if (length > 0) { |
740 | if (!WriteFile(handle, native, length, &result, NULL)) { | if (!WriteFile(handle, native, length, &result, NULL)) { |
741 | goto error; | goto error; |
742 | } | } |
743 | } | } |
744 | Tcl_DStringFree(&dstring); | Tcl_DStringFree(&dstring); |
745 | if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) { | if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) { |
746 | goto error; | goto error; |
747 | } | } |
748 | } | } |
749 | ||
750 | return TclWinMakeFile(handle); | return TclWinMakeFile(handle); |
751 | ||
752 | error: | error: |
753 | /* Free the native representation of the contents if necessary */ | /* Free the native representation of the contents if necessary */ |
754 | if (contents != NULL) { | if (contents != NULL) { |
755 | Tcl_DStringFree(&dstring); | Tcl_DStringFree(&dstring); |
756 | } | } |
757 | ||
758 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
759 | CloseHandle(handle); | CloseHandle(handle); |
760 | (*tclWinProcs->deleteFileProc)((TCHAR *) name); | (*tclWinProcs->deleteFileProc)((TCHAR *) name); |
761 | return NULL; | return NULL; |
762 | } | } |
763 | ||
764 | /* | /* |
765 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
766 | * | * |
767 | * TclpCreatePipe -- | * TclpCreatePipe -- |
768 | * | * |
769 | * Creates an anonymous pipe. | * Creates an anonymous pipe. |
770 | * | * |
771 | * Results: | * Results: |
772 | * Returns 1 on success, 0 on failure. | * Returns 1 on success, 0 on failure. |
773 | * | * |
774 | * Side effects: | * Side effects: |
775 | * Creates a pipe. | * Creates a pipe. |
776 | * | * |
777 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
778 | */ | */ |
779 | ||
780 | int | int |
781 | TclpCreatePipe( | TclpCreatePipe( |
782 | TclFile *readPipe, /* Location to store file handle for | TclFile *readPipe, /* Location to store file handle for |
783 | * read side of pipe. */ | * read side of pipe. */ |
784 | TclFile *writePipe) /* Location to store file handle for | TclFile *writePipe) /* Location to store file handle for |
785 | * write side of pipe. */ | * write side of pipe. */ |
786 | { | { |
787 | HANDLE readHandle, writeHandle; | HANDLE readHandle, writeHandle; |
788 | ||
789 | if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) { | if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) { |
790 | *readPipe = TclWinMakeFile(readHandle); | *readPipe = TclWinMakeFile(readHandle); |
791 | *writePipe = TclWinMakeFile(writeHandle); | *writePipe = TclWinMakeFile(writeHandle); |
792 | return 1; | return 1; |
793 | } | } |
794 | ||
795 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
796 | return 0; | return 0; |
797 | } | } |
798 | ||
799 | /* | /* |
800 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
801 | * | * |
802 | * TclpCloseFile -- | * TclpCloseFile -- |
803 | * | * |
804 | * Closes a pipeline file handle. These handles are created by | * Closes a pipeline file handle. These handles are created by |
805 | * TclpOpenFile, TclpCreatePipe, or TclpMakeFile. | * TclpOpenFile, TclpCreatePipe, or TclpMakeFile. |
806 | * | * |
807 | * Results: | * Results: |
808 | * 0 on success, -1 on failure. | * 0 on success, -1 on failure. |
809 | * | * |
810 | * Side effects: | * Side effects: |
811 | * The file is closed and deallocated. | * The file is closed and deallocated. |
812 | * | * |
813 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
814 | */ | */ |
815 | ||
816 | int | int |
817 | TclpCloseFile( | TclpCloseFile( |
818 | TclFile file) /* The file to close. */ | TclFile file) /* The file to close. */ |
819 | { | { |
820 | WinFile *filePtr = (WinFile *) file; | WinFile *filePtr = (WinFile *) file; |
821 | ||
822 | switch (filePtr->type) { | switch (filePtr->type) { |
823 | case WIN_FILE: | case WIN_FILE: |
824 | /* | /* |
825 | * Don't close the Win32 handle if the handle is a standard channel | * Don't close the Win32 handle if the handle is a standard channel |
826 | * during the exit process. Otherwise, one thread may kill the | * during the exit process. Otherwise, one thread may kill the |
827 | * stdio of another. | * stdio of another. |
828 | */ | */ |
829 | ||
830 | if (!TclInExit() | if (!TclInExit() |
831 | || ((GetStdHandle(STD_INPUT_HANDLE) != filePtr->handle) | || ((GetStdHandle(STD_INPUT_HANDLE) != filePtr->handle) |
832 | && (GetStdHandle(STD_OUTPUT_HANDLE) != filePtr->handle) | && (GetStdHandle(STD_OUTPUT_HANDLE) != filePtr->handle) |
833 | && (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) { | && (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) { |
834 | if (CloseHandle(filePtr->handle) == FALSE) { | if (CloseHandle(filePtr->handle) == FALSE) { |
835 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
836 | ckfree((char *) filePtr); | ckfree((char *) filePtr); |
837 | return -1; | return -1; |
838 | } | } |
839 | } | } |
840 | break; | break; |
841 | ||
842 | default: | default: |
843 | panic("TclpCloseFile: unexpected file type"); | panic("TclpCloseFile: unexpected file type"); |
844 | } | } |
845 | ||
846 | ckfree((char *) filePtr); | ckfree((char *) filePtr); |
847 | return 0; | return 0; |
848 | } | } |
849 | ||
850 | /* | /* |
851 | *-------------------------------------------------------------------------- | *-------------------------------------------------------------------------- |
852 | * | * |
853 | * TclpGetPid -- | * TclpGetPid -- |
854 | * | * |
855 | * Given a HANDLE to a child process, return the process id for that | * Given a HANDLE to a child process, return the process id for that |
856 | * child process. | * child process. |
857 | * | * |
858 | * Results: | * Results: |
859 | * Returns the process id for the child process. If the pid was not | * Returns the process id for the child process. If the pid was not |
860 | * known by Tcl, either because the pid was not created by Tcl or the | * known by Tcl, either because the pid was not created by Tcl or the |
861 | * child process has already been reaped, -1 is returned. | * child process has already been reaped, -1 is returned. |
862 | * | * |
863 | * Side effects: | * Side effects: |
864 | * None. | * None. |
865 | * | * |
866 | *-------------------------------------------------------------------------- | *-------------------------------------------------------------------------- |
867 | */ | */ |
868 | ||
869 | unsigned long | unsigned long |
870 | TclpGetPid( | TclpGetPid( |
871 | Tcl_Pid pid) /* The HANDLE of the child process. */ | Tcl_Pid pid) /* The HANDLE of the child process. */ |
872 | { | { |
873 | ProcInfo *infoPtr; | ProcInfo *infoPtr; |
874 | ||
875 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
876 | for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { | for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { |
877 | if (infoPtr->hProcess == (HANDLE) pid) { | if (infoPtr->hProcess == (HANDLE) pid) { |
878 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
879 | return infoPtr->dwProcessId; | return infoPtr->dwProcessId; |
880 | } | } |
881 | } | } |
882 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
883 | return (unsigned long) -1; | return (unsigned long) -1; |
884 | } | } |
885 | ||
886 | /* | /* |
887 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
888 | * | * |
889 | * TclpCreateProcess -- | * TclpCreateProcess -- |
890 | * | * |
891 | * Create a child process that has the specified files as its | * Create a child process that has the specified files as its |
892 | * standard input, output, and error. The child process runs | * standard input, output, and error. The child process runs |
893 | * asynchronously under Windows NT and Windows 9x, and runs | * asynchronously under Windows NT and Windows 9x, and runs |
894 | * with the same environment variables as the creating process. | * with the same environment variables as the creating process. |
895 | * | * |
896 | * The complete Windows search path is searched to find the specified | * The complete Windows search path is searched to find the specified |
897 | * executable. If an executable by the given name is not found, | * executable. If an executable by the given name is not found, |
898 | * automatically tries appending ".com", ".exe", and ".bat" to the | * automatically tries appending ".com", ".exe", and ".bat" to the |
899 | * executable name. | * executable name. |
900 | * | * |
901 | * Results: | * Results: |
902 | * The return value is TCL_ERROR and an error message is left in | * The return value is TCL_ERROR and an error message is left in |
903 | * the interp's result if there was a problem creating the child | * the interp's result if there was a problem creating the child |
904 | * process. Otherwise, the return value is TCL_OK and *pidPtr is | * process. Otherwise, the return value is TCL_OK and *pidPtr is |
905 | * filled with the process id of the child process. | * filled with the process id of the child process. |
906 | * | * |
907 | * Side effects: | * Side effects: |
908 | * A process is created. | * A process is created. |
909 | * | * |
910 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
911 | */ | */ |
912 | ||
913 | int | int |
914 | TclpCreateProcess( | TclpCreateProcess( |
915 | Tcl_Interp *interp, /* Interpreter in which to leave errors that | Tcl_Interp *interp, /* Interpreter in which to leave errors that |
916 | * occurred when creating the child process. | * occurred when creating the child process. |
917 | * Error messages from the child process | * Error messages from the child process |
918 | * itself are sent to errorFile. */ | * itself are sent to errorFile. */ |
919 | int argc, /* Number of arguments in following array. */ | int argc, /* Number of arguments in following array. */ |
920 | char **argv, /* Array of argument strings. argv[0] | char **argv, /* Array of argument strings. argv[0] |
921 | * contains the name of the executable | * contains the name of the executable |
922 | * converted to native format (using the | * converted to native format (using the |
923 | * Tcl_TranslateFileName call). Additional | * Tcl_TranslateFileName call). Additional |
924 | * arguments have not been converted. */ | * arguments have not been converted. */ |
925 | TclFile inputFile, /* If non-NULL, gives the file to use as | TclFile inputFile, /* If non-NULL, gives the file to use as |
926 | * input for the child process. If inputFile | * input for the child process. If inputFile |
927 | * file is not readable or is NULL, the child | * file is not readable or is NULL, the child |
928 | * will receive no standard input. */ | * will receive no standard input. */ |
929 | TclFile outputFile, /* If non-NULL, gives the file that | TclFile outputFile, /* If non-NULL, gives the file that |
930 | * receives output from the child process. If | * receives output from the child process. If |
931 | * outputFile file is not writeable or is | * outputFile file is not writeable or is |
932 | * NULL, output from the child will be | * NULL, output from the child will be |
933 | * discarded. */ | * discarded. */ |
934 | TclFile errorFile, /* If non-NULL, gives the file that | TclFile errorFile, /* If non-NULL, gives the file that |
935 | * receives errors from the child process. If | * receives errors from the child process. If |
936 | * errorFile file is not writeable or is NULL, | * errorFile file is not writeable or is NULL, |
937 | * errors from the child will be discarded. | * errors from the child will be discarded. |
938 | * errorFile may be the same as outputFile. */ | * errorFile may be the same as outputFile. */ |
939 | Tcl_Pid *pidPtr) /* If this procedure is successful, pidPtr | Tcl_Pid *pidPtr) /* If this procedure is successful, pidPtr |
940 | * is filled with the process id of the child | * is filled with the process id of the child |
941 | * process. */ | * process. */ |
942 | { | { |
943 | int result, applType, createFlags; | int result, applType, createFlags; |
944 | Tcl_DString cmdLine; /* Complete command line (TCHAR). */ | Tcl_DString cmdLine; /* Complete command line (TCHAR). */ |
945 | STARTUPINFOA startInfo; | STARTUPINFOA startInfo; |
946 | PROCESS_INFORMATION procInfo; | PROCESS_INFORMATION procInfo; |
947 | SECURITY_ATTRIBUTES secAtts; | SECURITY_ATTRIBUTES secAtts; |
948 | HANDLE hProcess, h, inputHandle, outputHandle, errorHandle; | HANDLE hProcess, h, inputHandle, outputHandle, errorHandle; |
949 | char execPath[MAX_PATH * TCL_UTF_MAX]; | char execPath[MAX_PATH * TCL_UTF_MAX]; |
950 | WinFile *filePtr; | WinFile *filePtr; |
951 | ||
952 | PipeInit(); | PipeInit(); |
953 | ||
954 | applType = ApplicationType(interp, argv[0], execPath); | applType = ApplicationType(interp, argv[0], execPath); |
955 | if (applType == APPL_NONE) { | if (applType == APPL_NONE) { |
956 | return TCL_ERROR; | return TCL_ERROR; |
957 | } | } |
958 | ||
959 | result = TCL_ERROR; | result = TCL_ERROR; |
960 | Tcl_DStringInit(&cmdLine); | Tcl_DStringInit(&cmdLine); |
961 | hProcess = GetCurrentProcess(); | hProcess = GetCurrentProcess(); |
962 | ||
963 | /* | /* |
964 | * STARTF_USESTDHANDLES must be used to pass handles to child process. | * STARTF_USESTDHANDLES must be used to pass handles to child process. |
965 | * Using SetStdHandle() and/or dup2() only works when a console mode | * Using SetStdHandle() and/or dup2() only works when a console mode |
966 | * parent process is spawning an attached console mode child process. | * parent process is spawning an attached console mode child process. |
967 | */ | */ |
968 | ||
969 | ZeroMemory(&startInfo, sizeof(startInfo)); | ZeroMemory(&startInfo, sizeof(startInfo)); |
970 | startInfo.cb = sizeof(startInfo); | startInfo.cb = sizeof(startInfo); |
971 | startInfo.dwFlags = STARTF_USESTDHANDLES; | startInfo.dwFlags = STARTF_USESTDHANDLES; |
972 | startInfo.hStdInput = INVALID_HANDLE_VALUE; | startInfo.hStdInput = INVALID_HANDLE_VALUE; |
973 | startInfo.hStdOutput= INVALID_HANDLE_VALUE; | startInfo.hStdOutput= INVALID_HANDLE_VALUE; |
974 | startInfo.hStdError = INVALID_HANDLE_VALUE; | startInfo.hStdError = INVALID_HANDLE_VALUE; |
975 | ||
976 | secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); | secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); |
977 | secAtts.lpSecurityDescriptor = NULL; | secAtts.lpSecurityDescriptor = NULL; |
978 | secAtts.bInheritHandle = TRUE; | secAtts.bInheritHandle = TRUE; |
979 | ||
980 | /* | /* |
981 | * We have to check the type of each file, since we cannot duplicate | * We have to check the type of each file, since we cannot duplicate |
982 | * some file types. | * some file types. |
983 | */ | */ |
984 | ||
985 | inputHandle = INVALID_HANDLE_VALUE; | inputHandle = INVALID_HANDLE_VALUE; |
986 | if (inputFile != NULL) { | if (inputFile != NULL) { |
987 | filePtr = (WinFile *)inputFile; | filePtr = (WinFile *)inputFile; |
988 | if (filePtr->type == WIN_FILE) { | if (filePtr->type == WIN_FILE) { |
989 | inputHandle = filePtr->handle; | inputHandle = filePtr->handle; |
990 | } | } |
991 | } | } |
992 | outputHandle = INVALID_HANDLE_VALUE; | outputHandle = INVALID_HANDLE_VALUE; |
993 | if (outputFile != NULL) { | if (outputFile != NULL) { |
994 | filePtr = (WinFile *)outputFile; | filePtr = (WinFile *)outputFile; |
995 | if (filePtr->type == WIN_FILE) { | if (filePtr->type == WIN_FILE) { |
996 | outputHandle = filePtr->handle; | outputHandle = filePtr->handle; |
997 | } | } |
998 | } | } |
999 | errorHandle = INVALID_HANDLE_VALUE; | errorHandle = INVALID_HANDLE_VALUE; |
1000 | if (errorFile != NULL) { | if (errorFile != NULL) { |
1001 | filePtr = (WinFile *)errorFile; | filePtr = (WinFile *)errorFile; |
1002 | if (filePtr->type == WIN_FILE) { | if (filePtr->type == WIN_FILE) { |
1003 | errorHandle = filePtr->handle; | errorHandle = filePtr->handle; |
1004 | } | } |
1005 | } | } |
1006 | ||
1007 | /* | /* |
1008 | * Duplicate all the handles which will be passed off as stdin, stdout | * Duplicate all the handles which will be passed off as stdin, stdout |
1009 | * and stderr of the child process. The duplicate handles are set to | * and stderr of the child process. The duplicate handles are set to |
1010 | * be inheritable, so the child process can use them. | * be inheritable, so the child process can use them. |
1011 | */ | */ |
1012 | ||
1013 | if (inputHandle == INVALID_HANDLE_VALUE) { | if (inputHandle == INVALID_HANDLE_VALUE) { |
1014 | /* | /* |
1015 | * If handle was not set, stdin should return immediate EOF. | * If handle was not set, stdin should return immediate EOF. |
1016 | * Under Windows95, some applications (both 16 and 32 bit!) | * Under Windows95, some applications (both 16 and 32 bit!) |
1017 | * cannot read from the NUL device; they read from console | * cannot read from the NUL device; they read from console |
1018 | * instead. When running tk, this is fatal because the child | * instead. When running tk, this is fatal because the child |
1019 | * process would hang forever waiting for EOF from the unmapped | * process would hang forever waiting for EOF from the unmapped |
1020 | * console window used by the helper application. | * console window used by the helper application. |
1021 | * | * |
1022 | * Fortunately, the helper application detects a closed pipe | * Fortunately, the helper application detects a closed pipe |
1023 | * as an immediate EOF and can pass that information to the | * as an immediate EOF and can pass that information to the |
1024 | * child process. | * child process. |
1025 | */ | */ |
1026 | ||
1027 | if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) { | if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) { |
1028 | CloseHandle(h); | CloseHandle(h); |
1029 | } | } |
1030 | } else { | } else { |
1031 | DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput, | DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput, |
1032 | 0, TRUE, DUPLICATE_SAME_ACCESS); | 0, TRUE, DUPLICATE_SAME_ACCESS); |
1033 | } | } |
1034 | if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { | if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { |
1035 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
1036 | Tcl_AppendResult(interp, "couldn't duplicate input handle: ", | Tcl_AppendResult(interp, "couldn't duplicate input handle: ", |
1037 | Tcl_PosixError(interp), (char *) NULL); | Tcl_PosixError(interp), (char *) NULL); |
1038 | goto end; | goto end; |
1039 | } | } |
1040 | ||
1041 | if (outputHandle == INVALID_HANDLE_VALUE) { | if (outputHandle == INVALID_HANDLE_VALUE) { |
1042 | /* | /* |
1043 | * If handle was not set, output should be sent to an infinitely | * If handle was not set, output should be sent to an infinitely |
1044 | * deep sink. Under Windows 95, some 16 bit applications cannot | * deep sink. Under Windows 95, some 16 bit applications cannot |
1045 | * have stdout redirected to NUL; they send their output to | * have stdout redirected to NUL; they send their output to |
1046 | * the console instead. Some applications, like "more" or "dir /p", | * the console instead. Some applications, like "more" or "dir /p", |
1047 | * when outputting multiple pages to the console, also then try and | * when outputting multiple pages to the console, also then try and |
1048 | * read from the console to go the next page. When running tk, this | * read from the console to go the next page. When running tk, this |
1049 | * is fatal because the child process would hang forever waiting | * is fatal because the child process would hang forever waiting |
1050 | * for input from the unmapped console window used by the helper | * for input from the unmapped console window used by the helper |
1051 | * application. | * application. |
1052 | * | * |
1053 | * Fortunately, the helper application will detect a closed pipe | * Fortunately, the helper application will detect a closed pipe |
1054 | * as a sink. | * as a sink. |
1055 | */ | */ |
1056 | ||
1057 | if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) | if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS) |
1058 | && (applType == APPL_DOS)) { | && (applType == APPL_DOS)) { |
1059 | if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) { | if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) { |
1060 | CloseHandle(h); | CloseHandle(h); |
1061 | } | } |
1062 | } else { | } else { |
1063 | startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0, | startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0, |
1064 | &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
1065 | } | } |
1066 | } else { | } else { |
1067 | DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput, | DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput, |
1068 | 0, TRUE, DUPLICATE_SAME_ACCESS); | 0, TRUE, DUPLICATE_SAME_ACCESS); |
1069 | } | } |
1070 | if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { | if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { |
1071 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
1072 | Tcl_AppendResult(interp, "couldn't duplicate output handle: ", | Tcl_AppendResult(interp, "couldn't duplicate output handle: ", |
1073 | Tcl_PosixError(interp), (char *) NULL); | Tcl_PosixError(interp), (char *) NULL); |
1074 | goto end; | goto end; |
1075 | } | } |
1076 | ||
1077 | if (errorHandle == INVALID_HANDLE_VALUE) { | if (errorHandle == INVALID_HANDLE_VALUE) { |
1078 | /* | /* |
1079 | * If handle was not set, errors should be sent to an infinitely | * If handle was not set, errors should be sent to an infinitely |
1080 | * deep sink. | * deep sink. |
1081 | */ | */ |
1082 | ||
1083 | startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0, | startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0, |
1084 | &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
1085 | } else { | } else { |
1086 | DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError, | DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError, |
1087 | 0, TRUE, DUPLICATE_SAME_ACCESS); | 0, TRUE, DUPLICATE_SAME_ACCESS); |
1088 | } | } |
1089 | if (startInfo.hStdError == INVALID_HANDLE_VALUE) { | if (startInfo.hStdError == INVALID_HANDLE_VALUE) { |
1090 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
1091 | Tcl_AppendResult(interp, "couldn't duplicate error handle: ", | Tcl_AppendResult(interp, "couldn't duplicate error handle: ", |
1092 | Tcl_PosixError(interp), (char *) NULL); | Tcl_PosixError(interp), (char *) NULL); |
1093 | goto end; | goto end; |
1094 | } | } |
1095 | /* | /* |
1096 | * If we do not have a console window, then we must run DOS and | * If we do not have a console window, then we must run DOS and |
1097 | * WIN32 console mode applications as detached processes. This tells | * WIN32 console mode applications as detached processes. This tells |
1098 | * the loader that the child application should not inherit the | * the loader that the child application should not inherit the |
1099 | * console, and that it should not create a new console window for | * console, and that it should not create a new console window for |
1100 | * the child application. The child application should get its stdio | * the child application. The child application should get its stdio |
1101 | * from the redirection handles provided by this application, and run | * from the redirection handles provided by this application, and run |
1102 | * in the background. | * in the background. |
1103 | * | * |
1104 | * If we are starting a GUI process, they don't automatically get a | * If we are starting a GUI process, they don't automatically get a |
1105 | * console, so it doesn't matter if they are started as foreground or | * console, so it doesn't matter if they are started as foreground or |
1106 | * detached processes. The GUI window will still pop up to the | * detached processes. The GUI window will still pop up to the |
1107 | * foreground. | * foreground. |
1108 | */ | */ |
1109 | ||
1110 | if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) { | if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) { |
1111 | if (HasConsole()) { | if (HasConsole()) { |
1112 | createFlags = 0; | createFlags = 0; |
1113 | } else if (applType == APPL_DOS) { | } else if (applType == APPL_DOS) { |
1114 | /* | /* |
1115 | * Under NT, 16-bit DOS applications will not run unless they | * Under NT, 16-bit DOS applications will not run unless they |
1116 | * can be attached to a console. If we are running without a | * can be attached to a console. If we are running without a |
1117 | * console, run the 16-bit program as an normal process inside | * console, run the 16-bit program as an normal process inside |
1118 | * of a hidden console application, and then run that hidden | * of a hidden console application, and then run that hidden |
1119 | * console as a detached process. | * console as a detached process. |
1120 | */ | */ |
1121 | ||
1122 | startInfo.wShowWindow = SW_HIDE; | startInfo.wShowWindow = SW_HIDE; |
1123 | startInfo.dwFlags |= STARTF_USESHOWWINDOW; | startInfo.dwFlags |= STARTF_USESHOWWINDOW; |
1124 | createFlags = CREATE_NEW_CONSOLE; | createFlags = CREATE_NEW_CONSOLE; |
1125 | Tcl_DStringAppend(&cmdLine, "cmd.exe /c ", -1); | Tcl_DStringAppend(&cmdLine, "cmd.exe /c ", -1); |
1126 | } else { | } else { |
1127 | createFlags = DETACHED_PROCESS; | createFlags = DETACHED_PROCESS; |
1128 | } | } |
1129 | } else { | } else { |
1130 | if (HasConsole()) { | if (HasConsole()) { |
1131 | createFlags = 0; | createFlags = 0; |
1132 | } else { | } else { |
1133 | createFlags = DETACHED_PROCESS; | createFlags = DETACHED_PROCESS; |
1134 | } | } |
1135 | ||
1136 | if (applType == APPL_DOS) { | if (applType == APPL_DOS) { |
1137 | /* | /* |
1138 | * Under Windows 95, 16-bit DOS applications do not work well | * Under Windows 95, 16-bit DOS applications do not work well |
1139 | * with pipes: | * with pipes: |
1140 | * | * |
1141 | * 1. EOF on a pipe between a detached 16-bit DOS application | * 1. EOF on a pipe between a detached 16-bit DOS application |
1142 | * and another application is not seen at the other | * and another application is not seen at the other |
1143 | * end of the pipe, so the listening process blocks forever on | * end of the pipe, so the listening process blocks forever on |
1144 | * reads. This inablity to detect EOF happens when either a | * reads. This inablity to detect EOF happens when either a |
1145 | * 16-bit app or the 32-bit app is the listener. | * 16-bit app or the 32-bit app is the listener. |
1146 | * | * |
1147 | * 2. If a 16-bit DOS application (detached or not) blocks when | * 2. If a 16-bit DOS application (detached or not) blocks when |
1148 | * writing to a pipe, it will never wake up again, and it | * writing to a pipe, it will never wake up again, and it |
1149 | * eventually brings the whole system down around it. | * eventually brings the whole system down around it. |
1150 | * | * |
1151 | * The 16-bit application is run as a normal process inside | * The 16-bit application is run as a normal process inside |
1152 | * of a hidden helper console app, and this helper may be run | * of a hidden helper console app, and this helper may be run |
1153 | * as a detached process. If any of the stdio handles is | * as a detached process. If any of the stdio handles is |
1154 | * a pipe, the helper application accumulates information | * a pipe, the helper application accumulates information |
1155 | * into temp files and forwards it to or from the DOS | * into temp files and forwards it to or from the DOS |
1156 | * application as appropriate. This means that DOS apps | * application as appropriate. This means that DOS apps |
1157 | * must receive EOF from a stdin pipe before they will actually | * must receive EOF from a stdin pipe before they will actually |
1158 | * begin, and must finish generating stdout or stderr before | * begin, and must finish generating stdout or stderr before |
1159 | * the data will be sent to the next stage of the pipe. | * the data will be sent to the next stage of the pipe. |
1160 | * | * |
1161 | * The helper app should be located in the same directory as | * The helper app should be located in the same directory as |
1162 | * the tcl dll. | * the tcl dll. |
1163 | */ | */ |
1164 | ||
1165 | if (createFlags != 0) { | if (createFlags != 0) { |
1166 | startInfo.wShowWindow = SW_HIDE; | startInfo.wShowWindow = SW_HIDE; |
1167 | startInfo.dwFlags |= STARTF_USESHOWWINDOW; | startInfo.dwFlags |= STARTF_USESHOWWINDOW; |
1168 | createFlags = CREATE_NEW_CONSOLE; | createFlags = CREATE_NEW_CONSOLE; |
1169 | } | } |
1170 | Tcl_DStringAppend(&cmdLine, "tclpip" STRINGIFY(TCL_MAJOR_VERSION) | Tcl_DStringAppend(&cmdLine, "tclpip" STRINGIFY(TCL_MAJOR_VERSION) |
1171 | STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1); | STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1); |
1172 | } | } |
1173 | } | } |
1174 | ||
1175 | /* | /* |
1176 | * cmdLine gets the full command line used to invoke the executable, | * cmdLine gets the full command line used to invoke the executable, |
1177 | * including the name of the executable itself. The command line | * including the name of the executable itself. The command line |
1178 | * arguments in argv[] are stored in cmdLine separated by spaces. | * arguments in argv[] are stored in cmdLine separated by spaces. |
1179 | * Special characters in individual arguments from argv[] must be | * Special characters in individual arguments from argv[] must be |
1180 | * quoted when being stored in cmdLine. | * quoted when being stored in cmdLine. |
1181 | * | * |
1182 | * When calling any application, bear in mind that arguments that | * When calling any application, bear in mind that arguments that |
1183 | * specify a path name are not converted. If an argument contains | * specify a path name are not converted. If an argument contains |
1184 | * forward slashes as path separators, it may or may not be | * forward slashes as path separators, it may or may not be |
1185 | * recognized as a path name, depending on the program. In general, | * recognized as a path name, depending on the program. In general, |
1186 | * most applications accept forward slashes only as option | * most applications accept forward slashes only as option |
1187 | * delimiters and backslashes only as paths. | * delimiters and backslashes only as paths. |
1188 | * | * |
1189 | * Additionally, when calling a 16-bit dos or windows application, | * Additionally, when calling a 16-bit dos or windows application, |
1190 | * all path names must use the short, cryptic, path format (e.g., | * all path names must use the short, cryptic, path format (e.g., |
1191 | * using ab~1.def instead of "a b.default"). | * using ab~1.def instead of "a b.default"). |
1192 | */ | */ |
1193 | ||
1194 | BuildCommandLine(execPath, argc, argv, &cmdLine); | BuildCommandLine(execPath, argc, argv, &cmdLine); |
1195 | ||
1196 | if ((*tclWinProcs->createProcessProc)(NULL, | if ((*tclWinProcs->createProcessProc)(NULL, |
1197 | (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE, | (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE, |
1198 | createFlags, NULL, NULL, &startInfo, &procInfo) == 0) { | createFlags, NULL, NULL, &startInfo, &procInfo) == 0) { |
1199 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
1200 | Tcl_AppendResult(interp, "couldn't execute \"", argv[0], | Tcl_AppendResult(interp, "couldn't execute \"", argv[0], |
1201 | "\": ", Tcl_PosixError(interp), (char *) NULL); | "\": ", Tcl_PosixError(interp), (char *) NULL); |
1202 | goto end; | goto end; |
1203 | } | } |
1204 | ||
1205 | /* | /* |
1206 | * This wait is used to force the OS to give some time to the DOS | * This wait is used to force the OS to give some time to the DOS |
1207 | * process. | * process. |
1208 | */ | */ |
1209 | ||
1210 | if (applType == APPL_DOS) { | if (applType == APPL_DOS) { |
1211 | WaitForSingleObject(procInfo.hProcess, 50); | WaitForSingleObject(procInfo.hProcess, 50); |
1212 | } | } |
1213 | ||
1214 | /* | /* |
1215 | * "When an application spawns a process repeatedly, a new thread | * "When an application spawns a process repeatedly, a new thread |
1216 | * instance will be created for each process but the previous | * instance will be created for each process but the previous |
1217 | * instances may not be cleaned up. This results in a significant | * instances may not be cleaned up. This results in a significant |
1218 | * virtual memory loss each time the process is spawned. If there | * virtual memory loss each time the process is spawned. If there |
1219 | * is a WaitForInputIdle() call between CreateProcess() and | * is a WaitForInputIdle() call between CreateProcess() and |
1220 | * CloseHandle(), the problem does not occur." PSS ID Number: Q124121 | * CloseHandle(), the problem does not occur." PSS ID Number: Q124121 |
1221 | */ | */ |
1222 | ||
1223 | WaitForInputIdle(procInfo.hProcess, 5000); | WaitForInputIdle(procInfo.hProcess, 5000); |
1224 | CloseHandle(procInfo.hThread); | CloseHandle(procInfo.hThread); |
1225 | ||
1226 | *pidPtr = (Tcl_Pid) procInfo.hProcess; | *pidPtr = (Tcl_Pid) procInfo.hProcess; |
1227 | if (*pidPtr != 0) { | if (*pidPtr != 0) { |
1228 | TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId); | TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId); |
1229 | } | } |
1230 | result = TCL_OK; | result = TCL_OK; |
1231 | ||
1232 | end: | end: |
1233 | Tcl_DStringFree(&cmdLine); | Tcl_DStringFree(&cmdLine); |
1234 | if (startInfo.hStdInput != INVALID_HANDLE_VALUE) { | if (startInfo.hStdInput != INVALID_HANDLE_VALUE) { |
1235 | CloseHandle(startInfo.hStdInput); | CloseHandle(startInfo.hStdInput); |
1236 | } | } |
1237 | if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) { | if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) { |
1238 | CloseHandle(startInfo.hStdOutput); | CloseHandle(startInfo.hStdOutput); |
1239 | } | } |
1240 | if (startInfo.hStdError != INVALID_HANDLE_VALUE) { | if (startInfo.hStdError != INVALID_HANDLE_VALUE) { |
1241 | CloseHandle(startInfo.hStdError); | CloseHandle(startInfo.hStdError); |
1242 | } | } |
1243 | return result; | return result; |
1244 | } | } |
1245 | ||
1246 | ||
1247 | /* | /* |
1248 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1249 | * | * |
1250 | * HasConsole -- | * HasConsole -- |
1251 | * | * |
1252 | * Determines whether the current application is attached to a | * Determines whether the current application is attached to a |
1253 | * console. | * console. |
1254 | * | * |
1255 | * Results: | * Results: |
1256 | * Returns TRUE if this application has a console, else FALSE. | * Returns TRUE if this application has a console, else FALSE. |
1257 | * | * |
1258 | * Side effects: | * Side effects: |
1259 | * None. | * None. |
1260 | * | * |
1261 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1262 | */ | */ |
1263 | ||
1264 | static BOOL | static BOOL |
1265 | HasConsole() | HasConsole() |
1266 | { | { |
1267 | HANDLE handle; | HANDLE handle; |
1268 | ||
1269 | handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, | handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, |
1270 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
1271 | ||
1272 | if (handle != INVALID_HANDLE_VALUE) { | if (handle != INVALID_HANDLE_VALUE) { |
1273 | CloseHandle(handle); | CloseHandle(handle); |
1274 | return TRUE; | return TRUE; |
1275 | } else { | } else { |
1276 | return FALSE; | return FALSE; |
1277 | } | } |
1278 | } | } |
1279 | ||
1280 | /* | /* |
1281 | *-------------------------------------------------------------------- | *-------------------------------------------------------------------- |
1282 | * | * |
1283 | * ApplicationType -- | * ApplicationType -- |
1284 | * | * |
1285 | * Search for the specified program and identify if it refers to a DOS, | * Search for the specified program and identify if it refers to a DOS, |
1286 | * Windows 3.X, or Win32 program. Used to determine how to invoke | * Windows 3.X, or Win32 program. Used to determine how to invoke |
1287 | * a program, or if it can even be invoked. | * a program, or if it can even be invoked. |
1288 | * | * |
1289 | * It is possible to almost positively identify DOS and Windows | * It is possible to almost positively identify DOS and Windows |
1290 | * applications that contain the appropriate magic numbers. However, | * applications that contain the appropriate magic numbers. However, |
1291 | * DOS .com files do not seem to contain a magic number; if the program | * DOS .com files do not seem to contain a magic number; if the program |
1292 | * name ends with .com and could not be identified as a Windows .com | * name ends with .com and could not be identified as a Windows .com |
1293 | * file, it will be assumed to be a DOS application, even if it was | * file, it will be assumed to be a DOS application, even if it was |
1294 | * just random data. If the program name does not end with .com, no | * just random data. If the program name does not end with .com, no |
1295 | * such assumption is made. | * such assumption is made. |
1296 | * | * |
1297 | * The Win32 procedure GetBinaryType incorrectly identifies any | * The Win32 procedure GetBinaryType incorrectly identifies any |
1298 | * junk file that ends with .exe as a dos executable and some | * junk file that ends with .exe as a dos executable and some |
1299 | * executables that don't end with .exe as not executable. Plus it | * executables that don't end with .exe as not executable. Plus it |
1300 | * doesn't exist under win95, so I won't feel bad about reimplementing | * doesn't exist under win95, so I won't feel bad about reimplementing |
1301 | * functionality. | * functionality. |
1302 | * | * |
1303 | * Results: | * Results: |
1304 | * The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32 | * The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32 |
1305 | * if the filename referred to the corresponding application type. | * if the filename referred to the corresponding application type. |
1306 | * If the file name could not be found or did not refer to any known | * If the file name could not be found or did not refer to any known |
1307 | * application type, APPL_NONE is returned and an error message is | * application type, APPL_NONE is returned and an error message is |
1308 | * left in interp. .bat files are identified as APPL_DOS. | * left in interp. .bat files are identified as APPL_DOS. |
1309 | * | * |
1310 | * Side effects: | * Side effects: |
1311 | * None. | * None. |
1312 | * | * |
1313 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1314 | */ | */ |
1315 | ||
1316 | static int | static int |
1317 | ApplicationType(interp, originalName, fullName) | ApplicationType(interp, originalName, fullName) |
1318 | Tcl_Interp *interp; /* Interp, for error message. */ | Tcl_Interp *interp; /* Interp, for error message. */ |
1319 | const char *originalName; /* Name of the application to find. */ | const char *originalName; /* Name of the application to find. */ |
1320 | char fullName[]; /* Filled with complete path to | char fullName[]; /* Filled with complete path to |
1321 | * application. */ | * application. */ |
1322 | { | { |
1323 | int applType, i, nameLen, found; | int applType, i, nameLen, found; |
1324 | HANDLE hFile; | HANDLE hFile; |
1325 | TCHAR *rest; | TCHAR *rest; |
1326 | char *ext; | char *ext; |
1327 | char buf[2]; | char buf[2]; |
1328 | DWORD attr, read; | DWORD attr, read; |
1329 | IMAGE_DOS_HEADER header; | IMAGE_DOS_HEADER header; |
1330 | Tcl_DString nameBuf, ds; | Tcl_DString nameBuf, ds; |
1331 | TCHAR *nativeName; | TCHAR *nativeName; |
1332 | WCHAR nativeFullPath[MAX_PATH]; | WCHAR nativeFullPath[MAX_PATH]; |
1333 | static char extensions[][5] = {"", ".com", ".exe", ".bat"}; | static char extensions[][5] = {"", ".com", ".exe", ".bat"}; |
1334 | ||
1335 | /* Look for the program as an external program. First try the name | /* Look for the program as an external program. First try the name |
1336 | * as it is, then try adding .com, .exe, and .bat, in that order, to | * as it is, then try adding .com, .exe, and .bat, in that order, to |
1337 | * the name, looking for an executable. | * the name, looking for an executable. |
1338 | * | * |
1339 | * Using the raw SearchPath() procedure doesn't do quite what is | * Using the raw SearchPath() procedure doesn't do quite what is |
1340 | * necessary. If the name of the executable already contains a '.' | * necessary. If the name of the executable already contains a '.' |
1341 | * character, it will not try appending the specified extension when | * character, it will not try appending the specified extension when |
1342 | * searching (in other words, SearchPath will not find the program | * searching (in other words, SearchPath will not find the program |
1343 | * "a.b.exe" if the arguments specified "a.b" and ".exe"). | * "a.b.exe" if the arguments specified "a.b" and ".exe"). |
1344 | * So, first look for the file as it is named. Then manually append | * So, first look for the file as it is named. Then manually append |
1345 | * the extensions, looking for a match. | * the extensions, looking for a match. |
1346 | */ | */ |
1347 | ||
1348 | applType = APPL_NONE; | applType = APPL_NONE; |
1349 | Tcl_DStringInit(&nameBuf); | Tcl_DStringInit(&nameBuf); |
1350 | Tcl_DStringAppend(&nameBuf, originalName, -1); | Tcl_DStringAppend(&nameBuf, originalName, -1); |
1351 | nameLen = Tcl_DStringLength(&nameBuf); | nameLen = Tcl_DStringLength(&nameBuf); |
1352 | ||
1353 | for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { | for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { |
1354 | Tcl_DStringSetLength(&nameBuf, nameLen); | Tcl_DStringSetLength(&nameBuf, nameLen); |
1355 | Tcl_DStringAppend(&nameBuf, extensions[i], -1); | Tcl_DStringAppend(&nameBuf, extensions[i], -1); |
1356 | nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf), | nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf), |
1357 | Tcl_DStringLength(&nameBuf), &ds); | Tcl_DStringLength(&nameBuf), &ds); |
1358 | found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL, | found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL, |
1359 | MAX_PATH, nativeFullPath, &rest); | MAX_PATH, nativeFullPath, &rest); |
1360 | Tcl_DStringFree(&ds); | Tcl_DStringFree(&ds); |
1361 | if (found == 0) { | if (found == 0) { |
1362 | continue; | continue; |
1363 | } | } |
1364 | ||
1365 | /* | /* |
1366 | * Ignore matches on directories or data files, return if identified | * Ignore matches on directories or data files, return if identified |
1367 | * a known type. | * a known type. |
1368 | */ | */ |
1369 | ||
1370 | attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath); | attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath); |
1371 | if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { | if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { |
1372 | continue; | continue; |
1373 | } | } |
1374 | strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); | strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); |
1375 | Tcl_DStringFree(&ds); | Tcl_DStringFree(&ds); |
1376 | ||
1377 | ext = strrchr(fullName, '.'); | ext = strrchr(fullName, '.'); |
1378 | if ((ext != NULL) && (stricmp(ext, ".bat") == 0)) { | if ((ext != NULL) && (stricmp(ext, ".bat") == 0)) { |
1379 | applType = APPL_DOS; | applType = APPL_DOS; |
1380 | break; | break; |
1381 | } | } |
1382 | ||
1383 | hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath, | hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath, |
1384 | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, |
1385 | FILE_ATTRIBUTE_NORMAL, NULL); | FILE_ATTRIBUTE_NORMAL, NULL); |
1386 | if (hFile == INVALID_HANDLE_VALUE) { | if (hFile == INVALID_HANDLE_VALUE) { |
1387 | continue; | continue; |
1388 | } | } |
1389 | ||
1390 | header.e_magic = 0; | header.e_magic = 0; |
1391 | ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); | ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); |
1392 | if (header.e_magic != IMAGE_DOS_SIGNATURE) { | if (header.e_magic != IMAGE_DOS_SIGNATURE) { |
1393 | /* | /* |
1394 | * Doesn't have the magic number for relocatable executables. If | * Doesn't have the magic number for relocatable executables. If |
1395 | * filename ends with .com, assume it's a DOS application anyhow. | * filename ends with .com, assume it's a DOS application anyhow. |
1396 | * Note that we didn't make this assumption at first, because some | * Note that we didn't make this assumption at first, because some |
1397 | * supposed .com files are really 32-bit executables with all the | * supposed .com files are really 32-bit executables with all the |
1398 | * magic numbers and everything. | * magic numbers and everything. |
1399 | */ | */ |
1400 | ||
1401 | CloseHandle(hFile); | CloseHandle(hFile); |
1402 | if ((ext != NULL) && (strcmp(ext, ".com") == 0)) { | if ((ext != NULL) && (strcmp(ext, ".com") == 0)) { |
1403 | applType = APPL_DOS; | applType = APPL_DOS; |
1404 | break; | break; |
1405 | } | } |
1406 | continue; | continue; |
1407 | } | } |
1408 | if (header.e_lfarlc != sizeof(header)) { | if (header.e_lfarlc != sizeof(header)) { |
1409 | /* | /* |
1410 | * All Windows 3.X and Win32 and some DOS programs have this value | * All Windows 3.X and Win32 and some DOS programs have this value |
1411 | * set here. If it doesn't, assume that since it already had the | * set here. If it doesn't, assume that since it already had the |
1412 | * other magic number it was a DOS application. | * other magic number it was a DOS application. |
1413 | */ | */ |
1414 | ||
1415 | CloseHandle(hFile); | CloseHandle(hFile); |
1416 | applType = APPL_DOS; | applType = APPL_DOS; |
1417 | break; | break; |
1418 | } | } |
1419 | ||
1420 | /* | /* |
1421 | * The DWORD at header.e_lfanew points to yet another magic number. | * The DWORD at header.e_lfanew points to yet another magic number. |
1422 | */ | */ |
1423 | ||
1424 | buf[0] = '\0'; | buf[0] = '\0'; |
1425 | SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN); | SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN); |
1426 | ReadFile(hFile, (void *) buf, 2, &read, NULL); | ReadFile(hFile, (void *) buf, 2, &read, NULL); |
1427 | CloseHandle(hFile); | CloseHandle(hFile); |
1428 | ||
1429 | if ((buf[0] == 'N') && (buf[1] == 'E')) { | if ((buf[0] == 'N') && (buf[1] == 'E')) { |
1430 | applType = APPL_WIN3X; | applType = APPL_WIN3X; |
1431 | } else if ((buf[0] == 'P') && (buf[1] == 'E')) { | } else if ((buf[0] == 'P') && (buf[1] == 'E')) { |
1432 | applType = APPL_WIN32; | applType = APPL_WIN32; |
1433 | } else { | } else { |
1434 | /* | /* |
1435 | * Strictly speaking, there should be a test that there | * Strictly speaking, there should be a test that there |
1436 | * is an 'L' and 'E' at buf[0..1], to identify the type as | * is an 'L' and 'E' at buf[0..1], to identify the type as |
1437 | * DOS, but of course we ran into a DOS executable that | * DOS, but of course we ran into a DOS executable that |
1438 | * _doesn't_ have the magic number -- specifically, one | * _doesn't_ have the magic number -- specifically, one |
1439 | * compiled using the Lahey Fortran90 compiler. | * compiled using the Lahey Fortran90 compiler. |
1440 | */ | */ |
1441 | ||
1442 | applType = APPL_DOS; | applType = APPL_DOS; |
1443 | } | } |
1444 | break; | break; |
1445 | } | } |
1446 | Tcl_DStringFree(&nameBuf); | Tcl_DStringFree(&nameBuf); |
1447 | ||
1448 | if (applType == APPL_NONE) { | if (applType == APPL_NONE) { |
1449 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
1450 | Tcl_AppendResult(interp, "couldn't execute \"", originalName, | Tcl_AppendResult(interp, "couldn't execute \"", originalName, |
1451 | "\": ", Tcl_PosixError(interp), (char *) NULL); | "\": ", Tcl_PosixError(interp), (char *) NULL); |
1452 | return APPL_NONE; | return APPL_NONE; |
1453 | } | } |
1454 | ||
1455 | if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { | if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { |
1456 | /* | /* |
1457 | * Replace long path name of executable with short path name for | * Replace long path name of executable with short path name for |
1458 | * 16-bit applications. Otherwise the application may not be able | * 16-bit applications. Otherwise the application may not be able |
1459 | * to correctly parse its own command line to separate off the | * to correctly parse its own command line to separate off the |
1460 | * application name from the arguments. | * application name from the arguments. |
1461 | */ | */ |
1462 | ||
1463 | (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath, | (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath, |
1464 | nativeFullPath, MAX_PATH); | nativeFullPath, MAX_PATH); |
1465 | strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); | strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds)); |
1466 | Tcl_DStringFree(&ds); | Tcl_DStringFree(&ds); |
1467 | } | } |
1468 | return applType; | return applType; |
1469 | } | } |
1470 | ||
1471 | /* | /* |
1472 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1473 | * | * |
1474 | * BuildCommandLine -- | * BuildCommandLine -- |
1475 | * | * |
1476 | * The command line arguments are stored in linePtr separated | * The command line arguments are stored in linePtr separated |
1477 | * by spaces, in a form that CreateProcess() understands. Special | * by spaces, in a form that CreateProcess() understands. Special |
1478 | * characters in individual arguments from argv[] must be quoted | * characters in individual arguments from argv[] must be quoted |
1479 | * when being stored in cmdLine. | * when being stored in cmdLine. |
1480 | * | * |
1481 | * Results: | * Results: |
1482 | * None. | * None. |
1483 | * | * |
1484 | * Side effects: | * Side effects: |
1485 | * None. | * None. |
1486 | * | * |
1487 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1488 | */ | */ |
1489 | ||
1490 | static void | static void |
1491 | BuildCommandLine( | BuildCommandLine( |
1492 | CONST char *executable, /* Full path of executable (including | CONST char *executable, /* Full path of executable (including |
1493 | * extension). Replacement for argv[0]. */ | * extension). Replacement for argv[0]. */ |
1494 | int argc, /* Number of arguments. */ | int argc, /* Number of arguments. */ |
1495 | char **argv, /* Argument strings in UTF. */ | char **argv, /* Argument strings in UTF. */ |
1496 | Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the | Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the |
1497 | * command line (TCHAR). */ | * command line (TCHAR). */ |
1498 | { | { |
1499 | CONST char *arg, *start, *special; | CONST char *arg, *start, *special; |
1500 | int quote, i; | int quote, i; |
1501 | Tcl_DString ds; | Tcl_DString ds; |
1502 | ||
1503 | Tcl_DStringInit(&ds); | Tcl_DStringInit(&ds); |
1504 | ||
1505 | /* | /* |
1506 | * Prime the path. | * Prime the path. |
1507 | */ | */ |
1508 | ||
1509 | Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1); | Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1); |
1510 | ||
1511 | for (i = 0; i < argc; i++) { | for (i = 0; i < argc; i++) { |
1512 | if (i == 0) { | if (i == 0) { |
1513 | arg = executable; | arg = executable; |
1514 | } else { | } else { |
1515 | arg = argv[i]; | arg = argv[i]; |
1516 | Tcl_DStringAppend(&ds, " ", 1); | Tcl_DStringAppend(&ds, " ", 1); |
1517 | } | } |
1518 | ||
1519 | quote = 0; | quote = 0; |
1520 | if (argv[i][0] == '\0') { | if (argv[i][0] == '\0') { |
1521 | quote = 1; | quote = 1; |
1522 | } else { | } else { |
1523 | for (start = argv[i]; *start != '\0'; start++) { | for (start = argv[i]; *start != '\0'; start++) { |
1524 | if (isspace(*start)) { /* INTL: ISO space. */ | if (isspace(*start)) { /* INTL: ISO space. */ |
1525 | quote = 1; | quote = 1; |
1526 | break; | break; |
1527 | } | } |
1528 | } | } |
1529 | } | } |
1530 | if (quote) { | if (quote) { |
1531 | Tcl_DStringAppend(&ds, "\"", 1); | Tcl_DStringAppend(&ds, "\"", 1); |
1532 | } | } |
1533 | ||
1534 | start = arg; | start = arg; |
1535 | for (special = arg; ; ) { | for (special = arg; ; ) { |
1536 | if ((*special == '\\') && | if ((*special == '\\') && |
1537 | (special[1] == '\\' || special[1] == '"')) { | (special[1] == '\\' || special[1] == '"')) { |
1538 | Tcl_DStringAppend(&ds, start, special - start); | Tcl_DStringAppend(&ds, start, special - start); |
1539 | start = special; | start = special; |
1540 | while (1) { | while (1) { |
1541 | special++; | special++; |
1542 | if (*special == '"') { | if (*special == '"') { |
1543 | /* | /* |
1544 | * N backslashes followed a quote -> insert | * N backslashes followed a quote -> insert |
1545 | * N * 2 + 1 backslashes then a quote. | * N * 2 + 1 backslashes then a quote. |
1546 | */ | */ |
1547 | ||
1548 | Tcl_DStringAppend(&ds, start, special - start); | Tcl_DStringAppend(&ds, start, special - start); |
1549 | break; | break; |
1550 | } | } |
1551 | if (*special != '\\') { | if (*special != '\\') { |
1552 | break; | break; |
1553 | } | } |
1554 | } | } |
1555 | Tcl_DStringAppend(&ds, start, special - start); | Tcl_DStringAppend(&ds, start, special - start); |
1556 | start = special; | start = special; |
1557 | } | } |
1558 | if (*special == '"') { | if (*special == '"') { |
1559 | Tcl_DStringAppend(&ds, start, special - start); | Tcl_DStringAppend(&ds, start, special - start); |
1560 | Tcl_DStringAppend(&ds, "\\\"", 2); | Tcl_DStringAppend(&ds, "\\\"", 2); |
1561 | start = special + 1; | start = special + 1; |
1562 | } | } |
1563 | if (*special == '\0') { | if (*special == '\0') { |
1564 | break; | break; |
1565 | } | } |
1566 | special++; | special++; |
1567 | } | } |
1568 | Tcl_DStringAppend(&ds, start, special - start); | Tcl_DStringAppend(&ds, start, special - start); |
1569 | if (quote) { | if (quote) { |
1570 | Tcl_DStringAppend(&ds, "\"", 1); | Tcl_DStringAppend(&ds, "\"", 1); |
1571 | } | } |
1572 | } | } |
1573 | Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr); | Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr); |
1574 | Tcl_DStringFree(&ds); | Tcl_DStringFree(&ds); |
1575 | } | } |
1576 | ||
1577 | /* | /* |
1578 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1579 | * | * |
1580 | * TclpCreateCommandChannel -- | * TclpCreateCommandChannel -- |
1581 | * | * |
1582 | * This function is called by Tcl_OpenCommandChannel to perform | * This function is called by Tcl_OpenCommandChannel to perform |
1583 | * the platform specific channel initialization for a command | * the platform specific channel initialization for a command |
1584 | * channel. | * channel. |
1585 | * | * |
1586 | * Results: | * Results: |
1587 | * Returns a new channel or NULL on failure. | * Returns a new channel or NULL on failure. |
1588 | * | * |
1589 | * Side effects: | * Side effects: |
1590 | * Allocates a new channel. | * Allocates a new channel. |
1591 | * | * |
1592 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1593 | */ | */ |
1594 | ||
1595 | Tcl_Channel | Tcl_Channel |
1596 | TclpCreateCommandChannel( | TclpCreateCommandChannel( |
1597 | TclFile readFile, /* If non-null, gives the file for reading. */ | TclFile readFile, /* If non-null, gives the file for reading. */ |
1598 | TclFile writeFile, /* If non-null, gives the file for writing. */ | TclFile writeFile, /* If non-null, gives the file for writing. */ |
1599 | TclFile errorFile, /* If non-null, gives the file where errors | TclFile errorFile, /* If non-null, gives the file where errors |
1600 | * can be read. */ | * can be read. */ |
1601 | int numPids, /* The number of pids in the pid array. */ | int numPids, /* The number of pids in the pid array. */ |
1602 | Tcl_Pid *pidPtr) /* An array of process identifiers. */ | Tcl_Pid *pidPtr) /* An array of process identifiers. */ |
1603 | { | { |
1604 | char channelName[16 + TCL_INTEGER_SPACE]; | char channelName[16 + TCL_INTEGER_SPACE]; |
1605 | int channelId; | int channelId; |
1606 | DWORD id; | DWORD id; |
1607 | PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo)); | PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo)); |
1608 | ||
1609 | PipeInit(); | PipeInit(); |
1610 | ||
1611 | infoPtr->watchMask = 0; | infoPtr->watchMask = 0; |
1612 | infoPtr->flags = 0; | infoPtr->flags = 0; |
1613 | infoPtr->readFlags = 0; | infoPtr->readFlags = 0; |
1614 | infoPtr->readFile = readFile; | infoPtr->readFile = readFile; |
1615 | infoPtr->writeFile = writeFile; | infoPtr->writeFile = writeFile; |
1616 | infoPtr->errorFile = errorFile; | infoPtr->errorFile = errorFile; |
1617 | infoPtr->numPids = numPids; | infoPtr->numPids = numPids; |
1618 | infoPtr->pidPtr = pidPtr; | infoPtr->pidPtr = pidPtr; |
1619 | infoPtr->writeBuf = 0; | infoPtr->writeBuf = 0; |
1620 | infoPtr->writeBufLen = 0; | infoPtr->writeBufLen = 0; |
1621 | infoPtr->writeError = 0; | infoPtr->writeError = 0; |
1622 | ||
1623 | /* | /* |
1624 | * Use one of the fds associated with the channel as the | * Use one of the fds associated with the channel as the |
1625 | * channel id. | * channel id. |
1626 | */ | */ |
1627 | ||
1628 | if (readFile) { | if (readFile) { |
1629 | channelId = (int) ((WinFile*)readFile)->handle; | channelId = (int) ((WinFile*)readFile)->handle; |
1630 | } else if (writeFile) { | } else if (writeFile) { |
1631 | channelId = (int) ((WinFile*)writeFile)->handle; | channelId = (int) ((WinFile*)writeFile)->handle; |
1632 | } else if (errorFile) { | } else if (errorFile) { |
1633 | channelId = (int) ((WinFile*)errorFile)->handle; | channelId = (int) ((WinFile*)errorFile)->handle; |
1634 | } else { | } else { |
1635 | channelId = 0; | channelId = 0; |
1636 | } | } |
1637 | ||
1638 | infoPtr->validMask = 0; | infoPtr->validMask = 0; |
1639 | ||
1640 | infoPtr->threadId = Tcl_GetCurrentThread(); | infoPtr->threadId = Tcl_GetCurrentThread(); |
1641 | ||
1642 | if (readFile != NULL) { | if (readFile != NULL) { |
1643 | /* | /* |
1644 | * Start the background reader thread. | * Start the background reader thread. |
1645 | */ | */ |
1646 | ||
1647 | infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); | infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL); |
1648 | infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); | infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL); |
1649 | infoPtr->readThread = CreateThread(NULL, 8000, PipeReaderThread, | infoPtr->readThread = CreateThread(NULL, 8000, PipeReaderThread, |
1650 | infoPtr, 0, &id); | infoPtr, 0, &id); |
1651 | SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); | SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); |
1652 | infoPtr->validMask |= TCL_READABLE; | infoPtr->validMask |= TCL_READABLE; |
1653 | } else { | } else { |
1654 | infoPtr->readThread = 0; | infoPtr->readThread = 0; |
1655 | } | } |
1656 | if (writeFile != NULL) { | if (writeFile != NULL) { |
1657 | /* | /* |
1658 | * Start the background writeer thwrite. | * Start the background writeer thwrite. |
1659 | */ | */ |
1660 | ||
1661 | infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); | infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL); |
1662 | infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); | infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL); |
1663 | infoPtr->writeThread = CreateThread(NULL, 8000, PipeWriterThread, | infoPtr->writeThread = CreateThread(NULL, 8000, PipeWriterThread, |
1664 | infoPtr, 0, &id); | infoPtr, 0, &id); |
1665 | SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); | SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST); |
1666 | infoPtr->validMask |= TCL_WRITABLE; | infoPtr->validMask |= TCL_WRITABLE; |
1667 | } | } |
1668 | ||
1669 | /* | /* |
1670 | * For backward compatibility with previous versions of Tcl, we | * For backward compatibility with previous versions of Tcl, we |
1671 | * use "file%d" as the base name for pipes even though it would | * use "file%d" as the base name for pipes even though it would |
1672 | * be more natural to use "pipe%d". | * be more natural to use "pipe%d". |
1673 | * Use the pointer to keep the channel names unique, in case | * Use the pointer to keep the channel names unique, in case |
1674 | * channels share handles (stdin/stdout). | * channels share handles (stdin/stdout). |
1675 | */ | */ |
1676 | ||
1677 | wsprintfA(channelName, "file%lx", infoPtr); | wsprintfA(channelName, "file%lx", infoPtr); |
1678 | infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, | infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, |
1679 | (ClientData) infoPtr, infoPtr->validMask); | (ClientData) infoPtr, infoPtr->validMask); |
1680 | ||
1681 | /* | /* |
1682 | * Pipes have AUTO translation mode on Windows and ^Z eof char, which | * Pipes have AUTO translation mode on Windows and ^Z eof char, which |
1683 | * means that a ^Z will be appended to them at close. This is needed | * means that a ^Z will be appended to them at close. This is needed |
1684 | * for Windows programs that expect a ^Z at EOF. | * for Windows programs that expect a ^Z at EOF. |
1685 | */ | */ |
1686 | ||
1687 | Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, | Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, |
1688 | "-translation", "auto"); | "-translation", "auto"); |
1689 | Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, | Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, |
1690 | "-eofchar", "\032 {}"); | "-eofchar", "\032 {}"); |
1691 | return infoPtr->channel; | return infoPtr->channel; |
1692 | } | } |
1693 | ||
1694 | /* | /* |
1695 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1696 | * | * |
1697 | * TclGetAndDetachPids -- | * TclGetAndDetachPids -- |
1698 | * | * |
1699 | * Stores a list of the command PIDs for a command channel in | * Stores a list of the command PIDs for a command channel in |
1700 | * the interp's result. | * the interp's result. |
1701 | * | * |
1702 | * Results: | * Results: |
1703 | * None. | * None. |
1704 | * | * |
1705 | * Side effects: | * Side effects: |
1706 | * Modifies the interp's result. | * Modifies the interp's result. |
1707 | * | * |
1708 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1709 | */ | */ |
1710 | ||
1711 | void | void |
1712 | TclGetAndDetachPids( | TclGetAndDetachPids( |
1713 | Tcl_Interp *interp, | Tcl_Interp *interp, |
1714 | Tcl_Channel chan) | Tcl_Channel chan) |
1715 | { | { |
1716 | PipeInfo *pipePtr; | PipeInfo *pipePtr; |
1717 | Tcl_ChannelType *chanTypePtr; | Tcl_ChannelType *chanTypePtr; |
1718 | int i; | int i; |
1719 | char buf[TCL_INTEGER_SPACE]; | char buf[TCL_INTEGER_SPACE]; |
1720 | ||
1721 | /* | /* |
1722 | * Punt if the channel is not a command channel. | * Punt if the channel is not a command channel. |
1723 | */ | */ |
1724 | ||
1725 | chanTypePtr = Tcl_GetChannelType(chan); | chanTypePtr = Tcl_GetChannelType(chan); |
1726 | if (chanTypePtr != &pipeChannelType) { | if (chanTypePtr != &pipeChannelType) { |
1727 | return; | return; |
1728 | } | } |
1729 | ||
1730 | pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); | pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); |
1731 | for (i = 0; i < pipePtr->numPids; i++) { | for (i = 0; i < pipePtr->numPids; i++) { |
1732 | wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); | wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); |
1733 | Tcl_AppendElement(interp, buf); | Tcl_AppendElement(interp, buf); |
1734 | Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); | Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); |
1735 | } | } |
1736 | if (pipePtr->numPids > 0) { | if (pipePtr->numPids > 0) { |
1737 | ckfree((char *) pipePtr->pidPtr); | ckfree((char *) pipePtr->pidPtr); |
1738 | pipePtr->numPids = 0; | pipePtr->numPids = 0; |
1739 | } | } |
1740 | } | } |
1741 | ||
1742 | /* | /* |
1743 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1744 | * | * |
1745 | * PipeBlockModeProc -- | * PipeBlockModeProc -- |
1746 | * | * |
1747 | * Set blocking or non-blocking mode on channel. | * Set blocking or non-blocking mode on channel. |
1748 | * | * |
1749 | * Results: | * Results: |
1750 | * 0 if successful, errno when failed. | * 0 if successful, errno when failed. |
1751 | * | * |
1752 | * Side effects: | * Side effects: |
1753 | * Sets the device into blocking or non-blocking mode. | * Sets the device into blocking or non-blocking mode. |
1754 | * | * |
1755 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1756 | */ | */ |
1757 | ||
1758 | static int | static int |
1759 | PipeBlockModeProc( | PipeBlockModeProc( |
1760 | ClientData instanceData, /* Instance data for channel. */ | ClientData instanceData, /* Instance data for channel. */ |
1761 | int mode) /* TCL_MODE_BLOCKING or | int mode) /* TCL_MODE_BLOCKING or |
1762 | * TCL_MODE_NONBLOCKING. */ | * TCL_MODE_NONBLOCKING. */ |
1763 | { | { |
1764 | PipeInfo *infoPtr = (PipeInfo *) instanceData; | PipeInfo *infoPtr = (PipeInfo *) instanceData; |
1765 | ||
1766 | /* | /* |
1767 | * Pipes on Windows can not be switched between blocking and nonblocking, | * Pipes on Windows can not be switched between blocking and nonblocking, |
1768 | * hence we have to emulate the behavior. This is done in the input | * hence we have to emulate the behavior. This is done in the input |
1769 | * function by checking against a bit in the state. We set or unset the | * function by checking against a bit in the state. We set or unset the |
1770 | * bit here to cause the input function to emulate the correct behavior. | * bit here to cause the input function to emulate the correct behavior. |
1771 | */ | */ |
1772 | ||
1773 | if (mode == TCL_MODE_NONBLOCKING) { | if (mode == TCL_MODE_NONBLOCKING) { |
1774 | infoPtr->flags |= PIPE_ASYNC; | infoPtr->flags |= PIPE_ASYNC; |
1775 | } else { | } else { |
1776 | infoPtr->flags &= ~(PIPE_ASYNC); | infoPtr->flags &= ~(PIPE_ASYNC); |
1777 | } | } |
1778 | return 0; | return 0; |
1779 | } | } |
1780 | ||
1781 | /* | /* |
1782 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1783 | * | * |
1784 | * PipeClose2Proc -- | * PipeClose2Proc -- |
1785 | * | * |
1786 | * Closes a pipe based IO channel. | * Closes a pipe based IO channel. |
1787 | * | * |
1788 | * Results: | * Results: |
1789 | * 0 on success, errno otherwise. | * 0 on success, errno otherwise. |
1790 | * | * |
1791 | * Side effects: | * Side effects: |
1792 | * Closes the physical channel. | * Closes the physical channel. |
1793 | * | * |
1794 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1795 | */ | */ |
1796 | ||
1797 | static int | static int |
1798 | PipeClose2Proc( | PipeClose2Proc( |
1799 | ClientData instanceData, /* Pointer to PipeInfo structure. */ | ClientData instanceData, /* Pointer to PipeInfo structure. */ |
1800 | Tcl_Interp *interp, /* For error reporting. */ | Tcl_Interp *interp, /* For error reporting. */ |
1801 | int flags) /* Flags that indicate which side to close. */ | int flags) /* Flags that indicate which side to close. */ |
1802 | { | { |
1803 | PipeInfo *pipePtr = (PipeInfo *) instanceData; | PipeInfo *pipePtr = (PipeInfo *) instanceData; |
1804 | Tcl_Channel errChan; | Tcl_Channel errChan; |
1805 | int errorCode, result; | int errorCode, result; |
1806 | PipeInfo *infoPtr, **nextPtrPtr; | PipeInfo *infoPtr, **nextPtrPtr; |
1807 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
1808 | ||
1809 | errorCode = 0; | errorCode = 0; |
1810 | if ((!flags || (flags == TCL_CLOSE_READ)) | if ((!flags || (flags == TCL_CLOSE_READ)) |
1811 | && (pipePtr->readFile != NULL)) { | && (pipePtr->readFile != NULL)) { |
1812 | /* | /* |
1813 | * Clean up the background thread if necessary. Note that this | * Clean up the background thread if necessary. Note that this |
1814 | * must be done before we can close the file, since the | * must be done before we can close the file, since the |
1815 | * thread may be blocking trying to read from the pipe. | * thread may be blocking trying to read from the pipe. |
1816 | */ | */ |
1817 | ||
1818 | if (pipePtr->readThread) { | if (pipePtr->readThread) { |
1819 | /* | /* |
1820 | * Forcibly terminate the background thread. We cannot rely on the | * Forcibly terminate the background thread. We cannot rely on the |
1821 | * thread to cleanly terminate itself because we have no way of | * thread to cleanly terminate itself because we have no way of |
1822 | * closing the pipe handle without blocking in the case where the | * closing the pipe handle without blocking in the case where the |
1823 | * thread is in the middle of an I/O operation. Note that we need | * thread is in the middle of an I/O operation. Note that we need |
1824 | * to guard against terminating the thread while it is in the | * to guard against terminating the thread while it is in the |
1825 | * middle of Tcl_ThreadAlert because it won't be able to release | * middle of Tcl_ThreadAlert because it won't be able to release |
1826 | * the notifier lock. | * the notifier lock. |
1827 | */ | */ |
1828 | ||
1829 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
1830 | TerminateThread(pipePtr->readThread, 0); | TerminateThread(pipePtr->readThread, 0); |
1831 | ||
1832 | /* | /* |
1833 | * Wait for the thread to terminate. This ensures that we are | * Wait for the thread to terminate. This ensures that we are |
1834 | * completely cleaned up before we leave this function. | * completely cleaned up before we leave this function. |
1835 | */ | */ |
1836 | ||
1837 | WaitForSingleObject(pipePtr->readThread, INFINITE); | WaitForSingleObject(pipePtr->readThread, INFINITE); |
1838 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
1839 | ||
1840 | CloseHandle(pipePtr->readThread); | CloseHandle(pipePtr->readThread); |
1841 | CloseHandle(pipePtr->readable); | CloseHandle(pipePtr->readable); |
1842 | CloseHandle(pipePtr->startReader); | CloseHandle(pipePtr->startReader); |
1843 | pipePtr->readThread = NULL; | pipePtr->readThread = NULL; |
1844 | } | } |
1845 | if (TclpCloseFile(pipePtr->readFile) != 0) { | if (TclpCloseFile(pipePtr->readFile) != 0) { |
1846 | errorCode = errno; | errorCode = errno; |
1847 | } | } |
1848 | pipePtr->validMask &= ~TCL_READABLE; | pipePtr->validMask &= ~TCL_READABLE; |
1849 | pipePtr->readFile = NULL; | pipePtr->readFile = NULL; |
1850 | } | } |
1851 | if ((!flags || (flags & TCL_CLOSE_WRITE)) | if ((!flags || (flags & TCL_CLOSE_WRITE)) |
1852 | && (pipePtr->writeFile != NULL)) { | && (pipePtr->writeFile != NULL)) { |
1853 | /* | /* |
1854 | * Wait for the writer thread to finish the current buffer, then | * Wait for the writer thread to finish the current buffer, then |
1855 | * terminate the thread and close the handles. If the channel is | * terminate the thread and close the handles. If the channel is |
1856 | * nonblocking, there should be no pending write operations. | * nonblocking, there should be no pending write operations. |
1857 | */ | */ |
1858 | ||
1859 | if (pipePtr->writeThread) { | if (pipePtr->writeThread) { |
1860 | WaitForSingleObject(pipePtr->writable, INFINITE); | WaitForSingleObject(pipePtr->writable, INFINITE); |
1861 | ||
1862 | /* | /* |
1863 | * Forcibly terminate the background thread. We cannot rely on the | * Forcibly terminate the background thread. We cannot rely on the |
1864 | * thread to cleanly terminate itself because we have no way of | * thread to cleanly terminate itself because we have no way of |
1865 | * closing the pipe handle without blocking in the case where the | * closing the pipe handle without blocking in the case where the |
1866 | * thread is in the middle of an I/O operation. Note that we need | * thread is in the middle of an I/O operation. Note that we need |
1867 | * to guard against terminating the thread while it is in the | * to guard against terminating the thread while it is in the |
1868 | * middle of Tcl_ThreadAlert because it won't be able to release | * middle of Tcl_ThreadAlert because it won't be able to release |
1869 | * the notifier lock. | * the notifier lock. |
1870 | */ | */ |
1871 | ||
1872 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
1873 | TerminateThread(pipePtr->writeThread, 0); | TerminateThread(pipePtr->writeThread, 0); |
1874 | ||
1875 | /* | /* |
1876 | * Wait for the thread to terminate. This ensures that we are | * Wait for the thread to terminate. This ensures that we are |
1877 | * completely cleaned up before we leave this function. | * completely cleaned up before we leave this function. |
1878 | */ | */ |
1879 | ||
1880 | WaitForSingleObject(pipePtr->writeThread, INFINITE); | WaitForSingleObject(pipePtr->writeThread, INFINITE); |
1881 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
1882 | ||
1883 | ||
1884 | CloseHandle(pipePtr->writeThread); | CloseHandle(pipePtr->writeThread); |
1885 | CloseHandle(pipePtr->writable); | CloseHandle(pipePtr->writable); |
1886 | CloseHandle(pipePtr->startWriter); | CloseHandle(pipePtr->startWriter); |
1887 | pipePtr->writeThread = NULL; | pipePtr->writeThread = NULL; |
1888 | } | } |
1889 | if (TclpCloseFile(pipePtr->writeFile) != 0) { | if (TclpCloseFile(pipePtr->writeFile) != 0) { |
1890 | if (errorCode == 0) { | if (errorCode == 0) { |
1891 | errorCode = errno; | errorCode = errno; |
1892 | } | } |
1893 | } | } |
1894 | pipePtr->validMask &= ~TCL_WRITABLE; | pipePtr->validMask &= ~TCL_WRITABLE; |
1895 | pipePtr->writeFile = NULL; | pipePtr->writeFile = NULL; |
1896 | } | } |
1897 | ||
1898 | pipePtr->watchMask &= pipePtr->validMask; | pipePtr->watchMask &= pipePtr->validMask; |
1899 | ||
1900 | /* | /* |
1901 | * Don't free the channel if any of the flags were set. | * Don't free the channel if any of the flags were set. |
1902 | */ | */ |
1903 | ||
1904 | if (flags) { | if (flags) { |
1905 | return errorCode; | return errorCode; |
1906 | } | } |
1907 | ||
1908 | /* | /* |
1909 | * Remove the file from the list of watched files. | * Remove the file from the list of watched files. |
1910 | */ | */ |
1911 | ||
1912 | for (nextPtrPtr = &(tsdPtr->firstPipePtr), infoPtr = *nextPtrPtr; | for (nextPtrPtr = &(tsdPtr->firstPipePtr), infoPtr = *nextPtrPtr; |
1913 | infoPtr != NULL; | infoPtr != NULL; |
1914 | nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { | nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) { |
1915 | if (infoPtr == (PipeInfo *)pipePtr) { | if (infoPtr == (PipeInfo *)pipePtr) { |
1916 | *nextPtrPtr = infoPtr->nextPtr; | *nextPtrPtr = infoPtr->nextPtr; |
1917 | break; | break; |
1918 | } | } |
1919 | } | } |
1920 | ||
1921 | /* | /* |
1922 | * Wrap the error file into a channel and give it to the cleanup | * Wrap the error file into a channel and give it to the cleanup |
1923 | * routine. | * routine. |
1924 | */ | */ |
1925 | ||
1926 | if (pipePtr->errorFile) { | if (pipePtr->errorFile) { |
1927 | WinFile *filePtr; | WinFile *filePtr; |
1928 | ||
1929 | filePtr = (WinFile*)pipePtr->errorFile; | filePtr = (WinFile*)pipePtr->errorFile; |
1930 | errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle, | errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle, |
1931 | TCL_READABLE); | TCL_READABLE); |
1932 | ckfree((char *) filePtr); | ckfree((char *) filePtr); |
1933 | } else { | } else { |
1934 | errChan = NULL; | errChan = NULL; |
1935 | } | } |
1936 | ||
1937 | result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, | result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, |
1938 | errChan); | errChan); |
1939 | ||
1940 | if (pipePtr->numPids > 0) { | if (pipePtr->numPids > 0) { |
1941 | ckfree((char *) pipePtr->pidPtr); | ckfree((char *) pipePtr->pidPtr); |
1942 | } | } |
1943 | ||
1944 | if (pipePtr->writeBuf != NULL) { | if (pipePtr->writeBuf != NULL) { |
1945 | ckfree(pipePtr->writeBuf); | ckfree(pipePtr->writeBuf); |
1946 | } | } |
1947 | ||
1948 | ckfree((char*) pipePtr); | ckfree((char*) pipePtr); |
1949 | ||
1950 | if (errorCode == 0) { | if (errorCode == 0) { |
1951 | return result; | return result; |
1952 | } | } |
1953 | return errorCode; | return errorCode; |
1954 | } | } |
1955 | ||
1956 | /* | /* |
1957 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1958 | * | * |
1959 | * PipeInputProc -- | * PipeInputProc -- |
1960 | * | * |
1961 | * Reads input from the IO channel into the buffer given. Returns | * Reads input from the IO channel into the buffer given. Returns |
1962 | * count of how many bytes were actually read, and an error indication. | * count of how many bytes were actually read, and an error indication. |
1963 | * | * |
1964 | * Results: | * Results: |
1965 | * A count of how many bytes were read is returned and an error | * A count of how many bytes were read is returned and an error |
1966 | * indication is returned in an output argument. | * indication is returned in an output argument. |
1967 | * | * |
1968 | * Side effects: | * Side effects: |
1969 | * Reads input from the actual channel. | * Reads input from the actual channel. |
1970 | * | * |
1971 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
1972 | */ | */ |
1973 | ||
1974 | static int | static int |
1975 | PipeInputProc( | PipeInputProc( |
1976 | ClientData instanceData, /* Pipe state. */ | ClientData instanceData, /* Pipe state. */ |
1977 | char *buf, /* Where to store data read. */ | char *buf, /* Where to store data read. */ |
1978 | int bufSize, /* How much space is available | int bufSize, /* How much space is available |
1979 | * in the buffer? */ | * in the buffer? */ |
1980 | int *errorCode) /* Where to store error code. */ | int *errorCode) /* Where to store error code. */ |
1981 | { | { |
1982 | PipeInfo *infoPtr = (PipeInfo *) instanceData; | PipeInfo *infoPtr = (PipeInfo *) instanceData; |
1983 | WinFile *filePtr = (WinFile*) infoPtr->readFile; | WinFile *filePtr = (WinFile*) infoPtr->readFile; |
1984 | DWORD count, bytesRead = 0; | DWORD count, bytesRead = 0; |
1985 | int result; | int result; |
1986 | ||
1987 | *errorCode = 0; | *errorCode = 0; |
1988 | /* | /* |
1989 | * Synchronize with the reader thread. | * Synchronize with the reader thread. |
1990 | */ | */ |
1991 | ||
1992 | result = WaitForRead(infoPtr, (infoPtr->flags & PIPE_ASYNC) ? 0 : 1); | result = WaitForRead(infoPtr, (infoPtr->flags & PIPE_ASYNC) ? 0 : 1); |
1993 | ||
1994 | /* | /* |
1995 | * If an error occurred, return immediately. | * If an error occurred, return immediately. |
1996 | */ | */ |
1997 | ||
1998 | if (result == -1) { | if (result == -1) { |
1999 | *errorCode = errno; | *errorCode = errno; |
2000 | return -1; | return -1; |
2001 | } | } |
2002 | ||
2003 | if (infoPtr->readFlags & PIPE_EXTRABYTE) { | if (infoPtr->readFlags & PIPE_EXTRABYTE) { |
2004 | /* | /* |
2005 | * The reader thread consumed 1 byte as a side effect of | * The reader thread consumed 1 byte as a side effect of |
2006 | * waiting so we need to move it into the buffer. | * waiting so we need to move it into the buffer. |
2007 | */ | */ |
2008 | ||
2009 | *buf = infoPtr->extraByte; | *buf = infoPtr->extraByte; |
2010 | infoPtr->readFlags &= ~PIPE_EXTRABYTE; | infoPtr->readFlags &= ~PIPE_EXTRABYTE; |
2011 | buf++; | buf++; |
2012 | bufSize--; | bufSize--; |
2013 | bytesRead = 1; | bytesRead = 1; |
2014 | ||
2015 | /* | /* |
2016 | * If further read attempts would block, return what we have. | * If further read attempts would block, return what we have. |
2017 | */ | */ |
2018 | ||
2019 | if (result == 0) { | if (result == 0) { |
2020 | return bytesRead; | return bytesRead; |
2021 | } | } |
2022 | } | } |
2023 | ||
2024 | /* | /* |
2025 | * Attempt to read bufSize bytes. The read will return immediately | * Attempt to read bufSize bytes. The read will return immediately |
2026 | * if there is any data available. Otherwise it will block until | * if there is any data available. Otherwise it will block until |
2027 | * at least one byte is available or an EOF occurs. | * at least one byte is available or an EOF occurs. |
2028 | */ | */ |
2029 | ||
2030 | if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &count, | if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &count, |
2031 | (LPOVERLAPPED) NULL) == TRUE) { | (LPOVERLAPPED) NULL) == TRUE) { |
2032 | return bytesRead + count; | return bytesRead + count; |
2033 | } else if (bytesRead) { | } else if (bytesRead) { |
2034 | /* | /* |
2035 | * Ignore errors if we have data to return. | * Ignore errors if we have data to return. |
2036 | */ | */ |
2037 | ||
2038 | return bytesRead; | return bytesRead; |
2039 | } | } |
2040 | ||
2041 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
2042 | if (errno == EPIPE) { | if (errno == EPIPE) { |
2043 | infoPtr->readFlags |= PIPE_EOF; | infoPtr->readFlags |= PIPE_EOF; |
2044 | return 0; | return 0; |
2045 | } | } |
2046 | *errorCode = errno; | *errorCode = errno; |
2047 | return -1; | return -1; |
2048 | } | } |
2049 | ||
2050 | /* | /* |
2051 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2052 | * | * |
2053 | * PipeOutputProc -- | * PipeOutputProc -- |
2054 | * | * |
2055 | * Writes the given output on the IO channel. Returns count of how | * Writes the given output on the IO channel. Returns count of how |
2056 | * many characters were actually written, and an error indication. | * many characters were actually written, and an error indication. |
2057 | * | * |
2058 | * Results: | * Results: |
2059 | * A count of how many characters were written is returned and an | * A count of how many characters were written is returned and an |
2060 | * error indication is returned in an output argument. | * error indication is returned in an output argument. |
2061 | * | * |
2062 | * Side effects: | * Side effects: |
2063 | * Writes output on the actual channel. | * Writes output on the actual channel. |
2064 | * | * |
2065 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2066 | */ | */ |
2067 | ||
2068 | static int | static int |
2069 | PipeOutputProc( | PipeOutputProc( |
2070 | ClientData instanceData, /* Pipe state. */ | ClientData instanceData, /* Pipe state. */ |
2071 | char *buf, /* The data buffer. */ | char *buf, /* The data buffer. */ |
2072 | int toWrite, /* How many bytes to write? */ | int toWrite, /* How many bytes to write? */ |
2073 | int *errorCode) /* Where to store error code. */ | int *errorCode) /* Where to store error code. */ |
2074 | { | { |
2075 | PipeInfo *infoPtr = (PipeInfo *) instanceData; | PipeInfo *infoPtr = (PipeInfo *) instanceData; |
2076 | WinFile *filePtr = (WinFile*) infoPtr->writeFile; | WinFile *filePtr = (WinFile*) infoPtr->writeFile; |
2077 | DWORD bytesWritten, timeout; | DWORD bytesWritten, timeout; |
2078 | ||
2079 | *errorCode = 0; | *errorCode = 0; |
2080 | timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE; | timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE; |
2081 | if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { | if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { |
2082 | /* | /* |
2083 | * The writer thread is blocked waiting for a write to complete | * The writer thread is blocked waiting for a write to complete |
2084 | * and the channel is in non-blocking mode. | * and the channel is in non-blocking mode. |
2085 | */ | */ |
2086 | ||
2087 | errno = EAGAIN; | errno = EAGAIN; |
2088 | goto error; | goto error; |
2089 | } | } |
2090 | ||
2091 | /* | /* |
2092 | * Check for a background error on the last write. | * Check for a background error on the last write. |
2093 | */ | */ |
2094 | ||
2095 | if (infoPtr->writeError) { | if (infoPtr->writeError) { |
2096 | TclWinConvertError(infoPtr->writeError); | TclWinConvertError(infoPtr->writeError); |
2097 | infoPtr->writeError = 0; | infoPtr->writeError = 0; |
2098 | goto error; | goto error; |
2099 | } | } |
2100 | ||
2101 | if (infoPtr->flags & PIPE_ASYNC) { | if (infoPtr->flags & PIPE_ASYNC) { |
2102 | /* | /* |
2103 | * The pipe is non-blocking, so copy the data into the output | * The pipe is non-blocking, so copy the data into the output |
2104 | * buffer and restart the writer thread. | * buffer and restart the writer thread. |
2105 | */ | */ |
2106 | ||
2107 | if (toWrite > infoPtr->writeBufLen) { | if (toWrite > infoPtr->writeBufLen) { |
2108 | /* | /* |
2109 | * Reallocate the buffer to be large enough to hold the data. | * Reallocate the buffer to be large enough to hold the data. |
2110 | */ | */ |
2111 | ||
2112 | if (infoPtr->writeBuf) { | if (infoPtr->writeBuf) { |
2113 | ckfree(infoPtr->writeBuf); | ckfree(infoPtr->writeBuf); |
2114 | } | } |
2115 | infoPtr->writeBufLen = toWrite; | infoPtr->writeBufLen = toWrite; |
2116 | infoPtr->writeBuf = ckalloc(toWrite); | infoPtr->writeBuf = ckalloc(toWrite); |
2117 | } | } |
2118 | memcpy(infoPtr->writeBuf, buf, toWrite); | memcpy(infoPtr->writeBuf, buf, toWrite); |
2119 | infoPtr->toWrite = toWrite; | infoPtr->toWrite = toWrite; |
2120 | ResetEvent(infoPtr->writable); | ResetEvent(infoPtr->writable); |
2121 | SetEvent(infoPtr->startWriter); | SetEvent(infoPtr->startWriter); |
2122 | bytesWritten = toWrite; | bytesWritten = toWrite; |
2123 | } else { | } else { |
2124 | /* | /* |
2125 | * In the blocking case, just try to write the buffer directly. | * In the blocking case, just try to write the buffer directly. |
2126 | * This avoids an unnecessary copy. | * This avoids an unnecessary copy. |
2127 | */ | */ |
2128 | ||
2129 | if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite, | if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite, |
2130 | &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { | &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) { |
2131 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
2132 | goto error; | goto error; |
2133 | } | } |
2134 | } | } |
2135 | return bytesWritten; | return bytesWritten; |
2136 | ||
2137 | error: | error: |
2138 | *errorCode = errno; | *errorCode = errno; |
2139 | return -1; | return -1; |
2140 | ||
2141 | } | } |
2142 | ||
2143 | /* | /* |
2144 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2145 | * | * |
2146 | * PipeEventProc -- | * PipeEventProc -- |
2147 | * | * |
2148 | * This function is invoked by Tcl_ServiceEvent when a file event | * This function is invoked by Tcl_ServiceEvent when a file event |
2149 | * reaches the front of the event queue. This procedure invokes | * reaches the front of the event queue. This procedure invokes |
2150 | * Tcl_NotifyChannel on the pipe. | * Tcl_NotifyChannel on the pipe. |
2151 | * | * |
2152 | * Results: | * Results: |
2153 | * Returns 1 if the event was handled, meaning it should be removed | * Returns 1 if the event was handled, meaning it should be removed |
2154 | * from the queue. Returns 0 if the event was not handled, meaning | * from the queue. Returns 0 if the event was not handled, meaning |
2155 | * it should stay on the queue. The only time the event isn't | * it should stay on the queue. The only time the event isn't |
2156 | * handled is if the TCL_FILE_EVENTS flag bit isn't set. | * handled is if the TCL_FILE_EVENTS flag bit isn't set. |
2157 | * | * |
2158 | * Side effects: | * Side effects: |
2159 | * Whatever the notifier callback does. | * Whatever the notifier callback does. |
2160 | * | * |
2161 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2162 | */ | */ |
2163 | ||
2164 | static int | static int |
2165 | PipeEventProc( | PipeEventProc( |
2166 | Tcl_Event *evPtr, /* Event to service. */ | Tcl_Event *evPtr, /* Event to service. */ |
2167 | int flags) /* Flags that indicate what events to | int flags) /* Flags that indicate what events to |
2168 | * handle, such as TCL_FILE_EVENTS. */ | * handle, such as TCL_FILE_EVENTS. */ |
2169 | { | { |
2170 | PipeEvent *pipeEvPtr = (PipeEvent *)evPtr; | PipeEvent *pipeEvPtr = (PipeEvent *)evPtr; |
2171 | PipeInfo *infoPtr; | PipeInfo *infoPtr; |
2172 | WinFile *filePtr; | WinFile *filePtr; |
2173 | int mask; | int mask; |
2174 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
2175 | ||
2176 | if (!(flags & TCL_FILE_EVENTS)) { | if (!(flags & TCL_FILE_EVENTS)) { |
2177 | return 0; | return 0; |
2178 | } | } |
2179 | ||
2180 | /* | /* |
2181 | * Search through the list of watched pipes for the one whose handle | * Search through the list of watched pipes for the one whose handle |
2182 | * matches the event. We do this rather than simply dereferencing | * matches the event. We do this rather than simply dereferencing |
2183 | * the handle in the event so that pipes can be deleted while the | * the handle in the event so that pipes can be deleted while the |
2184 | * event is in the queue. | * event is in the queue. |
2185 | */ | */ |
2186 | ||
2187 | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; | for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL; |
2188 | infoPtr = infoPtr->nextPtr) { | infoPtr = infoPtr->nextPtr) { |
2189 | if (pipeEvPtr->infoPtr == infoPtr) { | if (pipeEvPtr->infoPtr == infoPtr) { |
2190 | infoPtr->flags &= ~(PIPE_PENDING); | infoPtr->flags &= ~(PIPE_PENDING); |
2191 | break; | break; |
2192 | } | } |
2193 | } | } |
2194 | ||
2195 | /* | /* |
2196 | * Remove stale events. | * Remove stale events. |
2197 | */ | */ |
2198 | ||
2199 | if (!infoPtr) { | if (!infoPtr) { |
2200 | return 1; | return 1; |
2201 | } | } |
2202 | ||
2203 | /* | /* |
2204 | * Check to see if the pipe is readable. Note | * Check to see if the pipe is readable. Note |
2205 | * that we can't tell if a pipe is writable, so we always report it | * that we can't tell if a pipe is writable, so we always report it |
2206 | * as being writable unless we have detected EOF. | * as being writable unless we have detected EOF. |
2207 | */ | */ |
2208 | ||
2209 | filePtr = (WinFile*) ((PipeInfo*)infoPtr)->writeFile; | filePtr = (WinFile*) ((PipeInfo*)infoPtr)->writeFile; |
2210 | mask = 0; | mask = 0; |
2211 | if ((infoPtr->watchMask & TCL_WRITABLE) && | if ((infoPtr->watchMask & TCL_WRITABLE) && |
2212 | (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { | (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) { |
2213 | mask = TCL_WRITABLE; | mask = TCL_WRITABLE; |
2214 | } | } |
2215 | ||
2216 | filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile; | filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile; |
2217 | if ((infoPtr->watchMask & TCL_READABLE) && | if ((infoPtr->watchMask & TCL_READABLE) && |
2218 | (WaitForRead(infoPtr, 0) >= 0)) { | (WaitForRead(infoPtr, 0) >= 0)) { |
2219 | if (infoPtr->readFlags & PIPE_EOF) { | if (infoPtr->readFlags & PIPE_EOF) { |
2220 | mask = TCL_READABLE; | mask = TCL_READABLE; |
2221 | } else { | } else { |
2222 | mask |= TCL_READABLE; | mask |= TCL_READABLE; |
2223 | } | } |
2224 | } | } |
2225 | ||
2226 | /* | /* |
2227 | * Inform the channel of the events. | * Inform the channel of the events. |
2228 | */ | */ |
2229 | ||
2230 | Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); | Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); |
2231 | return 1; | return 1; |
2232 | } | } |
2233 | ||
2234 | /* | /* |
2235 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2236 | * | * |
2237 | * PipeWatchProc -- | * PipeWatchProc -- |
2238 | * | * |
2239 | * Called by the notifier to set up to watch for events on this | * Called by the notifier to set up to watch for events on this |
2240 | * channel. | * channel. |
2241 | * | * |
2242 | * Results: | * Results: |
2243 | * None. | * None. |
2244 | * | * |
2245 | * Side effects: | * Side effects: |
2246 | * None. | * None. |
2247 | * | * |
2248 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2249 | */ | */ |
2250 | ||
2251 | static void | static void |
2252 | PipeWatchProc( | PipeWatchProc( |
2253 | ClientData instanceData, /* Pipe state. */ | ClientData instanceData, /* Pipe state. */ |
2254 | int mask) /* What events to watch for, OR-ed | int mask) /* What events to watch for, OR-ed |
2255 | * combination of TCL_READABLE, | * combination of TCL_READABLE, |
2256 | * TCL_WRITABLE and TCL_EXCEPTION. */ | * TCL_WRITABLE and TCL_EXCEPTION. */ |
2257 | { | { |
2258 | PipeInfo **nextPtrPtr, *ptr; | PipeInfo **nextPtrPtr, *ptr; |
2259 | PipeInfo *infoPtr = (PipeInfo *) instanceData; | PipeInfo *infoPtr = (PipeInfo *) instanceData; |
2260 | int oldMask = infoPtr->watchMask; | int oldMask = infoPtr->watchMask; |
2261 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
2262 | ||
2263 | /* | /* |
2264 | * Since most of the work is handled by the background threads, | * Since most of the work is handled by the background threads, |
2265 | * we just need to update the watchMask and then force the notifier | * we just need to update the watchMask and then force the notifier |
2266 | * to poll once. | * to poll once. |
2267 | */ | */ |
2268 | ||
2269 | infoPtr->watchMask = mask & infoPtr->validMask; | infoPtr->watchMask = mask & infoPtr->validMask; |
2270 | if (infoPtr->watchMask) { | if (infoPtr->watchMask) { |
2271 | Tcl_Time blockTime = { 0, 0 }; | Tcl_Time blockTime = { 0, 0 }; |
2272 | if (!oldMask) { | if (!oldMask) { |
2273 | infoPtr->nextPtr = tsdPtr->firstPipePtr; | infoPtr->nextPtr = tsdPtr->firstPipePtr; |
2274 | tsdPtr->firstPipePtr = infoPtr; | tsdPtr->firstPipePtr = infoPtr; |
2275 | } | } |
2276 | Tcl_SetMaxBlockTime(&blockTime); | Tcl_SetMaxBlockTime(&blockTime); |
2277 | } else { | } else { |
2278 | if (oldMask) { | if (oldMask) { |
2279 | /* | /* |
2280 | * Remove the pipe from the list of watched pipes. | * Remove the pipe from the list of watched pipes. |
2281 | */ | */ |
2282 | ||
2283 | for (nextPtrPtr = &(tsdPtr->firstPipePtr), ptr = *nextPtrPtr; | for (nextPtrPtr = &(tsdPtr->firstPipePtr), ptr = *nextPtrPtr; |
2284 | ptr != NULL; | ptr != NULL; |
2285 | nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { | nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { |
2286 | if (infoPtr == ptr) { | if (infoPtr == ptr) { |
2287 | *nextPtrPtr = ptr->nextPtr; | *nextPtrPtr = ptr->nextPtr; |
2288 | break; | break; |
2289 | } | } |
2290 | } | } |
2291 | } | } |
2292 | } | } |
2293 | } | } |
2294 | ||
2295 | /* | /* |
2296 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2297 | * | * |
2298 | * PipeGetHandleProc -- | * PipeGetHandleProc -- |
2299 | * | * |
2300 | * Called from Tcl_GetChannelHandle to retrieve OS handles from | * Called from Tcl_GetChannelHandle to retrieve OS handles from |
2301 | * inside a command pipeline based channel. | * inside a command pipeline based channel. |
2302 | * | * |
2303 | * Results: | * Results: |
2304 | * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if | * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if |
2305 | * there is no handle for the specified direction. | * there is no handle for the specified direction. |
2306 | * | * |
2307 | * Side effects: | * Side effects: |
2308 | * None. | * None. |
2309 | * | * |
2310 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2311 | */ | */ |
2312 | ||
2313 | static int | static int |
2314 | PipeGetHandleProc( | PipeGetHandleProc( |
2315 | ClientData instanceData, /* The pipe state. */ | ClientData instanceData, /* The pipe state. */ |
2316 | int direction, /* TCL_READABLE or TCL_WRITABLE */ | int direction, /* TCL_READABLE or TCL_WRITABLE */ |
2317 | ClientData *handlePtr) /* Where to store the handle. */ | ClientData *handlePtr) /* Where to store the handle. */ |
2318 | { | { |
2319 | PipeInfo *infoPtr = (PipeInfo *) instanceData; | PipeInfo *infoPtr = (PipeInfo *) instanceData; |
2320 | WinFile *filePtr; | WinFile *filePtr; |
2321 | ||
2322 | if (direction == TCL_READABLE && infoPtr->readFile) { | if (direction == TCL_READABLE && infoPtr->readFile) { |
2323 | filePtr = (WinFile*) infoPtr->readFile; | filePtr = (WinFile*) infoPtr->readFile; |
2324 | *handlePtr = (ClientData) filePtr->handle; | *handlePtr = (ClientData) filePtr->handle; |
2325 | return TCL_OK; | return TCL_OK; |
2326 | } | } |
2327 | if (direction == TCL_WRITABLE && infoPtr->writeFile) { | if (direction == TCL_WRITABLE && infoPtr->writeFile) { |
2328 | filePtr = (WinFile*) infoPtr->writeFile; | filePtr = (WinFile*) infoPtr->writeFile; |
2329 | *handlePtr = (ClientData) filePtr->handle; | *handlePtr = (ClientData) filePtr->handle; |
2330 | return TCL_OK; | return TCL_OK; |
2331 | } | } |
2332 | return TCL_ERROR; | return TCL_ERROR; |
2333 | } | } |
2334 | ||
2335 | /* | /* |
2336 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2337 | * | * |
2338 | * Tcl_WaitPid -- | * Tcl_WaitPid -- |
2339 | * | * |
2340 | * Emulates the waitpid system call. | * Emulates the waitpid system call. |
2341 | * | * |
2342 | * Results: | * Results: |
2343 | * Returns 0 if the process is still alive, -1 on an error, or | * Returns 0 if the process is still alive, -1 on an error, or |
2344 | * the pid on a clean close. | * the pid on a clean close. |
2345 | * | * |
2346 | * Side effects: | * Side effects: |
2347 | * Unless WNOHANG is set and the wait times out, the process | * Unless WNOHANG is set and the wait times out, the process |
2348 | * information record will be deleted and the process handle | * information record will be deleted and the process handle |
2349 | * will be closed. | * will be closed. |
2350 | * | * |
2351 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2352 | */ | */ |
2353 | ||
2354 | Tcl_Pid | Tcl_Pid |
2355 | Tcl_WaitPid( | Tcl_WaitPid( |
2356 | Tcl_Pid pid, | Tcl_Pid pid, |
2357 | int *statPtr, | int *statPtr, |
2358 | int options) | int options) |
2359 | { | { |
2360 | ProcInfo *infoPtr, **prevPtrPtr; | ProcInfo *infoPtr, **prevPtrPtr; |
2361 | int flags; | int flags; |
2362 | Tcl_Pid result; | Tcl_Pid result; |
2363 | DWORD ret; | DWORD ret; |
2364 | ||
2365 | PipeInit(); | PipeInit(); |
2366 | ||
2367 | /* | /* |
2368 | * If no pid is specified, do nothing. | * If no pid is specified, do nothing. |
2369 | */ | */ |
2370 | ||
2371 | if (pid == 0) { | if (pid == 0) { |
2372 | *statPtr = 0; | *statPtr = 0; |
2373 | return 0; | return 0; |
2374 | } | } |
2375 | ||
2376 | /* | /* |
2377 | * Find the process on the process list. | * Find the process on the process list. |
2378 | */ | */ |
2379 | ||
2380 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
2381 | prevPtrPtr = &procList; | prevPtrPtr = &procList; |
2382 | for (infoPtr = procList; infoPtr != NULL; | for (infoPtr = procList; infoPtr != NULL; |
2383 | prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) { | prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) { |
2384 | if (infoPtr->hProcess == (HANDLE) pid) { | if (infoPtr->hProcess == (HANDLE) pid) { |
2385 | break; | break; |
2386 | } | } |
2387 | } | } |
2388 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
2389 | ||
2390 | /* | /* |
2391 | * If the pid is not one of the processes we know about (we started it) | * If the pid is not one of the processes we know about (we started it) |
2392 | * then do nothing. | * then do nothing. |
2393 | */ | */ |
2394 | ||
2395 | if (infoPtr == NULL) { | if (infoPtr == NULL) { |
2396 | *statPtr = 0; | *statPtr = 0; |
2397 | return 0; | return 0; |
2398 | } | } |
2399 | ||
2400 | /* | /* |
2401 | * Officially "wait" for it to finish. We either poll (WNOHANG) or | * Officially "wait" for it to finish. We either poll (WNOHANG) or |
2402 | * wait for an infinite amount of time. | * wait for an infinite amount of time. |
2403 | */ | */ |
2404 | ||
2405 | if (options & WNOHANG) { | if (options & WNOHANG) { |
2406 | flags = 0; | flags = 0; |
2407 | } else { | } else { |
2408 | flags = INFINITE; | flags = INFINITE; |
2409 | } | } |
2410 | ret = WaitForSingleObject(infoPtr->hProcess, flags); | ret = WaitForSingleObject(infoPtr->hProcess, flags); |
2411 | if (ret == WAIT_TIMEOUT) { | if (ret == WAIT_TIMEOUT) { |
2412 | *statPtr = 0; | *statPtr = 0; |
2413 | if (options & WNOHANG) { | if (options & WNOHANG) { |
2414 | return 0; | return 0; |
2415 | } else { | } else { |
2416 | result = 0; | result = 0; |
2417 | } | } |
2418 | } else if (ret != WAIT_FAILED) { | } else if (ret != WAIT_FAILED) { |
2419 | GetExitCodeProcess(infoPtr->hProcess, (DWORD*)statPtr); | GetExitCodeProcess(infoPtr->hProcess, (DWORD*)statPtr); |
2420 | *statPtr = ((*statPtr << 8) & 0xff00); | *statPtr = ((*statPtr << 8) & 0xff00); |
2421 | result = pid; | result = pid; |
2422 | } else { | } else { |
2423 | errno = ECHILD; | errno = ECHILD; |
2424 | *statPtr = ECHILD; | *statPtr = ECHILD; |
2425 | result = (Tcl_Pid) -1; | result = (Tcl_Pid) -1; |
2426 | } | } |
2427 | ||
2428 | /* | /* |
2429 | * Remove the process from the process list and close the process handle. | * Remove the process from the process list and close the process handle. |
2430 | */ | */ |
2431 | ||
2432 | CloseHandle(infoPtr->hProcess); | CloseHandle(infoPtr->hProcess); |
2433 | *prevPtrPtr = infoPtr->nextPtr; | *prevPtrPtr = infoPtr->nextPtr; |
2434 | ckfree((char*)infoPtr); | ckfree((char*)infoPtr); |
2435 | ||
2436 | return result; | return result; |
2437 | } | } |
2438 | ||
2439 | /* | /* |
2440 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2441 | * | * |
2442 | * TclWinAddProcess -- | * TclWinAddProcess -- |
2443 | * | * |
2444 | * Add a process to the process list so that we can use | * Add a process to the process list so that we can use |
2445 | * Tcl_WaitPid on the process. | * Tcl_WaitPid on the process. |
2446 | * | * |
2447 | * Results: | * Results: |
2448 | * None | * None |
2449 | * | * |
2450 | * Side effects: | * Side effects: |
2451 | * Adds the specified process handle to the process list so | * Adds the specified process handle to the process list so |
2452 | * Tcl_WaitPid knows about it. | * Tcl_WaitPid knows about it. |
2453 | * | * |
2454 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2455 | */ | */ |
2456 | ||
2457 | void | void |
2458 | TclWinAddProcess(hProcess, id) | TclWinAddProcess(hProcess, id) |
2459 | HANDLE hProcess; /* Handle to process */ | HANDLE hProcess; /* Handle to process */ |
2460 | DWORD id; /* Global process identifier */ | DWORD id; /* Global process identifier */ |
2461 | { | { |
2462 | ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo)); | ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo)); |
2463 | procPtr->hProcess = hProcess; | procPtr->hProcess = hProcess; |
2464 | procPtr->dwProcessId = id; | procPtr->dwProcessId = id; |
2465 | Tcl_MutexLock(&pipeMutex); | Tcl_MutexLock(&pipeMutex); |
2466 | procPtr->nextPtr = procList; | procPtr->nextPtr = procList; |
2467 | procList = procPtr; | procList = procPtr; |
2468 | Tcl_MutexUnlock(&pipeMutex); | Tcl_MutexUnlock(&pipeMutex); |
2469 | } | } |
2470 | ||
2471 | /* | /* |
2472 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2473 | * | * |
2474 | * Tcl_PidObjCmd -- | * Tcl_PidObjCmd -- |
2475 | * | * |
2476 | * This procedure is invoked to process the "pid" Tcl command. | * This procedure is invoked to process the "pid" Tcl command. |
2477 | * See the user documentation for details on what it does. | * See the user documentation for details on what it does. |
2478 | * | * |
2479 | * Results: | * Results: |
2480 | * A standard Tcl result. | * A standard Tcl result. |
2481 | * | * |
2482 | * Side effects: | * Side effects: |
2483 | * See the user documentation. | * See the user documentation. |
2484 | * | * |
2485 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2486 | */ | */ |
2487 | ||
2488 | /* ARGSUSED */ | /* ARGSUSED */ |
2489 | int | int |
2490 | Tcl_PidObjCmd( | Tcl_PidObjCmd( |
2491 | ClientData dummy, /* Not used. */ | ClientData dummy, /* Not used. */ |
2492 | Tcl_Interp *interp, /* Current interpreter. */ | Tcl_Interp *interp, /* Current interpreter. */ |
2493 | int objc, /* Number of arguments. */ | int objc, /* Number of arguments. */ |
2494 | Tcl_Obj *CONST *objv) /* Argument strings. */ | Tcl_Obj *CONST *objv) /* Argument strings. */ |
2495 | { | { |
2496 | Tcl_Channel chan; | Tcl_Channel chan; |
2497 | Tcl_ChannelType *chanTypePtr; | Tcl_ChannelType *chanTypePtr; |
2498 | PipeInfo *pipePtr; | PipeInfo *pipePtr; |
2499 | int i; | int i; |
2500 | Tcl_Obj *resultPtr; | Tcl_Obj *resultPtr; |
2501 | char buf[TCL_INTEGER_SPACE]; | char buf[TCL_INTEGER_SPACE]; |
2502 | ||
2503 | if (objc > 2) { | if (objc > 2) { |
2504 | Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); | Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); |
2505 | return TCL_ERROR; | return TCL_ERROR; |
2506 | } | } |
2507 | if (objc == 1) { | if (objc == 1) { |
2508 | resultPtr = Tcl_GetObjResult(interp); | resultPtr = Tcl_GetObjResult(interp); |
2509 | wsprintfA(buf, "%lu", (unsigned long) getpid()); | wsprintfA(buf, "%lu", (unsigned long) getpid()); |
2510 | Tcl_SetStringObj(resultPtr, buf, -1); | Tcl_SetStringObj(resultPtr, buf, -1); |
2511 | } else { | } else { |
2512 | chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), | chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), |
2513 | NULL); | NULL); |
2514 | if (chan == (Tcl_Channel) NULL) { | if (chan == (Tcl_Channel) NULL) { |
2515 | return TCL_ERROR; | return TCL_ERROR; |
2516 | } | } |
2517 | chanTypePtr = Tcl_GetChannelType(chan); | chanTypePtr = Tcl_GetChannelType(chan); |
2518 | if (chanTypePtr != &pipeChannelType) { | if (chanTypePtr != &pipeChannelType) { |
2519 | return TCL_OK; | return TCL_OK; |
2520 | } | } |
2521 | ||
2522 | pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); | pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); |
2523 | resultPtr = Tcl_GetObjResult(interp); | resultPtr = Tcl_GetObjResult(interp); |
2524 | for (i = 0; i < pipePtr->numPids; i++) { | for (i = 0; i < pipePtr->numPids; i++) { |
2525 | wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); | wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); |
2526 | Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, | Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, |
2527 | Tcl_NewStringObj(buf, -1)); | Tcl_NewStringObj(buf, -1)); |
2528 | } | } |
2529 | } | } |
2530 | return TCL_OK; | return TCL_OK; |
2531 | } | } |
2532 | ||
2533 | /* | /* |
2534 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2535 | * | * |
2536 | * WaitForRead -- | * WaitForRead -- |
2537 | * | * |
2538 | * Wait until some data is available, the pipe is at | * Wait until some data is available, the pipe is at |
2539 | * EOF or the reader thread is blocked waiting for data (if the | * EOF or the reader thread is blocked waiting for data (if the |
2540 | * channel is in non-blocking mode). | * channel is in non-blocking mode). |
2541 | * | * |
2542 | * Results: | * Results: |
2543 | * Returns 1 if pipe is readable. Returns 0 if there is no data | * Returns 1 if pipe is readable. Returns 0 if there is no data |
2544 | * on the pipe, but there is buffered data. Returns -1 if an | * on the pipe, but there is buffered data. Returns -1 if an |
2545 | * error occurred. If an error occurred, the threads may not | * error occurred. If an error occurred, the threads may not |
2546 | * be synchronized. | * be synchronized. |
2547 | * | * |
2548 | * Side effects: | * Side effects: |
2549 | * Updates the shared state flags and may consume 1 byte of data | * Updates the shared state flags and may consume 1 byte of data |
2550 | * from the pipe. If no error occurred, the reader thread is | * from the pipe. If no error occurred, the reader thread is |
2551 | * blocked waiting for a signal from the main thread. | * blocked waiting for a signal from the main thread. |
2552 | * | * |
2553 | *---------------------------------------------------------------------- | *---------------------------------------------------------------------- |
2554 | */ | */ |
2555 | ||
2556 | static int | static int |
2557 | WaitForRead( | WaitForRead( |
2558 | PipeInfo *infoPtr, /* Pipe state. */ | PipeInfo *infoPtr, /* Pipe state. */ |
2559 | int blocking) /* Indicates whether call should be | int blocking) /* Indicates whether call should be |
2560 | * blocking or not. */ | * blocking or not. */ |
2561 | { | { |
2562 | DWORD timeout, count; | DWORD timeout, count; |
2563 | HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; | HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle; |
2564 | ||
2565 | while (1) { | while (1) { |
2566 | /* | /* |
2567 | * Synchronize with the reader thread. | * Synchronize with the reader thread. |
2568 | */ | */ |
2569 | ||
2570 | timeout = blocking ? INFINITE : 0; | timeout = blocking ? INFINITE : 0; |
2571 | if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { | if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) { |
2572 | /* | /* |
2573 | * The reader thread is blocked waiting for data and the channel | * The reader thread is blocked waiting for data and the channel |
2574 | * is in non-blocking mode. | * is in non-blocking mode. |
2575 | */ | */ |
2576 | ||
2577 | errno = EAGAIN; | errno = EAGAIN; |
2578 | return -1; | return -1; |
2579 | } | } |
2580 | ||
2581 | /* | /* |
2582 | * At this point, the two threads are synchronized, so it is safe | * At this point, the two threads are synchronized, so it is safe |
2583 | * to access shared state. | * to access shared state. |
2584 | */ | */ |
2585 | ||
2586 | ||
2587 | /* | /* |
2588 | * If the pipe has hit EOF, it is always readable. | * If the pipe has hit EOF, it is always readable. |
2589 | */ | */ |
2590 | ||
2591 | if (infoPtr->readFlags & PIPE_EOF) { | if (infoPtr->readFlags & PIPE_EOF) { |
2592 | return 1; | return 1; |
2593 | } | } |
2594 | ||
2595 | /* | /* |
2596 | * Check to see if there is any data sitting in the pipe. | * Check to see if there is any data sitting in the pipe. |
2597 | */ | */ |
2598 | ||
2599 | if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0, | if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0, |
2600 | (LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) { | (LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) { |
2601 | TclWinConvertError(GetLastError()); | TclWinConvertError(GetLastError()); |
2602 | /* | /* |
2603 | * Check to see if the peek failed because of EOF. | * Check to see if the peek failed because of EOF. |
2604 | */ | */ |
2605 | ||
2606 | if (errno == EPIPE) { | if (errno == EPIPE) { |
2607 | infoPtr->readFlags |= PIPE_EOF; | infoPtr->readFlags |= PIPE_EOF; |
2608 | return 1; | return 1; |
2609 | } | } |
2610 | ||
2611 | /* | /* |
2612 | * Ignore errors if there is data in the buffer. | * Ignore errors if there is data in the buffer. |
2613 | */ | */ |
2614 | ||
2615 | if (infoPtr->readFlags & PIPE_EXTRABYTE) { | if (infoPtr->readFlags & PIPE_EXTRABYTE) { |
2616 | return 0; | return 0; |
2617 | } else { | } else { |
2618 | return -1; | return -1; |
2619 | } | } |