OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [win/] [tclWinPipe.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.