tclWinPipe.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:82k
源码类别:

通讯编程

开发平台:

Visual C++

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