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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [win/] [tclWinPipe.c] - Diff between revs 578 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 578 Rev 1765
/*
/*
 * tclWinPipe.c --
 * tclWinPipe.c --
 *
 *
 *      This file implements the Windows-specific exec pipeline functions,
 *      This file implements the Windows-specific exec pipeline functions,
 *      the "pipe" channel driver, and the "pid" Tcl command.
 *      the "pipe" channel driver, and the "pid" Tcl command.
 *
 *
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 *
 *
 * See the file "license.terms" for information on usage and redistribution
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *
 * RCS: @(#) $Id: tclWinPipe.c,v 1.1.1.1 2002-01-16 10:25:39 markom Exp $
 * RCS: @(#) $Id: tclWinPipe.c,v 1.1.1.1 2002-01-16 10:25:39 markom Exp $
 */
 */
 
 
#include "tclWinInt.h"
#include "tclWinInt.h"
 
 
/* CYGNUS LOCAL */
/* CYGNUS LOCAL */
#ifndef __CYGWIN__
#ifndef __CYGWIN__
#include <dos.h>
#include <dos.h>
#endif
#endif
/* END CYGNUS LOCAL */
/* END CYGNUS LOCAL */
#include <fcntl.h>
#include <fcntl.h>
#include <io.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/stat.h>
 
 
/*
/*
 * The following variable is used to tell whether this module has been
 * The following variable is used to tell whether this module has been
 * initialized.
 * initialized.
 */
 */
 
 
static int initialized = 0;
static int initialized = 0;
 
 
/*
/*
 * The following defines identify the various types of applications that
 * The following defines identify the various types of applications that
 * run under windows.  There is special case code for the various types.
 * run under windows.  There is special case code for the various types.
 */
 */
 
 
#define APPL_NONE       0
#define APPL_NONE       0
#define APPL_DOS        1
#define APPL_DOS        1
#define APPL_WIN3X      2
#define APPL_WIN3X      2
#define APPL_WIN32      3
#define APPL_WIN32      3
 
 
/*
/*
 * The following constants and structures are used to encapsulate the state
 * The following constants and structures are used to encapsulate the state
 * of various types of files used in a pipeline.
 * of various types of files used in a pipeline.
 */
 */
 
 
#define WIN32S_PIPE 1           /* Win32s emulated pipe. */
#define WIN32S_PIPE 1           /* Win32s emulated pipe. */
#define WIN32S_TMPFILE 2        /* Win32s emulated temporary file. */
#define WIN32S_TMPFILE 2        /* Win32s emulated temporary file. */
#define WIN_FILE 3              /* Basic Win32 file. */
#define WIN_FILE 3              /* Basic Win32 file. */
 
 
/*
/*
 * This structure encapsulates the common state associated with all file
 * This structure encapsulates the common state associated with all file
 * types used in a pipeline.
 * types used in a pipeline.
 */
 */
 
 
typedef struct WinFile {
typedef struct WinFile {
    int type;                   /* One of the file types defined above. */
    int type;                   /* One of the file types defined above. */
    HANDLE handle;              /* Open file handle. */
    HANDLE handle;              /* Open file handle. */
} WinFile;
} WinFile;
 
 
/*
/*
 * The following structure is used to keep track of temporary files under
 * The following structure is used to keep track of temporary files under
 * Win32s and delete the disk file when the open handle is closed.
 * Win32s and delete the disk file when the open handle is closed.
 * The type field will be WIN32S_TMPFILE.
 * The type field will be WIN32S_TMPFILE.
 */
 */
 
 
typedef struct TmpFile {
typedef struct TmpFile {
    WinFile file;               /* Common part. */
    WinFile file;               /* Common part. */
    char name[MAX_PATH];        /* Name of temp file. */
    char name[MAX_PATH];        /* Name of temp file. */
} TmpFile;
} TmpFile;
 
 
/*
/*
 * The following structure represents a synchronous pipe under Win32s.
 * The following structure represents a synchronous pipe under Win32s.
 * The type field will be WIN32S_PIPE.  The handle field will refer to
 * The type field will be WIN32S_PIPE.  The handle field will refer to
 * an open file when Tcl is reading from the "pipe", otherwise it is
 * an open file when Tcl is reading from the "pipe", otherwise it is
 * INVALID_HANDLE_VALUE.
 * INVALID_HANDLE_VALUE.
 */
 */
 
 
typedef struct WinPipe {
typedef struct WinPipe {
    WinFile file;               /* Common part. */
    WinFile file;               /* Common part. */
    struct WinPipe *otherPtr;   /* Pointer to the WinPipe structure that
    struct WinPipe *otherPtr;   /* Pointer to the WinPipe structure that
                                 * corresponds to the other end of this
                                 * corresponds to the other end of this
                                 * pipe. */
                                 * pipe. */
    char *fileName;             /* The name of the staging file that gets
    char *fileName;             /* The name of the staging file that gets
                                 * the data written to this pipe.  Malloc'd.
                                 * the data written to this pipe.  Malloc'd.
                                 * and shared by both ends of the pipe.  Only
                                 * and shared by both ends of the pipe.  Only
                                 * when both ends are freed will fileName be
                                 * when both ends are freed will fileName be
                                 * freed and the file it refers to deleted. */
                                 * freed and the file it refers to deleted. */
} WinPipe;
} WinPipe;
 
 
/*
/*
 * This list is used to map from pids to process handles.
 * This list is used to map from pids to process handles.
 */
 */
 
 
typedef struct ProcInfo {
typedef struct ProcInfo {
    HANDLE hProcess;
    HANDLE hProcess;
    DWORD dwProcessId;
    DWORD dwProcessId;
    struct ProcInfo *nextPtr;
    struct ProcInfo *nextPtr;
} ProcInfo;
} ProcInfo;
 
 
static ProcInfo *procList;
static ProcInfo *procList;
 
 
/*
/*
 * State flags used in the PipeInfo structure below.
 * State flags used in the PipeInfo structure below.
 */
 */
 
 
#define PIPE_PENDING    (1<<0)  /* Message is pending in the queue. */
#define PIPE_PENDING    (1<<0)  /* Message is pending in the queue. */
#define PIPE_ASYNC      (1<<1)  /* Channel is non-blocking. */
#define PIPE_ASYNC      (1<<1)  /* Channel is non-blocking. */
#define PIPE_READABLE   (1<<2)  /* Pipe is readable. */
#define PIPE_READABLE   (1<<2)  /* Pipe is readable. */
#define PIPE_CLOSED     (1<<3)  /* Pipe is being closed. */
#define PIPE_CLOSED     (1<<3)  /* Pipe is being closed. */
#define PIPE_HAS_THREAD (1<<4)  /* Pipe has an associated thread. */
#define PIPE_HAS_THREAD (1<<4)  /* Pipe has an associated thread. */
#define PIPE_READAHEAD  (1<<5)  /* Readahead byte is valid. */
#define PIPE_READAHEAD  (1<<5)  /* Readahead byte is valid. */
 
 
/*
/*
 * This structure describes per-instance data for a pipe based channel.
 * This structure describes per-instance data for a pipe based channel.
 */
 */
 
 
typedef struct PipeInfo {
typedef struct PipeInfo {
    Tcl_Channel channel;        /* Pointer to channel structure. */
    Tcl_Channel channel;        /* Pointer to channel structure. */
    int validMask;              /* OR'ed combination of TCL_READABLE,
    int validMask;              /* OR'ed combination of TCL_READABLE,
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
                                 * which operations are valid on the file. */
                                 * which operations are valid on the file. */
    int watchMask;              /* OR'ed combination of TCL_READABLE,
    int watchMask;              /* OR'ed combination of TCL_READABLE,
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
                                 * which events should be reported. */
                                 * which events should be reported. */
    int flags;                  /* State flags, see above for a list. */
    int flags;                  /* State flags, see above for a list. */
    TclFile readFile;           /* Output from pipe. */
    TclFile readFile;           /* Output from pipe. */
    TclFile writeFile;          /* Input from pipe. */
    TclFile writeFile;          /* Input from pipe. */
    TclFile errorFile;          /* Error output from pipe. */
    TclFile errorFile;          /* Error output from pipe. */
    int numPids;                /* Number of processes attached to pipe. */
    int numPids;                /* Number of processes attached to pipe. */
    Tcl_Pid *pidPtr;            /* Pids of attached processes. */
    Tcl_Pid *pidPtr;            /* Pids of attached processes. */
    struct PipeInfo *nextPtr;   /* Pointer to next registered pipe. */
    struct PipeInfo *nextPtr;   /* Pointer to next registered pipe. */
    /* CYGNUS LOCAL: Several new fields.  */
    /* CYGNUS LOCAL: Several new fields.  */
    HANDLE flagsMutex;          /* Mutex to control access to flags. */
    HANDLE flagsMutex;          /* Mutex to control access to flags. */
    HANDLE mutex;               /* Mutex for read fields.  */
    HANDLE mutex;               /* Mutex for read fields.  */
    HANDLE tryReadEvent;        /* Event to tell thread to try a read.  */
    HANDLE tryReadEvent;        /* Event to tell thread to try a read.  */
    char readAhead;             /* Read ahead byte.  */
    char readAhead;             /* Read ahead byte.  */
} PipeInfo;
} PipeInfo;
 
 
/*
/*
 * The following pointer refers to the head of the list of pipes
 * The following pointer refers to the head of the list of pipes
 * that are being watched for file events.
 * that are being watched for file events.
 */
 */
 
 
static PipeInfo *firstPipePtr;
static PipeInfo *firstPipePtr;
 
 
/*
/*
 * The following structure is what is added to the Tcl event queue when
 * The following structure is what is added to the Tcl event queue when
 * pipe events are generated.
 * pipe events are generated.
 */
 */
 
 
typedef struct PipeEvent {
typedef struct PipeEvent {
    Tcl_Event header;           /* Information that is standard for
    Tcl_Event header;           /* Information that is standard for
                                 * all events. */
                                 * all events. */
    PipeInfo *infoPtr;          /* Pointer to pipe info structure.  Note
    PipeInfo *infoPtr;          /* Pointer to pipe info structure.  Note
                                 * that we still have to verify that the
                                 * that we still have to verify that the
                                 * pipe exists before dereferencing this
                                 * pipe exists before dereferencing this
                                 * pointer. */
                                 * pointer. */
} PipeEvent;
} PipeEvent;
 
 
/*
/*
 * Declarations for functions used only in this file.
 * Declarations for functions used only in this file.
 */
 */
 
 
static int      ApplicationType(Tcl_Interp *interp, const char *fileName,
static int      ApplicationType(Tcl_Interp *interp, const char *fileName,
                    char *fullName);
                    char *fullName);
static void     BuildCommandLine(int argc, char **argv, Tcl_DString *linePtr);
static void     BuildCommandLine(int argc, char **argv, Tcl_DString *linePtr);
static void     CopyChannel(HANDLE dst, HANDLE src);
static void     CopyChannel(HANDLE dst, HANDLE src);
static BOOL     HasConsole(void);
static BOOL     HasConsole(void);
static TclFile  MakeFile(HANDLE handle);
static TclFile  MakeFile(HANDLE handle);
static char *   MakeTempFile(Tcl_DString *namePtr);
static char *   MakeTempFile(Tcl_DString *namePtr);
static int      PipeBlockModeProc(ClientData instanceData, int mode);
static int      PipeBlockModeProc(ClientData instanceData, int mode);
static void     PipeCheckProc _ANSI_ARGS_((ClientData clientData,
static void     PipeCheckProc _ANSI_ARGS_((ClientData clientData,
                    int flags));
                    int flags));
static int      PipeCloseProc(ClientData instanceData, Tcl_Interp *interp);
static int      PipeCloseProc(ClientData instanceData, Tcl_Interp *interp);
static int      PipeEventProc(Tcl_Event *evPtr, int flags);
static int      PipeEventProc(Tcl_Event *evPtr, int flags);
static void     PipeExitHandler(ClientData clientData);
static void     PipeExitHandler(ClientData clientData);
static int      PipeGetHandleProc(ClientData instanceData, int direction,
static int      PipeGetHandleProc(ClientData instanceData, int direction,
                    ClientData *handlePtr);
                    ClientData *handlePtr);
static void     PipeInit(void);
static void     PipeInit(void);
static int      PipeInputProc(ClientData instanceData, char *buf, int toRead,
static int      PipeInputProc(ClientData instanceData, char *buf, int toRead,
                    int *errorCode);
                    int *errorCode);
static int      PipeOutputProc(ClientData instanceData, char *buf, int toWrite,
static int      PipeOutputProc(ClientData instanceData, char *buf, int toWrite,
                    int *errorCode);
                    int *errorCode);
static void     PipeWatchProc(ClientData instanceData, int mask);
static void     PipeWatchProc(ClientData instanceData, int mask);
static void     PipeSetupProc _ANSI_ARGS_((ClientData clientData,
static void     PipeSetupProc _ANSI_ARGS_((ClientData clientData,
                    int flags));
                    int flags));
static int      TempFileName(char name[MAX_PATH]);
static int      TempFileName(char name[MAX_PATH]);
 
 
/* CYGNUS LOCAL.  */
/* CYGNUS LOCAL.  */
static int      PipeGetFlags _ANSI_ARGS_((PipeInfo *));
static int      PipeGetFlags _ANSI_ARGS_((PipeInfo *));
static void     PipeSetFlag _ANSI_ARGS_((PipeInfo *, int));
static void     PipeSetFlag _ANSI_ARGS_((PipeInfo *, int));
static void     PipeResetFlag _ANSI_ARGS_((PipeInfo *, int));
static void     PipeResetFlag _ANSI_ARGS_((PipeInfo *, int));
static DWORD    PipeThread _ANSI_ARGS_((LPVOID arg));
static DWORD    PipeThread _ANSI_ARGS_((LPVOID arg));
static LRESULT CALLBACK PipeProc _ANSI_ARGS_((HWND hwnd, UINT message,
static LRESULT CALLBACK PipeProc _ANSI_ARGS_((HWND hwnd, UINT message,
                            WPARAM wParam, LPARAM lParam));
                            WPARAM wParam, LPARAM lParam));
 
 
/*
/*
 * This structure describes the channel type structure for command pipe
 * This structure describes the channel type structure for command pipe
 * based IO.
 * based IO.
 */
 */
 
 
static Tcl_ChannelType pipeChannelType = {
static Tcl_ChannelType pipeChannelType = {
    "pipe",                     /* Type name. */
    "pipe",                     /* Type name. */
    PipeBlockModeProc,          /* Set blocking or non-blocking mode.*/
    PipeBlockModeProc,          /* Set blocking or non-blocking mode.*/
    PipeCloseProc,              /* Close proc. */
    PipeCloseProc,              /* Close proc. */
    PipeInputProc,              /* Input proc. */
    PipeInputProc,              /* Input proc. */
    PipeOutputProc,             /* Output proc. */
    PipeOutputProc,             /* Output proc. */
    NULL,                       /* Seek proc. */
    NULL,                       /* Seek proc. */
    NULL,                       /* Set option proc. */
    NULL,                       /* Set option proc. */
    NULL,                       /* Get option proc. */
    NULL,                       /* Get option proc. */
    PipeWatchProc,              /* Set up notifier to watch the channel. */
    PipeWatchProc,              /* Set up notifier to watch the channel. */
    PipeGetHandleProc,          /* Get an OS handle from channel. */
    PipeGetHandleProc,          /* Get an OS handle from channel. */
};
};
 
 
/* CYGNUS LOCAL: Event notification window.  */
/* CYGNUS LOCAL: Event notification window.  */
 
 
static HWND pipeHwnd;
static HWND pipeHwnd;
 
 
#define PIPE_MESSAGE (WM_USER + 1)
#define PIPE_MESSAGE (WM_USER + 1)


/* CYGNUS LOCAL: Because we use a thread that manipulates the flags
/* CYGNUS LOCAL: Because we use a thread that manipulates the flags
   field, we use helper routines for the field.  */
   field, we use helper routines for the field.  */
 
 
static int
static int
PipeGetFlags(pipe)
PipeGetFlags(pipe)
    PipeInfo *pipe;
    PipeInfo *pipe;
{
{
    int flags;
    int flags;
 
 
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    flags = pipe->flags;
    flags = pipe->flags;
    ReleaseMutex(pipe->flagsMutex);
    ReleaseMutex(pipe->flagsMutex);
    return flags;
    return flags;
}
}
 
 
static void
static void
PipeSetFlag(pipe, flag)
PipeSetFlag(pipe, flag)
    PipeInfo *pipe;
    PipeInfo *pipe;
    int flag;
    int flag;
{
{
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    pipe->flags |= flag;
    pipe->flags |= flag;
    ReleaseMutex(pipe->flagsMutex);
    ReleaseMutex(pipe->flagsMutex);
}
}
 
 
static void
static void
PipeResetFlag(pipe, flag)
PipeResetFlag(pipe, flag)
    PipeInfo *pipe;
    PipeInfo *pipe;
    int flag;
    int flag;
{
{
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    WaitForSingleObject(pipe->flagsMutex, INFINITE);
    pipe->flags &= ~ (flag);
    pipe->flags &= ~ (flag);
    ReleaseMutex(pipe->flagsMutex);
    ReleaseMutex(pipe->flagsMutex);
}
}
 
 
/* CYGNUS LOCAL: We use a thread to detect when a pipe may be read.
/* CYGNUS LOCAL: We use a thread to detect when a pipe may be read.
   The thread runs this function.  The argument is the pipe to read.  */
   The thread runs this function.  The argument is the pipe to read.  */
 
 
static DWORD
static DWORD
PipeThread(arg)
PipeThread(arg)
    LPVOID arg;
    LPVOID arg;
{
{
    PipeInfo *pipe = (PipeInfo *) arg;
    PipeInfo *pipe = (PipeInfo *) arg;
    WinFile *file = (WinFile*) pipe->readFile;
    WinFile *file = (WinFile*) pipe->readFile;
    HANDLE handle = file->handle;
    HANDLE handle = file->handle;
 
 
    while (1) {
    while (1) {
        char b;
        char b;
        DWORD got;
        DWORD got;
 
 
        WaitForSingleObject(pipe->tryReadEvent, INFINITE);
        WaitForSingleObject(pipe->tryReadEvent, INFINITE);
 
 
        if (PipeGetFlags(pipe) & PIPE_CLOSED) {
        if (PipeGetFlags(pipe) & PIPE_CLOSED) {
            break;
            break;
        }
        }
 
 
        WaitForSingleObject(pipe->mutex, INFINITE);
        WaitForSingleObject(pipe->mutex, INFINITE);
 
 
        if ((PipeGetFlags(pipe) & PIPE_READAHEAD) == 0) {
        if ((PipeGetFlags(pipe) & PIPE_READAHEAD) == 0) {
            if (ReadFile(handle, &b, 1, &got, NULL) && got == 1) {
            if (ReadFile(handle, &b, 1, &got, NULL) && got == 1) {
                pipe->readAhead = b;
                pipe->readAhead = b;
                PipeSetFlag(pipe, PIPE_READAHEAD);
                PipeSetFlag(pipe, PIPE_READAHEAD);
            }
            }
        }
        }
 
 
        PipeSetFlag(pipe, PIPE_READABLE);
        PipeSetFlag(pipe, PIPE_READABLE);
 
 
        /* We've indicated that the pipe is readable, so ignore any
        /* We've indicated that the pipe is readable, so ignore any
           recent requests to do so.  */
           recent requests to do so.  */
        ResetEvent(pipe->tryReadEvent);
        ResetEvent(pipe->tryReadEvent);
 
 
        ReleaseMutex(pipe->mutex);
        ReleaseMutex(pipe->mutex);
 
 
        if (PipeGetFlags(pipe) & PIPE_CLOSED) {
        if (PipeGetFlags(pipe) & PIPE_CLOSED) {
            break;
            break;
        }
        }
 
 
        /* Post a message to wake up the event loop.  */
        /* Post a message to wake up the event loop.  */
        PostMessage(pipeHwnd, PIPE_MESSAGE, 0, (LPARAM) pipe);
        PostMessage(pipeHwnd, PIPE_MESSAGE, 0, (LPARAM) pipe);
    }
    }
 
 
    /* PipeCloseProc will set PIPE_CLOSED when the pipe is ready to be
    /* PipeCloseProc will set PIPE_CLOSED when the pipe is ready to be
       closed and freed.  */
       closed and freed.  */
 
 
    CloseHandle(pipe->flagsMutex);
    CloseHandle(pipe->flagsMutex);
    CloseHandle(pipe->tryReadEvent);
    CloseHandle(pipe->tryReadEvent);
    CloseHandle(pipe->mutex);
    CloseHandle(pipe->mutex);
    ckfree((char *)pipe);
    ckfree((char *)pipe);
    return 0;
    return 0;
}
}
 
 
/* CYGNUS LOCAL: This function is called when the PipeThread posts a
/* CYGNUS LOCAL: This function is called when the PipeThread posts a
   message.  */
   message.  */
 
 
static LRESULT CALLBACK
static LRESULT CALLBACK
PipeProc(hwnd, message, wParam, lParam)
PipeProc(hwnd, message, wParam, lParam)
    HWND hwnd;
    HWND hwnd;
    UINT message;
    UINT message;
    WPARAM wParam;
    WPARAM wParam;
    LPARAM lParam;
    LPARAM lParam;
{
{
    if (message != PIPE_MESSAGE) {
    if (message != PIPE_MESSAGE) {
        return DefWindowProc(hwnd, message, wParam, lParam);
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    }
 
 
    /* This function really only exists to wake up the event loop.  We
    /* This function really only exists to wake up the event loop.  We
       don't actually have to do anything.  */
       don't actually have to do anything.  */
 
 
    return 0;
    return 0;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeInit --
 * PipeInit --
 *
 *
 *      This function initializes the static variables for this file.
 *      This function initializes the static variables for this file.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Creates a new event source.
 *      Creates a new event source.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
PipeInit()
PipeInit()
{
{
    WNDCLASS class;
    WNDCLASS class;
 
 
    initialized = 1;
    initialized = 1;
    firstPipePtr = NULL;
    firstPipePtr = NULL;
    procList = NULL;
    procList = NULL;
    Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
    Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
    Tcl_CreateExitHandler(PipeExitHandler, NULL);
    Tcl_CreateExitHandler(PipeExitHandler, NULL);
 
 
    /* CYGNUS LOCAL: Create a window for asynchronous notification.  */
    /* CYGNUS LOCAL: Create a window for asynchronous notification.  */
 
 
    class.style = 0;
    class.style = 0;
    class.cbClsExtra = 0;
    class.cbClsExtra = 0;
    class.cbWndExtra = 0;
    class.cbWndExtra = 0;
    class.hInstance = TclWinGetTclInstance();
    class.hInstance = TclWinGetTclInstance();
    class.hbrBackground = NULL;
    class.hbrBackground = NULL;
    class.lpszMenuName = NULL;
    class.lpszMenuName = NULL;
    class.lpszClassName = "TclPipe";
    class.lpszClassName = "TclPipe";
    class.lpfnWndProc = PipeProc;
    class.lpfnWndProc = PipeProc;
    class.hIcon = NULL;
    class.hIcon = NULL;
    class.hCursor = NULL;
    class.hCursor = NULL;
 
 
    if (RegisterClass(&class)) {
    if (RegisterClass(&class)) {
        pipeHwnd = CreateWindow("TclPipe", "TclPipe", WS_TILED, 0, 0,
        pipeHwnd = CreateWindow("TclPipe", "TclPipe", WS_TILED, 0, 0,
                0, 0, NULL, NULL, class.hInstance, NULL);
                0, 0, NULL, NULL, class.hInstance, NULL);
    } else {
    } else {
        pipeHwnd = NULL;
        pipeHwnd = NULL;
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeExitHandler --
 * PipeExitHandler --
 *
 *
 *      This function is called to cleanup the pipe module before
 *      This function is called to cleanup the pipe module before
 *      Tcl is unloaded.
 *      Tcl is unloaded.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Removes the pipe event source.
 *      Removes the pipe event source.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
PipeExitHandler(clientData)
PipeExitHandler(clientData)
    ClientData clientData;      /* Old window proc */
    ClientData clientData;      /* Old window proc */
{
{
    Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);
    Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);
    initialized = 0;
    initialized = 0;
    /* CYGNUS LOCAL: Delete the window.  */
    /* CYGNUS LOCAL: Delete the window.  */
    UnregisterClass("TclPipe", TclWinGetTclInstance());
    UnregisterClass("TclPipe", TclWinGetTclInstance());
    if (pipeHwnd != NULL) {
    if (pipeHwnd != NULL) {
        DestroyWindow(pipeHwnd);
        DestroyWindow(pipeHwnd);
        pipeHwnd = NULL;
        pipeHwnd = NULL;
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeSetupProc --
 * PipeSetupProc --
 *
 *
 *      This procedure is invoked before Tcl_DoOneEvent blocks waiting
 *      This procedure is invoked before Tcl_DoOneEvent blocks waiting
 *      for an event.
 *      for an event.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Adjusts the block time if needed.
 *      Adjusts the block time if needed.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
void
void
PipeSetupProc(data, flags)
PipeSetupProc(data, flags)
    ClientData data;            /* Not used. */
    ClientData data;            /* Not used. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
{
{
    PipeInfo *infoPtr;
    PipeInfo *infoPtr;
    Tcl_Time blockTime = { 0, 0 };
    Tcl_Time blockTime = { 0, 0 };
 
 
    if (!(flags & TCL_FILE_EVENTS)) {
    if (!(flags & TCL_FILE_EVENTS)) {
        return;
        return;
    }
    }
 
 
    /*
    /*
     * Check to see if there is a watched pipe.  If so, poll.
     * Check to see if there is a watched pipe.  If so, poll.
     */
     */
 
 
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
        /* CYGNUS LOCAL: Only poll for a readable pipe if it really is
        /* CYGNUS LOCAL: Only poll for a readable pipe if it really is
           readable.  */
           readable.  */
        if ((infoPtr->watchMask &~ TCL_READABLE)
        if ((infoPtr->watchMask &~ TCL_READABLE)
            || ((infoPtr->watchMask & TCL_READABLE)
            || ((infoPtr->watchMask & TCL_READABLE)
                && ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
                && ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
                    || (PipeGetFlags(infoPtr) & PIPE_READABLE)))) {
                    || (PipeGetFlags(infoPtr) & PIPE_READABLE)))) {
            Tcl_SetMaxBlockTime(&blockTime);
            Tcl_SetMaxBlockTime(&blockTime);
            break;
            break;
        } else if (infoPtr->watchMask & TCL_READABLE) {
        } else if (infoPtr->watchMask & TCL_READABLE) {
            /* CYGNUS LOCAL: Tell the thread to try a read, and let us
            /* CYGNUS LOCAL: Tell the thread to try a read, and let us
               know when it is done.  */
               know when it is done.  */
            SetEvent(infoPtr->tryReadEvent);
            SetEvent(infoPtr->tryReadEvent);
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeCheckProc --
 * PipeCheckProc --
 *
 *
 *      This procedure is called by Tcl_DoOneEvent to check the pipe
 *      This procedure is called by Tcl_DoOneEvent to check the pipe
 *      event source for events.
 *      event source for events.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      May queue an event.
 *      May queue an event.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
PipeCheckProc(data, flags)
PipeCheckProc(data, flags)
    ClientData data;            /* Not used. */
    ClientData data;            /* Not used. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
{
{
    PipeInfo *infoPtr;
    PipeInfo *infoPtr;
    PipeEvent *evPtr;
    PipeEvent *evPtr;
 
 
    if (!(flags & TCL_FILE_EVENTS)) {
    if (!(flags & TCL_FILE_EVENTS)) {
        return;
        return;
    }
    }
 
 
    /*
    /*
     * Queue events for any watched pipes that don't already have events
     * Queue events for any watched pipes that don't already have events
     * queued.
     * queued.
     */
     */
 
 
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
        /* CYGNUS LOCAL: Only poll for a readable pipe if it really is
        /* CYGNUS LOCAL: Only poll for a readable pipe if it really is
           readable.  */
           readable.  */
        if (((infoPtr->watchMask &~ TCL_READABLE)
        if (((infoPtr->watchMask &~ TCL_READABLE)
            || ((infoPtr->watchMask & TCL_READABLE)
            || ((infoPtr->watchMask & TCL_READABLE)
                && ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
                && ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
                    || (PipeGetFlags(infoPtr) & PIPE_READABLE))))
                    || (PipeGetFlags(infoPtr) & PIPE_READABLE))))
            && !(PipeGetFlags(infoPtr) & PIPE_PENDING)) {
            && !(PipeGetFlags(infoPtr) & PIPE_PENDING)) {
            PipeSetFlag(infoPtr, PIPE_PENDING);
            PipeSetFlag(infoPtr, PIPE_PENDING);
            evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
            evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
            evPtr->header.proc = PipeEventProc;
            evPtr->header.proc = PipeEventProc;
            evPtr->infoPtr = infoPtr;
            evPtr->infoPtr = infoPtr;
            Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
            Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * MakeFile --
 * MakeFile --
 *
 *
 *      This function constructs a new TclFile from a given data and
 *      This function constructs a new TclFile from a given data and
 *      type value.
 *      type value.
 *
 *
 * Results:
 * Results:
 *      Returns a newly allocated WinFile as a TclFile.
 *      Returns a newly allocated WinFile as a TclFile.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static TclFile
static TclFile
MakeFile(handle)
MakeFile(handle)
    HANDLE handle;              /* Type-specific data. */
    HANDLE handle;              /* Type-specific data. */
{
{
    WinFile *filePtr;
    WinFile *filePtr;
 
 
    filePtr = (WinFile *) ckalloc(sizeof(WinFile));
    filePtr = (WinFile *) ckalloc(sizeof(WinFile));
    filePtr->type = WIN_FILE;
    filePtr->type = WIN_FILE;
    filePtr->handle = handle;
    filePtr->handle = handle;
 
 
    return (TclFile)filePtr;
    return (TclFile)filePtr;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpMakeFile --
 * TclpMakeFile --
 *
 *
 *      Make a TclFile from a channel.
 *      Make a TclFile from a channel.
 *
 *
 * Results:
 * Results:
 *      Returns a new TclFile or NULL on failure.
 *      Returns a new TclFile or NULL on failure.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
TclFile
TclFile
TclpMakeFile(channel, direction)
TclpMakeFile(channel, direction)
    Tcl_Channel channel;        /* Channel to get file from. */
    Tcl_Channel channel;        /* Channel to get file from. */
    int direction;              /* Either TCL_READABLE or TCL_WRITABLE. */
    int direction;              /* Either TCL_READABLE or TCL_WRITABLE. */
{
{
    HANDLE handle;
    HANDLE handle;
 
 
    if (Tcl_GetChannelHandle(channel, direction,
    if (Tcl_GetChannelHandle(channel, direction,
            (ClientData *) &handle) == TCL_OK) {
            (ClientData *) &handle) == TCL_OK) {
        return MakeFile(handle);
        return MakeFile(handle);
    } else {
    } else {
        return (TclFile) NULL;
        return (TclFile) NULL;
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TempFileName --
 * TempFileName --
 *
 *
 *      Gets a temporary file name and deals with the fact that the
 *      Gets a temporary file name and deals with the fact that the
 *      temporary file path provided by Windows may not actually exist
 *      temporary file path provided by Windows may not actually exist
 *      if the TMP or TEMP environment variables refer to a
 *      if the TMP or TEMP environment variables refer to a
 *      non-existent directory.
 *      non-existent directory.
 *
 *
 * Results:
 * Results:
 *      0 if error, non-zero otherwise.  If non-zero is returned, the
 *      0 if error, non-zero otherwise.  If non-zero is returned, the
 *      name buffer will be filled with a name that can be used to
 *      name buffer will be filled with a name that can be used to
 *      construct a temporary file.
 *      construct a temporary file.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
TempFileName(name)
TempFileName(name)
    char name[MAX_PATH];        /* Buffer in which name for temporary
    char name[MAX_PATH];        /* Buffer in which name for temporary
                                 * file gets stored. */
                                 * file gets stored. */
{
{
    if ((GetTempPath(MAX_PATH, name) == 0) ||
    if ((GetTempPath(MAX_PATH, name) == 0) ||
            (GetTempFileName(name, "TCL", 0, name) == 0)) {
            (GetTempFileName(name, "TCL", 0, name) == 0)) {
        name[0] = '.';
        name[0] = '.';
        name[1] = '\0';
        name[1] = '\0';
        if (GetTempFileName(name, "TCL", 0, name) == 0) {
        if (GetTempFileName(name, "TCL", 0, name) == 0) {
            return 0;
            return 0;
        }
        }
    }
    }
    return 1;
    return 1;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpCreateTempFile --
 * TclpCreateTempFile --
 *
 *
 *      This function opens a unique file with the property that it
 *      This function opens a unique file with the property that it
 *      will be deleted when its file handle is closed.  The temporary
 *      will be deleted when its file handle is closed.  The temporary
 *      file is created in the system temporary directory.
 *      file is created in the system temporary directory.
 *
 *
 * Results:
 * Results:
 *      Returns a valid TclFile, or NULL on failure.
 *      Returns a valid TclFile, or NULL on failure.
 *
 *
 * Side effects:
 * Side effects:
 *      Creates a new temporary file.
 *      Creates a new temporary file.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
TclFile
TclFile
TclpCreateTempFile(contents, namePtr)
TclpCreateTempFile(contents, namePtr)
    char *contents;             /* String to write into temp file, or NULL. */
    char *contents;             /* String to write into temp file, or NULL. */
    Tcl_DString *namePtr;       /* If non-NULL, pointer to initialized
    Tcl_DString *namePtr;       /* If non-NULL, pointer to initialized
                                 * DString that is filled with the name of
                                 * DString that is filled with the name of
                                 * the temp file that was created. */
                                 * the temp file that was created. */
{
{
    char name[MAX_PATH];
    char name[MAX_PATH];
    HANDLE handle;
    HANDLE handle;
 
 
    if (TempFileName(name) == 0) {
    if (TempFileName(name) == 0) {
        return NULL;
        return NULL;
    }
    }
 
 
    handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
    handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,
            CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,
            NULL);
            NULL);
    if (handle == INVALID_HANDLE_VALUE) {
    if (handle == INVALID_HANDLE_VALUE) {
        goto error;
        goto error;
    }
    }
 
 
    /*
    /*
     * Write the file out, doing line translations on the way.
     * Write the file out, doing line translations on the way.
     */
     */
 
 
    if (contents != NULL) {
    if (contents != NULL) {
        DWORD result, length;
        DWORD result, length;
        char *p;
        char *p;
 
 
        for (p = contents; *p != '\0'; p++) {
        for (p = contents; *p != '\0'; p++) {
            if (*p == '\n') {
            if (*p == '\n') {
                length = p - contents;
                length = p - contents;
                if (length > 0) {
                if (length > 0) {
                    if (!WriteFile(handle, contents, length, &result, NULL)) {
                    if (!WriteFile(handle, contents, length, &result, NULL)) {
                        goto error;
                        goto error;
                    }
                    }
                }
                }
                if (!WriteFile(handle, "\r\n", 2, &result, NULL)) {
                if (!WriteFile(handle, "\r\n", 2, &result, NULL)) {
                    goto error;
                    goto error;
                }
                }
                contents = p+1;
                contents = p+1;
            }
            }
        }
        }
        length = p - contents;
        length = p - contents;
        if (length > 0) {
        if (length > 0) {
            if (!WriteFile(handle, contents, length, &result, NULL)) {
            if (!WriteFile(handle, contents, length, &result, NULL)) {
                goto error;
                goto error;
            }
            }
        }
        }
    }
    }
 
 
    if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) {
    if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) {
        goto error;
        goto error;
    }
    }
 
 
    if (namePtr != NULL) {
    if (namePtr != NULL) {
        Tcl_DStringAppend(namePtr, name, -1);
        Tcl_DStringAppend(namePtr, name, -1);
    }
    }
 
 
    /*
    /*
     * Under Win32s a file created with FILE_FLAG_DELETE_ON_CLOSE won't
     * Under Win32s a file created with FILE_FLAG_DELETE_ON_CLOSE won't
     * actually be deleted when it is closed, so we have to do it ourselves.
     * actually be deleted when it is closed, so we have to do it ourselves.
     */
     */
 
 
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
        TmpFile *tmpFilePtr = (TmpFile *) ckalloc(sizeof(TmpFile));
        TmpFile *tmpFilePtr = (TmpFile *) ckalloc(sizeof(TmpFile));
        tmpFilePtr->file.type = WIN32S_TMPFILE;
        tmpFilePtr->file.type = WIN32S_TMPFILE;
        tmpFilePtr->file.handle = handle;
        tmpFilePtr->file.handle = handle;
        strcpy(tmpFilePtr->name, name);
        strcpy(tmpFilePtr->name, name);
        return (TclFile)tmpFilePtr;
        return (TclFile)tmpFilePtr;
    } else {
    } else {
        return MakeFile(handle);
        return MakeFile(handle);
    }
    }
 
 
  error:
  error:
    TclWinConvertError(GetLastError());
    TclWinConvertError(GetLastError());
    CloseHandle(handle);
    CloseHandle(handle);
    DeleteFile(name);
    DeleteFile(name);
    return NULL;
    return NULL;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpOpenFile --
 * TclpOpenFile --
 *
 *
 *      This function opens files for use in a pipeline.
 *      This function opens files for use in a pipeline.
 *
 *
 * Results:
 * Results:
 *      Returns a newly allocated TclFile structure containing the
 *      Returns a newly allocated TclFile structure containing the
 *      file handle.
 *      file handle.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
TclFile
TclFile
TclpOpenFile(path, mode)
TclpOpenFile(path, mode)
    char *path;
    char *path;
    int mode;
    int mode;
{
{
    HANDLE handle;
    HANDLE handle;
    DWORD accessMode, createMode, shareMode, flags;
    DWORD accessMode, createMode, shareMode, flags;
    SECURITY_ATTRIBUTES sec;
    SECURITY_ATTRIBUTES sec;
 
 
    /*
    /*
     * Map the access bits to the NT access mode.
     * Map the access bits to the NT access mode.
     */
     */
 
 
    switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
    switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
        case O_RDONLY:
        case O_RDONLY:
            accessMode = GENERIC_READ;
            accessMode = GENERIC_READ;
            break;
            break;
        case O_WRONLY:
        case O_WRONLY:
            accessMode = GENERIC_WRITE;
            accessMode = GENERIC_WRITE;
            break;
            break;
        case O_RDWR:
        case O_RDWR:
            accessMode = (GENERIC_READ | GENERIC_WRITE);
            accessMode = (GENERIC_READ | GENERIC_WRITE);
            break;
            break;
        default:
        default:
            TclWinConvertError(ERROR_INVALID_FUNCTION);
            TclWinConvertError(ERROR_INVALID_FUNCTION);
            return NULL;
            return NULL;
    }
    }
 
 
    /*
    /*
     * Map the creation flags to the NT create mode.
     * Map the creation flags to the NT create mode.
     */
     */
 
 
    switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
    switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
        case (O_CREAT | O_EXCL):
        case (O_CREAT | O_EXCL):
        case (O_CREAT | O_EXCL | O_TRUNC):
        case (O_CREAT | O_EXCL | O_TRUNC):
            createMode = CREATE_NEW;
            createMode = CREATE_NEW;
            break;
            break;
        case (O_CREAT | O_TRUNC):
        case (O_CREAT | O_TRUNC):
            createMode = CREATE_ALWAYS;
            createMode = CREATE_ALWAYS;
            break;
            break;
        case O_CREAT:
        case O_CREAT:
            createMode = OPEN_ALWAYS;
            createMode = OPEN_ALWAYS;
            break;
            break;
        case O_TRUNC:
        case O_TRUNC:
        case (O_TRUNC | O_EXCL):
        case (O_TRUNC | O_EXCL):
            createMode = TRUNCATE_EXISTING;
            createMode = TRUNCATE_EXISTING;
            break;
            break;
        default:
        default:
            createMode = OPEN_EXISTING;
            createMode = OPEN_EXISTING;
            break;
            break;
    }
    }
 
 
    /*
    /*
     * If the file is not being created, use the existing file attributes.
     * If the file is not being created, use the existing file attributes.
     */
     */
 
 
    flags = 0;
    flags = 0;
    if (!(mode & O_CREAT)) {
    if (!(mode & O_CREAT)) {
        flags = GetFileAttributes(path);
        flags = GetFileAttributes(path);
        if (flags == 0xFFFFFFFF) {
        if (flags == 0xFFFFFFFF) {
            flags = 0;
            flags = 0;
        }
        }
    }
    }
 
 
    /*
    /*
     * Set up the security attributes so this file is not inherited by
     * Set up the security attributes so this file is not inherited by
     * child processes.
     * child processes.
     */
     */
 
 
    sec.nLength = sizeof(sec);
    sec.nLength = sizeof(sec);
    sec.lpSecurityDescriptor = NULL;
    sec.lpSecurityDescriptor = NULL;
    sec.bInheritHandle = 0;
    sec.bInheritHandle = 0;
 
 
    /*
    /*
     * Set up the file sharing mode.  We want to allow simultaneous access.
     * Set up the file sharing mode.  We want to allow simultaneous access.
     */
     */
 
 
    shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
 
 
    /*
    /*
     * Now we get to create the file.
     * Now we get to create the file.
     */
     */
 
 
    handle = CreateFile(path, accessMode, shareMode, &sec, createMode, flags,
    handle = CreateFile(path, accessMode, shareMode, &sec, createMode, flags,
            (HANDLE) NULL);
            (HANDLE) NULL);
    if (handle == INVALID_HANDLE_VALUE) {
    if (handle == INVALID_HANDLE_VALUE) {
        DWORD err = GetLastError();
        DWORD err = GetLastError();
        if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
        if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
            err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
            err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
        }
        }
        TclWinConvertError(err);
        TclWinConvertError(err);
        return NULL;
        return NULL;
    }
    }
 
 
    /*
    /*
     * Seek to the end of file if we are writing.
     * Seek to the end of file if we are writing.
     */
     */
 
 
    if (mode & O_WRONLY) {
    if (mode & O_WRONLY) {
        SetFilePointer(handle, 0, NULL, FILE_END);
        SetFilePointer(handle, 0, NULL, FILE_END);
    }
    }
 
 
    return MakeFile(handle);
    return MakeFile(handle);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpCreatePipe --
 * TclpCreatePipe --
 *
 *
 *      Creates an anonymous pipe.  Under Win32s, creates a temp file
 *      Creates an anonymous pipe.  Under Win32s, creates a temp file
 *      that is used to simulate a pipe.
 *      that is used to simulate a pipe.
 *
 *
 * Results:
 * Results:
 *      Returns 1 on success, 0 on failure.
 *      Returns 1 on success, 0 on failure.
 *
 *
 * Side effects:
 * Side effects:
 *      Creates a pipe.
 *      Creates a pipe.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
int
int
TclpCreatePipe(readPipe, writePipe)
TclpCreatePipe(readPipe, writePipe)
    TclFile *readPipe;  /* Location to store file handle for
    TclFile *readPipe;  /* Location to store file handle for
                                 * read side of pipe. */
                                 * read side of pipe. */
    TclFile *writePipe; /* Location to store file handle for
    TclFile *writePipe; /* Location to store file handle for
                                 * write side of pipe. */
                                 * write side of pipe. */
{
{
    HANDLE readHandle, writeHandle;
    HANDLE readHandle, writeHandle;
 
 
    if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) {
    if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) {
        *readPipe = MakeFile(readHandle);
        *readPipe = MakeFile(readHandle);
        *writePipe = MakeFile(writeHandle);
        *writePipe = MakeFile(writeHandle);
        return 1;
        return 1;
    }
    }
 
 
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
        WinPipe *readPipePtr, *writePipePtr;
        WinPipe *readPipePtr, *writePipePtr;
        char buf[MAX_PATH];
        char buf[MAX_PATH];
 
 
        if (TempFileName(buf) != 0) {
        if (TempFileName(buf) != 0) {
            readPipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
            readPipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
            writePipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
            writePipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
 
 
            readPipePtr->file.type = WIN32S_PIPE;
            readPipePtr->file.type = WIN32S_PIPE;
            readPipePtr->otherPtr = writePipePtr;
            readPipePtr->otherPtr = writePipePtr;
            readPipePtr->fileName = strcpy(ckalloc(strlen(buf) + 1), buf);
            readPipePtr->fileName = strcpy(ckalloc(strlen(buf) + 1), buf);
            readPipePtr->file.handle = INVALID_HANDLE_VALUE;
            readPipePtr->file.handle = INVALID_HANDLE_VALUE;
            writePipePtr->file.type = WIN32S_PIPE;
            writePipePtr->file.type = WIN32S_PIPE;
            writePipePtr->otherPtr = readPipePtr;
            writePipePtr->otherPtr = readPipePtr;
            writePipePtr->fileName = readPipePtr->fileName;
            writePipePtr->fileName = readPipePtr->fileName;
            writePipePtr->file.handle = INVALID_HANDLE_VALUE;
            writePipePtr->file.handle = INVALID_HANDLE_VALUE;
 
 
            *readPipe = (TclFile)readPipePtr;
            *readPipe = (TclFile)readPipePtr;
            *writePipe = (TclFile)writePipePtr;
            *writePipe = (TclFile)writePipePtr;
 
 
            return 1;
            return 1;
        }
        }
    }
    }
 
 
    TclWinConvertError(GetLastError());
    TclWinConvertError(GetLastError());
    return 0;
    return 0;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpCloseFile --
 * TclpCloseFile --
 *
 *
 *      Closes a pipeline file handle.  These handles are created by
 *      Closes a pipeline file handle.  These handles are created by
 *      TclpOpenFile, TclpCreatePipe, or TclpMakeFile.
 *      TclpOpenFile, TclpCreatePipe, or TclpMakeFile.
 *
 *
 * Results:
 * Results:
 *      0 on success, -1 on failure.
 *      0 on success, -1 on failure.
 *
 *
 * Side effects:
 * Side effects:
 *      The file is closed and deallocated.
 *      The file is closed and deallocated.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
int
int
TclpCloseFile(file)
TclpCloseFile(file)
    TclFile file;       /* The file to close. */
    TclFile file;       /* The file to close. */
{
{
    WinFile *filePtr = (WinFile *) file;
    WinFile *filePtr = (WinFile *) file;
    WinPipe *pipePtr;
    WinPipe *pipePtr;
 
 
    switch (filePtr->type) {
    switch (filePtr->type) {
        case WIN_FILE:
        case WIN_FILE:
        case WIN32S_TMPFILE:
        case WIN32S_TMPFILE:
            if (CloseHandle(filePtr->handle) == FALSE) {
            if (CloseHandle(filePtr->handle) == FALSE) {
                TclWinConvertError(GetLastError());
                TclWinConvertError(GetLastError());
                ckfree((char *) filePtr);
                ckfree((char *) filePtr);
                return -1;
                return -1;
            }
            }
            /*
            /*
             * Simulate deleting the file on close for Win32s.
             * Simulate deleting the file on close for Win32s.
             */
             */
 
 
            if (filePtr->type == WIN32S_TMPFILE) {
            if (filePtr->type == WIN32S_TMPFILE) {
                DeleteFile(((TmpFile*)filePtr)->name);
                DeleteFile(((TmpFile*)filePtr)->name);
            }
            }
            break;
            break;
 
 
        case WIN32S_PIPE:
        case WIN32S_PIPE:
            pipePtr = (WinPipe *) file;
            pipePtr = (WinPipe *) file;
 
 
            if (pipePtr->otherPtr != NULL) {
            if (pipePtr->otherPtr != NULL) {
                pipePtr->otherPtr->otherPtr = NULL;
                pipePtr->otherPtr->otherPtr = NULL;
            } else {
            } else {
                if (pipePtr->file.handle != INVALID_HANDLE_VALUE) {
                if (pipePtr->file.handle != INVALID_HANDLE_VALUE) {
                    CloseHandle(pipePtr->file.handle);
                    CloseHandle(pipePtr->file.handle);
                }
                }
                DeleteFile(pipePtr->fileName);
                DeleteFile(pipePtr->fileName);
                ckfree((char *) pipePtr->fileName);
                ckfree((char *) pipePtr->fileName);
            }
            }
            break;
            break;
 
 
        default:
        default:
            panic("Tcl_CloseFile: unexpected file type");
            panic("Tcl_CloseFile: unexpected file type");
    }
    }
 
 
    ckfree((char *) filePtr);
    ckfree((char *) filePtr);
    return 0;
    return 0;
}
}


/*
/*
 *--------------------------------------------------------------------------
 *--------------------------------------------------------------------------
 *
 *
 * TclpGetPid --
 * TclpGetPid --
 *
 *
 *      Given a HANDLE to a child process, return the process id for that
 *      Given a HANDLE to a child process, return the process id for that
 *      child process.
 *      child process.
 *
 *
 * Results:
 * Results:
 *      Returns the process id for the child process.  If the pid was not
 *      Returns the process id for the child process.  If the pid was not
 *      known by Tcl, either because the pid was not created by Tcl or the
 *      known by Tcl, either because the pid was not created by Tcl or the
 *      child process has already been reaped, -1 is returned.
 *      child process has already been reaped, -1 is returned.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *--------------------------------------------------------------------------
 *--------------------------------------------------------------------------
 */
 */
 
 
unsigned long
unsigned long
TclpGetPid(pid)
TclpGetPid(pid)
    Tcl_Pid pid;                /* The HANDLE of the child process. */
    Tcl_Pid pid;                /* The HANDLE of the child process. */
{
{
    ProcInfo *infoPtr;
    ProcInfo *infoPtr;
 
 
    for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
    for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
        if (infoPtr->hProcess == (HANDLE) pid) {
        if (infoPtr->hProcess == (HANDLE) pid) {
            return infoPtr->dwProcessId;
            return infoPtr->dwProcessId;
        }
        }
    }
    }
    return (unsigned long) -1;
    return (unsigned long) -1;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpCreateProcess --
 * TclpCreateProcess --
 *
 *
 *      Create a child process that has the specified files as its
 *      Create a child process that has the specified files as its
 *      standard input, output, and error.  The child process runs
 *      standard input, output, and error.  The child process runs
 *      synchronously under Win32s and asynchronously under Windows NT
 *      synchronously under Win32s and asynchronously under Windows NT
 *      and Windows 95, and runs with the same environment variables
 *      and Windows 95, and runs with the same environment variables
 *      as the creating process.
 *      as the creating process.
 *
 *
 *      The complete Windows search path is searched to find the specified
 *      The complete Windows search path is searched to find the specified
 *      executable.  If an executable by the given name is not found,
 *      executable.  If an executable by the given name is not found,
 *      automatically tries appending ".com", ".exe", and ".bat" to the
 *      automatically tries appending ".com", ".exe", and ".bat" to the
 *      executable name.
 *      executable name.
 *
 *
 * Results:
 * Results:
 *      The return value is TCL_ERROR and an error message is left in
 *      The return value is TCL_ERROR and an error message is left in
 *      interp->result if there was a problem creating the child
 *      interp->result if there was a problem creating the child
 *      process.  Otherwise, the return value is TCL_OK and *pidPtr is
 *      process.  Otherwise, the return value is TCL_OK and *pidPtr is
 *      filled with the process id of the child process.
 *      filled with the process id of the child process.
 *
 *
 * Side effects:
 * Side effects:
 *      A process is created.
 *      A process is created.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
int
int
TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
        pidPtr)
        pidPtr)
    Tcl_Interp *interp;         /* Interpreter in which to leave errors that
    Tcl_Interp *interp;         /* Interpreter in which to leave errors that
                                 * occurred when creating the child process.
                                 * occurred when creating the child process.
                                 * Error messages from the child process
                                 * Error messages from the child process
                                 * itself are sent to errorFile. */
                                 * itself are sent to errorFile. */
    int argc;                   /* Number of arguments in following array. */
    int argc;                   /* Number of arguments in following array. */
    char **argv;                /* Array of argument strings.  argv[0]
    char **argv;                /* Array of argument strings.  argv[0]
                                 * contains the name of the executable
                                 * contains the name of the executable
                                 * converted to native format (using the
                                 * converted to native format (using the
                                 * Tcl_TranslateFileName call).  Additional
                                 * Tcl_TranslateFileName call).  Additional
                                 * arguments have not been converted. */
                                 * arguments have not been converted. */
    TclFile inputFile;          /* If non-NULL, gives the file to use as
    TclFile inputFile;          /* If non-NULL, gives the file to use as
                                 * input for the child process.  If inputFile
                                 * input for the child process.  If inputFile
                                 * file is not readable or is NULL, the child
                                 * file is not readable or is NULL, the child
                                 * will receive no standard input. */
                                 * will receive no standard input. */
    TclFile outputFile;         /* If non-NULL, gives the file that
    TclFile outputFile;         /* If non-NULL, gives the file that
                                 * receives output from the child process.  If
                                 * receives output from the child process.  If
                                 * outputFile file is not writeable or is
                                 * outputFile file is not writeable or is
                                 * NULL, output from the child will be
                                 * NULL, output from the child will be
                                 * discarded. */
                                 * discarded. */
    TclFile errorFile;          /* If non-NULL, gives the file that
    TclFile errorFile;          /* If non-NULL, gives the file that
                                 * receives errors from the child process.  If
                                 * receives errors from the child process.  If
                                 * errorFile file is not writeable or is NULL,
                                 * errorFile file is not writeable or is NULL,
                                 * errors from the child will be discarded.
                                 * errors from the child will be discarded.
                                 * errorFile may be the same as outputFile. */
                                 * errorFile may be the same as outputFile. */
    Tcl_Pid *pidPtr;            /* If this procedure is successful, pidPtr
    Tcl_Pid *pidPtr;            /* If this procedure is successful, pidPtr
                                 * is filled with the process id of the child
                                 * is filled with the process id of the child
                                 * process. */
                                 * process. */
{
{
    int result, applType, createFlags;
    int result, applType, createFlags;
    Tcl_DString cmdLine;
    Tcl_DString cmdLine;
    STARTUPINFO startInfo;
    STARTUPINFO startInfo;
    PROCESS_INFORMATION procInfo;
    PROCESS_INFORMATION procInfo;
    SECURITY_ATTRIBUTES secAtts;
    SECURITY_ATTRIBUTES secAtts;
    HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
    HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
    char execPath[MAX_PATH];
    char execPath[MAX_PATH];
    char *originalName;
    char *originalName;
    WinFile *filePtr;
    WinFile *filePtr;
 
 
    if (!initialized) {
    if (!initialized) {
        PipeInit();
        PipeInit();
    }
    }
 
 
    applType = ApplicationType(interp, argv[0], execPath);
    applType = ApplicationType(interp, argv[0], execPath);
    if (applType == APPL_NONE) {
    if (applType == APPL_NONE) {
        return TCL_ERROR;
        return TCL_ERROR;
    }
    }
    originalName = argv[0];
    originalName = argv[0];
    argv[0] = execPath;
    argv[0] = execPath;
 
 
    result = TCL_ERROR;
    result = TCL_ERROR;
    Tcl_DStringInit(&cmdLine);
    Tcl_DStringInit(&cmdLine);
 
 
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
        /*
        /*
         * Under Win32s, there are no pipes.  In order to simulate pipe
         * Under Win32s, there are no pipes.  In order to simulate pipe
         * behavior, the child processes are run synchronously and their
         * behavior, the child processes are run synchronously and their
         * I/O is redirected from/to temporary files before the next
         * I/O is redirected from/to temporary files before the next
         * stage of the pipeline is started.
         * stage of the pipeline is started.
         */
         */
 
 
        MSG msg;
        MSG msg;
        DWORD status;
        DWORD status;
        DWORD args[4];
        DWORD args[4];
        void *trans[5];
        void *trans[5];
        char *inputFileName, *outputFileName;
        char *inputFileName, *outputFileName;
        Tcl_DString inputTempFile, outputTempFile;
        Tcl_DString inputTempFile, outputTempFile;
 
 
        BuildCommandLine(argc, argv, &cmdLine);
        BuildCommandLine(argc, argv, &cmdLine);
 
 
        ZeroMemory(&startInfo, sizeof(startInfo));
        ZeroMemory(&startInfo, sizeof(startInfo));
        startInfo.cb = sizeof(startInfo);
        startInfo.cb = sizeof(startInfo);
 
 
        Tcl_DStringInit(&inputTempFile);
        Tcl_DStringInit(&inputTempFile);
        Tcl_DStringInit(&outputTempFile);
        Tcl_DStringInit(&outputTempFile);
        outputHandle = INVALID_HANDLE_VALUE;
        outputHandle = INVALID_HANDLE_VALUE;
 
 
        inputFileName = NULL;
        inputFileName = NULL;
        outputFileName = NULL;
        outputFileName = NULL;
        if (inputFile != NULL) {
        if (inputFile != NULL) {
            filePtr = (WinFile *) inputFile;
            filePtr = (WinFile *) inputFile;
            switch (filePtr->type) {
            switch (filePtr->type) {
                case WIN_FILE:
                case WIN_FILE:
                case WIN32S_TMPFILE: {
                case WIN32S_TMPFILE: {
                    h = INVALID_HANDLE_VALUE;
                    h = INVALID_HANDLE_VALUE;
                    inputFileName = MakeTempFile(&inputTempFile);
                    inputFileName = MakeTempFile(&inputTempFile);
                    if (inputFileName != NULL) {
                    if (inputFileName != NULL) {
                        h = CreateFile(inputFileName, GENERIC_WRITE, 0,
                        h = CreateFile(inputFileName, GENERIC_WRITE, 0,
                                NULL, CREATE_ALWAYS, 0, NULL);
                                NULL, CREATE_ALWAYS, 0, NULL);
                    }
                    }
                    if (h == INVALID_HANDLE_VALUE) {
                    if (h == INVALID_HANDLE_VALUE) {
                        Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
                        Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
                                Tcl_PosixError(interp), (char *) NULL);
                                Tcl_PosixError(interp), (char *) NULL);
                        goto end32s;
                        goto end32s;
                    }
                    }
                    CopyChannel(h, filePtr->handle);
                    CopyChannel(h, filePtr->handle);
                    CloseHandle(h);
                    CloseHandle(h);
                    break;
                    break;
                }
                }
                case WIN32S_PIPE: {
                case WIN32S_PIPE: {
                    inputFileName = ((WinPipe*)inputFile)->fileName;
                    inputFileName = ((WinPipe*)inputFile)->fileName;
                    break;
                    break;
                }
                }
            }
            }
        }
        }
        if (inputFileName == NULL) {
        if (inputFileName == NULL) {
            inputFileName = "nul";
            inputFileName = "nul";
        }
        }
        if (outputFile != NULL) {
        if (outputFile != NULL) {
            filePtr = (WinFile *)outputFile;
            filePtr = (WinFile *)outputFile;
            if (filePtr->type == WIN_FILE) {
            if (filePtr->type == WIN_FILE) {
                outputFileName = MakeTempFile(&outputTempFile);
                outputFileName = MakeTempFile(&outputTempFile);
                if (outputFileName == NULL) {
                if (outputFileName == NULL) {
                    Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
                    Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
                            Tcl_PosixError(interp), (char *) NULL);
                            Tcl_PosixError(interp), (char *) NULL);
                    goto end32s;
                    goto end32s;
                }
                }
                outputHandle = filePtr->handle;
                outputHandle = filePtr->handle;
            } else if (filePtr->type == WIN32S_PIPE) {
            } else if (filePtr->type == WIN32S_PIPE) {
                outputFileName = ((WinPipe*)outputFile)->fileName;
                outputFileName = ((WinPipe*)outputFile)->fileName;
            }
            }
        }
        }
        if (outputFileName == NULL) {
        if (outputFileName == NULL) {
            outputFileName = "nul";
            outputFileName = "nul";
        }
        }
 
 
        if (applType == APPL_DOS) {
        if (applType == APPL_DOS) {
            args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
            args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
            args[1] = (DWORD) inputFileName;
            args[1] = (DWORD) inputFileName;
            args[2] = (DWORD) outputFileName;
            args[2] = (DWORD) outputFileName;
            trans[0] = &args[0];
            trans[0] = &args[0];
            trans[1] = &args[1];
            trans[1] = &args[1];
            trans[2] = &args[2];
            trans[2] = &args[2];
            trans[3] = NULL;
            trans[3] = NULL;
            if (TclWinSynchSpawn(args, 0, trans, pidPtr) != 0) {
            if (TclWinSynchSpawn(args, 0, trans, pidPtr) != 0) {
                result = TCL_OK;
                result = TCL_OK;
            }
            }
        } else if (applType == APPL_WIN3X) {
        } else if (applType == APPL_WIN3X) {
            args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
            args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
            trans[0] = &args[0];
            trans[0] = &args[0];
            trans[1] = NULL;
            trans[1] = NULL;
            if (TclWinSynchSpawn(args, 1, trans, pidPtr) != 0) {
            if (TclWinSynchSpawn(args, 1, trans, pidPtr) != 0) {
                result = TCL_OK;
                result = TCL_OK;
            }
            }
        } else {
        } else {
            if (CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL,
            if (CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL,
                    FALSE, DETACHED_PROCESS, NULL, NULL, &startInfo,
                    FALSE, DETACHED_PROCESS, NULL, NULL, &startInfo,
                    &procInfo) != 0) {
                    &procInfo) != 0) {
                CloseHandle(procInfo.hThread);
                CloseHandle(procInfo.hThread);
                while (1) {
                while (1) {
                    if (GetExitCodeProcess(procInfo.hProcess, &status) == FALSE) {
                    if (GetExitCodeProcess(procInfo.hProcess, &status) == FALSE) {
                        break;
                        break;
                    }
                    }
                    if (status != STILL_ACTIVE) {
                    if (status != STILL_ACTIVE) {
                        break;
                        break;
                    }
                    }
                    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) {
                    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) {
                        TranslateMessage(&msg);
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                        DispatchMessage(&msg);
                    }
                    }
                }
                }
                *pidPtr = (Tcl_Pid) procInfo.hProcess;
                *pidPtr = (Tcl_Pid) procInfo.hProcess;
                if (*pidPtr != 0) {
                if (*pidPtr != 0) {
                    ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
                    ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
                    procPtr->hProcess = procInfo.hProcess;
                    procPtr->hProcess = procInfo.hProcess;
                    procPtr->dwProcessId = procInfo.dwProcessId;
                    procPtr->dwProcessId = procInfo.dwProcessId;
                    procPtr->nextPtr = procList;
                    procPtr->nextPtr = procList;
                    procList = procPtr;
                    procList = procPtr;
                }
                }
                result = TCL_OK;
                result = TCL_OK;
            }
            }
        }
        }
        if (result != TCL_OK) {
        if (result != TCL_OK) {
            TclWinConvertError(GetLastError());
            TclWinConvertError(GetLastError());
            Tcl_AppendResult(interp, "couldn't execute \"", originalName,
            Tcl_AppendResult(interp, "couldn't execute \"", originalName,
                    "\": ", Tcl_PosixError(interp), (char *) NULL);
                    "\": ", Tcl_PosixError(interp), (char *) NULL);
        }
        }
 
 
        end32s:
        end32s:
        if (outputHandle != INVALID_HANDLE_VALUE) {
        if (outputHandle != INVALID_HANDLE_VALUE) {
            /*
            /*
             * Now copy stuff from temp file to actual output handle. Don't
             * Now copy stuff from temp file to actual output handle. Don't
             * close outputHandle because it is associated with the output
             * close outputHandle because it is associated with the output
             * file owned by the caller.
             * file owned by the caller.
             */
             */
 
 
            h = CreateFile(outputFileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
            h = CreateFile(outputFileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
                    0, NULL);
                    0, NULL);
            if (h != INVALID_HANDLE_VALUE) {
            if (h != INVALID_HANDLE_VALUE) {
                CopyChannel(outputHandle, h);
                CopyChannel(outputHandle, h);
            }
            }
            CloseHandle(h);
            CloseHandle(h);
        }
        }
 
 
        if (inputFileName == Tcl_DStringValue(&inputTempFile)) {
        if (inputFileName == Tcl_DStringValue(&inputTempFile)) {
            DeleteFile(inputFileName);
            DeleteFile(inputFileName);
        }
        }
 
 
        if (outputFileName == Tcl_DStringValue(&outputTempFile)) {
        if (outputFileName == Tcl_DStringValue(&outputTempFile)) {
            DeleteFile(outputFileName);
            DeleteFile(outputFileName);
        }
        }
 
 
        Tcl_DStringFree(&inputTempFile);
        Tcl_DStringFree(&inputTempFile);
        Tcl_DStringFree(&outputTempFile);
        Tcl_DStringFree(&outputTempFile);
        Tcl_DStringFree(&cmdLine);
        Tcl_DStringFree(&cmdLine);
        return result;
        return result;
    }
    }
    hProcess = GetCurrentProcess();
    hProcess = GetCurrentProcess();
 
 
    /*
    /*
     * STARTF_USESTDHANDLES must be used to pass handles to child process.
     * STARTF_USESTDHANDLES must be used to pass handles to child process.
     * Using SetStdHandle() and/or dup2() only works when a console mode
     * Using SetStdHandle() and/or dup2() only works when a console mode
     * parent process is spawning an attached console mode child process.
     * parent process is spawning an attached console mode child process.
     */
     */
 
 
    ZeroMemory(&startInfo, sizeof(startInfo));
    ZeroMemory(&startInfo, sizeof(startInfo));
    startInfo.cb = sizeof(startInfo);
    startInfo.cb = sizeof(startInfo);
    startInfo.dwFlags   = STARTF_USESTDHANDLES;
    startInfo.dwFlags   = STARTF_USESTDHANDLES;
    startInfo.hStdInput = INVALID_HANDLE_VALUE;
    startInfo.hStdInput = INVALID_HANDLE_VALUE;
    startInfo.hStdOutput= INVALID_HANDLE_VALUE;
    startInfo.hStdOutput= INVALID_HANDLE_VALUE;
    startInfo.hStdError = INVALID_HANDLE_VALUE;
    startInfo.hStdError = INVALID_HANDLE_VALUE;
 
 
    secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
    secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
    secAtts.lpSecurityDescriptor = NULL;
    secAtts.lpSecurityDescriptor = NULL;
    secAtts.bInheritHandle = TRUE;
    secAtts.bInheritHandle = TRUE;
 
 
    /*
    /*
     * We have to check the type of each file, since we cannot duplicate
     * We have to check the type of each file, since we cannot duplicate
     * some file types.
     * some file types.
     */
     */
 
 
    inputHandle = INVALID_HANDLE_VALUE;
    inputHandle = INVALID_HANDLE_VALUE;
    if (inputFile != NULL) {
    if (inputFile != NULL) {
        filePtr = (WinFile *)inputFile;
        filePtr = (WinFile *)inputFile;
        if (filePtr->type == WIN_FILE) {
        if (filePtr->type == WIN_FILE) {
            inputHandle = filePtr->handle;
            inputHandle = filePtr->handle;
        }
        }
    }
    }
    outputHandle = INVALID_HANDLE_VALUE;
    outputHandle = INVALID_HANDLE_VALUE;
    if (outputFile != NULL) {
    if (outputFile != NULL) {
        filePtr = (WinFile *)outputFile;
        filePtr = (WinFile *)outputFile;
        if (filePtr->type == WIN_FILE) {
        if (filePtr->type == WIN_FILE) {
            outputHandle = filePtr->handle;
            outputHandle = filePtr->handle;
        }
        }
    }
    }
    errorHandle = INVALID_HANDLE_VALUE;
    errorHandle = INVALID_HANDLE_VALUE;
    if (errorFile != NULL) {
    if (errorFile != NULL) {
        filePtr = (WinFile *)errorFile;
        filePtr = (WinFile *)errorFile;
        if (filePtr->type == WIN_FILE) {
        if (filePtr->type == WIN_FILE) {
            errorHandle = filePtr->handle;
            errorHandle = filePtr->handle;
        }
        }
    }
    }
 
 
    /*
    /*
     * Duplicate all the handles which will be passed off as stdin, stdout
     * Duplicate all the handles which will be passed off as stdin, stdout
     * and stderr of the child process. The duplicate handles are set to
     * and stderr of the child process. The duplicate handles are set to
     * be inheritable, so the child process can use them.
     * be inheritable, so the child process can use them.
     */
     */
 
 
    if (inputHandle == INVALID_HANDLE_VALUE) {
    if (inputHandle == INVALID_HANDLE_VALUE) {
        /*
        /*
         * If handle was not set, stdin should return immediate EOF.
         * If handle was not set, stdin should return immediate EOF.
         * Under Windows95, some applications (both 16 and 32 bit!)
         * Under Windows95, some applications (both 16 and 32 bit!)
         * cannot read from the NUL device; they read from console
         * cannot read from the NUL device; they read from console
         * instead.  When running tk, this is fatal because the child
         * instead.  When running tk, this is fatal because the child
         * process would hang forever waiting for EOF from the unmapped
         * process would hang forever waiting for EOF from the unmapped
         * console window used by the helper application.
         * console window used by the helper application.
         *
         *
         * Fortunately, the helper application detects a closed pipe
         * Fortunately, the helper application detects a closed pipe
         * as an immediate EOF and can pass that information to the
         * as an immediate EOF and can pass that information to the
         * child process.
         * child process.
         */
         */
 
 
        if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) {
        if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) {
            CloseHandle(h);
            CloseHandle(h);
        }
        }
    } else {
    } else {
        DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput,
        DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput,
                0, TRUE, DUPLICATE_SAME_ACCESS);
                0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    }
    if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
    if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
        Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
                Tcl_PosixError(interp), (char *) NULL);
                Tcl_PosixError(interp), (char *) NULL);
        goto end;
        goto end;
    }
    }
 
 
    if (outputHandle == INVALID_HANDLE_VALUE) {
    if (outputHandle == INVALID_HANDLE_VALUE) {
        /*
        /*
         * If handle was not set, output should be sent to an infinitely
         * If handle was not set, output should be sent to an infinitely
         * deep sink.  Under Windows 95, some 16 bit applications cannot
         * deep sink.  Under Windows 95, some 16 bit applications cannot
         * have stdout redirected to NUL; they send their output to
         * have stdout redirected to NUL; they send their output to
         * the console instead.  Some applications, like "more" or "dir /p",
         * the console instead.  Some applications, like "more" or "dir /p",
         * when outputting multiple pages to the console, also then try and
         * when outputting multiple pages to the console, also then try and
         * read from the console to go the next page.  When running tk, this
         * read from the console to go the next page.  When running tk, this
         * is fatal because the child process would hang forever waiting
         * is fatal because the child process would hang forever waiting
         * for input from the unmapped console window used by the helper
         * for input from the unmapped console window used by the helper
         * application.
         * application.
         *
         *
         * Fortunately, the helper application will detect a closed pipe
         * Fortunately, the helper application will detect a closed pipe
         * as a sink.
         * as a sink.
         */
         */
 
 
        if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
        if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
                && (applType == APPL_DOS)) {
                && (applType == APPL_DOS)) {
            if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
            if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
                CloseHandle(h);
                CloseHandle(h);
            }
            }
        } else {
        } else {
            startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
            startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
                    &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
                    &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        }
        }
    } else {
    } else {
        DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput,
        DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput,
                0, TRUE, DUPLICATE_SAME_ACCESS);
                0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    }
    if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
    if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
        Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
                Tcl_PosixError(interp), (char *) NULL);
                Tcl_PosixError(interp), (char *) NULL);
        goto end;
        goto end;
    }
    }
 
 
    if (errorHandle == INVALID_HANDLE_VALUE) {
    if (errorHandle == INVALID_HANDLE_VALUE) {
        /*
        /*
         * If handle was not set, errors should be sent to an infinitely
         * If handle was not set, errors should be sent to an infinitely
         * deep sink.
         * deep sink.
         */
         */
 
 
        startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
        startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
                &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    } else {
    } else {
        DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError,
        DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError,
                0, TRUE, DUPLICATE_SAME_ACCESS);
                0, TRUE, DUPLICATE_SAME_ACCESS);
    }
    }
    if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
    if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
        Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
                Tcl_PosixError(interp), (char *) NULL);
                Tcl_PosixError(interp), (char *) NULL);
        goto end;
        goto end;
    }
    }
    /*
    /*
     * If we do not have a console window, then we must run DOS and
     * If we do not have a console window, then we must run DOS and
     * WIN32 console mode applications as detached processes. This tells
     * WIN32 console mode applications as detached processes. This tells
     * the loader that the child application should not inherit the
     * the loader that the child application should not inherit the
     * console, and that it should not create a new console window for
     * console, and that it should not create a new console window for
     * the child application.  The child application should get its stdio
     * the child application.  The child application should get its stdio
     * from the redirection handles provided by this application, and run
     * from the redirection handles provided by this application, and run
     * in the background.
     * in the background.
     *
     *
     * If we are starting a GUI process, they don't automatically get a
     * If we are starting a GUI process, they don't automatically get a
     * console, so it doesn't matter if they are started as foreground or
     * console, so it doesn't matter if they are started as foreground or
     * detached processes.  The GUI window will still pop up to the
     * detached processes.  The GUI window will still pop up to the
     * foreground.
     * foreground.
     */
     */
 
 
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
        if (HasConsole()) {
        if (HasConsole()) {
            createFlags = 0;
            createFlags = 0;
        } else if (applType == APPL_DOS) {
        } else if (applType == APPL_DOS) {
            /*
            /*
             * Under NT, 16-bit DOS applications will not run unless they
             * Under NT, 16-bit DOS applications will not run unless they
             * can be attached to a console.  If we are running without a
             * can be attached to a console.  If we are running without a
             * console, run the 16-bit program as an normal process inside
             * console, run the 16-bit program as an normal process inside
             * of a hidden console application, and then run that hidden
             * of a hidden console application, and then run that hidden
             * console as a detached process.
             * console as a detached process.
             */
             */
 
 
            startInfo.wShowWindow = SW_HIDE;
            startInfo.wShowWindow = SW_HIDE;
            startInfo.dwFlags |= STARTF_USESHOWWINDOW;
            startInfo.dwFlags |= STARTF_USESHOWWINDOW;
            createFlags = CREATE_NEW_CONSOLE;
            createFlags = CREATE_NEW_CONSOLE;
            Tcl_DStringAppend(&cmdLine, "cmd.exe /c ", -1);
            Tcl_DStringAppend(&cmdLine, "cmd.exe /c ", -1);
        } else {
        } else {
            createFlags = DETACHED_PROCESS;
            createFlags = DETACHED_PROCESS;
        }
        }
    } else {
    } else {
        if (HasConsole()) {
        if (HasConsole()) {
            createFlags = 0;
            createFlags = 0;
        } else {
        } else {
            createFlags = DETACHED_PROCESS;
            createFlags = DETACHED_PROCESS;
        }
        }
 
 
        if (applType == APPL_DOS) {
        if (applType == APPL_DOS) {
            /*
            /*
             * Under Windows 95, 16-bit DOS applications do not work well
             * Under Windows 95, 16-bit DOS applications do not work well
             * with pipes:
             * with pipes:
             *
             *
             * 1. EOF on a pipe between a detached 16-bit DOS application
             * 1. EOF on a pipe between a detached 16-bit DOS application
             * and another application is not seen at the other
             * and another application is not seen at the other
             * end of the pipe, so the listening process blocks forever on
             * end of the pipe, so the listening process blocks forever on
             * reads.  This inablity to detect EOF happens when either a
             * reads.  This inablity to detect EOF happens when either a
             * 16-bit app or the 32-bit app is the listener.
             * 16-bit app or the 32-bit app is the listener.
             *
             *
             * 2. If a 16-bit DOS application (detached or not) blocks when
             * 2. If a 16-bit DOS application (detached or not) blocks when
             * writing to a pipe, it will never wake up again, and it
             * writing to a pipe, it will never wake up again, and it
             * eventually brings the whole system down around it.
             * eventually brings the whole system down around it.
             *
             *
             * The 16-bit application is run as a normal process inside
             * The 16-bit application is run as a normal process inside
             * of a hidden helper console app, and this helper may be run
             * of a hidden helper console app, and this helper may be run
             * as a detached process.  If any of the stdio handles is
             * as a detached process.  If any of the stdio handles is
             * a pipe, the helper application accumulates information
             * a pipe, the helper application accumulates information
             * into temp files and forwards it to or from the DOS
             * into temp files and forwards it to or from the DOS
             * application as appropriate.  This means that DOS apps
             * application as appropriate.  This means that DOS apps
             * must receive EOF from a stdin pipe before they will actually
             * must receive EOF from a stdin pipe before they will actually
             * begin, and must finish generating stdout or stderr before
             * begin, and must finish generating stdout or stderr before
             * the data will be sent to the next stage of the pipe.
             * the data will be sent to the next stage of the pipe.
             *
             *
             * The helper app should be located in the same directory as
             * The helper app should be located in the same directory as
             * the tcl dll.
             * the tcl dll.
             */
             */
 
 
            if (createFlags != 0) {
            if (createFlags != 0) {
                startInfo.wShowWindow = SW_HIDE;
                startInfo.wShowWindow = SW_HIDE;
                startInfo.dwFlags |= STARTF_USESHOWWINDOW;
                startInfo.dwFlags |= STARTF_USESHOWWINDOW;
                createFlags = CREATE_NEW_CONSOLE;
                createFlags = CREATE_NEW_CONSOLE;
            }
            }
            /* CYGNUS LOCAL: We name the DLL cygtclpip.  */
            /* CYGNUS LOCAL: We name the DLL cygtclpip.  */
            Tcl_DStringAppend(&cmdLine, "cygtclpip" STRINGIFY(TCL_MAJOR_VERSION)
            Tcl_DStringAppend(&cmdLine, "cygtclpip" STRINGIFY(TCL_MAJOR_VERSION)
                    STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1);
                    STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1);
        }
        }
    }
    }
 
 
    /*
    /*
     * cmdLine gets the full command line used to invoke the executable,
     * cmdLine gets the full command line used to invoke the executable,
     * including the name of the executable itself.  The command line
     * including the name of the executable itself.  The command line
     * arguments in argv[] are stored in cmdLine separated by spaces.
     * arguments in argv[] are stored in cmdLine separated by spaces.
     * Special characters in individual arguments from argv[] must be
     * Special characters in individual arguments from argv[] must be
     * quoted when being stored in cmdLine.
     * quoted when being stored in cmdLine.
     *
     *
     * When calling any application, bear in mind that arguments that
     * When calling any application, bear in mind that arguments that
     * specify a path name are not converted.  If an argument contains
     * specify a path name are not converted.  If an argument contains
     * forward slashes as path separators, it may or may not be
     * forward slashes as path separators, it may or may not be
     * recognized as a path name, depending on the program.  In general,
     * recognized as a path name, depending on the program.  In general,
     * most applications accept forward slashes only as option
     * most applications accept forward slashes only as option
     * delimiters and backslashes only as paths.
     * delimiters and backslashes only as paths.
     *
     *
     * Additionally, when calling a 16-bit dos or windows application,
     * Additionally, when calling a 16-bit dos or windows application,
     * all path names must use the short, cryptic, path format (e.g.,
     * all path names must use the short, cryptic, path format (e.g.,
     * using ab~1.def instead of "a b.default").
     * using ab~1.def instead of "a b.default").
     */
     */
 
 
    BuildCommandLine(argc, argv, &cmdLine);
    BuildCommandLine(argc, argv, &cmdLine);
 
 
    if (!CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
    if (!CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
            createFlags, NULL, NULL, &startInfo, &procInfo)) {
            createFlags, NULL, NULL, &startInfo, &procInfo)) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        Tcl_AppendResult(interp, "couldn't execute \"", originalName,
        Tcl_AppendResult(interp, "couldn't execute \"", originalName,
                "\": ", Tcl_PosixError(interp), (char *) NULL);
                "\": ", Tcl_PosixError(interp), (char *) NULL);
        goto end;
        goto end;
    }
    }
 
 
    if (applType == APPL_DOS) {
    if (applType == APPL_DOS) {
        WaitForSingleObject(hProcess, 50);
        WaitForSingleObject(hProcess, 50);
    }
    }
 
 
    /*
    /*
     * "When an application spawns a process repeatedly, a new thread
     * "When an application spawns a process repeatedly, a new thread
     * instance will be created for each process but the previous
     * instance will be created for each process but the previous
     * instances may not be cleaned up.  This results in a significant
     * instances may not be cleaned up.  This results in a significant
     * virtual memory loss each time the process is spawned.  If there
     * virtual memory loss each time the process is spawned.  If there
     * is a WaitForInputIdle() call between CreateProcess() and
     * is a WaitForInputIdle() call between CreateProcess() and
     * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
     * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
     */
     */
 
 
    WaitForInputIdle(procInfo.hProcess, 5000);
    WaitForInputIdle(procInfo.hProcess, 5000);
    CloseHandle(procInfo.hThread);
    CloseHandle(procInfo.hThread);
 
 
    *pidPtr = (Tcl_Pid) procInfo.hProcess;
    *pidPtr = (Tcl_Pid) procInfo.hProcess;
    if (*pidPtr != 0) {
    if (*pidPtr != 0) {
        ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
        ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
        procPtr->hProcess = procInfo.hProcess;
        procPtr->hProcess = procInfo.hProcess;
        procPtr->dwProcessId = procInfo.dwProcessId;
        procPtr->dwProcessId = procInfo.dwProcessId;
        procPtr->nextPtr = procList;
        procPtr->nextPtr = procList;
        procList = procPtr;
        procList = procPtr;
    }
    }
    result = TCL_OK;
    result = TCL_OK;
 
 
    end:
    end:
    Tcl_DStringFree(&cmdLine);
    Tcl_DStringFree(&cmdLine);
    if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
    if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdInput);
        CloseHandle(startInfo.hStdInput);
    }
    }
    if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
    if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdOutput);
        CloseHandle(startInfo.hStdOutput);
    }
    }
    if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
    if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
        CloseHandle(startInfo.hStdError);
        CloseHandle(startInfo.hStdError);
    }
    }
    return result;
    return result;
}
}
 
 


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * HasConsole --
 * HasConsole --
 *
 *
 *      Determines whether the current application is attached to a
 *      Determines whether the current application is attached to a
 *      console.
 *      console.
 *
 *
 * Results:
 * Results:
 *      Returns TRUE if this application has a console, else FALSE.
 *      Returns TRUE if this application has a console, else FALSE.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static BOOL
static BOOL
HasConsole()
HasConsole()
{
{
    HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
    HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 
 
    if (handle != INVALID_HANDLE_VALUE) {
    if (handle != INVALID_HANDLE_VALUE) {
        CloseHandle(handle);
        CloseHandle(handle);
        return TRUE;
        return TRUE;
    } else {
    } else {
        return FALSE;
        return FALSE;
    }
    }
}
}


/*
/*
 *--------------------------------------------------------------------
 *--------------------------------------------------------------------
 *
 *
 * ApplicationType --
 * ApplicationType --
 *
 *
 *      Search for the specified program and identify if it refers to a DOS,
 *      Search for the specified program and identify if it refers to a DOS,
 *      Windows 3.X, or Win32 program.  Used to determine how to invoke
 *      Windows 3.X, or Win32 program.  Used to determine how to invoke
 *      a program, or if it can even be invoked.
 *      a program, or if it can even be invoked.
 *
 *
 *      It is possible to almost positively identify DOS and Windows
 *      It is possible to almost positively identify DOS and Windows
 *      applications that contain the appropriate magic numbers.  However,
 *      applications that contain the appropriate magic numbers.  However,
 *      DOS .com files do not seem to contain a magic number; if the program
 *      DOS .com files do not seem to contain a magic number; if the program
 *      name ends with .com and could not be identified as a Windows .com
 *      name ends with .com and could not be identified as a Windows .com
 *      file, it will be assumed to be a DOS application, even if it was
 *      file, it will be assumed to be a DOS application, even if it was
 *      just random data.  If the program name does not end with .com, no
 *      just random data.  If the program name does not end with .com, no
 *      such assumption is made.
 *      such assumption is made.
 *
 *
 *      The Win32 procedure GetBinaryType incorrectly identifies any
 *      The Win32 procedure GetBinaryType incorrectly identifies any
 *      junk file that ends with .exe as a dos executable and some
 *      junk file that ends with .exe as a dos executable and some
 *      executables that don't end with .exe as not executable.  Plus it
 *      executables that don't end with .exe as not executable.  Plus it
 *      doesn't exist under win95, so I won't feel bad about reimplementing
 *      doesn't exist under win95, so I won't feel bad about reimplementing
 *      functionality.
 *      functionality.
 *
 *
 * Results:
 * Results:
 *      The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32
 *      The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32
 *      if the filename referred to the corresponding application type.
 *      if the filename referred to the corresponding application type.
 *      If the file name could not be found or did not refer to any known
 *      If the file name could not be found or did not refer to any known
 *      application type, APPL_NONE is returned and an error message is
 *      application type, APPL_NONE is returned and an error message is
 *      left in interp.  .bat files are identified as APPL_DOS.
 *      left in interp.  .bat files are identified as APPL_DOS.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
ApplicationType(interp, originalName, fullPath)
ApplicationType(interp, originalName, fullPath)
    Tcl_Interp *interp;         /* Interp, for error message. */
    Tcl_Interp *interp;         /* Interp, for error message. */
    const char *originalName;   /* Name of the application to find. */
    const char *originalName;   /* Name of the application to find. */
    char fullPath[MAX_PATH];    /* Filled with complete path to
    char fullPath[MAX_PATH];    /* Filled with complete path to
                                 * application. */
                                 * application. */
{
{
    int applType, i;
    int applType, i;
    HANDLE hFile;
    HANDLE hFile;
    char *ext, *rest;
    char *ext, *rest;
    char buf[2];
    char buf[2];
    DWORD read;
    DWORD read;
    IMAGE_DOS_HEADER header;
    IMAGE_DOS_HEADER header;
    static char extensions[][5] = {"", ".com", ".exe", ".bat"};
    static char extensions[][5] = {"", ".com", ".exe", ".bat"};
 
 
    /* Look for the program as an external program.  First try the name
    /* Look for the program as an external program.  First try the name
     * as it is, then try adding .com, .exe, and .bat, in that order, to
     * as it is, then try adding .com, .exe, and .bat, in that order, to
     * the name, looking for an executable.
     * the name, looking for an executable.
     *
     *
     * Using the raw SearchPath() procedure doesn't do quite what is
     * Using the raw SearchPath() procedure doesn't do quite what is
     * necessary.  If the name of the executable already contains a '.'
     * necessary.  If the name of the executable already contains a '.'
     * character, it will not try appending the specified extension when
     * character, it will not try appending the specified extension when
     * searching (in other words, SearchPath will not find the program
     * searching (in other words, SearchPath will not find the program
     * "a.b.exe" if the arguments specified "a.b" and ".exe").
     * "a.b.exe" if the arguments specified "a.b" and ".exe").
     * So, first look for the file as it is named.  Then manually append
     * So, first look for the file as it is named.  Then manually append
     * the extensions, looking for a match.
     * the extensions, looking for a match.
     */
     */
 
 
    applType = APPL_NONE;
    applType = APPL_NONE;
    for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
    for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
        lstrcpyn(fullPath, originalName, MAX_PATH - 5);
        lstrcpyn(fullPath, originalName, MAX_PATH - 5);
        lstrcat(fullPath, extensions[i]);
        lstrcat(fullPath, extensions[i]);
 
 
        SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, &rest);
        SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, &rest);
 
 
        /*
        /*
         * Ignore matches on directories or data files, return if identified
         * Ignore matches on directories or data files, return if identified
         * a known type.
         * a known type.
         */
         */
 
 
        if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
        if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
            continue;
            continue;
        }
        }
 
 
        ext = strrchr(fullPath, '.');
        ext = strrchr(fullPath, '.');
        if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) {
        if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) {
            applType = APPL_DOS;
            applType = APPL_DOS;
            break;
            break;
        }
        }
 
 
        hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
        hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
        if (hFile == INVALID_HANDLE_VALUE) {
            continue;
            continue;
        }
        }
 
 
        header.e_magic = 0;
        header.e_magic = 0;
        ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
        ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
        if (header.e_magic != IMAGE_DOS_SIGNATURE) {
        if (header.e_magic != IMAGE_DOS_SIGNATURE) {
            /*
            /*
             * Doesn't have the magic number for relocatable executables.  If
             * Doesn't have the magic number for relocatable executables.  If
             * filename ends with .com, assume it's a DOS application anyhow.
             * filename ends with .com, assume it's a DOS application anyhow.
             * Note that we didn't make this assumption at first, because some
             * Note that we didn't make this assumption at first, because some
             * supposed .com files are really 32-bit executables with all the
             * supposed .com files are really 32-bit executables with all the
             * magic numbers and everything.
             * magic numbers and everything.
             */
             */
 
 
            CloseHandle(hFile);
            CloseHandle(hFile);
            if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) {
            if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) {
                applType = APPL_DOS;
                applType = APPL_DOS;
                break;
                break;
            }
            }
            continue;
            continue;
        }
        }
        if (header.e_lfarlc != sizeof(header)) {
        if (header.e_lfarlc != sizeof(header)) {
            /*
            /*
             * All Windows 3.X and Win32 and some DOS programs have this value
             * All Windows 3.X and Win32 and some DOS programs have this value
             * set here.  If it doesn't, assume that since it already had the
             * set here.  If it doesn't, assume that since it already had the
             * other magic number it was a DOS application.
             * other magic number it was a DOS application.
             */
             */
 
 
            CloseHandle(hFile);
            CloseHandle(hFile);
            applType = APPL_DOS;
            applType = APPL_DOS;
            break;
            break;
        }
        }
 
 
        /*
        /*
         * The DWORD at header.e_lfanew points to yet another magic number.
         * The DWORD at header.e_lfanew points to yet another magic number.
         */
         */
 
 
        buf[0] = '\0';
        buf[0] = '\0';
        SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
        SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
        ReadFile(hFile, (void *) buf, 2, &read, NULL);
        ReadFile(hFile, (void *) buf, 2, &read, NULL);
        CloseHandle(hFile);
        CloseHandle(hFile);
 
 
        if ((buf[0] == 'N') && (buf[1] == 'E')) {
        if ((buf[0] == 'N') && (buf[1] == 'E')) {
            applType = APPL_WIN3X;
            applType = APPL_WIN3X;
        } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
        } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
            applType = APPL_WIN32;
            applType = APPL_WIN32;
        } else {
        } else {
            /*
            /*
             * Strictly speaking, there should be a test that there
             * Strictly speaking, there should be a test that there
             * is an 'L' and 'E' at buf[0..1], to identify the type as
             * is an 'L' and 'E' at buf[0..1], to identify the type as
             * DOS, but of course we ran into a DOS executable that
             * DOS, but of course we ran into a DOS executable that
             * _doesn't_ have the magic number -- specifically, one
             * _doesn't_ have the magic number -- specifically, one
             * compiled using the Lahey Fortran90 compiler.
             * compiled using the Lahey Fortran90 compiler.
             */
             */
 
 
            applType = APPL_DOS;
            applType = APPL_DOS;
        }
        }
        break;
        break;
    }
    }
 
 
    if (applType == APPL_NONE) {
    if (applType == APPL_NONE) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        Tcl_AppendResult(interp, "couldn't execute \"", originalName,
        Tcl_AppendResult(interp, "couldn't execute \"", originalName,
                "\": ", Tcl_PosixError(interp), (char *) NULL);
                "\": ", Tcl_PosixError(interp), (char *) NULL);
        return APPL_NONE;
        return APPL_NONE;
    }
    }
 
 
    if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
    if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
        /*
        /*
         * Replace long path name of executable with short path name for
         * Replace long path name of executable with short path name for
         * 16-bit applications.  Otherwise the application may not be able
         * 16-bit applications.  Otherwise the application may not be able
         * to correctly parse its own command line to separate off the
         * to correctly parse its own command line to separate off the
         * application name from the arguments.
         * application name from the arguments.
         */
         */
 
 
        GetShortPathName(fullPath, fullPath, MAX_PATH);
        GetShortPathName(fullPath, fullPath, MAX_PATH);
    }
    }
    return applType;
    return applType;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * BuildCommandLine --
 * BuildCommandLine --
 *
 *
 *      The command line arguments are stored in linePtr separated
 *      The command line arguments are stored in linePtr separated
 *      by spaces, in a form that CreateProcess() understands.  Special
 *      by spaces, in a form that CreateProcess() understands.  Special
 *      characters in individual arguments from argv[] must be quoted
 *      characters in individual arguments from argv[] must be quoted
 *      when being stored in cmdLine.
 *      when being stored in cmdLine.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
BuildCommandLine(argc, argv, linePtr)
BuildCommandLine(argc, argv, linePtr)
    int argc;                   /* Number of arguments. */
    int argc;                   /* Number of arguments. */
    char **argv;                /* Argument strings. */
    char **argv;                /* Argument strings. */
    Tcl_DString *linePtr;       /* Initialized Tcl_DString that receives the
    Tcl_DString *linePtr;       /* Initialized Tcl_DString that receives the
                                 * command line. */
                                 * command line. */
{
{
    char *start, *special;
    char *start, *special;
    int quote, i;
    int quote, i;
 
 
    for (i = 0; i < argc; i++) {
    for (i = 0; i < argc; i++) {
        if (i > 0) {
        if (i > 0) {
            Tcl_DStringAppend(linePtr, " ", 1);
            Tcl_DStringAppend(linePtr, " ", 1);
        }
        }
 
 
        quote = 0;
        quote = 0;
        if (argv[i][0] == '\0') {
        if (argv[i][0] == '\0') {
            quote = 1;
            quote = 1;
        } else {
        } else {
            for (start = argv[i]; *start != '\0'; start++) {
            for (start = argv[i]; *start != '\0'; start++) {
                if (isspace(*start)) {
                if (isspace(*start)) {
                    quote = 1;
                    quote = 1;
                    break;
                    break;
                }
                }
            }
            }
        }
        }
        if (quote) {
        if (quote) {
            Tcl_DStringAppend(linePtr, "\"", 1);
            Tcl_DStringAppend(linePtr, "\"", 1);
        }
        }
 
 
        start = argv[i];
        start = argv[i];
        for (special = argv[i]; ; ) {
        for (special = argv[i]; ; ) {
            if ((*special == '\\') &&
            if ((*special == '\\') &&
                    (special[1] == '\\' || special[1] == '"')) {
                    (special[1] == '\\' || special[1] == '"')) {
                Tcl_DStringAppend(linePtr, start, special - start);
                Tcl_DStringAppend(linePtr, start, special - start);
                start = special;
                start = special;
                while (1) {
                while (1) {
                    special++;
                    special++;
                    if (*special == '"') {
                    if (*special == '"') {
                        /*
                        /*
                         * N backslashes followed a quote -> insert
                         * N backslashes followed a quote -> insert
                         * N * 2 + 1 backslashes then a quote.
                         * N * 2 + 1 backslashes then a quote.
                         */
                         */
 
 
                        Tcl_DStringAppend(linePtr, start, special - start);
                        Tcl_DStringAppend(linePtr, start, special - start);
                        break;
                        break;
                    }
                    }
                    if (*special != '\\') {
                    if (*special != '\\') {
                        break;
                        break;
                    }
                    }
                }
                }
                Tcl_DStringAppend(linePtr, start, special - start);
                Tcl_DStringAppend(linePtr, start, special - start);
                start = special;
                start = special;
            }
            }
            if (*special == '"') {
            if (*special == '"') {
                Tcl_DStringAppend(linePtr, start, special - start);
                Tcl_DStringAppend(linePtr, start, special - start);
                Tcl_DStringAppend(linePtr, "\\\"", 2);
                Tcl_DStringAppend(linePtr, "\\\"", 2);
                start = special + 1;
                start = special + 1;
            }
            }
            if (*special == '\0') {
            if (*special == '\0') {
                break;
                break;
            }
            }
            special++;
            special++;
        }
        }
        Tcl_DStringAppend(linePtr, start, special - start);
        Tcl_DStringAppend(linePtr, start, special - start);
        if (quote) {
        if (quote) {
            Tcl_DStringAppend(linePtr, "\"", 1);
            Tcl_DStringAppend(linePtr, "\"", 1);
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * MakeTempFile --
 * MakeTempFile --
 *
 *
 *      Helper function for TclpCreateProcess under Win32s.  Makes a
 *      Helper function for TclpCreateProcess under Win32s.  Makes a
 *      temporary file that _won't_ go away automatically when it's file
 *      temporary file that _won't_ go away automatically when it's file
 *      handle is closed.  Used for simulated pipes, which are written
 *      handle is closed.  Used for simulated pipes, which are written
 *      in one pass and reopened and read in the next pass.
 *      in one pass and reopened and read in the next pass.
 *
 *
 * Results:
 * Results:
 *      namePtr is filled with the name of the temporary file.
 *      namePtr is filled with the name of the temporary file.
 *
 *
 * Side effects:
 * Side effects:
 *      A temporary file with the name specified by namePtr is created.
 *      A temporary file with the name specified by namePtr is created.
 *      The caller is responsible for deleting this temporary file.
 *      The caller is responsible for deleting this temporary file.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static char *
static char *
MakeTempFile(namePtr)
MakeTempFile(namePtr)
    Tcl_DString *namePtr;       /* Initialized Tcl_DString that is filled
    Tcl_DString *namePtr;       /* Initialized Tcl_DString that is filled
                                 * with the name of the temporary file that
                                 * with the name of the temporary file that
                                 * was created. */
                                 * was created. */
{
{
    char name[MAX_PATH];
    char name[MAX_PATH];
 
 
    if (TempFileName(name) == 0) {
    if (TempFileName(name) == 0) {
        return NULL;
        return NULL;
    }
    }
 
 
    Tcl_DStringAppend(namePtr, name, -1);
    Tcl_DStringAppend(namePtr, name, -1);
    return Tcl_DStringValue(namePtr);
    return Tcl_DStringValue(namePtr);
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * CopyChannel --
 * CopyChannel --
 *
 *
 *      Helper function used by TclpCreateProcess under Win32s.  Copies
 *      Helper function used by TclpCreateProcess under Win32s.  Copies
 *      what remains of source file to destination file; source file
 *      what remains of source file to destination file; source file
 *      pointer need not be positioned at the beginning of the file if
 *      pointer need not be positioned at the beginning of the file if
 *      all of source file is not desired, but data is copied up to end
 *      all of source file is not desired, but data is copied up to end
 *      of source file.
 *      of source file.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
CopyChannel(dst, src)
CopyChannel(dst, src)
    HANDLE dst;                 /* Destination file. */
    HANDLE dst;                 /* Destination file. */
    HANDLE src;                 /* Source file. */
    HANDLE src;                 /* Source file. */
{
{
    char buf[8192];
    char buf[8192];
    DWORD dwRead, dwWrite;
    DWORD dwRead, dwWrite;
 
 
    while (ReadFile(src, buf, sizeof(buf), &dwRead, NULL) != FALSE) {
    while (ReadFile(src, buf, sizeof(buf), &dwRead, NULL) != FALSE) {
        if (dwRead == 0) {
        if (dwRead == 0) {
            break;
            break;
        }
        }
        if (WriteFile(dst, buf, dwRead, &dwWrite, NULL) == FALSE) {
        if (WriteFile(dst, buf, dwRead, &dwWrite, NULL) == FALSE) {
            break;
            break;
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclpCreateCommandChannel --
 * TclpCreateCommandChannel --
 *
 *
 *      This function is called by Tcl_OpenCommandChannel to perform
 *      This function is called by Tcl_OpenCommandChannel to perform
 *      the platform specific channel initialization for a command
 *      the platform specific channel initialization for a command
 *      channel.
 *      channel.
 *
 *
 * Results:
 * Results:
 *      Returns a new channel or NULL on failure.
 *      Returns a new channel or NULL on failure.
 *
 *
 * Side effects:
 * Side effects:
 *      Allocates a new channel.
 *      Allocates a new channel.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
Tcl_Channel
Tcl_Channel
TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
    TclFile readFile;           /* If non-null, gives the file for reading. */
    TclFile readFile;           /* If non-null, gives the file for reading. */
    TclFile writeFile;          /* If non-null, gives the file for writing. */
    TclFile writeFile;          /* If non-null, gives the file for writing. */
    TclFile errorFile;          /* If non-null, gives the file where errors
    TclFile errorFile;          /* If non-null, gives the file where errors
                                 * can be read. */
                                 * can be read. */
    int numPids;                /* The number of pids in the pid array. */
    int numPids;                /* The number of pids in the pid array. */
    Tcl_Pid *pidPtr;            /* An array of process identifiers. */
    Tcl_Pid *pidPtr;            /* An array of process identifiers. */
{
{
    char channelName[20];
    char channelName[20];
    int channelId;
    int channelId;
    PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
    PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
 
 
    if (!initialized) {
    if (!initialized) {
        PipeInit();
        PipeInit();
    }
    }
 
 
    infoPtr->watchMask = 0;
    infoPtr->watchMask = 0;
    infoPtr->flags = 0;
    infoPtr->flags = 0;
    infoPtr->readFile = readFile;
    infoPtr->readFile = readFile;
    infoPtr->writeFile = writeFile;
    infoPtr->writeFile = writeFile;
    infoPtr->errorFile = errorFile;
    infoPtr->errorFile = errorFile;
    infoPtr->numPids = numPids;
    infoPtr->numPids = numPids;
    infoPtr->pidPtr = pidPtr;
    infoPtr->pidPtr = pidPtr;
 
 
    /* CYGNUS LOCAL: Mutex for flags.  */
    /* CYGNUS LOCAL: Mutex for flags.  */
    infoPtr->flagsMutex = CreateMutex(NULL, FALSE, NULL);
    infoPtr->flagsMutex = CreateMutex(NULL, FALSE, NULL);
 
 
    /*
    /*
     * Use one of the fds associated with the channel as the
     * Use one of the fds associated with the channel as the
     * channel id.
     * channel id.
     */
     */
 
 
    if (readFile) {
    if (readFile) {
        WinPipe *pipePtr = (WinPipe *) readFile;
        WinPipe *pipePtr = (WinPipe *) readFile;
        if (pipePtr->file.type == WIN32S_PIPE
        if (pipePtr->file.type == WIN32S_PIPE
                && pipePtr->file.handle == INVALID_HANDLE_VALUE) {
                && pipePtr->file.handle == INVALID_HANDLE_VALUE) {
            pipePtr->file.handle = CreateFile(pipePtr->fileName, GENERIC_READ,
            pipePtr->file.handle = CreateFile(pipePtr->fileName, GENERIC_READ,
                    0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
                    0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        }
        }
        channelId = (int) pipePtr->file.handle;
        channelId = (int) pipePtr->file.handle;
    } else if (writeFile) {
    } else if (writeFile) {
        channelId = (int) ((WinFile*)writeFile)->handle;
        channelId = (int) ((WinFile*)writeFile)->handle;
    } else if (errorFile) {
    } else if (errorFile) {
        channelId = (int) ((WinFile*)errorFile)->handle;
        channelId = (int) ((WinFile*)errorFile)->handle;
    } else {
    } else {
        channelId = 0;
        channelId = 0;
    }
    }
 
 
    infoPtr->validMask = 0;
    infoPtr->validMask = 0;
    if (readFile != NULL) {
    if (readFile != NULL) {
        infoPtr->validMask |= TCL_READABLE;
        infoPtr->validMask |= TCL_READABLE;
    }
    }
    if (writeFile != NULL) {
    if (writeFile != NULL) {
        infoPtr->validMask |= TCL_WRITABLE;
        infoPtr->validMask |= TCL_WRITABLE;
    }
    }
 
 
    /*
    /*
     * For backward compatibility with previous versions of Tcl, we
     * For backward compatibility with previous versions of Tcl, we
     * use "file%d" as the base name for pipes even though it would
     * use "file%d" as the base name for pipes even though it would
     * be more natural to use "pipe%d".
     * be more natural to use "pipe%d".
     */
     */
 
 
    sprintf(channelName, "file%d", channelId);
    sprintf(channelName, "file%d", channelId);
    infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
    infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
            (ClientData) infoPtr, infoPtr->validMask);
            (ClientData) infoPtr, infoPtr->validMask);
 
 
    /*
    /*
     * Pipes have AUTO translation mode on Windows and ^Z eof char, which
     * Pipes have AUTO translation mode on Windows and ^Z eof char, which
     * means that a ^Z will be appended to them at close. This is needed
     * means that a ^Z will be appended to them at close. This is needed
     * for Windows programs that expect a ^Z at EOF.
     * for Windows programs that expect a ^Z at EOF.
     */
     */
 
 
    Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
    Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
            "-translation", "auto");
            "-translation", "auto");
    Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
    Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
            "-eofchar", "\032 {}");
            "-eofchar", "\032 {}");
    return infoPtr->channel;
    return infoPtr->channel;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * TclGetAndDetachPids --
 * TclGetAndDetachPids --
 *
 *
 *      Stores a list of the command PIDs for a command channel in
 *      Stores a list of the command PIDs for a command channel in
 *      interp->result.
 *      interp->result.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      Modifies interp->result.
 *      Modifies interp->result.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
void
void
TclGetAndDetachPids(interp, chan)
TclGetAndDetachPids(interp, chan)
    Tcl_Interp *interp;
    Tcl_Interp *interp;
    Tcl_Channel chan;
    Tcl_Channel chan;
{
{
    PipeInfo *pipePtr;
    PipeInfo *pipePtr;
    Tcl_ChannelType *chanTypePtr;
    Tcl_ChannelType *chanTypePtr;
    int i;
    int i;
    char buf[20];
    char buf[20];
 
 
    /*
    /*
     * Punt if the channel is not a command channel.
     * Punt if the channel is not a command channel.
     */
     */
 
 
    chanTypePtr = Tcl_GetChannelType(chan);
    chanTypePtr = Tcl_GetChannelType(chan);
    if (chanTypePtr != &pipeChannelType) {
    if (chanTypePtr != &pipeChannelType) {
        return;
        return;
    }
    }
 
 
    pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
    pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
    for (i = 0; i < pipePtr->numPids; i++) {
    for (i = 0; i < pipePtr->numPids; i++) {
        sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
        sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
        Tcl_AppendElement(interp, buf);
        Tcl_AppendElement(interp, buf);
        Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
        Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
    }
    }
    if (pipePtr->numPids > 0) {
    if (pipePtr->numPids > 0) {
        ckfree((char *) pipePtr->pidPtr);
        ckfree((char *) pipePtr->pidPtr);
        pipePtr->numPids = 0;
        pipePtr->numPids = 0;
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeBlockModeProc --
 * PipeBlockModeProc --
 *
 *
 *      Set blocking or non-blocking mode on channel.
 *      Set blocking or non-blocking mode on channel.
 *
 *
 * Results:
 * Results:
 *      0 if successful, errno when failed.
 *      0 if successful, errno when failed.
 *
 *
 * Side effects:
 * Side effects:
 *      Sets the device into blocking or non-blocking mode.
 *      Sets the device into blocking or non-blocking mode.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeBlockModeProc(instanceData, mode)
PipeBlockModeProc(instanceData, mode)
    ClientData instanceData;    /* Instance data for channel. */
    ClientData instanceData;    /* Instance data for channel. */
    int mode;                   /* TCL_MODE_BLOCKING or
    int mode;                   /* TCL_MODE_BLOCKING or
                                 * TCL_MODE_NONBLOCKING. */
                                 * TCL_MODE_NONBLOCKING. */
{
{
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
 
 
    /*
    /*
     * Pipes on Windows can not be switched between blocking and nonblocking,
     * Pipes on Windows can not be switched between blocking and nonblocking,
     * hence we have to emulate the behavior. This is done in the input
     * hence we have to emulate the behavior. This is done in the input
     * function by checking against a bit in the state. We set or unset the
     * function by checking against a bit in the state. We set or unset the
     * bit here to cause the input function to emulate the correct behavior.
     * bit here to cause the input function to emulate the correct behavior.
     */
     */
 
 
    if (mode == TCL_MODE_NONBLOCKING) {
    if (mode == TCL_MODE_NONBLOCKING) {
        PipeSetFlag(infoPtr, PIPE_ASYNC);
        PipeSetFlag(infoPtr, PIPE_ASYNC);
    } else {
    } else {
        PipeResetFlag(infoPtr, PIPE_ASYNC);
        PipeResetFlag(infoPtr, PIPE_ASYNC);
    }
    }
    return 0;
    return 0;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeCloseProc --
 * PipeCloseProc --
 *
 *
 *      Closes a pipe based IO channel.
 *      Closes a pipe based IO channel.
 *
 *
 * Results:
 * Results:
 *      0 on success, errno otherwise.
 *      0 on success, errno otherwise.
 *
 *
 * Side effects:
 * Side effects:
 *      Closes the physical channel.
 *      Closes the physical channel.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeCloseProc(instanceData, interp)
PipeCloseProc(instanceData, interp)
    ClientData instanceData;    /* Pointer to PipeInfo structure. */
    ClientData instanceData;    /* Pointer to PipeInfo structure. */
    Tcl_Interp *interp;         /* For error reporting. */
    Tcl_Interp *interp;         /* For error reporting. */
{
{
    PipeInfo *pipePtr = (PipeInfo *) instanceData;
    PipeInfo *pipePtr = (PipeInfo *) instanceData;
    Tcl_Channel errChan;
    Tcl_Channel errChan;
    int errorCode, result;
    int errorCode, result;
    PipeInfo *infoPtr, **nextPtrPtr;
    PipeInfo *infoPtr, **nextPtrPtr;
 
 
    /*
    /*
     * Remove the file from the list of watched files.
     * Remove the file from the list of watched files.
     */
     */
 
 
    for (nextPtrPtr = &firstPipePtr, infoPtr = *nextPtrPtr; infoPtr != NULL;
    for (nextPtrPtr = &firstPipePtr, infoPtr = *nextPtrPtr; infoPtr != NULL;
            nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
            nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
        if (infoPtr == (PipeInfo *)pipePtr) {
        if (infoPtr == (PipeInfo *)pipePtr) {
            *nextPtrPtr = infoPtr->nextPtr;
            *nextPtrPtr = infoPtr->nextPtr;
            break;
            break;
        }
        }
    }
    }
 
 
    errorCode = 0;
    errorCode = 0;
    if (pipePtr->readFile != NULL) {
    if (pipePtr->readFile != NULL) {
        if (TclpCloseFile(pipePtr->readFile) != 0) {
        if (TclpCloseFile(pipePtr->readFile) != 0) {
            errorCode = errno;
            errorCode = errno;
        }
        }
    }
    }
    if (pipePtr->writeFile != NULL) {
    if (pipePtr->writeFile != NULL) {
        if (TclpCloseFile(pipePtr->writeFile) != 0) {
        if (TclpCloseFile(pipePtr->writeFile) != 0) {
            if (errorCode == 0) {
            if (errorCode == 0) {
                errorCode = errno;
                errorCode = errno;
            }
            }
        }
        }
    }
    }
 
 
    /*
    /*
     * Wrap the error file into a channel and give it to the cleanup
     * Wrap the error file into a channel and give it to the cleanup
     * routine.  If we are running in Win32s, just delete the error file
     * routine.  If we are running in Win32s, just delete the error file
     * immediately, because it was never used.
     * immediately, because it was never used.
     */
     */
 
 
    if (pipePtr->errorFile) {
    if (pipePtr->errorFile) {
        WinFile *filePtr;
        WinFile *filePtr;
        OSVERSIONINFO os;
        OSVERSIONINFO os;
 
 
        os.dwOSVersionInfoSize = sizeof(os);
        os.dwOSVersionInfoSize = sizeof(os);
        GetVersionEx(&os);
        GetVersionEx(&os);
        if (os.dwPlatformId == VER_PLATFORM_WIN32s) {
        if (os.dwPlatformId == VER_PLATFORM_WIN32s) {
            TclpCloseFile(pipePtr->errorFile);
            TclpCloseFile(pipePtr->errorFile);
            errChan = NULL;
            errChan = NULL;
        } else {
        } else {
            filePtr = (WinFile*)pipePtr->errorFile;
            filePtr = (WinFile*)pipePtr->errorFile;
            errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
            errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
                    TCL_READABLE);
                    TCL_READABLE);
        }
        }
    } else {
    } else {
        errChan = NULL;
        errChan = NULL;
    }
    }
    result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
    result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
            errChan);
            errChan);
    if (pipePtr->numPids > 0) {
    if (pipePtr->numPids > 0) {
        ckfree((char *) pipePtr->pidPtr);
        ckfree((char *) pipePtr->pidPtr);
    }
    }
 
 
    /* CYGNUS LOCAL: If the pipe has a thread, let the thread free the
    /* CYGNUS LOCAL: If the pipe has a thread, let the thread free the
       structure.  */
       structure.  */
    if (PipeGetFlags(pipePtr) & PIPE_HAS_THREAD) {
    if (PipeGetFlags(pipePtr) & PIPE_HAS_THREAD) {
        WaitForSingleObject(pipePtr->flagsMutex, INFINITE);
        WaitForSingleObject(pipePtr->flagsMutex, INFINITE);
        pipePtr->flags |= PIPE_CLOSED;
        pipePtr->flags |= PIPE_CLOSED;
        SetEvent(pipePtr->tryReadEvent);
        SetEvent(pipePtr->tryReadEvent);
        ReleaseMutex(pipePtr->flagsMutex);
        ReleaseMutex(pipePtr->flagsMutex);
    } else {
    } else {
        CloseHandle(pipePtr->flagsMutex);
        CloseHandle(pipePtr->flagsMutex);
        ckfree((char*) pipePtr);
        ckfree((char*) pipePtr);
    }
    }
 
 
    if (errorCode == 0) {
    if (errorCode == 0) {
        return result;
        return result;
    }
    }
    return errorCode;
    return errorCode;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeInputProc --
 * PipeInputProc --
 *
 *
 *      Reads input from the IO channel into the buffer given. Returns
 *      Reads input from the IO channel into the buffer given. Returns
 *      count of how many bytes were actually read, and an error indication.
 *      count of how many bytes were actually read, and an error indication.
 *
 *
 * Results:
 * Results:
 *      A count of how many bytes were read is returned and an error
 *      A count of how many bytes were read is returned and an error
 *      indication is returned in an output argument.
 *      indication is returned in an output argument.
 *
 *
 * Side effects:
 * Side effects:
 *      Reads input from the actual channel.
 *      Reads input from the actual channel.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeInputProc(instanceData, buf, bufSize, errorCode)
PipeInputProc(instanceData, buf, bufSize, errorCode)
    ClientData instanceData;            /* Pipe state. */
    ClientData instanceData;            /* Pipe state. */
    char *buf;                          /* Where to store data read. */
    char *buf;                          /* Where to store data read. */
    int bufSize;                        /* How much space is available
    int bufSize;                        /* How much space is available
                                         * in the buffer? */
                                         * in the buffer? */
    int *errorCode;                     /* Where to store error code. */
    int *errorCode;                     /* Where to store error code. */
{
{
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    WinFile *filePtr = (WinFile*) infoPtr->readFile;
    WinFile *filePtr = (WinFile*) infoPtr->readFile;
    DWORD count;
    DWORD count;
    DWORD bytesRead;
    DWORD bytesRead;
    int gotReadAhead = 0;
    int gotReadAhead = 0;
    int origBufSize = bufSize;
    int origBufSize = bufSize;
 
 
    /* CYGNUS LOCAL: If the pipe has a thread, lock it.  */
    /* CYGNUS LOCAL: If the pipe has a thread, lock it.  */
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
        WaitForSingleObject(infoPtr->mutex, INFINITE);
        WaitForSingleObject(infoPtr->mutex, INFINITE);
    }
    }
 
 
    *errorCode = 0;
    *errorCode = 0;
    if (filePtr->type == WIN32S_PIPE) {
    if (filePtr->type == WIN32S_PIPE) {
        if (((WinPipe *)filePtr)->otherPtr != NULL) {
        if (((WinPipe *)filePtr)->otherPtr != NULL) {
            panic("PipeInputProc: child process isn't finished writing");
            panic("PipeInputProc: child process isn't finished writing");
        }
        }
        if (filePtr->handle == INVALID_HANDLE_VALUE) {
        if (filePtr->handle == INVALID_HANDLE_VALUE) {
            filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
            filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
                    GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
                    GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
                    NULL);
                    NULL);
        }
        }
        if (filePtr->handle == INVALID_HANDLE_VALUE) {
        if (filePtr->handle == INVALID_HANDLE_VALUE) {
            goto error;
            goto error;
        }
        }
    } else {
    } else {
        /*
        /*
         * Pipes will block until the requested number of bytes has been
         * Pipes will block until the requested number of bytes has been
         * read.  To avoid blocking unnecessarily, we look ahead and only
         * read.  To avoid blocking unnecessarily, we look ahead and only
         * read as much as is available.
         * read as much as is available.
         */
         */
 
 
        if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0,
        if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0,
                (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) {
                (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) {
            if ((count != 0) && ((DWORD) bufSize > count)) {
            if ((count != 0) && ((DWORD) bufSize > count)) {
                bufSize = (int) count;
                bufSize = (int) count;
 
 
                /*
                /*
                 * This code is commented out because on Win95 we don't get
                 * This code is commented out because on Win95 we don't get
                 * notifier of eof on a pipe unless we try to read it.
                 * notifier of eof on a pipe unless we try to read it.
                 * The correct solution is to move to threads.
                 * The correct solution is to move to threads.
                 */
                 */
 
 
/*          } else if ((count == 0) && (PipeGetFlags(infoPtr) & PIPE_ASYNC)) { */
/*          } else if ((count == 0) && (PipeGetFlags(infoPtr) & PIPE_ASYNC)) { */
/*              errno = *errorCode = EAGAIN; */
/*              errno = *errorCode = EAGAIN; */
/*              return -1; */
/*              return -1; */
            } else if ((count == 0) && !(PipeGetFlags(infoPtr) & PIPE_ASYNC)) {
            } else if ((count == 0) && !(PipeGetFlags(infoPtr) & PIPE_ASYNC)) {
                bufSize = 1;
                bufSize = 1;
            }
            }
        } else {
        } else {
            goto error;
            goto error;
        }
        }
    }
    }
 
 
    /* CYGNUS LOCAL: Check for the readahead byte.  */
    /* CYGNUS LOCAL: Check for the readahead byte.  */
    if (PipeGetFlags(infoPtr) & PIPE_READAHEAD) {
    if (PipeGetFlags(infoPtr) & PIPE_READAHEAD) {
        *buf++ = infoPtr->readAhead;
        *buf++ = infoPtr->readAhead;
        PipeResetFlag(infoPtr, PIPE_READAHEAD);
        PipeResetFlag(infoPtr, PIPE_READAHEAD);
        if (bufSize <= 1) {
        if (bufSize <= 1) {
            PipeResetFlag(infoPtr, PIPE_READABLE);
            PipeResetFlag(infoPtr, PIPE_READABLE);
            ReleaseMutex(infoPtr->mutex);
            ReleaseMutex(infoPtr->mutex);
            return 1;
            return 1;
        }
        }
        gotReadAhead = 1;
        gotReadAhead = 1;
        if (bufSize == origBufSize) {
        if (bufSize == origBufSize) {
            --bufSize;
            --bufSize;
        }
        }
    }
    }
 
 
    /*
    /*
     * Note that we will block on reads from a console buffer until a
     * Note that we will block on reads from a console buffer until a
     * full line has been entered.  The only way I know of to get
     * full line has been entered.  The only way I know of to get
     * around this is to write a console driver.  We should probably
     * around this is to write a console driver.  We should probably
     * do this at some point, but for now, we just block.
     * do this at some point, but for now, we just block.
     */
     */
 
 
    if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
    if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
            (LPOVERLAPPED) NULL) == FALSE) {
            (LPOVERLAPPED) NULL) == FALSE) {
        goto error;
        goto error;
    }
    }
 
 
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
        PipeResetFlag(infoPtr, PIPE_READABLE);
        PipeResetFlag(infoPtr, PIPE_READABLE);
        ReleaseMutex(infoPtr->mutex);
        ReleaseMutex(infoPtr->mutex);
    }
    }
 
 
    return bytesRead + gotReadAhead;
    return bytesRead + gotReadAhead;
 
 
    error:
    error:
    TclWinConvertError(GetLastError());
    TclWinConvertError(GetLastError());
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
    if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
        ReleaseMutex(infoPtr->mutex);
        ReleaseMutex(infoPtr->mutex);
    }
    }
    if (errno == EPIPE) {
    if (errno == EPIPE) {
        return 0;
        return 0;
    }
    }
    *errorCode = errno;
    *errorCode = errno;
    return -1;
    return -1;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeOutputProc --
 * PipeOutputProc --
 *
 *
 *      Writes the given output on the IO channel. Returns count of how
 *      Writes the given output on the IO channel. Returns count of how
 *      many characters were actually written, and an error indication.
 *      many characters were actually written, and an error indication.
 *
 *
 * Results:
 * Results:
 *      A count of how many characters were written is returned and an
 *      A count of how many characters were written is returned and an
 *      error indication is returned in an output argument.
 *      error indication is returned in an output argument.
 *
 *
 * Side effects:
 * Side effects:
 *      Writes output on the actual channel.
 *      Writes output on the actual channel.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeOutputProc(instanceData, buf, toWrite, errorCode)
PipeOutputProc(instanceData, buf, toWrite, errorCode)
    ClientData instanceData;            /* Pipe state. */
    ClientData instanceData;            /* Pipe state. */
    char *buf;                          /* The data buffer. */
    char *buf;                          /* The data buffer. */
    int toWrite;                        /* How many bytes to write? */
    int toWrite;                        /* How many bytes to write? */
    int *errorCode;                     /* Where to store error code. */
    int *errorCode;                     /* Where to store error code. */
{
{
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    WinFile *filePtr = (WinFile*) infoPtr->writeFile;
    WinFile *filePtr = (WinFile*) infoPtr->writeFile;
    DWORD bytesWritten;
    DWORD bytesWritten;
 
 
    *errorCode = 0;
    *errorCode = 0;
    if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,
    if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,
            &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
            &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
        TclWinConvertError(GetLastError());
        TclWinConvertError(GetLastError());
        if (errno == EPIPE) {
        if (errno == EPIPE) {
            return 0;
            return 0;
        }
        }
        *errorCode = errno;
        *errorCode = errno;
        return -1;
        return -1;
    }
    }
    return bytesWritten;
    return bytesWritten;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeEventProc --
 * PipeEventProc --
 *
 *
 *      This function is invoked by Tcl_ServiceEvent when a file event
 *      This function is invoked by Tcl_ServiceEvent when a file event
 *      reaches the front of the event queue.  This procedure invokes
 *      reaches the front of the event queue.  This procedure invokes
 *      Tcl_NotifyChannel on the pipe.
 *      Tcl_NotifyChannel on the pipe.
 *
 *
 * Results:
 * Results:
 *      Returns 1 if the event was handled, meaning it should be removed
 *      Returns 1 if the event was handled, meaning it should be removed
 *      from the queue.  Returns 0 if the event was not handled, meaning
 *      from the queue.  Returns 0 if the event was not handled, meaning
 *      it should stay on the queue.  The only time the event isn't
 *      it should stay on the queue.  The only time the event isn't
 *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
 *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
 *
 *
 * Side effects:
 * Side effects:
 *      Whatever the notifier callback does.
 *      Whatever the notifier callback does.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeEventProc(evPtr, flags)
PipeEventProc(evPtr, flags)
    Tcl_Event *evPtr;           /* Event to service. */
    Tcl_Event *evPtr;           /* Event to service. */
    int flags;                  /* Flags that indicate what events to
    int flags;                  /* Flags that indicate what events to
                                 * handle, such as TCL_FILE_EVENTS. */
                                 * handle, such as TCL_FILE_EVENTS. */
{
{
    PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;
    PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;
    PipeInfo *infoPtr;
    PipeInfo *infoPtr;
    WinFile *filePtr;
    WinFile *filePtr;
    int mask;
    int mask;
/*    DWORD count;*/
/*    DWORD count;*/
 
 
    if (!(flags & TCL_FILE_EVENTS)) {
    if (!(flags & TCL_FILE_EVENTS)) {
        return 0;
        return 0;
    }
    }
 
 
    /*
    /*
     * Search through the list of watched pipes for the one whose handle
     * Search through the list of watched pipes for the one whose handle
     * matches the event.  We do this rather than simply dereferencing
     * matches the event.  We do this rather than simply dereferencing
     * the handle in the event so that pipes can be deleted while the
     * the handle in the event so that pipes can be deleted while the
     * event is in the queue.
     * event is in the queue.
     */
     */
 
 
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
    for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
        if (pipeEvPtr->infoPtr == infoPtr) {
        if (pipeEvPtr->infoPtr == infoPtr) {
            PipeResetFlag(infoPtr, PIPE_PENDING);
            PipeResetFlag(infoPtr, PIPE_PENDING);
            break;
            break;
        }
        }
    }
    }
 
 
    /*
    /*
     * Remove stale events.
     * Remove stale events.
     */
     */
 
 
    if (!infoPtr) {
    if (!infoPtr) {
        return 1;
        return 1;
    }
    }
 
 
    /*
    /*
     * If we aren't on Win32s, check to see if the pipe is readable.  Note
     * If we aren't on Win32s, check to see if the pipe is readable.  Note
     * that we can't tell if a pipe is writable, so we always report it
     * that we can't tell if a pipe is writable, so we always report it
     * as being writable.
     * as being writable.
     */
     */
 
 
    filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile;
    filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile;
    if (filePtr->type != WIN32S_PIPE) {
    if (filePtr->type != WIN32S_PIPE) {
 
 
        /* CYGNUS LOCAL: Check PIPE_READABLE if we have a thread.  */
        /* CYGNUS LOCAL: Check PIPE_READABLE if we have a thread.  */
        if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
        if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
            mask = TCL_WRITABLE;
            mask = TCL_WRITABLE;
            if (PipeGetFlags(infoPtr) & PIPE_READABLE) {
            if (PipeGetFlags(infoPtr) & PIPE_READABLE) {
                mask |= TCL_READABLE;
                mask |= TCL_READABLE;
            }
            }
        } else {
        } else {
            mask = TCL_WRITABLE|TCL_READABLE;
            mask = TCL_WRITABLE|TCL_READABLE;
        }
        }
 
 
/*      if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0, */
/*      if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0, */
/*              (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) { */
/*              (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) { */
/*          if (count != 0) { */
/*          if (count != 0) { */
/*              mask |= TCL_READABLE; */
/*              mask |= TCL_READABLE; */
/*          } */
/*          } */
/*      } else { */
/*      } else { */
 
 
            /*
            /*
             * If the pipe has been closed by the other side, then
             * If the pipe has been closed by the other side, then
             * mark the pipe as readable, but not writable.
             * mark the pipe as readable, but not writable.
             */
             */
 
 
/*          if (GetLastError() == ERROR_BROKEN_PIPE) { */
/*          if (GetLastError() == ERROR_BROKEN_PIPE) { */
/*              mask = TCL_READABLE; */
/*              mask = TCL_READABLE; */
/*          } */
/*          } */
/*      } */
/*      } */
    } else {
    } else {
        mask = TCL_READABLE | TCL_WRITABLE;
        mask = TCL_READABLE | TCL_WRITABLE;
    }
    }
 
 
    /*
    /*
     * Inform the channel of the events.
     * Inform the channel of the events.
     */
     */
 
 
    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
    return 1;
    return 1;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeWatchProc --
 * PipeWatchProc --
 *
 *
 *      Called by the notifier to set up to watch for events on this
 *      Called by the notifier to set up to watch for events on this
 *      channel.
 *      channel.
 *
 *
 * Results:
 * Results:
 *      None.
 *      None.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static void
static void
PipeWatchProc(instanceData, mask)
PipeWatchProc(instanceData, mask)
    ClientData instanceData;            /* Pipe state. */
    ClientData instanceData;            /* Pipe state. */
    int mask;                           /* What events to watch for; OR-ed
    int mask;                           /* What events to watch for; OR-ed
                                         * combination of TCL_READABLE,
                                         * combination of TCL_READABLE,
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
{
{
    PipeInfo **nextPtrPtr, *ptr;
    PipeInfo **nextPtrPtr, *ptr;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    int oldMask = infoPtr->watchMask;
    int oldMask = infoPtr->watchMask;
 
 
    /*
    /*
     * For now, we just send a message to ourselves so we can poll the
     * For now, we just send a message to ourselves so we can poll the
     * channel for readable events.
     * channel for readable events.
     */
     */
 
 
    infoPtr->watchMask = mask & infoPtr->validMask;
    infoPtr->watchMask = mask & infoPtr->validMask;
    if (infoPtr->watchMask) {
    if (infoPtr->watchMask) {
        Tcl_Time blockTime = { 0, 0 };
        Tcl_Time blockTime = { 0, 0 };
 
 
        /* CYGNUS LOCAL: Set up a thread if necessary.  */
        /* CYGNUS LOCAL: Set up a thread if necessary.  */
        if ((infoPtr->watchMask & TCL_READABLE) != 0
        if ((infoPtr->watchMask & TCL_READABLE) != 0
            && (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0) {
            && (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0) {
            HANDLE thread;
            HANDLE thread;
            DWORD tid;
            DWORD tid;
 
 
            infoPtr->tryReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
            infoPtr->tryReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
            infoPtr->mutex = CreateMutex(NULL, FALSE, NULL);
            infoPtr->mutex = CreateMutex(NULL, FALSE, NULL);
            PipeSetFlag(infoPtr, PIPE_HAS_THREAD);
            PipeSetFlag(infoPtr, PIPE_HAS_THREAD);
            thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PipeThread, infoPtr, 0, &tid);
            thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PipeThread, infoPtr, 0, &tid);
            CloseHandle(thread);
            CloseHandle(thread);
        }
        }
 
 
        if (!oldMask) {
        if (!oldMask) {
            infoPtr->nextPtr = firstPipePtr;
            infoPtr->nextPtr = firstPipePtr;
            firstPipePtr = infoPtr;
            firstPipePtr = infoPtr;
        }
        }
        Tcl_SetMaxBlockTime(&blockTime);
        Tcl_SetMaxBlockTime(&blockTime);
    } else {
    } else {
        if (oldMask) {
        if (oldMask) {
            /*
            /*
             * Remove the pipe from the list of watched pipes.
             * Remove the pipe from the list of watched pipes.
             */
             */
 
 
            for (nextPtrPtr = &firstPipePtr, ptr = *nextPtrPtr;
            for (nextPtrPtr = &firstPipePtr, ptr = *nextPtrPtr;
                 ptr != NULL;
                 ptr != NULL;
                 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
                 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
                if (infoPtr == ptr) {
                if (infoPtr == ptr) {
                    *nextPtrPtr = ptr->nextPtr;
                    *nextPtrPtr = ptr->nextPtr;
                    break;
                    break;
                }
                }
            }
            }
        }
        }
    }
    }
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * PipeGetHandleProc --
 * PipeGetHandleProc --
 *
 *
 *      Called from Tcl_GetChannelHandle to retrieve OS handles from
 *      Called from Tcl_GetChannelHandle to retrieve OS handles from
 *      inside a command pipeline based channel.
 *      inside a command pipeline based channel.
 *
 *
 * Results:
 * Results:
 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
 *      there is no handle for the specified direction.
 *      there is no handle for the specified direction.
 *
 *
 * Side effects:
 * Side effects:
 *      None.
 *      None.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
static int
static int
PipeGetHandleProc(instanceData, direction, handlePtr)
PipeGetHandleProc(instanceData, direction, handlePtr)
    ClientData instanceData;    /* The pipe state. */
    ClientData instanceData;    /* The pipe state. */
    int direction;              /* TCL_READABLE or TCL_WRITABLE */
    int direction;              /* TCL_READABLE or TCL_WRITABLE */
    ClientData *handlePtr;      /* Where to store the handle.  */
    ClientData *handlePtr;      /* Where to store the handle.  */
{
{
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    PipeInfo *infoPtr = (PipeInfo *) instanceData;
    WinFile *filePtr;
    WinFile *filePtr;
 
 
    if (direction == TCL_READABLE && infoPtr->readFile) {
    if (direction == TCL_READABLE && infoPtr->readFile) {
        filePtr = (WinFile*) infoPtr->readFile;
        filePtr = (WinFile*) infoPtr->readFile;
        if (filePtr->type == WIN32S_PIPE) {
        if (filePtr->type == WIN32S_PIPE) {
            if (filePtr->handle == INVALID_HANDLE_VALUE) {
            if (filePtr->handle == INVALID_HANDLE_VALUE) {
                filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
                filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
                        GENERIC_READ, 0, NULL, OPEN_ALWAYS,
                        GENERIC_READ, 0, NULL, OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL, NULL);
                        FILE_ATTRIBUTE_NORMAL, NULL);
            }
            }
            if (filePtr->handle == INVALID_HANDLE_VALUE) {
            if (filePtr->handle == INVALID_HANDLE_VALUE) {
                return TCL_ERROR;
                return TCL_ERROR;
            }
            }
        }
        }
        *handlePtr = (ClientData) filePtr->handle;
        *handlePtr = (ClientData) filePtr->handle;
        return TCL_OK;
        return TCL_OK;
    }
    }
    if (direction == TCL_WRITABLE && infoPtr->writeFile) {
    if (direction == TCL_WRITABLE && infoPtr->writeFile) {
        filePtr = (WinFile*) infoPtr->writeFile;
        filePtr = (WinFile*) infoPtr->writeFile;
        *handlePtr = (ClientData) filePtr->handle;
        *handlePtr = (ClientData) filePtr->handle;
        return TCL_OK;
        return TCL_OK;
    }
    }
    return TCL_ERROR;
    return TCL_ERROR;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * Tcl_WaitPid --
 * Tcl_WaitPid --
 *
 *
 *      Emulates the waitpid system call.
 *      Emulates the waitpid system call.
 *
 *
 * Results:
 * Results:
 *      Returns 0 if the process is still alive, -1 on an error, or
 *      Returns 0 if the process is still alive, -1 on an error, or
 *      the pid on a clean close.
 *      the pid on a clean close.
 *
 *
 * Side effects:
 * Side effects:
 *      Unless WNOHANG is set and the wait times out, the process
 *      Unless WNOHANG is set and the wait times out, the process
 *      information record will be deleted and the process handle
 *      information record will be deleted and the process handle
 *      will be closed.
 *      will be closed.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
Tcl_Pid
Tcl_Pid
Tcl_WaitPid(pid, statPtr, options)
Tcl_WaitPid(pid, statPtr, options)
    Tcl_Pid pid;
    Tcl_Pid pid;
    int *statPtr;
    int *statPtr;
    int options;
    int options;
{
{
    ProcInfo *infoPtr, **prevPtrPtr;
    ProcInfo *infoPtr, **prevPtrPtr;
    int flags;
    int flags;
    Tcl_Pid result;
    Tcl_Pid result;
    DWORD ret;
    DWORD ret;
 
 
    if (!initialized) {
    if (!initialized) {
        PipeInit();
        PipeInit();
    }
    }
 
 
    /*
    /*
     * If no pid is specified, do nothing.
     * If no pid is specified, do nothing.
     */
     */
 
 
    if (pid == 0) {
    if (pid == 0) {
        *statPtr = 0;
        *statPtr = 0;
        return 0;
        return 0;
    }
    }
 
 
    /*
    /*
     * Find the process on the process list.
     * Find the process on the process list.
     */
     */
 
 
    prevPtrPtr = &procList;
    prevPtrPtr = &procList;
    for (infoPtr = procList; infoPtr != NULL;
    for (infoPtr = procList; infoPtr != NULL;
            prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
            prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
         if (infoPtr->hProcess == (HANDLE) pid) {
         if (infoPtr->hProcess == (HANDLE) pid) {
            break;
            break;
        }
        }
    }
    }
 
 
    /*
    /*
     * If the pid is not one of the processes we know about (we started it)
     * If the pid is not one of the processes we know about (we started it)
     * then do nothing.
     * then do nothing.
     */
     */
 
 
    if (infoPtr == NULL) {
    if (infoPtr == NULL) {
        *statPtr = 0;
        *statPtr = 0;
        return 0;
        return 0;
    }
    }
 
 
    /*
    /*
     * Officially "wait" for it to finish. We either poll (WNOHANG) or
     * Officially "wait" for it to finish. We either poll (WNOHANG) or
     * wait for an infinite amount of time.
     * wait for an infinite amount of time.
     */
     */
 
 
    if (options & WNOHANG) {
    if (options & WNOHANG) {
        flags = 0;
        flags = 0;
    } else {
    } else {
        flags = INFINITE;
        flags = INFINITE;
    }
    }
    ret = WaitForSingleObject(infoPtr->hProcess, flags);
    ret = WaitForSingleObject(infoPtr->hProcess, flags);
    if (ret == WAIT_TIMEOUT) {
    if (ret == WAIT_TIMEOUT) {
        *statPtr = 0;
        *statPtr = 0;
        if (options & WNOHANG) {
        if (options & WNOHANG) {
            return 0;
            return 0;
        } else {
        } else {
            result = 0;
            result = 0;
        }
        }
    } else if (ret != WAIT_FAILED) {
    } else if (ret != WAIT_FAILED) {
        GetExitCodeProcess(infoPtr->hProcess, (DWORD*)statPtr);
        GetExitCodeProcess(infoPtr->hProcess, (DWORD*)statPtr);
#ifdef __OLD_CYGWIN__
#ifdef __OLD_CYGWIN__
        /* A cygwin program that exits because of a signal will set
        /* A cygwin program that exits because of a signal will set
           the exit status to 0x10000 | (sig << 8).  Fix that back
           the exit status to 0x10000 | (sig << 8).  Fix that back
           into a standard Unix wait status.  */
           into a standard Unix wait status.  */
        if ((*statPtr & 0x10000) != 0
        if ((*statPtr & 0x10000) != 0
            && (*statPtr & 0xff00) != 0
            && (*statPtr & 0xff00) != 0
            && (*statPtr & ~ 0x1ff00) == 0) {
            && (*statPtr & ~ 0x1ff00) == 0) {
            *statPtr = (*statPtr >> 8) & 0xff;
            *statPtr = (*statPtr >> 8) & 0xff;
        } else
        } else
#endif
#endif
        *statPtr = ((*statPtr << 8) & 0xff00);
        *statPtr = ((*statPtr << 8) & 0xff00);
        result = pid;
        result = pid;
    } else {
    } else {
        errno = ECHILD;
        errno = ECHILD;
        *statPtr = ECHILD;
        *statPtr = ECHILD;
        result = (Tcl_Pid) -1;
        result = (Tcl_Pid) -1;
    }
    }
 
 
    /*
    /*
     * Remove the process from the process list and close the process handle.
     * Remove the process from the process list and close the process handle.
     */
     */
 
 
    CloseHandle(infoPtr->hProcess);
    CloseHandle(infoPtr->hProcess);
    *prevPtrPtr = infoPtr->nextPtr;
    *prevPtrPtr = infoPtr->nextPtr;
    ckfree((char*)infoPtr);
    ckfree((char*)infoPtr);
 
 
    return result;
    return result;
}
}


/*
/*
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 *
 *
 * Tcl_PidObjCmd --
 * Tcl_PidObjCmd --
 *
 *
 *      This procedure is invoked to process the "pid" Tcl command.
 *      This procedure is invoked to process the "pid" Tcl command.
 *      See the user documentation for details on what it does.
 *      See the user documentation for details on what it does.
 *
 *
 * Results:
 * Results:
 *      A standard Tcl result.
 *      A standard Tcl result.
 *
 *
 * Side effects:
 * Side effects:
 *      See the user documentation.
 *      See the user documentation.
 *
 *
 *----------------------------------------------------------------------
 *----------------------------------------------------------------------
 */
 */
 
 
        /* ARGSUSED */
        /* ARGSUSED */
int
int
Tcl_PidObjCmd(dummy, interp, objc, objv)
Tcl_PidObjCmd(dummy, interp, objc, objv)
    ClientData dummy;           /* Not used. */
    ClientData dummy;           /* Not used. */
    Tcl_Interp *interp;         /* Current interpreter. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int objc;                   /* Number of arguments. */
    int objc;                   /* Number of arguments. */
    Tcl_Obj *CONST *objv;       /* Argument strings. */
    Tcl_Obj *CONST *objv;       /* Argument strings. */
{
{
    Tcl_Channel chan;
    Tcl_Channel chan;
    Tcl_ChannelType *chanTypePtr;
    Tcl_ChannelType *chanTypePtr;
    PipeInfo *pipePtr;
    PipeInfo *pipePtr;
    int i;
    int i;
    Tcl_Obj *resultPtr;
    Tcl_Obj *resultPtr;
    char buf[20];
    char buf[20];
 
 
    if (objc > 2) {
    if (objc > 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
        Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
        return TCL_ERROR;
        return TCL_ERROR;
    }
    }
    if (objc == 1) {
    if (objc == 1) {
        resultPtr = Tcl_GetObjResult(interp);
        resultPtr = Tcl_GetObjResult(interp);
        sprintf(buf, "%lu", (unsigned long) getpid());
        sprintf(buf, "%lu", (unsigned long) getpid());
        Tcl_SetStringObj(resultPtr, buf, -1);
        Tcl_SetStringObj(resultPtr, buf, -1);
    } else {
    } else {
        chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
        chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
                NULL);
                NULL);
        if (chan == (Tcl_Channel) NULL) {
        if (chan == (Tcl_Channel) NULL) {
            return TCL_ERROR;
            return TCL_ERROR;
        }
        }
        chanTypePtr = Tcl_GetChannelType(chan);
        chanTypePtr = Tcl_GetChannelType(chan);
        if (chanTypePtr != &pipeChannelType) {
        if (chanTypePtr != &pipeChannelType) {
            return TCL_OK;
            return TCL_OK;
        }
        }
 
 
        pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
        pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
        resultPtr = Tcl_GetObjResult(interp);
        resultPtr = Tcl_GetObjResult(interp);
        for (i = 0; i < pipePtr->numPids; i++) {
        for (i = 0; i < pipePtr->numPids; i++) {
            sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
            sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
            Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr,
            Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr,
                    Tcl_NewStringObj(buf, -1));
                    Tcl_NewStringObj(buf, -1));
        }
        }
    }
    }
    return TCL_OK;
    return TCL_OK;
}
}
 
 

powered by: WebSVN 2.1.0

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