os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinPipe.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinPipe.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,3093 @@
1.4 +/*
1.5 + * tclWinPipe.c --
1.6 + *
1.7 + * This file implements the Windows-specific exec pipeline functions,
1.8 + * the "pipe" channel driver, and the "pid" Tcl command.
1.9 + *
1.10 + * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclWinPipe.c,v 1.33.2.17 2006/03/14 20:36:39 andreas_kupries Exp $
1.16 + */
1.17 +
1.18 +#include "tclWinInt.h"
1.19 +
1.20 +#include <fcntl.h>
1.21 +#include <io.h>
1.22 +#include <sys/stat.h>
1.23 +
1.24 +/*
1.25 + * The following variable is used to tell whether this module has been
1.26 + * initialized.
1.27 + */
1.28 +
1.29 +static int initialized = 0;
1.30 +
1.31 +/*
1.32 + * The pipeMutex locks around access to the initialized and procList variables,
1.33 + * and it is used to protect background threads from being terminated while
1.34 + * they are using APIs that hold locks.
1.35 + */
1.36 +
1.37 +TCL_DECLARE_MUTEX(pipeMutex)
1.38 +
1.39 +/*
1.40 + * The following defines identify the various types of applications that
1.41 + * run under windows. There is special case code for the various types.
1.42 + */
1.43 +
1.44 +#define APPL_NONE 0
1.45 +#define APPL_DOS 1
1.46 +#define APPL_WIN3X 2
1.47 +#define APPL_WIN32 3
1.48 +
1.49 +/*
1.50 + * The following constants and structures are used to encapsulate the state
1.51 + * of various types of files used in a pipeline.
1.52 + * This used to have a 1 && 2 that supported Win32s.
1.53 + */
1.54 +
1.55 +#define WIN_FILE 3 /* Basic Win32 file. */
1.56 +
1.57 +/*
1.58 + * This structure encapsulates the common state associated with all file
1.59 + * types used in a pipeline.
1.60 + */
1.61 +
1.62 +typedef struct WinFile {
1.63 + int type; /* One of the file types defined above. */
1.64 + HANDLE handle; /* Open file handle. */
1.65 +} WinFile;
1.66 +
1.67 +/*
1.68 + * This list is used to map from pids to process handles.
1.69 + */
1.70 +
1.71 +typedef struct ProcInfo {
1.72 + HANDLE hProcess;
1.73 + DWORD dwProcessId;
1.74 + struct ProcInfo *nextPtr;
1.75 +} ProcInfo;
1.76 +
1.77 +static ProcInfo *procList;
1.78 +
1.79 +/*
1.80 + * Bit masks used in the flags field of the PipeInfo structure below.
1.81 + */
1.82 +
1.83 +#define PIPE_PENDING (1<<0) /* Message is pending in the queue. */
1.84 +#define PIPE_ASYNC (1<<1) /* Channel is non-blocking. */
1.85 +
1.86 +/*
1.87 + * Bit masks used in the sharedFlags field of the PipeInfo structure below.
1.88 + */
1.89 +
1.90 +#define PIPE_EOF (1<<2) /* Pipe has reached EOF. */
1.91 +#define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */
1.92 +
1.93 +/*
1.94 + * This structure describes per-instance data for a pipe based channel.
1.95 + */
1.96 +
1.97 +typedef struct PipeInfo {
1.98 + struct PipeInfo *nextPtr; /* Pointer to next registered pipe. */
1.99 + Tcl_Channel channel; /* Pointer to channel structure. */
1.100 + int validMask; /* OR'ed combination of TCL_READABLE,
1.101 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.102 + * which operations are valid on the file. */
1.103 + int watchMask; /* OR'ed combination of TCL_READABLE,
1.104 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.105 + * which events should be reported. */
1.106 + int flags; /* State flags, see above for a list. */
1.107 + TclFile readFile; /* Output from pipe. */
1.108 + TclFile writeFile; /* Input from pipe. */
1.109 + TclFile errorFile; /* Error output from pipe. */
1.110 + int numPids; /* Number of processes attached to pipe. */
1.111 + Tcl_Pid *pidPtr; /* Pids of attached processes. */
1.112 + Tcl_ThreadId threadId; /* Thread to which events should be reported.
1.113 + * This value is used by the reader/writer
1.114 + * threads. */
1.115 + HANDLE writeThread; /* Handle to writer thread. */
1.116 + HANDLE readThread; /* Handle to reader thread. */
1.117 + HANDLE writable; /* Manual-reset event to signal when the
1.118 + * writer thread has finished waiting for
1.119 + * the current buffer to be written. */
1.120 + HANDLE readable; /* Manual-reset event to signal when the
1.121 + * reader thread has finished waiting for
1.122 + * input. */
1.123 + HANDLE startWriter; /* Auto-reset event used by the main thread to
1.124 + * signal when the writer thread should attempt
1.125 + * to write to the pipe. */
1.126 + HANDLE stopWriter; /* Manual-reset event used to alert the reader
1.127 + * thread to fall-out and exit */
1.128 + HANDLE startReader; /* Auto-reset event used by the main thread to
1.129 + * signal when the reader thread should attempt
1.130 + * to read from the pipe. */
1.131 + HANDLE stopReader; /* Manual-reset event used to alert the reader
1.132 + * thread to fall-out and exit */
1.133 + DWORD writeError; /* An error caused by the last background
1.134 + * write. Set to 0 if no error has been
1.135 + * detected. This word is shared with the
1.136 + * writer thread so access must be
1.137 + * synchronized with the writable object.
1.138 + */
1.139 + char *writeBuf; /* Current background output buffer.
1.140 + * Access is synchronized with the writable
1.141 + * object. */
1.142 + int writeBufLen; /* Size of write buffer. Access is
1.143 + * synchronized with the writable
1.144 + * object. */
1.145 + int toWrite; /* Current amount to be written. Access is
1.146 + * synchronized with the writable object. */
1.147 + int readFlags; /* Flags that are shared with the reader
1.148 + * thread. Access is synchronized with the
1.149 + * readable object. */
1.150 + char extraByte; /* Buffer for extra character consumed by
1.151 + * reader thread. This byte is shared with
1.152 + * the reader thread so access must be
1.153 + * synchronized with the readable object. */
1.154 +} PipeInfo;
1.155 +
1.156 +typedef struct ThreadSpecificData {
1.157 + /*
1.158 + * The following pointer refers to the head of the list of pipes
1.159 + * that are being watched for file events.
1.160 + */
1.161 +
1.162 + PipeInfo *firstPipePtr;
1.163 +} ThreadSpecificData;
1.164 +
1.165 +static Tcl_ThreadDataKey dataKey;
1.166 +
1.167 +/*
1.168 + * The following structure is what is added to the Tcl event queue when
1.169 + * pipe events are generated.
1.170 + */
1.171 +
1.172 +typedef struct PipeEvent {
1.173 + Tcl_Event header; /* Information that is standard for
1.174 + * all events. */
1.175 + PipeInfo *infoPtr; /* Pointer to pipe info structure. Note
1.176 + * that we still have to verify that the
1.177 + * pipe exists before dereferencing this
1.178 + * pointer. */
1.179 +} PipeEvent;
1.180 +
1.181 +/*
1.182 + * Declarations for functions used only in this file.
1.183 + */
1.184 +
1.185 +static int ApplicationType(Tcl_Interp *interp,
1.186 + const char *fileName, char *fullName);
1.187 +static void BuildCommandLine(const char *executable, int argc,
1.188 + CONST char **argv, Tcl_DString *linePtr);
1.189 +static BOOL HasConsole(void);
1.190 +static int PipeBlockModeProc(ClientData instanceData, int mode);
1.191 +static void PipeCheckProc(ClientData clientData, int flags);
1.192 +static int PipeClose2Proc(ClientData instanceData,
1.193 + Tcl_Interp *interp, int flags);
1.194 +static int PipeEventProc(Tcl_Event *evPtr, int flags);
1.195 +static int PipeGetHandleProc(ClientData instanceData,
1.196 + int direction, ClientData *handlePtr);
1.197 +static void PipeInit(void);
1.198 +static int PipeInputProc(ClientData instanceData, char *buf,
1.199 + int toRead, int *errorCode);
1.200 +static int PipeOutputProc(ClientData instanceData,
1.201 + CONST char *buf, int toWrite, int *errorCode);
1.202 +static DWORD WINAPI PipeReaderThread(LPVOID arg);
1.203 +static void PipeSetupProc(ClientData clientData, int flags);
1.204 +static void PipeWatchProc(ClientData instanceData, int mask);
1.205 +static DWORD WINAPI PipeWriterThread(LPVOID arg);
1.206 +static int TempFileName(WCHAR name[MAX_PATH]);
1.207 +static int WaitForRead(PipeInfo *infoPtr, int blocking);
1.208 +
1.209 +static void PipeThreadActionProc _ANSI_ARGS_ ((
1.210 + ClientData instanceData, int action));
1.211 +
1.212 +/*
1.213 + * This structure describes the channel type structure for command pipe
1.214 + * based IO.
1.215 + */
1.216 +
1.217 +static Tcl_ChannelType pipeChannelType = {
1.218 + "pipe", /* Type name. */
1.219 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.220 + TCL_CLOSE2PROC, /* Close proc. */
1.221 + PipeInputProc, /* Input proc. */
1.222 + PipeOutputProc, /* Output proc. */
1.223 + NULL, /* Seek proc. */
1.224 + NULL, /* Set option proc. */
1.225 + NULL, /* Get option proc. */
1.226 + PipeWatchProc, /* Set up notifier to watch the channel. */
1.227 + PipeGetHandleProc, /* Get an OS handle from channel. */
1.228 + PipeClose2Proc, /* close2proc */
1.229 + PipeBlockModeProc, /* Set blocking or non-blocking mode.*/
1.230 + NULL, /* flush proc. */
1.231 + NULL, /* handler proc. */
1.232 + NULL, /* wide seek proc */
1.233 + PipeThreadActionProc, /* thread action proc */
1.234 +};
1.235 +
1.236 +/*
1.237 + *----------------------------------------------------------------------
1.238 + *
1.239 + * PipeInit --
1.240 + *
1.241 + * This function initializes the static variables for this file.
1.242 + *
1.243 + * Results:
1.244 + * None.
1.245 + *
1.246 + * Side effects:
1.247 + * Creates a new event source.
1.248 + *
1.249 + *----------------------------------------------------------------------
1.250 + */
1.251 +
1.252 +static void
1.253 +PipeInit()
1.254 +{
1.255 + ThreadSpecificData *tsdPtr;
1.256 +
1.257 + /*
1.258 + * Check the initialized flag first, then check again in the mutex.
1.259 + * This is a speed enhancement.
1.260 + */
1.261 +
1.262 + if (!initialized) {
1.263 + Tcl_MutexLock(&pipeMutex);
1.264 + if (!initialized) {
1.265 + initialized = 1;
1.266 + procList = NULL;
1.267 + }
1.268 + Tcl_MutexUnlock(&pipeMutex);
1.269 + }
1.270 +
1.271 + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.272 + if (tsdPtr == NULL) {
1.273 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.274 + tsdPtr->firstPipePtr = NULL;
1.275 + Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
1.276 + }
1.277 +}
1.278 +
1.279 +/*
1.280 + *----------------------------------------------------------------------
1.281 + *
1.282 + * TclpFinalizePipes --
1.283 + *
1.284 + * This function is called from Tcl_FinalizeThread to finalize the
1.285 + * platform specific pipe subsystem.
1.286 + *
1.287 + * Results:
1.288 + * None.
1.289 + *
1.290 + * Side effects:
1.291 + * Removes the pipe event source.
1.292 + *
1.293 + *----------------------------------------------------------------------
1.294 + */
1.295 +
1.296 +void
1.297 +TclpFinalizePipes()
1.298 +{
1.299 + ThreadSpecificData *tsdPtr;
1.300 +
1.301 + tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.302 + if (tsdPtr != NULL) {
1.303 + Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);
1.304 + }
1.305 +}
1.306 +
1.307 +/*
1.308 + *----------------------------------------------------------------------
1.309 + *
1.310 + * PipeSetupProc --
1.311 + *
1.312 + * This procedure is invoked before Tcl_DoOneEvent blocks waiting
1.313 + * for an event.
1.314 + *
1.315 + * Results:
1.316 + * None.
1.317 + *
1.318 + * Side effects:
1.319 + * Adjusts the block time if needed.
1.320 + *
1.321 + *----------------------------------------------------------------------
1.322 + */
1.323 +
1.324 +void
1.325 +PipeSetupProc(
1.326 + ClientData data, /* Not used. */
1.327 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.328 +{
1.329 + PipeInfo *infoPtr;
1.330 + Tcl_Time blockTime = { 0, 0 };
1.331 + int block = 1;
1.332 + WinFile *filePtr;
1.333 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.334 +
1.335 + if (!(flags & TCL_FILE_EVENTS)) {
1.336 + return;
1.337 + }
1.338 +
1.339 + /*
1.340 + * Look to see if any events are already pending. If they are, poll.
1.341 + */
1.342 +
1.343 + for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL;
1.344 + infoPtr = infoPtr->nextPtr) {
1.345 + if (infoPtr->watchMask & TCL_WRITABLE) {
1.346 + filePtr = (WinFile*) infoPtr->writeFile;
1.347 + if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
1.348 + block = 0;
1.349 + }
1.350 + }
1.351 + if (infoPtr->watchMask & TCL_READABLE) {
1.352 + filePtr = (WinFile*) infoPtr->readFile;
1.353 + if (WaitForRead(infoPtr, 0) >= 0) {
1.354 + block = 0;
1.355 + }
1.356 + }
1.357 + }
1.358 + if (!block) {
1.359 + Tcl_SetMaxBlockTime(&blockTime);
1.360 + }
1.361 +}
1.362 +
1.363 +/*
1.364 + *----------------------------------------------------------------------
1.365 + *
1.366 + * PipeCheckProc --
1.367 + *
1.368 + * This procedure is called by Tcl_DoOneEvent to check the pipe
1.369 + * event source for events.
1.370 + *
1.371 + * Results:
1.372 + * None.
1.373 + *
1.374 + * Side effects:
1.375 + * May queue an event.
1.376 + *
1.377 + *----------------------------------------------------------------------
1.378 + */
1.379 +
1.380 +static void
1.381 +PipeCheckProc(
1.382 + ClientData data, /* Not used. */
1.383 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.384 +{
1.385 + PipeInfo *infoPtr;
1.386 + PipeEvent *evPtr;
1.387 + WinFile *filePtr;
1.388 + int needEvent;
1.389 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.390 +
1.391 + if (!(flags & TCL_FILE_EVENTS)) {
1.392 + return;
1.393 + }
1.394 +
1.395 + /*
1.396 + * Queue events for any ready pipes that don't already have events
1.397 + * queued.
1.398 + */
1.399 +
1.400 + for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL;
1.401 + infoPtr = infoPtr->nextPtr) {
1.402 + if (infoPtr->flags & PIPE_PENDING) {
1.403 + continue;
1.404 + }
1.405 +
1.406 + /*
1.407 + * Queue an event if the pipe is signaled for reading or writing.
1.408 + */
1.409 +
1.410 + needEvent = 0;
1.411 + filePtr = (WinFile*) infoPtr->writeFile;
1.412 + if ((infoPtr->watchMask & TCL_WRITABLE) &&
1.413 + (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) {
1.414 + needEvent = 1;
1.415 + }
1.416 +
1.417 + filePtr = (WinFile*) infoPtr->readFile;
1.418 + if ((infoPtr->watchMask & TCL_READABLE) &&
1.419 + (WaitForRead(infoPtr, 0) >= 0)) {
1.420 + needEvent = 1;
1.421 + }
1.422 +
1.423 + if (needEvent) {
1.424 + infoPtr->flags |= PIPE_PENDING;
1.425 + evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
1.426 + evPtr->header.proc = PipeEventProc;
1.427 + evPtr->infoPtr = infoPtr;
1.428 + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
1.429 + }
1.430 + }
1.431 +}
1.432 +
1.433 +/*
1.434 + *----------------------------------------------------------------------
1.435 + *
1.436 + * TclWinMakeFile --
1.437 + *
1.438 + * This function constructs a new TclFile from a given data and
1.439 + * type value.
1.440 + *
1.441 + * Results:
1.442 + * Returns a newly allocated WinFile as a TclFile.
1.443 + *
1.444 + * Side effects:
1.445 + * None.
1.446 + *
1.447 + *----------------------------------------------------------------------
1.448 + */
1.449 +
1.450 +TclFile
1.451 +TclWinMakeFile(
1.452 + HANDLE handle) /* Type-specific data. */
1.453 +{
1.454 + WinFile *filePtr;
1.455 +
1.456 + filePtr = (WinFile *) ckalloc(sizeof(WinFile));
1.457 + filePtr->type = WIN_FILE;
1.458 + filePtr->handle = handle;
1.459 +
1.460 + return (TclFile)filePtr;
1.461 +}
1.462 +
1.463 +/*
1.464 + *----------------------------------------------------------------------
1.465 + *
1.466 + * TempFileName --
1.467 + *
1.468 + * Gets a temporary file name and deals with the fact that the
1.469 + * temporary file path provided by Windows may not actually exist
1.470 + * if the TMP or TEMP environment variables refer to a
1.471 + * non-existent directory.
1.472 + *
1.473 + * Results:
1.474 + * 0 if error, non-zero otherwise. If non-zero is returned, the
1.475 + * name buffer will be filled with a name that can be used to
1.476 + * construct a temporary file.
1.477 + *
1.478 + * Side effects:
1.479 + * None.
1.480 + *
1.481 + *----------------------------------------------------------------------
1.482 + */
1.483 +
1.484 +static int
1.485 +TempFileName(name)
1.486 + WCHAR name[MAX_PATH]; /* Buffer in which name for temporary
1.487 + * file gets stored. */
1.488 +{
1.489 + TCHAR *prefix;
1.490 +
1.491 + prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL";
1.492 + if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) {
1.493 + if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0,
1.494 + name) != 0) {
1.495 + return 1;
1.496 + }
1.497 + }
1.498 + if (tclWinProcs->useWide) {
1.499 + ((WCHAR *) name)[0] = '.';
1.500 + ((WCHAR *) name)[1] = '\0';
1.501 + } else {
1.502 + ((char *) name)[0] = '.';
1.503 + ((char *) name)[1] = '\0';
1.504 + }
1.505 + return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0,
1.506 + name);
1.507 +}
1.508 +
1.509 +/*
1.510 + *----------------------------------------------------------------------
1.511 + *
1.512 + * TclpMakeFile --
1.513 + *
1.514 + * Make a TclFile from a channel.
1.515 + *
1.516 + * Results:
1.517 + * Returns a new TclFile or NULL on failure.
1.518 + *
1.519 + * Side effects:
1.520 + * None.
1.521 + *
1.522 + *----------------------------------------------------------------------
1.523 + */
1.524 +
1.525 +TclFile
1.526 +TclpMakeFile(channel, direction)
1.527 + Tcl_Channel channel; /* Channel to get file from. */
1.528 + int direction; /* Either TCL_READABLE or TCL_WRITABLE. */
1.529 +{
1.530 + HANDLE handle;
1.531 +
1.532 + if (Tcl_GetChannelHandle(channel, direction,
1.533 + (ClientData *) &handle) == TCL_OK) {
1.534 + return TclWinMakeFile(handle);
1.535 + } else {
1.536 + return (TclFile) NULL;
1.537 + }
1.538 +}
1.539 +
1.540 +/*
1.541 + *----------------------------------------------------------------------
1.542 + *
1.543 + * TclpOpenFile --
1.544 + *
1.545 + * This function opens files for use in a pipeline.
1.546 + *
1.547 + * Results:
1.548 + * Returns a newly allocated TclFile structure containing the
1.549 + * file handle.
1.550 + *
1.551 + * Side effects:
1.552 + * None.
1.553 + *
1.554 + *----------------------------------------------------------------------
1.555 + */
1.556 +
1.557 +TclFile
1.558 +TclpOpenFile(path, mode)
1.559 + CONST char *path; /* The name of the file to open. */
1.560 + int mode; /* In what mode to open the file? */
1.561 +{
1.562 + HANDLE handle;
1.563 + DWORD accessMode, createMode, shareMode, flags;
1.564 + Tcl_DString ds;
1.565 + CONST TCHAR *nativePath;
1.566 +
1.567 + /*
1.568 + * Map the access bits to the NT access mode.
1.569 + */
1.570 +
1.571 + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1.572 + case O_RDONLY:
1.573 + accessMode = GENERIC_READ;
1.574 + break;
1.575 + case O_WRONLY:
1.576 + accessMode = GENERIC_WRITE;
1.577 + break;
1.578 + case O_RDWR:
1.579 + accessMode = (GENERIC_READ | GENERIC_WRITE);
1.580 + break;
1.581 + default:
1.582 + TclWinConvertError(ERROR_INVALID_FUNCTION);
1.583 + return NULL;
1.584 + }
1.585 +
1.586 + /*
1.587 + * Map the creation flags to the NT create mode.
1.588 + */
1.589 +
1.590 + switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
1.591 + case (O_CREAT | O_EXCL):
1.592 + case (O_CREAT | O_EXCL | O_TRUNC):
1.593 + createMode = CREATE_NEW;
1.594 + break;
1.595 + case (O_CREAT | O_TRUNC):
1.596 + createMode = CREATE_ALWAYS;
1.597 + break;
1.598 + case O_CREAT:
1.599 + createMode = OPEN_ALWAYS;
1.600 + break;
1.601 + case O_TRUNC:
1.602 + case (O_TRUNC | O_EXCL):
1.603 + createMode = TRUNCATE_EXISTING;
1.604 + break;
1.605 + default:
1.606 + createMode = OPEN_EXISTING;
1.607 + break;
1.608 + }
1.609 +
1.610 + nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
1.611 +
1.612 + /*
1.613 + * If the file is not being created, use the existing file attributes.
1.614 + */
1.615 +
1.616 + flags = 0;
1.617 + if (!(mode & O_CREAT)) {
1.618 + flags = (*tclWinProcs->getFileAttributesProc)(nativePath);
1.619 + if (flags == 0xFFFFFFFF) {
1.620 + flags = 0;
1.621 + }
1.622 + }
1.623 +
1.624 + /*
1.625 + * Set up the file sharing mode. We want to allow simultaneous access.
1.626 + */
1.627 +
1.628 + shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
1.629 +
1.630 + /*
1.631 + * Now we get to create the file.
1.632 + */
1.633 +
1.634 + handle = (*tclWinProcs->createFileProc)(nativePath, accessMode,
1.635 + shareMode, NULL, createMode, flags, NULL);
1.636 + Tcl_DStringFree(&ds);
1.637 +
1.638 + if (handle == INVALID_HANDLE_VALUE) {
1.639 + DWORD err;
1.640 +
1.641 + err = GetLastError();
1.642 + if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
1.643 + err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
1.644 + }
1.645 + TclWinConvertError(err);
1.646 + return NULL;
1.647 + }
1.648 +
1.649 + /*
1.650 + * Seek to the end of file if we are writing.
1.651 + */
1.652 +
1.653 + if (mode & (O_WRONLY|O_APPEND)) {
1.654 + SetFilePointer(handle, 0, NULL, FILE_END);
1.655 + }
1.656 +
1.657 + return TclWinMakeFile(handle);
1.658 +}
1.659 +
1.660 +/*
1.661 + *----------------------------------------------------------------------
1.662 + *
1.663 + * TclpCreateTempFile --
1.664 + *
1.665 + * This function opens a unique file with the property that it
1.666 + * will be deleted when its file handle is closed. The temporary
1.667 + * file is created in the system temporary directory.
1.668 + *
1.669 + * Results:
1.670 + * Returns a valid TclFile, or NULL on failure.
1.671 + *
1.672 + * Side effects:
1.673 + * Creates a new temporary file.
1.674 + *
1.675 + *----------------------------------------------------------------------
1.676 + */
1.677 +
1.678 +TclFile
1.679 +TclpCreateTempFile(contents)
1.680 + CONST char *contents; /* String to write into temp file, or NULL. */
1.681 +{
1.682 + WCHAR name[MAX_PATH];
1.683 + CONST char *native;
1.684 + Tcl_DString dstring;
1.685 + HANDLE handle;
1.686 +
1.687 + if (TempFileName(name) == 0) {
1.688 + return NULL;
1.689 + }
1.690 +
1.691 + handle = (*tclWinProcs->createFileProc)((TCHAR *) name,
1.692 + GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1.693 + FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);
1.694 + if (handle == INVALID_HANDLE_VALUE) {
1.695 + goto error;
1.696 + }
1.697 +
1.698 + /*
1.699 + * Write the file out, doing line translations on the way.
1.700 + */
1.701 +
1.702 + if (contents != NULL) {
1.703 + DWORD result, length;
1.704 + CONST char *p;
1.705 +
1.706 + /*
1.707 + * Convert the contents from UTF to native encoding
1.708 + */
1.709 + native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
1.710 +
1.711 + for (p = native; *p != '\0'; p++) {
1.712 + if (*p == '\n') {
1.713 + length = p - native;
1.714 + if (length > 0) {
1.715 + if (!WriteFile(handle, native, length, &result, NULL)) {
1.716 + goto error;
1.717 + }
1.718 + }
1.719 + if (!WriteFile(handle, "\r\n", 2, &result, NULL)) {
1.720 + goto error;
1.721 + }
1.722 + native = p+1;
1.723 + }
1.724 + }
1.725 + length = p - native;
1.726 + if (length > 0) {
1.727 + if (!WriteFile(handle, native, length, &result, NULL)) {
1.728 + goto error;
1.729 + }
1.730 + }
1.731 + Tcl_DStringFree(&dstring);
1.732 + if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) {
1.733 + goto error;
1.734 + }
1.735 + }
1.736 +
1.737 + return TclWinMakeFile(handle);
1.738 +
1.739 + error:
1.740 + /* Free the native representation of the contents if necessary */
1.741 + if (contents != NULL) {
1.742 + Tcl_DStringFree(&dstring);
1.743 + }
1.744 +
1.745 + TclWinConvertError(GetLastError());
1.746 + CloseHandle(handle);
1.747 + (*tclWinProcs->deleteFileProc)((TCHAR *) name);
1.748 + return NULL;
1.749 +}
1.750 +
1.751 +/*
1.752 + *----------------------------------------------------------------------
1.753 + *
1.754 + * TclpTempFileName --
1.755 + *
1.756 + * This function returns a unique filename.
1.757 + *
1.758 + * Results:
1.759 + * Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
1.760 + *
1.761 + * Side effects:
1.762 + * None.
1.763 + *
1.764 + *----------------------------------------------------------------------
1.765 + */
1.766 +
1.767 +Tcl_Obj*
1.768 +TclpTempFileName()
1.769 +{
1.770 + WCHAR fileName[MAX_PATH];
1.771 +
1.772 + if (TempFileName(fileName) == 0) {
1.773 + return NULL;
1.774 + }
1.775 +
1.776 + return TclpNativeToNormalized((ClientData) fileName);
1.777 +}
1.778 +
1.779 +/*
1.780 + *----------------------------------------------------------------------
1.781 + *
1.782 + * TclpCreatePipe --
1.783 + *
1.784 + * Creates an anonymous pipe.
1.785 + *
1.786 + * Results:
1.787 + * Returns 1 on success, 0 on failure.
1.788 + *
1.789 + * Side effects:
1.790 + * Creates a pipe.
1.791 + *
1.792 + *----------------------------------------------------------------------
1.793 + */
1.794 +
1.795 +int
1.796 +TclpCreatePipe(
1.797 + TclFile *readPipe, /* Location to store file handle for
1.798 + * read side of pipe. */
1.799 + TclFile *writePipe) /* Location to store file handle for
1.800 + * write side of pipe. */
1.801 +{
1.802 + HANDLE readHandle, writeHandle;
1.803 +
1.804 + if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) {
1.805 + *readPipe = TclWinMakeFile(readHandle);
1.806 + *writePipe = TclWinMakeFile(writeHandle);
1.807 + return 1;
1.808 + }
1.809 +
1.810 + TclWinConvertError(GetLastError());
1.811 + return 0;
1.812 +}
1.813 +
1.814 +/*
1.815 + *----------------------------------------------------------------------
1.816 + *
1.817 + * TclpCloseFile --
1.818 + *
1.819 + * Closes a pipeline file handle. These handles are created by
1.820 + * TclpOpenFile, TclpCreatePipe, or TclpMakeFile.
1.821 + *
1.822 + * Results:
1.823 + * 0 on success, -1 on failure.
1.824 + *
1.825 + * Side effects:
1.826 + * The file is closed and deallocated.
1.827 + *
1.828 + *----------------------------------------------------------------------
1.829 + */
1.830 +
1.831 +int
1.832 +TclpCloseFile(
1.833 + TclFile file) /* The file to close. */
1.834 +{
1.835 + WinFile *filePtr = (WinFile *) file;
1.836 +
1.837 + switch (filePtr->type) {
1.838 + case WIN_FILE:
1.839 + /*
1.840 + * Don't close the Win32 handle if the handle is a standard channel
1.841 + * during the thread exit process. Otherwise, one thread may kill
1.842 + * the stdio of another.
1.843 + */
1.844 +
1.845 + if (!TclInThreadExit()
1.846 + || ((GetStdHandle(STD_INPUT_HANDLE) != filePtr->handle)
1.847 + && (GetStdHandle(STD_OUTPUT_HANDLE) != filePtr->handle)
1.848 + && (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) {
1.849 + if (filePtr->handle != NULL &&
1.850 + CloseHandle(filePtr->handle) == FALSE) {
1.851 + TclWinConvertError(GetLastError());
1.852 + ckfree((char *) filePtr);
1.853 + return -1;
1.854 + }
1.855 + }
1.856 + break;
1.857 +
1.858 + default:
1.859 + panic("TclpCloseFile: unexpected file type");
1.860 + }
1.861 +
1.862 + ckfree((char *) filePtr);
1.863 + return 0;
1.864 +}
1.865 +
1.866 +/*
1.867 + *--------------------------------------------------------------------------
1.868 + *
1.869 + * TclpGetPid --
1.870 + *
1.871 + * Given a HANDLE to a child process, return the process id for that
1.872 + * child process.
1.873 + *
1.874 + * Results:
1.875 + * Returns the process id for the child process. If the pid was not
1.876 + * known by Tcl, either because the pid was not created by Tcl or the
1.877 + * child process has already been reaped, -1 is returned.
1.878 + *
1.879 + * Side effects:
1.880 + * None.
1.881 + *
1.882 + *--------------------------------------------------------------------------
1.883 + */
1.884 +
1.885 +unsigned long
1.886 +TclpGetPid(
1.887 + Tcl_Pid pid) /* The HANDLE of the child process. */
1.888 +{
1.889 + ProcInfo *infoPtr;
1.890 +
1.891 + PipeInit();
1.892 +
1.893 + Tcl_MutexLock(&pipeMutex);
1.894 + for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
1.895 + if (infoPtr->hProcess == (HANDLE) pid) {
1.896 + Tcl_MutexUnlock(&pipeMutex);
1.897 + return infoPtr->dwProcessId;
1.898 + }
1.899 + }
1.900 + Tcl_MutexUnlock(&pipeMutex);
1.901 + return (unsigned long) -1;
1.902 +}
1.903 +
1.904 +/*
1.905 + *----------------------------------------------------------------------
1.906 + *
1.907 + * TclpCreateProcess --
1.908 + *
1.909 + * Create a child process that has the specified files as its
1.910 + * standard input, output, and error. The child process runs
1.911 + * asynchronously under Windows NT and Windows 9x, and runs
1.912 + * with the same environment variables as the creating process.
1.913 + *
1.914 + * The complete Windows search path is searched to find the specified
1.915 + * executable. If an executable by the given name is not found,
1.916 + * automatically tries appending ".com", ".exe", and ".bat" to the
1.917 + * executable name.
1.918 + *
1.919 + * Results:
1.920 + * The return value is TCL_ERROR and an error message is left in
1.921 + * the interp's result if there was a problem creating the child
1.922 + * process. Otherwise, the return value is TCL_OK and *pidPtr is
1.923 + * filled with the process id of the child process.
1.924 + *
1.925 + * Side effects:
1.926 + * A process is created.
1.927 + *
1.928 + *----------------------------------------------------------------------
1.929 + */
1.930 +
1.931 +int
1.932 +TclpCreateProcess(
1.933 + Tcl_Interp *interp, /* Interpreter in which to leave errors that
1.934 + * occurred when creating the child process.
1.935 + * Error messages from the child process
1.936 + * itself are sent to errorFile. */
1.937 + int argc, /* Number of arguments in following array. */
1.938 + CONST char **argv, /* Array of argument strings. argv[0]
1.939 + * contains the name of the executable
1.940 + * converted to native format (using the
1.941 + * Tcl_TranslateFileName call). Additional
1.942 + * arguments have not been converted. */
1.943 + TclFile inputFile, /* If non-NULL, gives the file to use as
1.944 + * input for the child process. If inputFile
1.945 + * file is not readable or is NULL, the child
1.946 + * will receive no standard input. */
1.947 + TclFile outputFile, /* If non-NULL, gives the file that
1.948 + * receives output from the child process. If
1.949 + * outputFile file is not writeable or is
1.950 + * NULL, output from the child will be
1.951 + * discarded. */
1.952 + TclFile errorFile, /* If non-NULL, gives the file that
1.953 + * receives errors from the child process. If
1.954 + * errorFile file is not writeable or is NULL,
1.955 + * errors from the child will be discarded.
1.956 + * errorFile may be the same as outputFile. */
1.957 + Tcl_Pid *pidPtr) /* If this procedure is successful, pidPtr
1.958 + * is filled with the process id of the child
1.959 + * process. */
1.960 +{
1.961 + int result, applType, createFlags;
1.962 + Tcl_DString cmdLine; /* Complete command line (TCHAR). */
1.963 + STARTUPINFOA startInfo;
1.964 + PROCESS_INFORMATION procInfo;
1.965 + SECURITY_ATTRIBUTES secAtts;
1.966 + HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
1.967 + char execPath[MAX_PATH * TCL_UTF_MAX];
1.968 + WinFile *filePtr;
1.969 +
1.970 + PipeInit();
1.971 +
1.972 + applType = ApplicationType(interp, argv[0], execPath);
1.973 + if (applType == APPL_NONE) {
1.974 + return TCL_ERROR;
1.975 + }
1.976 +
1.977 + result = TCL_ERROR;
1.978 + Tcl_DStringInit(&cmdLine);
1.979 + hProcess = GetCurrentProcess();
1.980 +
1.981 + /*
1.982 + * STARTF_USESTDHANDLES must be used to pass handles to child process.
1.983 + * Using SetStdHandle() and/or dup2() only works when a console mode
1.984 + * parent process is spawning an attached console mode child process.
1.985 + */
1.986 +
1.987 + ZeroMemory(&startInfo, sizeof(startInfo));
1.988 + startInfo.cb = sizeof(startInfo);
1.989 + startInfo.dwFlags = STARTF_USESTDHANDLES;
1.990 + startInfo.hStdInput = INVALID_HANDLE_VALUE;
1.991 + startInfo.hStdOutput= INVALID_HANDLE_VALUE;
1.992 + startInfo.hStdError = INVALID_HANDLE_VALUE;
1.993 +
1.994 + secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
1.995 + secAtts.lpSecurityDescriptor = NULL;
1.996 + secAtts.bInheritHandle = TRUE;
1.997 +
1.998 + /*
1.999 + * We have to check the type of each file, since we cannot duplicate
1.1000 + * some file types.
1.1001 + */
1.1002 +
1.1003 + inputHandle = INVALID_HANDLE_VALUE;
1.1004 + if (inputFile != NULL) {
1.1005 + filePtr = (WinFile *)inputFile;
1.1006 + if (filePtr->type == WIN_FILE) {
1.1007 + inputHandle = filePtr->handle;
1.1008 + }
1.1009 + }
1.1010 + outputHandle = INVALID_HANDLE_VALUE;
1.1011 + if (outputFile != NULL) {
1.1012 + filePtr = (WinFile *)outputFile;
1.1013 + if (filePtr->type == WIN_FILE) {
1.1014 + outputHandle = filePtr->handle;
1.1015 + }
1.1016 + }
1.1017 + errorHandle = INVALID_HANDLE_VALUE;
1.1018 + if (errorFile != NULL) {
1.1019 + filePtr = (WinFile *)errorFile;
1.1020 + if (filePtr->type == WIN_FILE) {
1.1021 + errorHandle = filePtr->handle;
1.1022 + }
1.1023 + }
1.1024 +
1.1025 + /*
1.1026 + * Duplicate all the handles which will be passed off as stdin, stdout
1.1027 + * and stderr of the child process. The duplicate handles are set to
1.1028 + * be inheritable, so the child process can use them.
1.1029 + */
1.1030 +
1.1031 + if (inputHandle == INVALID_HANDLE_VALUE) {
1.1032 + /*
1.1033 + * If handle was not set, stdin should return immediate EOF.
1.1034 + * Under Windows95, some applications (both 16 and 32 bit!)
1.1035 + * cannot read from the NUL device; they read from console
1.1036 + * instead. When running tk, this is fatal because the child
1.1037 + * process would hang forever waiting for EOF from the unmapped
1.1038 + * console window used by the helper application.
1.1039 + *
1.1040 + * Fortunately, the helper application detects a closed pipe
1.1041 + * as an immediate EOF and can pass that information to the
1.1042 + * child process.
1.1043 + */
1.1044 +
1.1045 + if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) {
1.1046 + CloseHandle(h);
1.1047 + }
1.1048 + } else {
1.1049 + DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput,
1.1050 + 0, TRUE, DUPLICATE_SAME_ACCESS);
1.1051 + }
1.1052 + if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
1.1053 + TclWinConvertError(GetLastError());
1.1054 + Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
1.1055 + Tcl_PosixError(interp), (char *) NULL);
1.1056 + goto end;
1.1057 + }
1.1058 +
1.1059 + if (outputHandle == INVALID_HANDLE_VALUE) {
1.1060 + /*
1.1061 + * If handle was not set, output should be sent to an infinitely
1.1062 + * deep sink. Under Windows 95, some 16 bit applications cannot
1.1063 + * have stdout redirected to NUL; they send their output to
1.1064 + * the console instead. Some applications, like "more" or "dir /p",
1.1065 + * when outputting multiple pages to the console, also then try and
1.1066 + * read from the console to go the next page. When running tk, this
1.1067 + * is fatal because the child process would hang forever waiting
1.1068 + * for input from the unmapped console window used by the helper
1.1069 + * application.
1.1070 + *
1.1071 + * Fortunately, the helper application will detect a closed pipe
1.1072 + * as a sink.
1.1073 + */
1.1074 +
1.1075 + if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
1.1076 + && (applType == APPL_DOS)) {
1.1077 + if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
1.1078 + CloseHandle(h);
1.1079 + }
1.1080 + } else {
1.1081 + startInfo.hStdOutput = CreateFileA("NUL:", GENERIC_WRITE, 0,
1.1082 + &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1.1083 + }
1.1084 + } else {
1.1085 + DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput,
1.1086 + 0, TRUE, DUPLICATE_SAME_ACCESS);
1.1087 + }
1.1088 + if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
1.1089 + TclWinConvertError(GetLastError());
1.1090 + Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
1.1091 + Tcl_PosixError(interp), (char *) NULL);
1.1092 + goto end;
1.1093 + }
1.1094 +
1.1095 + if (errorHandle == INVALID_HANDLE_VALUE) {
1.1096 + /*
1.1097 + * If handle was not set, errors should be sent to an infinitely
1.1098 + * deep sink.
1.1099 + */
1.1100 +
1.1101 + startInfo.hStdError = CreateFileA("NUL:", GENERIC_WRITE, 0,
1.1102 + &secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1.1103 + } else {
1.1104 + DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError,
1.1105 + 0, TRUE, DUPLICATE_SAME_ACCESS);
1.1106 + }
1.1107 + if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
1.1108 + TclWinConvertError(GetLastError());
1.1109 + Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
1.1110 + Tcl_PosixError(interp), (char *) NULL);
1.1111 + goto end;
1.1112 + }
1.1113 + /*
1.1114 + * If we do not have a console window, then we must run DOS and
1.1115 + * WIN32 console mode applications as detached processes. This tells
1.1116 + * the loader that the child application should not inherit the
1.1117 + * console, and that it should not create a new console window for
1.1118 + * the child application. The child application should get its stdio
1.1119 + * from the redirection handles provided by this application, and run
1.1120 + * in the background.
1.1121 + *
1.1122 + * If we are starting a GUI process, they don't automatically get a
1.1123 + * console, so it doesn't matter if they are started as foreground or
1.1124 + * detached processes. The GUI window will still pop up to the
1.1125 + * foreground.
1.1126 + */
1.1127 +
1.1128 + if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
1.1129 + if (HasConsole()) {
1.1130 + createFlags = 0;
1.1131 + } else if (applType == APPL_DOS) {
1.1132 + /*
1.1133 + * Under NT, 16-bit DOS applications will not run unless they
1.1134 + * can be attached to a console. If we are running without a
1.1135 + * console, run the 16-bit program as an normal process inside
1.1136 + * of a hidden console application, and then run that hidden
1.1137 + * console as a detached process.
1.1138 + */
1.1139 +
1.1140 + startInfo.wShowWindow = SW_HIDE;
1.1141 + startInfo.dwFlags |= STARTF_USESHOWWINDOW;
1.1142 + createFlags = CREATE_NEW_CONSOLE;
1.1143 + Tcl_DStringAppend(&cmdLine, "cmd.exe /c", -1);
1.1144 + } else {
1.1145 + createFlags = DETACHED_PROCESS;
1.1146 + }
1.1147 + } else {
1.1148 + if (HasConsole()) {
1.1149 + createFlags = 0;
1.1150 + } else {
1.1151 + createFlags = DETACHED_PROCESS;
1.1152 + }
1.1153 +
1.1154 + if (applType == APPL_DOS) {
1.1155 + /*
1.1156 + * Under Windows 95, 16-bit DOS applications do not work well
1.1157 + * with pipes:
1.1158 + *
1.1159 + * 1. EOF on a pipe between a detached 16-bit DOS application
1.1160 + * and another application is not seen at the other
1.1161 + * end of the pipe, so the listening process blocks forever on
1.1162 + * reads. This inablity to detect EOF happens when either a
1.1163 + * 16-bit app or the 32-bit app is the listener.
1.1164 + *
1.1165 + * 2. If a 16-bit DOS application (detached or not) blocks when
1.1166 + * writing to a pipe, it will never wake up again, and it
1.1167 + * eventually brings the whole system down around it.
1.1168 + *
1.1169 + * The 16-bit application is run as a normal process inside
1.1170 + * of a hidden helper console app, and this helper may be run
1.1171 + * as a detached process. If any of the stdio handles is
1.1172 + * a pipe, the helper application accumulates information
1.1173 + * into temp files and forwards it to or from the DOS
1.1174 + * application as appropriate. This means that DOS apps
1.1175 + * must receive EOF from a stdin pipe before they will actually
1.1176 + * begin, and must finish generating stdout or stderr before
1.1177 + * the data will be sent to the next stage of the pipe.
1.1178 + *
1.1179 + * The helper app should be located in the same directory as
1.1180 + * the tcl dll.
1.1181 + */
1.1182 +
1.1183 + if (createFlags != 0) {
1.1184 + startInfo.wShowWindow = SW_HIDE;
1.1185 + startInfo.dwFlags |= STARTF_USESHOWWINDOW;
1.1186 + createFlags = CREATE_NEW_CONSOLE;
1.1187 + }
1.1188 +
1.1189 + {
1.1190 + Tcl_Obj *tclExePtr, *pipeDllPtr;
1.1191 + int i, fileExists;
1.1192 + char *start,*end;
1.1193 + Tcl_DString pipeDll;
1.1194 + Tcl_DStringInit(&pipeDll);
1.1195 + Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1);
1.1196 + tclExePtr = Tcl_NewStringObj(TclpFindExecutable(""), -1);
1.1197 + start = Tcl_GetStringFromObj(tclExePtr, &i);
1.1198 + for (end = start + (i-1); end > start; end--) {
1.1199 + if (*end == '/')
1.1200 + break;
1.1201 + }
1.1202 + if (*end != '/')
1.1203 + panic("no / in executable path name");
1.1204 + i = (end - start) + 1;
1.1205 + pipeDllPtr = Tcl_NewStringObj(start, i);
1.1206 + Tcl_AppendToObj(pipeDllPtr, Tcl_DStringValue(&pipeDll), -1);
1.1207 + Tcl_IncrRefCount(pipeDllPtr);
1.1208 + if (Tcl_FSConvertToPathType(interp, pipeDllPtr) != TCL_OK)
1.1209 + panic("Tcl_FSConvertToPathType failed");
1.1210 + fileExists = (Tcl_FSAccess(pipeDllPtr, F_OK) == 0);
1.1211 + if (!fileExists) {
1.1212 + panic("Tcl pipe dll \"%s\" not found",
1.1213 + Tcl_DStringValue(&pipeDll));
1.1214 + }
1.1215 + Tcl_DStringAppend(&cmdLine, Tcl_DStringValue(&pipeDll), -1);
1.1216 + Tcl_DecrRefCount(tclExePtr);
1.1217 + Tcl_DecrRefCount(pipeDllPtr);
1.1218 + Tcl_DStringFree(&pipeDll);
1.1219 + }
1.1220 + }
1.1221 + }
1.1222 +
1.1223 + /*
1.1224 + * cmdLine gets the full command line used to invoke the executable,
1.1225 + * including the name of the executable itself. The command line
1.1226 + * arguments in argv[] are stored in cmdLine separated by spaces.
1.1227 + * Special characters in individual arguments from argv[] must be
1.1228 + * quoted when being stored in cmdLine.
1.1229 + *
1.1230 + * When calling any application, bear in mind that arguments that
1.1231 + * specify a path name are not converted. If an argument contains
1.1232 + * forward slashes as path separators, it may or may not be
1.1233 + * recognized as a path name, depending on the program. In general,
1.1234 + * most applications accept forward slashes only as option
1.1235 + * delimiters and backslashes only as paths.
1.1236 + *
1.1237 + * Additionally, when calling a 16-bit dos or windows application,
1.1238 + * all path names must use the short, cryptic, path format (e.g.,
1.1239 + * using ab~1.def instead of "a b.default").
1.1240 + */
1.1241 +
1.1242 + BuildCommandLine(execPath, argc, argv, &cmdLine);
1.1243 +
1.1244 + if ((*tclWinProcs->createProcessProc)(NULL,
1.1245 + (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
1.1246 + (DWORD) createFlags, NULL, NULL, &startInfo, &procInfo) == 0) {
1.1247 + TclWinConvertError(GetLastError());
1.1248 + Tcl_AppendResult(interp, "couldn't execute \"", argv[0],
1.1249 + "\": ", Tcl_PosixError(interp), (char *) NULL);
1.1250 + goto end;
1.1251 + }
1.1252 +
1.1253 + /*
1.1254 + * This wait is used to force the OS to give some time to the DOS
1.1255 + * process.
1.1256 + */
1.1257 +
1.1258 + if (applType == APPL_DOS) {
1.1259 + WaitForSingleObject(procInfo.hProcess, 50);
1.1260 + }
1.1261 +
1.1262 + /*
1.1263 + * "When an application spawns a process repeatedly, a new thread
1.1264 + * instance will be created for each process but the previous
1.1265 + * instances may not be cleaned up. This results in a significant
1.1266 + * virtual memory loss each time the process is spawned. If there
1.1267 + * is a WaitForInputIdle() call between CreateProcess() and
1.1268 + * CloseHandle(), the problem does not occur." PSS ID Number: Q124121
1.1269 + */
1.1270 +
1.1271 + WaitForInputIdle(procInfo.hProcess, 5000);
1.1272 + CloseHandle(procInfo.hThread);
1.1273 +
1.1274 + *pidPtr = (Tcl_Pid) procInfo.hProcess;
1.1275 + if (*pidPtr != 0) {
1.1276 + TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId);
1.1277 + }
1.1278 + result = TCL_OK;
1.1279 +
1.1280 + end:
1.1281 + Tcl_DStringFree(&cmdLine);
1.1282 + if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
1.1283 + CloseHandle(startInfo.hStdInput);
1.1284 + }
1.1285 + if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
1.1286 + CloseHandle(startInfo.hStdOutput);
1.1287 + }
1.1288 + if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
1.1289 + CloseHandle(startInfo.hStdError);
1.1290 + }
1.1291 + return result;
1.1292 +}
1.1293 +
1.1294 +
1.1295 +/*
1.1296 + *----------------------------------------------------------------------
1.1297 + *
1.1298 + * HasConsole --
1.1299 + *
1.1300 + * Determines whether the current application is attached to a
1.1301 + * console.
1.1302 + *
1.1303 + * Results:
1.1304 + * Returns TRUE if this application has a console, else FALSE.
1.1305 + *
1.1306 + * Side effects:
1.1307 + * None.
1.1308 + *
1.1309 + *----------------------------------------------------------------------
1.1310 + */
1.1311 +
1.1312 +static BOOL
1.1313 +HasConsole()
1.1314 +{
1.1315 + HANDLE handle;
1.1316 +
1.1317 + handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
1.1318 + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1.1319 +
1.1320 + if (handle != INVALID_HANDLE_VALUE) {
1.1321 + CloseHandle(handle);
1.1322 + return TRUE;
1.1323 + } else {
1.1324 + return FALSE;
1.1325 + }
1.1326 +}
1.1327 +
1.1328 +/*
1.1329 + *--------------------------------------------------------------------
1.1330 + *
1.1331 + * ApplicationType --
1.1332 + *
1.1333 + * Search for the specified program and identify if it refers to a DOS,
1.1334 + * Windows 3.X, or Win32 program. Used to determine how to invoke
1.1335 + * a program, or if it can even be invoked.
1.1336 + *
1.1337 + * It is possible to almost positively identify DOS and Windows
1.1338 + * applications that contain the appropriate magic numbers. However,
1.1339 + * DOS .com files do not seem to contain a magic number; if the program
1.1340 + * name ends with .com and could not be identified as a Windows .com
1.1341 + * file, it will be assumed to be a DOS application, even if it was
1.1342 + * just random data. If the program name does not end with .com, no
1.1343 + * such assumption is made.
1.1344 + *
1.1345 + * The Win32 procedure GetBinaryType incorrectly identifies any
1.1346 + * junk file that ends with .exe as a dos executable and some
1.1347 + * executables that don't end with .exe as not executable. Plus it
1.1348 + * doesn't exist under win95, so I won't feel bad about reimplementing
1.1349 + * functionality.
1.1350 + *
1.1351 + * Results:
1.1352 + * The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32
1.1353 + * if the filename referred to the corresponding application type.
1.1354 + * If the file name could not be found or did not refer to any known
1.1355 + * application type, APPL_NONE is returned and an error message is
1.1356 + * left in interp. .bat files are identified as APPL_DOS.
1.1357 + *
1.1358 + * Side effects:
1.1359 + * None.
1.1360 + *
1.1361 + *----------------------------------------------------------------------
1.1362 + */
1.1363 +
1.1364 +static int
1.1365 +ApplicationType(interp, originalName, fullName)
1.1366 + Tcl_Interp *interp; /* Interp, for error message. */
1.1367 + const char *originalName; /* Name of the application to find. */
1.1368 + char fullName[]; /* Filled with complete path to
1.1369 + * application. */
1.1370 +{
1.1371 + int applType, i, nameLen, found;
1.1372 + HANDLE hFile;
1.1373 + TCHAR *rest;
1.1374 + char *ext;
1.1375 + char buf[2];
1.1376 + DWORD attr, read;
1.1377 + IMAGE_DOS_HEADER header;
1.1378 + Tcl_DString nameBuf, ds;
1.1379 + CONST TCHAR *nativeName;
1.1380 + WCHAR nativeFullPath[MAX_PATH];
1.1381 + static char extensions[][5] = {"", ".com", ".exe", ".bat"};
1.1382 +
1.1383 + /* Look for the program as an external program. First try the name
1.1384 + * as it is, then try adding .com, .exe, and .bat, in that order, to
1.1385 + * the name, looking for an executable.
1.1386 + *
1.1387 + * Using the raw SearchPath() procedure doesn't do quite what is
1.1388 + * necessary. If the name of the executable already contains a '.'
1.1389 + * character, it will not try appending the specified extension when
1.1390 + * searching (in other words, SearchPath will not find the program
1.1391 + * "a.b.exe" if the arguments specified "a.b" and ".exe").
1.1392 + * So, first look for the file as it is named. Then manually append
1.1393 + * the extensions, looking for a match.
1.1394 + */
1.1395 +
1.1396 + applType = APPL_NONE;
1.1397 + Tcl_DStringInit(&nameBuf);
1.1398 + Tcl_DStringAppend(&nameBuf, originalName, -1);
1.1399 + nameLen = Tcl_DStringLength(&nameBuf);
1.1400 +
1.1401 + for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
1.1402 + Tcl_DStringSetLength(&nameBuf, nameLen);
1.1403 + Tcl_DStringAppend(&nameBuf, extensions[i], -1);
1.1404 + nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf),
1.1405 + Tcl_DStringLength(&nameBuf), &ds);
1.1406 + found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL,
1.1407 + MAX_PATH, nativeFullPath, &rest);
1.1408 + Tcl_DStringFree(&ds);
1.1409 + if (found == 0) {
1.1410 + continue;
1.1411 + }
1.1412 +
1.1413 + /*
1.1414 + * Ignore matches on directories or data files, return if identified
1.1415 + * a known type.
1.1416 + */
1.1417 +
1.1418 + attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath);
1.1419 + if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
1.1420 + continue;
1.1421 + }
1.1422 + strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
1.1423 + Tcl_DStringFree(&ds);
1.1424 +
1.1425 + ext = strrchr(fullName, '.');
1.1426 + if ((ext != NULL) && (stricmp(ext, ".bat") == 0)) {
1.1427 + applType = APPL_DOS;
1.1428 + break;
1.1429 + }
1.1430 +
1.1431 + hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath,
1.1432 + GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1.1433 + FILE_ATTRIBUTE_NORMAL, NULL);
1.1434 + if (hFile == INVALID_HANDLE_VALUE) {
1.1435 + continue;
1.1436 + }
1.1437 +
1.1438 + header.e_magic = 0;
1.1439 + ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
1.1440 + if (header.e_magic != IMAGE_DOS_SIGNATURE) {
1.1441 + /*
1.1442 + * Doesn't have the magic number for relocatable executables. If
1.1443 + * filename ends with .com, assume it's a DOS application anyhow.
1.1444 + * Note that we didn't make this assumption at first, because some
1.1445 + * supposed .com files are really 32-bit executables with all the
1.1446 + * magic numbers and everything.
1.1447 + */
1.1448 +
1.1449 + CloseHandle(hFile);
1.1450 + if ((ext != NULL) && (stricmp(ext, ".com") == 0)) {
1.1451 + applType = APPL_DOS;
1.1452 + break;
1.1453 + }
1.1454 + continue;
1.1455 + }
1.1456 + if (header.e_lfarlc != sizeof(header)) {
1.1457 + /*
1.1458 + * All Windows 3.X and Win32 and some DOS programs have this value
1.1459 + * set here. If it doesn't, assume that since it already had the
1.1460 + * other magic number it was a DOS application.
1.1461 + */
1.1462 +
1.1463 + CloseHandle(hFile);
1.1464 + applType = APPL_DOS;
1.1465 + break;
1.1466 + }
1.1467 +
1.1468 + /*
1.1469 + * The DWORD at header.e_lfanew points to yet another magic number.
1.1470 + */
1.1471 +
1.1472 + buf[0] = '\0';
1.1473 + SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
1.1474 + ReadFile(hFile, (void *) buf, 2, &read, NULL);
1.1475 + CloseHandle(hFile);
1.1476 +
1.1477 + if ((buf[0] == 'N') && (buf[1] == 'E')) {
1.1478 + applType = APPL_WIN3X;
1.1479 + } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
1.1480 + applType = APPL_WIN32;
1.1481 + } else {
1.1482 + /*
1.1483 + * Strictly speaking, there should be a test that there
1.1484 + * is an 'L' and 'E' at buf[0..1], to identify the type as
1.1485 + * DOS, but of course we ran into a DOS executable that
1.1486 + * _doesn't_ have the magic number -- specifically, one
1.1487 + * compiled using the Lahey Fortran90 compiler.
1.1488 + */
1.1489 +
1.1490 + applType = APPL_DOS;
1.1491 + }
1.1492 + break;
1.1493 + }
1.1494 + Tcl_DStringFree(&nameBuf);
1.1495 +
1.1496 + if (applType == APPL_NONE) {
1.1497 + TclWinConvertError(GetLastError());
1.1498 + Tcl_AppendResult(interp, "couldn't execute \"", originalName,
1.1499 + "\": ", Tcl_PosixError(interp), (char *) NULL);
1.1500 + return APPL_NONE;
1.1501 + }
1.1502 +
1.1503 + if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
1.1504 + /*
1.1505 + * Replace long path name of executable with short path name for
1.1506 + * 16-bit applications. Otherwise the application may not be able
1.1507 + * to correctly parse its own command line to separate off the
1.1508 + * application name from the arguments.
1.1509 + */
1.1510 +
1.1511 + (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath,
1.1512 + nativeFullPath, MAX_PATH);
1.1513 + strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
1.1514 + Tcl_DStringFree(&ds);
1.1515 + }
1.1516 + return applType;
1.1517 +}
1.1518 +
1.1519 +/*
1.1520 + *----------------------------------------------------------------------
1.1521 + *
1.1522 + * BuildCommandLine --
1.1523 + *
1.1524 + * The command line arguments are stored in linePtr separated
1.1525 + * by spaces, in a form that CreateProcess() understands. Special
1.1526 + * characters in individual arguments from argv[] must be quoted
1.1527 + * when being stored in cmdLine.
1.1528 + *
1.1529 + * Results:
1.1530 + * None.
1.1531 + *
1.1532 + * Side effects:
1.1533 + * None.
1.1534 + *
1.1535 + *----------------------------------------------------------------------
1.1536 + */
1.1537 +
1.1538 +static void
1.1539 +BuildCommandLine(
1.1540 + CONST char *executable, /* Full path of executable (including
1.1541 + * extension). Replacement for argv[0]. */
1.1542 + int argc, /* Number of arguments. */
1.1543 + CONST char **argv, /* Argument strings in UTF. */
1.1544 + Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the
1.1545 + * command line (TCHAR). */
1.1546 +{
1.1547 + CONST char *arg, *start, *special;
1.1548 + int quote, i;
1.1549 + Tcl_DString ds;
1.1550 +
1.1551 + Tcl_DStringInit(&ds);
1.1552 +
1.1553 + /*
1.1554 + * Prime the path. Add a space separator if we were primed with
1.1555 + * something.
1.1556 + */
1.1557 +
1.1558 + Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1);
1.1559 + if (Tcl_DStringLength(&ds) > 0) Tcl_DStringAppend(&ds, " ", 1);
1.1560 +
1.1561 + for (i = 0; i < argc; i++) {
1.1562 + if (i == 0) {
1.1563 + arg = executable;
1.1564 + } else {
1.1565 + arg = argv[i];
1.1566 + Tcl_DStringAppend(&ds, " ", 1);
1.1567 + }
1.1568 +
1.1569 + quote = 0;
1.1570 + if (arg[0] == '\0') {
1.1571 + quote = 1;
1.1572 + } else {
1.1573 + int count;
1.1574 + Tcl_UniChar ch;
1.1575 + for (start = arg; *start != '\0'; start += count) {
1.1576 + count = Tcl_UtfToUniChar(start, &ch);
1.1577 + if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */
1.1578 + quote = 1;
1.1579 + break;
1.1580 + }
1.1581 + }
1.1582 + }
1.1583 + if (quote) {
1.1584 + Tcl_DStringAppend(&ds, "\"", 1);
1.1585 + }
1.1586 + start = arg;
1.1587 + for (special = arg; ; ) {
1.1588 + if ((*special == '\\') &&
1.1589 + (special[1] == '\\' || special[1] == '"' || (quote && special[1] == '\0'))) {
1.1590 + Tcl_DStringAppend(&ds, start, (int) (special - start));
1.1591 + start = special;
1.1592 + while (1) {
1.1593 + special++;
1.1594 + if (*special == '"' || (quote && *special == '\0')) {
1.1595 + /*
1.1596 + * N backslashes followed a quote -> insert
1.1597 + * N * 2 + 1 backslashes then a quote.
1.1598 + */
1.1599 +
1.1600 + Tcl_DStringAppend(&ds, start,
1.1601 + (int) (special - start));
1.1602 + break;
1.1603 + }
1.1604 + if (*special != '\\') {
1.1605 + break;
1.1606 + }
1.1607 + }
1.1608 + Tcl_DStringAppend(&ds, start, (int) (special - start));
1.1609 + start = special;
1.1610 + }
1.1611 + if (*special == '"') {
1.1612 + Tcl_DStringAppend(&ds, start, (int) (special - start));
1.1613 + Tcl_DStringAppend(&ds, "\\\"", 2);
1.1614 + start = special + 1;
1.1615 + }
1.1616 + if (*special == '\0') {
1.1617 + break;
1.1618 + }
1.1619 + special++;
1.1620 + }
1.1621 + Tcl_DStringAppend(&ds, start, (int) (special - start));
1.1622 + if (quote) {
1.1623 + Tcl_DStringAppend(&ds, "\"", 1);
1.1624 + }
1.1625 + }
1.1626 + Tcl_DStringFree(linePtr);
1.1627 + Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr);
1.1628 + Tcl_DStringFree(&ds);
1.1629 +}
1.1630 +
1.1631 +/*
1.1632 + *----------------------------------------------------------------------
1.1633 + *
1.1634 + * TclpCreateCommandChannel --
1.1635 + *
1.1636 + * This function is called by Tcl_OpenCommandChannel to perform
1.1637 + * the platform specific channel initialization for a command
1.1638 + * channel.
1.1639 + *
1.1640 + * Results:
1.1641 + * Returns a new channel or NULL on failure.
1.1642 + *
1.1643 + * Side effects:
1.1644 + * Allocates a new channel.
1.1645 + *
1.1646 + *----------------------------------------------------------------------
1.1647 + */
1.1648 +
1.1649 +Tcl_Channel
1.1650 +TclpCreateCommandChannel(
1.1651 + TclFile readFile, /* If non-null, gives the file for reading. */
1.1652 + TclFile writeFile, /* If non-null, gives the file for writing. */
1.1653 + TclFile errorFile, /* If non-null, gives the file where errors
1.1654 + * can be read. */
1.1655 + int numPids, /* The number of pids in the pid array. */
1.1656 + Tcl_Pid *pidPtr) /* An array of process identifiers. */
1.1657 +{
1.1658 + char channelName[16 + TCL_INTEGER_SPACE];
1.1659 + int channelId;
1.1660 + DWORD id;
1.1661 + PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
1.1662 +
1.1663 + PipeInit();
1.1664 +
1.1665 + infoPtr->watchMask = 0;
1.1666 + infoPtr->flags = 0;
1.1667 + infoPtr->readFlags = 0;
1.1668 + infoPtr->readFile = readFile;
1.1669 + infoPtr->writeFile = writeFile;
1.1670 + infoPtr->errorFile = errorFile;
1.1671 + infoPtr->numPids = numPids;
1.1672 + infoPtr->pidPtr = pidPtr;
1.1673 + infoPtr->writeBuf = 0;
1.1674 + infoPtr->writeBufLen = 0;
1.1675 + infoPtr->writeError = 0;
1.1676 + infoPtr->channel = (Tcl_Channel) NULL;
1.1677 +
1.1678 + /*
1.1679 + * Use one of the fds associated with the channel as the
1.1680 + * channel id.
1.1681 + */
1.1682 +
1.1683 + if (readFile) {
1.1684 + channelId = (int) ((WinFile*)readFile)->handle;
1.1685 + } else if (writeFile) {
1.1686 + channelId = (int) ((WinFile*)writeFile)->handle;
1.1687 + } else if (errorFile) {
1.1688 + channelId = (int) ((WinFile*)errorFile)->handle;
1.1689 + } else {
1.1690 + channelId = 0;
1.1691 + }
1.1692 +
1.1693 + infoPtr->validMask = 0;
1.1694 +
1.1695 + infoPtr->threadId = Tcl_GetCurrentThread();
1.1696 +
1.1697 + if (readFile != NULL) {
1.1698 + /*
1.1699 + * Start the background reader thread.
1.1700 + */
1.1701 +
1.1702 + infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
1.1703 + infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
1.1704 + infoPtr->stopReader = CreateEvent(NULL, TRUE, FALSE, NULL);
1.1705 + infoPtr->readThread = CreateThread(NULL, 256, PipeReaderThread,
1.1706 + infoPtr, 0, &id);
1.1707 + SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
1.1708 + infoPtr->validMask |= TCL_READABLE;
1.1709 + } else {
1.1710 + infoPtr->readThread = 0;
1.1711 + }
1.1712 + if (writeFile != NULL) {
1.1713 + /*
1.1714 + * Start the background writer thread.
1.1715 + */
1.1716 +
1.1717 + infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
1.1718 + infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
1.1719 + infoPtr->stopWriter = CreateEvent(NULL, TRUE, FALSE, NULL);
1.1720 + infoPtr->writeThread = CreateThread(NULL, 256, PipeWriterThread,
1.1721 + infoPtr, 0, &id);
1.1722 + SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
1.1723 + infoPtr->validMask |= TCL_WRITABLE;
1.1724 + }
1.1725 +
1.1726 + /*
1.1727 + * For backward compatibility with previous versions of Tcl, we
1.1728 + * use "file%d" as the base name for pipes even though it would
1.1729 + * be more natural to use "pipe%d".
1.1730 + * Use the pointer to keep the channel names unique, in case
1.1731 + * channels share handles (stdin/stdout).
1.1732 + */
1.1733 +
1.1734 + wsprintfA(channelName, "file%lx", infoPtr);
1.1735 + infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
1.1736 + (ClientData) infoPtr, infoPtr->validMask);
1.1737 +
1.1738 + /*
1.1739 + * Pipes have AUTO translation mode on Windows and ^Z eof char, which
1.1740 + * means that a ^Z will be appended to them at close. This is needed
1.1741 + * for Windows programs that expect a ^Z at EOF.
1.1742 + */
1.1743 +
1.1744 + Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
1.1745 + "-translation", "auto");
1.1746 + Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
1.1747 + "-eofchar", "\032 {}");
1.1748 + return infoPtr->channel;
1.1749 +}
1.1750 +
1.1751 +/*
1.1752 + *----------------------------------------------------------------------
1.1753 + *
1.1754 + * TclGetAndDetachPids --
1.1755 + *
1.1756 + * Stores a list of the command PIDs for a command channel in
1.1757 + * the interp's result.
1.1758 + *
1.1759 + * Results:
1.1760 + * None.
1.1761 + *
1.1762 + * Side effects:
1.1763 + * Modifies the interp's result.
1.1764 + *
1.1765 + *----------------------------------------------------------------------
1.1766 + */
1.1767 +
1.1768 +void
1.1769 +TclGetAndDetachPids(
1.1770 + Tcl_Interp *interp,
1.1771 + Tcl_Channel chan)
1.1772 +{
1.1773 + PipeInfo *pipePtr;
1.1774 + Tcl_ChannelType *chanTypePtr;
1.1775 + int i;
1.1776 + char buf[TCL_INTEGER_SPACE];
1.1777 +
1.1778 + /*
1.1779 + * Punt if the channel is not a command channel.
1.1780 + */
1.1781 +
1.1782 + chanTypePtr = Tcl_GetChannelType(chan);
1.1783 + if (chanTypePtr != &pipeChannelType) {
1.1784 + return;
1.1785 + }
1.1786 +
1.1787 + pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
1.1788 + for (i = 0; i < pipePtr->numPids; i++) {
1.1789 + wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
1.1790 + Tcl_AppendElement(interp, buf);
1.1791 + Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
1.1792 + }
1.1793 + if (pipePtr->numPids > 0) {
1.1794 + ckfree((char *) pipePtr->pidPtr);
1.1795 + pipePtr->numPids = 0;
1.1796 + }
1.1797 +}
1.1798 +
1.1799 +/*
1.1800 + *----------------------------------------------------------------------
1.1801 + *
1.1802 + * PipeBlockModeProc --
1.1803 + *
1.1804 + * Set blocking or non-blocking mode on channel.
1.1805 + *
1.1806 + * Results:
1.1807 + * 0 if successful, errno when failed.
1.1808 + *
1.1809 + * Side effects:
1.1810 + * Sets the device into blocking or non-blocking mode.
1.1811 + *
1.1812 + *----------------------------------------------------------------------
1.1813 + */
1.1814 +
1.1815 +static int
1.1816 +PipeBlockModeProc(
1.1817 + ClientData instanceData, /* Instance data for channel. */
1.1818 + int mode) /* TCL_MODE_BLOCKING or
1.1819 + * TCL_MODE_NONBLOCKING. */
1.1820 +{
1.1821 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.1822 +
1.1823 + /*
1.1824 + * Pipes on Windows can not be switched between blocking and nonblocking,
1.1825 + * hence we have to emulate the behavior. This is done in the input
1.1826 + * function by checking against a bit in the state. We set or unset the
1.1827 + * bit here to cause the input function to emulate the correct behavior.
1.1828 + */
1.1829 +
1.1830 + if (mode == TCL_MODE_NONBLOCKING) {
1.1831 + infoPtr->flags |= PIPE_ASYNC;
1.1832 + } else {
1.1833 + infoPtr->flags &= ~(PIPE_ASYNC);
1.1834 + }
1.1835 + return 0;
1.1836 +}
1.1837 +
1.1838 +/*
1.1839 + *----------------------------------------------------------------------
1.1840 + *
1.1841 + * PipeClose2Proc --
1.1842 + *
1.1843 + * Closes a pipe based IO channel.
1.1844 + *
1.1845 + * Results:
1.1846 + * 0 on success, errno otherwise.
1.1847 + *
1.1848 + * Side effects:
1.1849 + * Closes the physical channel.
1.1850 + *
1.1851 + *----------------------------------------------------------------------
1.1852 + */
1.1853 +
1.1854 +static int
1.1855 +PipeClose2Proc(
1.1856 + ClientData instanceData, /* Pointer to PipeInfo structure. */
1.1857 + Tcl_Interp *interp, /* For error reporting. */
1.1858 + int flags) /* Flags that indicate which side to close. */
1.1859 +{
1.1860 + PipeInfo *pipePtr = (PipeInfo *) instanceData;
1.1861 + Tcl_Channel errChan;
1.1862 + int errorCode, result;
1.1863 + PipeInfo *infoPtr, **nextPtrPtr;
1.1864 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1865 + DWORD exitCode;
1.1866 +
1.1867 + errorCode = 0;
1.1868 + if ((!flags || (flags == TCL_CLOSE_READ))
1.1869 + && (pipePtr->readFile != NULL)) {
1.1870 + /*
1.1871 + * Clean up the background thread if necessary. Note that this
1.1872 + * must be done before we can close the file, since the
1.1873 + * thread may be blocking trying to read from the pipe.
1.1874 + */
1.1875 +
1.1876 + if (pipePtr->readThread) {
1.1877 + /*
1.1878 + * The thread may already have closed on it's own. Check it's
1.1879 + * exit code.
1.1880 + */
1.1881 +
1.1882 + GetExitCodeThread(pipePtr->readThread, &exitCode);
1.1883 +
1.1884 + if (exitCode == STILL_ACTIVE) {
1.1885 + /*
1.1886 + * Set the stop event so that if the reader thread is blocked
1.1887 + * in PipeReaderThread on WaitForMultipleEvents, it will exit
1.1888 + * cleanly.
1.1889 + */
1.1890 +
1.1891 + SetEvent(pipePtr->stopReader);
1.1892 +
1.1893 + /*
1.1894 + * Wait at most 20 milliseconds for the reader thread to close.
1.1895 + */
1.1896 +
1.1897 + if (WaitForSingleObject(pipePtr->readThread, 20)
1.1898 + == WAIT_TIMEOUT) {
1.1899 + /*
1.1900 + * The thread must be blocked waiting for the pipe to
1.1901 + * become readable in ReadFile(). There isn't a clean way
1.1902 + * to exit the thread from this condition. We should
1.1903 + * terminate the child process instead to get the reader
1.1904 + * thread to fall out of ReadFile with a FALSE. (below) is
1.1905 + * not the correct way to do this, but will stay here until
1.1906 + * a better solution is found.
1.1907 + *
1.1908 + * Note that we need to guard against terminating the
1.1909 + * thread while it is in the middle of Tcl_ThreadAlert
1.1910 + * because it won't be able to release the notifier lock.
1.1911 + */
1.1912 +
1.1913 + Tcl_MutexLock(&pipeMutex);
1.1914 +
1.1915 + /* BUG: this leaks memory */
1.1916 + TerminateThread(pipePtr->readThread, 0);
1.1917 + Tcl_MutexUnlock(&pipeMutex);
1.1918 + }
1.1919 + }
1.1920 +
1.1921 + CloseHandle(pipePtr->readThread);
1.1922 + CloseHandle(pipePtr->readable);
1.1923 + CloseHandle(pipePtr->startReader);
1.1924 + CloseHandle(pipePtr->stopReader);
1.1925 + pipePtr->readThread = NULL;
1.1926 + }
1.1927 + if (TclpCloseFile(pipePtr->readFile) != 0) {
1.1928 + errorCode = errno;
1.1929 + }
1.1930 + pipePtr->validMask &= ~TCL_READABLE;
1.1931 + pipePtr->readFile = NULL;
1.1932 + }
1.1933 + if ((!flags || (flags & TCL_CLOSE_WRITE))
1.1934 + && (pipePtr->writeFile != NULL)) {
1.1935 +
1.1936 + if (pipePtr->writeThread) {
1.1937 + /*
1.1938 + * Wait for the writer thread to finish the current buffer,
1.1939 + * then terminate the thread and close the handles. If the
1.1940 + * channel is nonblocking, there should be no pending write
1.1941 + * operations.
1.1942 + */
1.1943 +
1.1944 + WaitForSingleObject(pipePtr->writable, INFINITE);
1.1945 +
1.1946 + /*
1.1947 + * The thread may already have closed on it's own. Check it's
1.1948 + * exit code.
1.1949 + */
1.1950 +
1.1951 + GetExitCodeThread(pipePtr->writeThread, &exitCode);
1.1952 +
1.1953 + if (exitCode == STILL_ACTIVE) {
1.1954 + /*
1.1955 + * Set the stop event so that if the reader thread is blocked
1.1956 + * in PipeReaderThread on WaitForMultipleEvents, it will exit
1.1957 + * cleanly.
1.1958 + */
1.1959 +
1.1960 + SetEvent(pipePtr->stopWriter);
1.1961 +
1.1962 + /*
1.1963 + * Wait at most 20 milliseconds for the reader thread to close.
1.1964 + */
1.1965 +
1.1966 + if (WaitForSingleObject(pipePtr->writeThread, 20)
1.1967 + == WAIT_TIMEOUT) {
1.1968 + /*
1.1969 + * The thread must be blocked waiting for the pipe to
1.1970 + * consume input in WriteFile(). There isn't a clean way
1.1971 + * to exit the thread from this condition. We should
1.1972 + * terminate the child process instead to get the writer
1.1973 + * thread to fall out of WriteFile with a FALSE. (below) is
1.1974 + * not the correct way to do this, but will stay here until
1.1975 + * a better solution is found.
1.1976 + *
1.1977 + * Note that we need to guard against terminating the
1.1978 + * thread while it is in the middle of Tcl_ThreadAlert
1.1979 + * because it won't be able to release the notifier lock.
1.1980 + */
1.1981 +
1.1982 + Tcl_MutexLock(&pipeMutex);
1.1983 +
1.1984 + /* BUG: this leaks memory */
1.1985 + TerminateThread(pipePtr->writeThread, 0);
1.1986 + Tcl_MutexUnlock(&pipeMutex);
1.1987 + }
1.1988 + }
1.1989 +
1.1990 + CloseHandle(pipePtr->writeThread);
1.1991 + CloseHandle(pipePtr->writable);
1.1992 + CloseHandle(pipePtr->startWriter);
1.1993 + CloseHandle(pipePtr->stopWriter);
1.1994 + pipePtr->writeThread = NULL;
1.1995 + }
1.1996 + if (TclpCloseFile(pipePtr->writeFile) != 0) {
1.1997 + if (errorCode == 0) {
1.1998 + errorCode = errno;
1.1999 + }
1.2000 + }
1.2001 + pipePtr->validMask &= ~TCL_WRITABLE;
1.2002 + pipePtr->writeFile = NULL;
1.2003 + }
1.2004 +
1.2005 + pipePtr->watchMask &= pipePtr->validMask;
1.2006 +
1.2007 + /*
1.2008 + * Don't free the channel if any of the flags were set.
1.2009 + */
1.2010 +
1.2011 + if (flags) {
1.2012 + return errorCode;
1.2013 + }
1.2014 +
1.2015 + /*
1.2016 + * Remove the file from the list of watched files.
1.2017 + */
1.2018 +
1.2019 + for (nextPtrPtr = &(tsdPtr->firstPipePtr), infoPtr = *nextPtrPtr;
1.2020 + infoPtr != NULL;
1.2021 + nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
1.2022 + if (infoPtr == (PipeInfo *)pipePtr) {
1.2023 + *nextPtrPtr = infoPtr->nextPtr;
1.2024 + break;
1.2025 + }
1.2026 + }
1.2027 +
1.2028 + if ((pipePtr->flags & PIPE_ASYNC) || TclInExit()) {
1.2029 + /*
1.2030 + * If the channel is non-blocking or Tcl is being cleaned up,
1.2031 + * just detach the children PIDs, reap them (important if we are
1.2032 + * in a dynamic load module), and discard the errorFile.
1.2033 + */
1.2034 +
1.2035 + Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
1.2036 + Tcl_ReapDetachedProcs();
1.2037 +
1.2038 + if (pipePtr->errorFile) {
1.2039 + if (TclpCloseFile(pipePtr->errorFile) != 0) {
1.2040 + if ( errorCode == 0 ) {
1.2041 + errorCode = errno;
1.2042 + }
1.2043 + }
1.2044 + }
1.2045 + result = 0;
1.2046 + } else {
1.2047 + /*
1.2048 + * Wrap the error file into a channel and give it to the cleanup
1.2049 + * routine.
1.2050 + */
1.2051 +
1.2052 + if (pipePtr->errorFile) {
1.2053 + WinFile *filePtr;
1.2054 +
1.2055 + filePtr = (WinFile*)pipePtr->errorFile;
1.2056 + errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
1.2057 + TCL_READABLE);
1.2058 + ckfree((char *) filePtr);
1.2059 + } else {
1.2060 + errChan = NULL;
1.2061 + }
1.2062 +
1.2063 + result = TclCleanupChildren(interp, pipePtr->numPids,
1.2064 + pipePtr->pidPtr, errChan);
1.2065 + }
1.2066 +
1.2067 + if (pipePtr->numPids > 0) {
1.2068 + ckfree((char *) pipePtr->pidPtr);
1.2069 + }
1.2070 +
1.2071 + if (pipePtr->writeBuf != NULL) {
1.2072 + ckfree(pipePtr->writeBuf);
1.2073 + }
1.2074 +
1.2075 + ckfree((char*) pipePtr);
1.2076 +
1.2077 + if (errorCode == 0) {
1.2078 + return result;
1.2079 + }
1.2080 + return errorCode;
1.2081 +}
1.2082 +
1.2083 +/*
1.2084 + *----------------------------------------------------------------------
1.2085 + *
1.2086 + * PipeInputProc --
1.2087 + *
1.2088 + * Reads input from the IO channel into the buffer given. Returns
1.2089 + * count of how many bytes were actually read, and an error indication.
1.2090 + *
1.2091 + * Results:
1.2092 + * A count of how many bytes were read is returned and an error
1.2093 + * indication is returned in an output argument.
1.2094 + *
1.2095 + * Side effects:
1.2096 + * Reads input from the actual channel.
1.2097 + *
1.2098 + *----------------------------------------------------------------------
1.2099 + */
1.2100 +
1.2101 +static int
1.2102 +PipeInputProc(
1.2103 + ClientData instanceData, /* Pipe state. */
1.2104 + char *buf, /* Where to store data read. */
1.2105 + int bufSize, /* How much space is available
1.2106 + * in the buffer? */
1.2107 + int *errorCode) /* Where to store error code. */
1.2108 +{
1.2109 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.2110 + WinFile *filePtr = (WinFile*) infoPtr->readFile;
1.2111 + DWORD count, bytesRead = 0;
1.2112 + int result;
1.2113 +
1.2114 + *errorCode = 0;
1.2115 + /*
1.2116 + * Synchronize with the reader thread.
1.2117 + */
1.2118 +
1.2119 + result = WaitForRead(infoPtr, (infoPtr->flags & PIPE_ASYNC) ? 0 : 1);
1.2120 +
1.2121 + /*
1.2122 + * If an error occurred, return immediately.
1.2123 + */
1.2124 +
1.2125 + if (result == -1) {
1.2126 + *errorCode = errno;
1.2127 + return -1;
1.2128 + }
1.2129 +
1.2130 + if (infoPtr->readFlags & PIPE_EXTRABYTE) {
1.2131 + /*
1.2132 + * The reader thread consumed 1 byte as a side effect of
1.2133 + * waiting so we need to move it into the buffer.
1.2134 + */
1.2135 +
1.2136 + *buf = infoPtr->extraByte;
1.2137 + infoPtr->readFlags &= ~PIPE_EXTRABYTE;
1.2138 + buf++;
1.2139 + bufSize--;
1.2140 + bytesRead = 1;
1.2141 +
1.2142 + /*
1.2143 + * If further read attempts would block, return what we have.
1.2144 + */
1.2145 +
1.2146 + if (result == 0) {
1.2147 + return bytesRead;
1.2148 + }
1.2149 + }
1.2150 +
1.2151 + /*
1.2152 + * Attempt to read bufSize bytes. The read will return immediately
1.2153 + * if there is any data available. Otherwise it will block until
1.2154 + * at least one byte is available or an EOF occurs.
1.2155 + */
1.2156 +
1.2157 + if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
1.2158 + (LPOVERLAPPED) NULL) == TRUE) {
1.2159 + return bytesRead + count;
1.2160 + } else if (bytesRead) {
1.2161 + /*
1.2162 + * Ignore errors if we have data to return.
1.2163 + */
1.2164 +
1.2165 + return bytesRead;
1.2166 + }
1.2167 +
1.2168 + TclWinConvertError(GetLastError());
1.2169 + if (errno == EPIPE) {
1.2170 + infoPtr->readFlags |= PIPE_EOF;
1.2171 + return 0;
1.2172 + }
1.2173 + *errorCode = errno;
1.2174 + return -1;
1.2175 +}
1.2176 +
1.2177 +/*
1.2178 + *----------------------------------------------------------------------
1.2179 + *
1.2180 + * PipeOutputProc --
1.2181 + *
1.2182 + * Writes the given output on the IO channel. Returns count of how
1.2183 + * many characters were actually written, and an error indication.
1.2184 + *
1.2185 + * Results:
1.2186 + * A count of how many characters were written is returned and an
1.2187 + * error indication is returned in an output argument.
1.2188 + *
1.2189 + * Side effects:
1.2190 + * Writes output on the actual channel.
1.2191 + *
1.2192 + *----------------------------------------------------------------------
1.2193 + */
1.2194 +
1.2195 +static int
1.2196 +PipeOutputProc(
1.2197 + ClientData instanceData, /* Pipe state. */
1.2198 + CONST char *buf, /* The data buffer. */
1.2199 + int toWrite, /* How many bytes to write? */
1.2200 + int *errorCode) /* Where to store error code. */
1.2201 +{
1.2202 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.2203 + WinFile *filePtr = (WinFile*) infoPtr->writeFile;
1.2204 + DWORD bytesWritten, timeout;
1.2205 +
1.2206 + *errorCode = 0;
1.2207 + timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE;
1.2208 + if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
1.2209 + /*
1.2210 + * The writer thread is blocked waiting for a write to complete
1.2211 + * and the channel is in non-blocking mode.
1.2212 + */
1.2213 +
1.2214 + errno = EAGAIN;
1.2215 + goto error;
1.2216 + }
1.2217 +
1.2218 + /*
1.2219 + * Check for a background error on the last write.
1.2220 + */
1.2221 +
1.2222 + if (infoPtr->writeError) {
1.2223 + TclWinConvertError(infoPtr->writeError);
1.2224 + infoPtr->writeError = 0;
1.2225 + goto error;
1.2226 + }
1.2227 +
1.2228 + if (infoPtr->flags & PIPE_ASYNC) {
1.2229 + /*
1.2230 + * The pipe is non-blocking, so copy the data into the output
1.2231 + * buffer and restart the writer thread.
1.2232 + */
1.2233 +
1.2234 + if (toWrite > infoPtr->writeBufLen) {
1.2235 + /*
1.2236 + * Reallocate the buffer to be large enough to hold the data.
1.2237 + */
1.2238 +
1.2239 + if (infoPtr->writeBuf) {
1.2240 + ckfree(infoPtr->writeBuf);
1.2241 + }
1.2242 + infoPtr->writeBufLen = toWrite;
1.2243 + infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
1.2244 + }
1.2245 + memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
1.2246 + infoPtr->toWrite = toWrite;
1.2247 + ResetEvent(infoPtr->writable);
1.2248 + SetEvent(infoPtr->startWriter);
1.2249 + bytesWritten = toWrite;
1.2250 + } else {
1.2251 + /*
1.2252 + * In the blocking case, just try to write the buffer directly.
1.2253 + * This avoids an unnecessary copy.
1.2254 + */
1.2255 +
1.2256 + if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,
1.2257 + &bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
1.2258 + TclWinConvertError(GetLastError());
1.2259 + goto error;
1.2260 + }
1.2261 + }
1.2262 + return bytesWritten;
1.2263 +
1.2264 + error:
1.2265 + *errorCode = errno;
1.2266 + return -1;
1.2267 +
1.2268 +}
1.2269 +
1.2270 +/*
1.2271 + *----------------------------------------------------------------------
1.2272 + *
1.2273 + * PipeEventProc --
1.2274 + *
1.2275 + * This function is invoked by Tcl_ServiceEvent when a file event
1.2276 + * reaches the front of the event queue. This procedure invokes
1.2277 + * Tcl_NotifyChannel on the pipe.
1.2278 + *
1.2279 + * Results:
1.2280 + * Returns 1 if the event was handled, meaning it should be removed
1.2281 + * from the queue. Returns 0 if the event was not handled, meaning
1.2282 + * it should stay on the queue. The only time the event isn't
1.2283 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.2284 + *
1.2285 + * Side effects:
1.2286 + * Whatever the notifier callback does.
1.2287 + *
1.2288 + *----------------------------------------------------------------------
1.2289 + */
1.2290 +
1.2291 +static int
1.2292 +PipeEventProc(
1.2293 + Tcl_Event *evPtr, /* Event to service. */
1.2294 + int flags) /* Flags that indicate what events to
1.2295 + * handle, such as TCL_FILE_EVENTS. */
1.2296 +{
1.2297 + PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;
1.2298 + PipeInfo *infoPtr;
1.2299 + WinFile *filePtr;
1.2300 + int mask;
1.2301 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.2302 +
1.2303 + if (!(flags & TCL_FILE_EVENTS)) {
1.2304 + return 0;
1.2305 + }
1.2306 +
1.2307 + /*
1.2308 + * Search through the list of watched pipes for the one whose handle
1.2309 + * matches the event. We do this rather than simply dereferencing
1.2310 + * the handle in the event so that pipes can be deleted while the
1.2311 + * event is in the queue.
1.2312 + */
1.2313 +
1.2314 + for (infoPtr = tsdPtr->firstPipePtr; infoPtr != NULL;
1.2315 + infoPtr = infoPtr->nextPtr) {
1.2316 + if (pipeEvPtr->infoPtr == infoPtr) {
1.2317 + infoPtr->flags &= ~(PIPE_PENDING);
1.2318 + break;
1.2319 + }
1.2320 + }
1.2321 +
1.2322 + /*
1.2323 + * Remove stale events.
1.2324 + */
1.2325 +
1.2326 + if (!infoPtr) {
1.2327 + return 1;
1.2328 + }
1.2329 +
1.2330 + /*
1.2331 + * Check to see if the pipe is readable. Note
1.2332 + * that we can't tell if a pipe is writable, so we always report it
1.2333 + * as being writable unless we have detected EOF.
1.2334 + */
1.2335 +
1.2336 + filePtr = (WinFile*) ((PipeInfo*)infoPtr)->writeFile;
1.2337 + mask = 0;
1.2338 + if ((infoPtr->watchMask & TCL_WRITABLE) &&
1.2339 + (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT)) {
1.2340 + mask = TCL_WRITABLE;
1.2341 + }
1.2342 +
1.2343 + filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile;
1.2344 + if ((infoPtr->watchMask & TCL_READABLE) &&
1.2345 + (WaitForRead(infoPtr, 0) >= 0)) {
1.2346 + if (infoPtr->readFlags & PIPE_EOF) {
1.2347 + mask = TCL_READABLE;
1.2348 + } else {
1.2349 + mask |= TCL_READABLE;
1.2350 + }
1.2351 + }
1.2352 +
1.2353 + /*
1.2354 + * Inform the channel of the events.
1.2355 + */
1.2356 +
1.2357 + Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
1.2358 + return 1;
1.2359 +}
1.2360 +
1.2361 +/*
1.2362 + *----------------------------------------------------------------------
1.2363 + *
1.2364 + * PipeWatchProc --
1.2365 + *
1.2366 + * Called by the notifier to set up to watch for events on this
1.2367 + * channel.
1.2368 + *
1.2369 + * Results:
1.2370 + * None.
1.2371 + *
1.2372 + * Side effects:
1.2373 + * None.
1.2374 + *
1.2375 + *----------------------------------------------------------------------
1.2376 + */
1.2377 +
1.2378 +static void
1.2379 +PipeWatchProc(
1.2380 + ClientData instanceData, /* Pipe state. */
1.2381 + int mask) /* What events to watch for, OR-ed
1.2382 + * combination of TCL_READABLE,
1.2383 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.2384 +{
1.2385 + PipeInfo **nextPtrPtr, *ptr;
1.2386 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.2387 + int oldMask = infoPtr->watchMask;
1.2388 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.2389 +
1.2390 + /*
1.2391 + * Since most of the work is handled by the background threads,
1.2392 + * we just need to update the watchMask and then force the notifier
1.2393 + * to poll once.
1.2394 + */
1.2395 +
1.2396 + infoPtr->watchMask = mask & infoPtr->validMask;
1.2397 + if (infoPtr->watchMask) {
1.2398 + Tcl_Time blockTime = { 0, 0 };
1.2399 + if (!oldMask) {
1.2400 + infoPtr->nextPtr = tsdPtr->firstPipePtr;
1.2401 + tsdPtr->firstPipePtr = infoPtr;
1.2402 + }
1.2403 + Tcl_SetMaxBlockTime(&blockTime);
1.2404 + } else {
1.2405 + if (oldMask) {
1.2406 + /*
1.2407 + * Remove the pipe from the list of watched pipes.
1.2408 + */
1.2409 +
1.2410 + for (nextPtrPtr = &(tsdPtr->firstPipePtr), ptr = *nextPtrPtr;
1.2411 + ptr != NULL;
1.2412 + nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
1.2413 + if (infoPtr == ptr) {
1.2414 + *nextPtrPtr = ptr->nextPtr;
1.2415 + break;
1.2416 + }
1.2417 + }
1.2418 + }
1.2419 + }
1.2420 +}
1.2421 +
1.2422 +/*
1.2423 + *----------------------------------------------------------------------
1.2424 + *
1.2425 + * PipeGetHandleProc --
1.2426 + *
1.2427 + * Called from Tcl_GetChannelHandle to retrieve OS handles from
1.2428 + * inside a command pipeline based channel.
1.2429 + *
1.2430 + * Results:
1.2431 + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1.2432 + * there is no handle for the specified direction.
1.2433 + *
1.2434 + * Side effects:
1.2435 + * None.
1.2436 + *
1.2437 + *----------------------------------------------------------------------
1.2438 + */
1.2439 +
1.2440 +static int
1.2441 +PipeGetHandleProc(
1.2442 + ClientData instanceData, /* The pipe state. */
1.2443 + int direction, /* TCL_READABLE or TCL_WRITABLE */
1.2444 + ClientData *handlePtr) /* Where to store the handle. */
1.2445 +{
1.2446 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.2447 + WinFile *filePtr;
1.2448 +
1.2449 + if (direction == TCL_READABLE && infoPtr->readFile) {
1.2450 + filePtr = (WinFile*) infoPtr->readFile;
1.2451 + *handlePtr = (ClientData) filePtr->handle;
1.2452 + return TCL_OK;
1.2453 + }
1.2454 + if (direction == TCL_WRITABLE && infoPtr->writeFile) {
1.2455 + filePtr = (WinFile*) infoPtr->writeFile;
1.2456 + *handlePtr = (ClientData) filePtr->handle;
1.2457 + return TCL_OK;
1.2458 + }
1.2459 + return TCL_ERROR;
1.2460 +}
1.2461 +
1.2462 +/*
1.2463 + *----------------------------------------------------------------------
1.2464 + *
1.2465 + * Tcl_WaitPid --
1.2466 + *
1.2467 + * Emulates the waitpid system call.
1.2468 + *
1.2469 + * Results:
1.2470 + * Returns 0 if the process is still alive, -1 on an error, or
1.2471 + * the pid on a clean close.
1.2472 + *
1.2473 + * Side effects:
1.2474 + * Unless WNOHANG is set and the wait times out, the process
1.2475 + * information record will be deleted and the process handle
1.2476 + * will be closed.
1.2477 + *
1.2478 + *----------------------------------------------------------------------
1.2479 + */
1.2480 +
1.2481 +Tcl_Pid
1.2482 +Tcl_WaitPid(
1.2483 + Tcl_Pid pid,
1.2484 + int *statPtr,
1.2485 + int options)
1.2486 +{
1.2487 + ProcInfo *infoPtr = NULL, **prevPtrPtr;
1.2488 + DWORD flags;
1.2489 + Tcl_Pid result;
1.2490 + DWORD ret, exitCode;
1.2491 +
1.2492 + PipeInit();
1.2493 +
1.2494 + /*
1.2495 + * If no pid is specified, do nothing.
1.2496 + */
1.2497 +
1.2498 + if (pid == 0) {
1.2499 + *statPtr = 0;
1.2500 + return 0;
1.2501 + }
1.2502 +
1.2503 + /*
1.2504 + * Find the process and cut it from the process list.
1.2505 + * SF Tcl Bug 859820, Backport of its fix.
1.2506 + * SF Tcl Bug 1381436, asking for the backport.
1.2507 + *
1.2508 + * [x] Cutting the infoPtr after the closehandle allows the
1.2509 + * pointer to become stale. We do it here, and compensate if the
1.2510 + * process was not done yet.
1.2511 + */
1.2512 +
1.2513 + Tcl_MutexLock(&pipeMutex);
1.2514 + prevPtrPtr = &procList;
1.2515 + for (infoPtr = procList; infoPtr != NULL;
1.2516 + prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
1.2517 + if (infoPtr->hProcess == (HANDLE) pid) {
1.2518 + *prevPtrPtr = infoPtr->nextPtr;
1.2519 + break;
1.2520 + }
1.2521 + }
1.2522 + Tcl_MutexUnlock(&pipeMutex);
1.2523 +
1.2524 + /*
1.2525 + * If the pid is not one of the processes we know about (we started it)
1.2526 + * then do nothing.
1.2527 + */
1.2528 +
1.2529 + if (infoPtr == NULL) {
1.2530 + *statPtr = 0;
1.2531 + return 0;
1.2532 + }
1.2533 +
1.2534 + /*
1.2535 + * Officially "wait" for it to finish. We either poll (WNOHANG) or
1.2536 + * wait for an infinite amount of time.
1.2537 + */
1.2538 +
1.2539 + if (options & WNOHANG) {
1.2540 + flags = 0;
1.2541 + } else {
1.2542 + flags = INFINITE;
1.2543 + }
1.2544 + ret = WaitForSingleObject(infoPtr->hProcess, flags);
1.2545 + if (ret == WAIT_TIMEOUT) {
1.2546 + *statPtr = 0;
1.2547 + if (options & WNOHANG) {
1.2548 + /*
1.2549 + * Re-insert the cut infoPtr back on the list.
1.2550 + * See [x] for explanation.
1.2551 + */
1.2552 + Tcl_MutexLock(&pipeMutex);
1.2553 + infoPtr->nextPtr = procList;
1.2554 + procList = infoPtr;
1.2555 + Tcl_MutexUnlock(&pipeMutex);
1.2556 + return 0;
1.2557 + } else {
1.2558 + result = 0;
1.2559 + }
1.2560 + } else if (ret == WAIT_OBJECT_0) {
1.2561 + GetExitCodeProcess(infoPtr->hProcess, &exitCode);
1.2562 + if (exitCode & 0xC0000000) {
1.2563 + /*
1.2564 + * A fatal exception occured.
1.2565 + */
1.2566 + switch (exitCode) {
1.2567 + case EXCEPTION_FLT_DENORMAL_OPERAND:
1.2568 + case EXCEPTION_FLT_DIVIDE_BY_ZERO:
1.2569 + case EXCEPTION_FLT_INEXACT_RESULT:
1.2570 + case EXCEPTION_FLT_INVALID_OPERATION:
1.2571 + case EXCEPTION_FLT_OVERFLOW:
1.2572 + case EXCEPTION_FLT_STACK_CHECK:
1.2573 + case EXCEPTION_FLT_UNDERFLOW:
1.2574 + case EXCEPTION_INT_DIVIDE_BY_ZERO:
1.2575 + case EXCEPTION_INT_OVERFLOW:
1.2576 + *statPtr = 0xC0000000 | SIGFPE;
1.2577 + break;
1.2578 +
1.2579 + case EXCEPTION_PRIV_INSTRUCTION:
1.2580 + case EXCEPTION_ILLEGAL_INSTRUCTION:
1.2581 + *statPtr = 0xC0000000 | SIGILL;
1.2582 + break;
1.2583 +
1.2584 + case EXCEPTION_ACCESS_VIOLATION:
1.2585 + case EXCEPTION_DATATYPE_MISALIGNMENT:
1.2586 + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
1.2587 + case EXCEPTION_STACK_OVERFLOW:
1.2588 + case EXCEPTION_NONCONTINUABLE_EXCEPTION:
1.2589 + case EXCEPTION_INVALID_DISPOSITION:
1.2590 + case EXCEPTION_GUARD_PAGE:
1.2591 + case EXCEPTION_INVALID_HANDLE:
1.2592 + *statPtr = 0xC0000000 | SIGSEGV;
1.2593 + break;
1.2594 +
1.2595 + case CONTROL_C_EXIT:
1.2596 + *statPtr = 0xC0000000 | SIGINT;
1.2597 + break;
1.2598 +
1.2599 + default:
1.2600 + *statPtr = 0xC0000000 | SIGABRT;
1.2601 + break;
1.2602 + }
1.2603 + } else {
1.2604 + *statPtr = exitCode;
1.2605 + }
1.2606 + result = pid;
1.2607 + } else {
1.2608 + errno = ECHILD;
1.2609 + *statPtr = 0xC0000000 | ECHILD;
1.2610 + result = (Tcl_Pid) -1;
1.2611 + }
1.2612 +
1.2613 + /*
1.2614 + * Officially close the process handle.
1.2615 + */
1.2616 +
1.2617 + CloseHandle(infoPtr->hProcess);
1.2618 + ckfree((char*)infoPtr);
1.2619 +
1.2620 + return result;
1.2621 +}
1.2622 +
1.2623 +/*
1.2624 + *----------------------------------------------------------------------
1.2625 + *
1.2626 + * TclWinAddProcess --
1.2627 + *
1.2628 + * Add a process to the process list so that we can use
1.2629 + * Tcl_WaitPid on the process.
1.2630 + *
1.2631 + * Results:
1.2632 + * None
1.2633 + *
1.2634 + * Side effects:
1.2635 + * Adds the specified process handle to the process list so
1.2636 + * Tcl_WaitPid knows about it.
1.2637 + *
1.2638 + *----------------------------------------------------------------------
1.2639 + */
1.2640 +
1.2641 +void
1.2642 +TclWinAddProcess(hProcess, id)
1.2643 + HANDLE hProcess; /* Handle to process */
1.2644 + DWORD id; /* Global process identifier */
1.2645 +{
1.2646 + ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
1.2647 +
1.2648 + PipeInit();
1.2649 +
1.2650 + procPtr->hProcess = hProcess;
1.2651 + procPtr->dwProcessId = id;
1.2652 + Tcl_MutexLock(&pipeMutex);
1.2653 + procPtr->nextPtr = procList;
1.2654 + procList = procPtr;
1.2655 + Tcl_MutexUnlock(&pipeMutex);
1.2656 +}
1.2657 +
1.2658 +/*
1.2659 + *----------------------------------------------------------------------
1.2660 + *
1.2661 + * Tcl_PidObjCmd --
1.2662 + *
1.2663 + * This procedure is invoked to process the "pid" Tcl command.
1.2664 + * See the user documentation for details on what it does.
1.2665 + *
1.2666 + * Results:
1.2667 + * A standard Tcl result.
1.2668 + *
1.2669 + * Side effects:
1.2670 + * See the user documentation.
1.2671 + *
1.2672 + *----------------------------------------------------------------------
1.2673 + */
1.2674 +
1.2675 + /* ARGSUSED */
1.2676 +int
1.2677 +Tcl_PidObjCmd(
1.2678 + ClientData dummy, /* Not used. */
1.2679 + Tcl_Interp *interp, /* Current interpreter. */
1.2680 + int objc, /* Number of arguments. */
1.2681 + Tcl_Obj *CONST *objv) /* Argument strings. */
1.2682 +{
1.2683 + Tcl_Channel chan;
1.2684 + Tcl_ChannelType *chanTypePtr;
1.2685 + PipeInfo *pipePtr;
1.2686 + int i;
1.2687 + Tcl_Obj *resultPtr;
1.2688 + char buf[TCL_INTEGER_SPACE];
1.2689 +
1.2690 + if (objc > 2) {
1.2691 + Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
1.2692 + return TCL_ERROR;
1.2693 + }
1.2694 + if (objc == 1) {
1.2695 + resultPtr = Tcl_GetObjResult(interp);
1.2696 + wsprintfA(buf, "%lu", (unsigned long) getpid());
1.2697 + Tcl_SetStringObj(resultPtr, buf, -1);
1.2698 + } else {
1.2699 + chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
1.2700 + NULL);
1.2701 + if (chan == (Tcl_Channel) NULL) {
1.2702 + return TCL_ERROR;
1.2703 + }
1.2704 + chanTypePtr = Tcl_GetChannelType(chan);
1.2705 + if (chanTypePtr != &pipeChannelType) {
1.2706 + return TCL_OK;
1.2707 + }
1.2708 +
1.2709 + pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
1.2710 + resultPtr = Tcl_GetObjResult(interp);
1.2711 + for (i = 0; i < pipePtr->numPids; i++) {
1.2712 + wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
1.2713 + Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr,
1.2714 + Tcl_NewStringObj(buf, -1));
1.2715 + }
1.2716 + }
1.2717 + return TCL_OK;
1.2718 +}
1.2719 +
1.2720 +/*
1.2721 + *----------------------------------------------------------------------
1.2722 + *
1.2723 + * WaitForRead --
1.2724 + *
1.2725 + * Wait until some data is available, the pipe is at
1.2726 + * EOF or the reader thread is blocked waiting for data (if the
1.2727 + * channel is in non-blocking mode).
1.2728 + *
1.2729 + * Results:
1.2730 + * Returns 1 if pipe is readable. Returns 0 if there is no data
1.2731 + * on the pipe, but there is buffered data. Returns -1 if an
1.2732 + * error occurred. If an error occurred, the threads may not
1.2733 + * be synchronized.
1.2734 + *
1.2735 + * Side effects:
1.2736 + * Updates the shared state flags and may consume 1 byte of data
1.2737 + * from the pipe. If no error occurred, the reader thread is
1.2738 + * blocked waiting for a signal from the main thread.
1.2739 + *
1.2740 + *----------------------------------------------------------------------
1.2741 + */
1.2742 +
1.2743 +static int
1.2744 +WaitForRead(
1.2745 + PipeInfo *infoPtr, /* Pipe state. */
1.2746 + int blocking) /* Indicates whether call should be
1.2747 + * blocking or not. */
1.2748 +{
1.2749 + DWORD timeout, count;
1.2750 + HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
1.2751 +
1.2752 + while (1) {
1.2753 + /*
1.2754 + * Synchronize with the reader thread.
1.2755 + */
1.2756 +
1.2757 + timeout = blocking ? INFINITE : 0;
1.2758 + if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
1.2759 + /*
1.2760 + * The reader thread is blocked waiting for data and the channel
1.2761 + * is in non-blocking mode.
1.2762 + */
1.2763 +
1.2764 + errno = EAGAIN;
1.2765 + return -1;
1.2766 + }
1.2767 +
1.2768 + /*
1.2769 + * At this point, the two threads are synchronized, so it is safe
1.2770 + * to access shared state.
1.2771 + */
1.2772 +
1.2773 +
1.2774 + /*
1.2775 + * If the pipe has hit EOF, it is always readable.
1.2776 + */
1.2777 +
1.2778 + if (infoPtr->readFlags & PIPE_EOF) {
1.2779 + return 1;
1.2780 + }
1.2781 +
1.2782 + /*
1.2783 + * Check to see if there is any data sitting in the pipe.
1.2784 + */
1.2785 +
1.2786 + if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0,
1.2787 + (LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) {
1.2788 + TclWinConvertError(GetLastError());
1.2789 + /*
1.2790 + * Check to see if the peek failed because of EOF.
1.2791 + */
1.2792 +
1.2793 + if (errno == EPIPE) {
1.2794 + infoPtr->readFlags |= PIPE_EOF;
1.2795 + return 1;
1.2796 + }
1.2797 +
1.2798 + /*
1.2799 + * Ignore errors if there is data in the buffer.
1.2800 + */
1.2801 +
1.2802 + if (infoPtr->readFlags & PIPE_EXTRABYTE) {
1.2803 + return 0;
1.2804 + } else {
1.2805 + return -1;
1.2806 + }
1.2807 + }
1.2808 +
1.2809 + /*
1.2810 + * We found some data in the pipe, so it must be readable.
1.2811 + */
1.2812 +
1.2813 + if (count > 0) {
1.2814 + return 1;
1.2815 + }
1.2816 +
1.2817 + /*
1.2818 + * The pipe isn't readable, but there is some data sitting
1.2819 + * in the buffer, so return immediately.
1.2820 + */
1.2821 +
1.2822 + if (infoPtr->readFlags & PIPE_EXTRABYTE) {
1.2823 + return 0;
1.2824 + }
1.2825 +
1.2826 + /*
1.2827 + * There wasn't any data available, so reset the thread and
1.2828 + * try again.
1.2829 + */
1.2830 +
1.2831 + ResetEvent(infoPtr->readable);
1.2832 + SetEvent(infoPtr->startReader);
1.2833 + }
1.2834 +}
1.2835 +
1.2836 +/*
1.2837 + *----------------------------------------------------------------------
1.2838 + *
1.2839 + * PipeReaderThread --
1.2840 + *
1.2841 + * This function runs in a separate thread and waits for input
1.2842 + * to become available on a pipe.
1.2843 + *
1.2844 + * Results:
1.2845 + * None.
1.2846 + *
1.2847 + * Side effects:
1.2848 + * Signals the main thread when input become available. May
1.2849 + * cause the main thread to wake up by posting a message. May
1.2850 + * consume one byte from the pipe for each wait operation. Will
1.2851 + * cause a memory leak of ~4k, if forcefully terminated with
1.2852 + * TerminateThread().
1.2853 + *
1.2854 + *----------------------------------------------------------------------
1.2855 + */
1.2856 +
1.2857 +static DWORD WINAPI
1.2858 +PipeReaderThread(LPVOID arg)
1.2859 +{
1.2860 + PipeInfo *infoPtr = (PipeInfo *)arg;
1.2861 + HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
1.2862 + DWORD count, err;
1.2863 + int done = 0;
1.2864 + HANDLE wEvents[2];
1.2865 + DWORD waitResult;
1.2866 +
1.2867 + wEvents[0] = infoPtr->stopReader;
1.2868 + wEvents[1] = infoPtr->startReader;
1.2869 +
1.2870 + while (!done) {
1.2871 + /*
1.2872 + * Wait for the main thread to signal before attempting to wait
1.2873 + * on the pipe becoming readable.
1.2874 + */
1.2875 +
1.2876 + waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1.2877 +
1.2878 + if (waitResult != (WAIT_OBJECT_0 + 1)) {
1.2879 + /*
1.2880 + * The start event was not signaled. It might be the stop event
1.2881 + * or an error, so exit.
1.2882 + */
1.2883 +
1.2884 + break;
1.2885 + }
1.2886 +
1.2887 + /*
1.2888 + * Try waiting for 0 bytes. This will block until some data is
1.2889 + * available on NT, but will return immediately on Win 95. So,
1.2890 + * if no data is available after the first read, we block until
1.2891 + * we can read a single byte off of the pipe.
1.2892 + */
1.2893 +
1.2894 + if ((ReadFile(handle, NULL, 0, &count, NULL) == FALSE)
1.2895 + || (PeekNamedPipe(handle, NULL, 0, NULL, &count,
1.2896 + NULL) == FALSE)) {
1.2897 + /*
1.2898 + * The error is a result of an EOF condition, so set the
1.2899 + * EOF bit before signalling the main thread.
1.2900 + */
1.2901 +
1.2902 + err = GetLastError();
1.2903 + if (err == ERROR_BROKEN_PIPE) {
1.2904 + infoPtr->readFlags |= PIPE_EOF;
1.2905 + done = 1;
1.2906 + } else if (err == ERROR_INVALID_HANDLE) {
1.2907 + break;
1.2908 + }
1.2909 + } else if (count == 0) {
1.2910 + if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
1.2911 + != FALSE) {
1.2912 + /*
1.2913 + * One byte was consumed as a side effect of waiting
1.2914 + * for the pipe to become readable.
1.2915 + */
1.2916 +
1.2917 + infoPtr->readFlags |= PIPE_EXTRABYTE;
1.2918 + } else {
1.2919 + err = GetLastError();
1.2920 + if (err == ERROR_BROKEN_PIPE) {
1.2921 + /*
1.2922 + * The error is a result of an EOF condition, so set the
1.2923 + * EOF bit before signalling the main thread.
1.2924 + */
1.2925 +
1.2926 + infoPtr->readFlags |= PIPE_EOF;
1.2927 + done = 1;
1.2928 + } else if (err == ERROR_INVALID_HANDLE) {
1.2929 + break;
1.2930 + }
1.2931 + }
1.2932 + }
1.2933 +
1.2934 +
1.2935 + /*
1.2936 + * Signal the main thread by signalling the readable event and
1.2937 + * then waking up the notifier thread.
1.2938 + */
1.2939 +
1.2940 + SetEvent(infoPtr->readable);
1.2941 +
1.2942 + /*
1.2943 + * Alert the foreground thread. Note that we need to treat this like
1.2944 + * a critical section so the foreground thread does not terminate
1.2945 + * this thread while we are holding a mutex in the notifier code.
1.2946 + */
1.2947 +
1.2948 + Tcl_MutexLock(&pipeMutex);
1.2949 + if (infoPtr->threadId != NULL) {
1.2950 + /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1.2951 + Tcl_ThreadAlert(infoPtr->threadId);
1.2952 + }
1.2953 + Tcl_MutexUnlock(&pipeMutex);
1.2954 + }
1.2955 +
1.2956 + return 0;
1.2957 +}
1.2958 +
1.2959 +/*
1.2960 + *----------------------------------------------------------------------
1.2961 + *
1.2962 + * PipeWriterThread --
1.2963 + *
1.2964 + * This function runs in a separate thread and writes data
1.2965 + * onto a pipe.
1.2966 + *
1.2967 + * Results:
1.2968 + * Always returns 0.
1.2969 + *
1.2970 + * Side effects:
1.2971 + * Signals the main thread when an output operation is completed.
1.2972 + * May cause the main thread to wake up by posting a message.
1.2973 + *
1.2974 + *----------------------------------------------------------------------
1.2975 + */
1.2976 +
1.2977 +static DWORD WINAPI
1.2978 +PipeWriterThread(LPVOID arg)
1.2979 +{
1.2980 +
1.2981 + PipeInfo *infoPtr = (PipeInfo *)arg;
1.2982 + HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle;
1.2983 + DWORD count, toWrite;
1.2984 + char *buf;
1.2985 + int done = 0;
1.2986 + HANDLE wEvents[2];
1.2987 + DWORD waitResult;
1.2988 +
1.2989 + wEvents[0] = infoPtr->stopWriter;
1.2990 + wEvents[1] = infoPtr->startWriter;
1.2991 +
1.2992 + while (!done) {
1.2993 + /*
1.2994 + * Wait for the main thread to signal before attempting to write.
1.2995 + */
1.2996 +
1.2997 + waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
1.2998 +
1.2999 + if (waitResult != (WAIT_OBJECT_0 + 1)) {
1.3000 + /*
1.3001 + * The start event was not signaled. It might be the stop event
1.3002 + * or an error, so exit.
1.3003 + */
1.3004 +
1.3005 + break;
1.3006 + }
1.3007 +
1.3008 + buf = infoPtr->writeBuf;
1.3009 + toWrite = infoPtr->toWrite;
1.3010 +
1.3011 + /*
1.3012 + * Loop until all of the bytes are written or an error occurs.
1.3013 + */
1.3014 +
1.3015 + while (toWrite > 0) {
1.3016 + if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
1.3017 + infoPtr->writeError = GetLastError();
1.3018 + done = 1;
1.3019 + break;
1.3020 + } else {
1.3021 + toWrite -= count;
1.3022 + buf += count;
1.3023 + }
1.3024 + }
1.3025 +
1.3026 + /*
1.3027 + * Signal the main thread by signalling the writable event and
1.3028 + * then waking up the notifier thread.
1.3029 + */
1.3030 +
1.3031 + SetEvent(infoPtr->writable);
1.3032 +
1.3033 + /*
1.3034 + * Alert the foreground thread. Note that we need to treat this like
1.3035 + * a critical section so the foreground thread does not terminate
1.3036 + * this thread while we are holding a mutex in the notifier code.
1.3037 + */
1.3038 +
1.3039 + Tcl_MutexLock(&pipeMutex);
1.3040 + if (infoPtr->threadId != NULL) {
1.3041 + /* TIP #218. When in flight ignore the event, no one will receive it anyway */
1.3042 + Tcl_ThreadAlert(infoPtr->threadId);
1.3043 + }
1.3044 + Tcl_MutexUnlock(&pipeMutex);
1.3045 + }
1.3046 +
1.3047 + return 0;
1.3048 +}
1.3049 +
1.3050 +/*
1.3051 + *----------------------------------------------------------------------
1.3052 + *
1.3053 + * PipeThreadActionProc --
1.3054 + *
1.3055 + * Insert or remove any thread local refs to this channel.
1.3056 + *
1.3057 + * Results:
1.3058 + * None.
1.3059 + *
1.3060 + * Side effects:
1.3061 + * Changes thread local list of valid channels.
1.3062 + *
1.3063 + *----------------------------------------------------------------------
1.3064 + */
1.3065 +
1.3066 +static void
1.3067 +PipeThreadActionProc (instanceData, action)
1.3068 + ClientData instanceData;
1.3069 + int action;
1.3070 +{
1.3071 + PipeInfo *infoPtr = (PipeInfo *) instanceData;
1.3072 +
1.3073 + /* We do not access firstPipePtr in the thread structures. This is
1.3074 + * not for all pipes managed by the thread, but only those we are
1.3075 + * watching. Removal of the filevent handlers before transfer thus
1.3076 + * takes care of this structure.
1.3077 + */
1.3078 +
1.3079 + Tcl_MutexLock(&pipeMutex);
1.3080 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.3081 + /* We can't copy the thread information from the channel when
1.3082 + * the channel is created. At this time the channel back
1.3083 + * pointer has not been set yet. However in that case the
1.3084 + * threadId has already been set by TclpCreateCommandChannel
1.3085 + * itself, so the structure is still good.
1.3086 + */
1.3087 +
1.3088 + PipeInit ();
1.3089 + if (infoPtr->channel != NULL) {
1.3090 + infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
1.3091 + }
1.3092 + } else {
1.3093 + infoPtr->threadId = NULL;
1.3094 + }
1.3095 + Tcl_MutexUnlock(&pipeMutex);
1.3096 +}