OSDN Git Service

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