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