/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinpipe.c
ViewVC logotype

Diff of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinpipe.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch 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              }              }