root/tcl-8.0/trunk/win/tclWinPipe.c @ 1151

Revision 1151, 67.3 KB (checked in by ucackha, 14 years ago)

Add TclpGetProcess? which just does the opposite of TclpGetPid? (ie return the process handle given an id rather than returning an id given a handle). KH

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