diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c	Fri Jun 15 03:10:57 2012 +0200
@@ -0,0 +1,1421 @@
+/* 
+ * tclWinConsole.c --
+ *
+ *	This file implements the Windows-specific console functions,
+ *	and the "console" channel driver.
+ *
+ * Copyright (c) 1999 by Scriptics Corp.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
+ */
+
+#include "tclWinInt.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+
+/*
+ * The following variable is used to tell whether this module has been
+ * initialized.
+ */
+
+static int initialized = 0;
+
+/*
+ * The consoleMutex locks around access to the initialized variable, and it is
+ * used to protect background threads from being terminated while they are
+ * using APIs that hold locks.
+ */
+
+TCL_DECLARE_MUTEX(consoleMutex)
+
+/*
+ * Bit masks used in the flags field of the ConsoleInfo structure below.
+ */
+
+#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
+#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
+
+/*
+ * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
+ */
+
+#define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */
+#define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader
+				     thread */
+
+#define CONSOLE_BUFFER_SIZE (8*1024)
+/*
+ * This structure describes per-instance data for a console based channel.
+ */
+
+typedef struct ConsoleInfo {
+    HANDLE handle;
+    int type;
+    struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
+    Tcl_Channel channel;	/* Pointer to channel structure. */
+    int validMask;		/* OR'ed combination of TCL_READABLE,
+				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
+				 * which operations are valid on the file. */
+    int watchMask;		/* OR'ed combination of TCL_READABLE,
+				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
+				 * which events should be reported. */
+    int flags;			/* State flags, see above for a list. */
+    Tcl_ThreadId threadId;	/* Thread to which events should be reported.
+				 * This value is used by the reader/writer
+				 * threads. */
+    HANDLE writeThread;		/* Handle to writer thread. */
+    HANDLE readThread;		/* Handle to reader thread. */
+    HANDLE writable;		/* Manual-reset event to signal when the
+				 * writer thread has finished waiting for
+				 * the current buffer to be written. */
+    HANDLE readable;		/* Manual-reset event to signal when the
+				 * reader thread has finished waiting for
+				 * input. */
+    HANDLE startWriter;		/* Auto-reset event used by the main thread to
+				 * signal when the writer thread should attempt
+				 * to write to the console. */
+    HANDLE stopWriter;		/* Auto-reset event used by the main thread to
+				 * signal when the writer thread should exit.
+				 */
+    HANDLE startReader;		/* Auto-reset event used by the main thread to
+				 * signal when the reader thread should attempt
+				 * to read from the console. */
+    HANDLE stopReader;		/* Auto-reset event used by the main thread to
+				 * signal when the reader thread should exit.
+				 */
+    DWORD writeError;		/* An error caused by the last background
+				 * write.  Set to 0 if no error has been
+				 * detected.  This word is shared with the
+				 * writer thread so access must be
+				 * synchronized with the writable object.
+				 */
+    char *writeBuf;		/* Current background output buffer.
+				 * Access is synchronized with the writable
+				 * object. */
+    int writeBufLen;		/* Size of write buffer.  Access is
+				 * synchronized with the writable
+				 * object. */
+    int toWrite;		/* Current amount to be written.  Access is
+				 * synchronized with the writable object. */
+    int readFlags;		/* Flags that are shared with the reader
+				 * thread.  Access is synchronized with the
+				 * readable object.  */
+    int bytesRead;              /* number of bytes in the buffer */
+    int offset;                 /* number of bytes read out of the buffer */
+    char buffer[CONSOLE_BUFFER_SIZE];
+                                /* Data consumed by reader thread. */
+} ConsoleInfo;
+
+typedef struct ThreadSpecificData {
+    /*
+     * The following pointer refers to the head of the list of consoles
+     * that are being watched for file events.
+     */
+    
+    ConsoleInfo *firstConsolePtr;
+} ThreadSpecificData;
+
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * The following structure is what is added to the Tcl event queue when
+ * console events are generated.
+ */
+
+typedef struct ConsoleEvent {
+    Tcl_Event header;		/* Information that is standard for
+				 * all events. */
+    ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note
+				 * that we still have to verify that the
+				 * console exists before dereferencing this
+				 * pointer. */
+} ConsoleEvent;
+
+/*
+ * Declarations for functions used only in this file.
+ */
+
+static int		ConsoleBlockModeProc(ClientData instanceData, int mode);
+static void		ConsoleCheckProc(ClientData clientData, int flags);
+static int		ConsoleCloseProc(ClientData instanceData,
+			    Tcl_Interp *interp);
+static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);
+static void		ConsoleExitHandler(ClientData clientData);
+static int		ConsoleGetHandleProc(ClientData instanceData,
+			    int direction, ClientData *handlePtr);
+static void             ConsoleInit(void);
+static int		ConsoleInputProc(ClientData instanceData, char *buf,
+			    int toRead, int *errorCode);
+static int		ConsoleOutputProc(ClientData instanceData,
+			    CONST char *buf, int toWrite, int *errorCode);
+static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
+static void		ConsoleSetupProc(ClientData clientData, int flags);
+static void		ConsoleWatchProc(ClientData instanceData, int mask);
+static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
+static void		ProcExitHandler(ClientData clientData);
+static int		WaitForRead(ConsoleInfo *infoPtr, int blocking);
+
+static void             ConsoleThreadActionProc _ANSI_ARGS_ ((
+			   ClientData instanceData, int action));
+
+/*
+ * This structure describes the channel type structure for command console
+ * based IO.
+ */
+
+static Tcl_ChannelType consoleChannelType = {
+    "console",			/* Type name. */
+    TCL_CHANNEL_VERSION_4,	/* v4 channel */
+    ConsoleCloseProc,		/* Close proc. */
+    ConsoleInputProc,		/* Input proc. */
+    ConsoleOutputProc,		/* Output proc. */
+    NULL,			/* Seek proc. */
+    NULL,			/* Set option proc. */
+    NULL,			/* Get option proc. */
+    ConsoleWatchProc,		/* Set up notifier to watch the channel. */
+    ConsoleGetHandleProc,	/* Get an OS handle from channel. */
+    NULL,			/* close2proc. */
+    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/
+    NULL,			/* flush proc. */
+    NULL,			/* handler proc. */
+    NULL,                       /* wide seek proc */
+    ConsoleThreadActionProc,    /* thread action proc */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleInit --
+ *
+ *	This function initializes the static variables for this file.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Creates a new event source.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleInit()
+{
+    ThreadSpecificData *tsdPtr;
+
+    /*
+     * Check the initialized flag first, then check again in the mutex.
+     * This is a speed enhancement.
+     */
+
+    if (!initialized) {
+	Tcl_MutexLock(&consoleMutex);
+	if (!initialized) {
+	    initialized = 1;
+	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
+	}
+	Tcl_MutexUnlock(&consoleMutex);
+    }
+
+    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
+    if (tsdPtr == NULL) {
+	tsdPtr = TCL_TSD_INIT(&dataKey);
+	tsdPtr->firstConsolePtr = NULL;
+	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
+	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleExitHandler --
+ *
+ *	This function is called to cleanup the console module before
+ *	Tcl is unloaded.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Removes the console event source.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleExitHandler(
+    ClientData clientData)	/* Old window proc */
+{
+    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcExitHandler --
+ *
+ *	This function is called to cleanup the process list before
+ *	Tcl is unloaded.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Resets the process list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ProcExitHandler(
+    ClientData clientData)	/* Old window proc */
+{
+    Tcl_MutexLock(&consoleMutex);
+    initialized = 0;
+    Tcl_MutexUnlock(&consoleMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleSetupProc --
+ *
+ *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
+ *	for an event.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Adjusts the block time if needed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+ConsoleSetupProc(
+    ClientData data,		/* Not used. */
+    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
+{
+    ConsoleInfo *infoPtr;
+    Tcl_Time blockTime = { 0, 0 };
+    int block = 1;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return;
+    }
+    
+    /*
+     * Look to see if any events are already pending.  If they are, poll.
+     */
+
+    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
+	    infoPtr = infoPtr->nextPtr) {
+	if (infoPtr->watchMask & TCL_WRITABLE) {
+	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
+		block = 0;
+	    }
+	}
+	if (infoPtr->watchMask & TCL_READABLE) {
+	    if (WaitForRead(infoPtr, 0) >= 0) {
+		block = 0;
+	    }
+	}
+    }
+    if (!block) {
+	Tcl_SetMaxBlockTime(&blockTime);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleCheckProc --
+ *
+ *	This procedure is called by Tcl_DoOneEvent to check the console
+ *	event source for events. 
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	May queue an event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleCheckProc(
+    ClientData data,		/* Not used. */
+    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
+{
+    ConsoleInfo *infoPtr;
+    ConsoleEvent *evPtr;
+    int needEvent;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return;
+    }
+    
+    /*
+     * Queue events for any ready consoles that don't already have events
+     * queued.
+     */
+
+    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
+	    infoPtr = infoPtr->nextPtr) {
+	if (infoPtr->flags & CONSOLE_PENDING) {
+	    continue;
+	}
+	
+	/*
+	 * Queue an event if the console is signaled for reading or writing.
+	 */
+
+	needEvent = 0;
+	if (infoPtr->watchMask & TCL_WRITABLE) {
+	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
+		needEvent = 1;
+	    }
+	}
+	
+	if (infoPtr->watchMask & TCL_READABLE) {
+	    if (WaitForRead(infoPtr, 0) >= 0) {
+		needEvent = 1;
+	    }
+	}
+
+	if (needEvent) {
+	    infoPtr->flags |= CONSOLE_PENDING;
+	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
+	    evPtr->header.proc = ConsoleEventProc;
+	    evPtr->infoPtr = infoPtr;
+	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
+	}
+    }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleBlockModeProc --
+ *
+ *	Set blocking or non-blocking mode on channel.
+ *
+ * Results:
+ *	0 if successful, errno when failed.
+ *
+ * Side effects:
+ *	Sets the device into blocking or non-blocking mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleBlockModeProc(
+    ClientData instanceData,	/* Instance data for channel. */
+    int mode)			/* TCL_MODE_BLOCKING or
+                                 * TCL_MODE_NONBLOCKING. */
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+    
+    /*
+     * Consoles on Windows can not be switched between blocking and nonblocking,
+     * hence we have to emulate the behavior. This is done in the input
+     * function by checking against a bit in the state. We set or unset the
+     * bit here to cause the input function to emulate the correct behavior.
+     */
+
+    if (mode == TCL_MODE_NONBLOCKING) {
+	infoPtr->flags |= CONSOLE_ASYNC;
+    } else {
+	infoPtr->flags &= ~(CONSOLE_ASYNC);
+    }
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleCloseProc --
+ *
+ *	Closes a console based IO channel.
+ *
+ * Results:
+ *	0 on success, errno otherwise.
+ *
+ * Side effects:
+ *	Closes the physical channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleCloseProc(
+    ClientData instanceData,	/* Pointer to ConsoleInfo structure. */
+    Tcl_Interp *interp)		/* For error reporting. */
+{
+    ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
+    int errorCode;
+    ConsoleInfo *infoPtr, **nextPtrPtr;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+    DWORD exitCode;
+
+    errorCode = 0;
+    
+    /*
+     * Clean up the background thread if necessary.  Note that this
+     * must be done before we can close the file, since the 
+     * thread may be blocking trying to read from the console.
+     */
+    
+    if (consolePtr->readThread) {
+
+	/*
+	 * The thread may already have closed on it's own.  Check it's
+	 * exit code.
+	 */
+
+	GetExitCodeThread(consolePtr->readThread, &exitCode);
+
+	if (exitCode == STILL_ACTIVE) {
+
+	    /*
+	     * Set the stop event so that if the reader thread is blocked
+	     * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
+	     * cleanly.
+	     */
+
+	    SetEvent(consolePtr->stopReader);
+
+	    /*
+	     * Wait at most 20 milliseconds for the reader thread to close.
+	     */
+
+	    if (WaitForSingleObject(consolePtr->readThread, 20)
+		    == WAIT_TIMEOUT) {
+		/*
+		 * Forcibly terminate the background thread as a last
+		 * resort.  Note that we need to guard against
+		 * terminating the thread while it is in the middle of
+		 * Tcl_ThreadAlert because it won't be able to release
+		 * the notifier lock.
+		 */
+
+		Tcl_MutexLock(&consoleMutex);
+
+		/* BUG: this leaks memory. */
+		TerminateThread(consolePtr->readThread, 0);
+		Tcl_MutexUnlock(&consoleMutex);
+	    }
+	}
+
+	CloseHandle(consolePtr->readThread);
+	CloseHandle(consolePtr->readable);
+	CloseHandle(consolePtr->startReader);
+	CloseHandle(consolePtr->stopReader);
+	consolePtr->readThread = NULL;
+    }
+    consolePtr->validMask &= ~TCL_READABLE;
+
+    /*
+     * Wait for the writer thread to finish the current buffer, then
+     * terminate the thread and close the handles.  If the channel is
+     * nonblocking, there should be no pending write operations.
+     */
+    
+    if (consolePtr->writeThread) {
+	if (consolePtr->toWrite) {
+	    /*
+	     * We only need to wait if there is something to write.
+	     * This may prevent infinite wait on exit. [python bug 216289]
+	     */
+	    WaitForSingleObject(consolePtr->writable, INFINITE);
+	}
+
+	/*
+	 * The thread may already have closed on it's own.  Check it's
+	 * exit code.
+	 */
+
+	GetExitCodeThread(consolePtr->writeThread, &exitCode);
+
+	if (exitCode == STILL_ACTIVE) {
+	    /*
+	     * Set the stop event so that if the reader thread is blocked
+	     * in ConsoleWriterThread on WaitForMultipleEvents, it will
+	     * exit cleanly.
+	     */
+
+	    SetEvent(consolePtr->stopWriter);
+
+	    /*
+	     * Wait at most 20 milliseconds for the writer thread to close.
+	     */
+
+	    if (WaitForSingleObject(consolePtr->writeThread, 20)
+		    == WAIT_TIMEOUT) {
+		/*
+		 * Forcibly terminate the background thread as a last
+		 * resort.  Note that we need to guard against
+		 * terminating the thread while it is in the middle of
+		 * Tcl_ThreadAlert because it won't be able to release
+		 * the notifier lock.
+		 */
+
+		Tcl_MutexLock(&consoleMutex);
+
+		/* BUG: this leaks memory. */
+		TerminateThread(consolePtr->writeThread, 0);
+		Tcl_MutexUnlock(&consoleMutex);
+	    }
+	}
+
+	CloseHandle(consolePtr->writeThread);
+	CloseHandle(consolePtr->writable);
+	CloseHandle(consolePtr->startWriter);
+	CloseHandle(consolePtr->stopWriter);
+	consolePtr->writeThread = NULL;
+    }
+    consolePtr->validMask &= ~TCL_WRITABLE;
+
+
+    /*
+     * Don't close the Win32 handle if the handle is a standard channel
+     * during the thread exit process.  Otherwise, one thread may kill
+     * the stdio of another.
+     */
+
+    if (!TclInThreadExit() 
+	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
+		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
+		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
+	if (CloseHandle(consolePtr->handle) == FALSE) {
+	    TclWinConvertError(GetLastError());
+	    errorCode = errno;
+	}
+    }
+    
+    consolePtr->watchMask &= consolePtr->validMask;
+
+    /*
+     * Remove the file from the list of watched files.
+     */
+
+    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
+	    infoPtr != NULL;
+	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
+	if (infoPtr == (ConsoleInfo *)consolePtr) {
+	    *nextPtrPtr = infoPtr->nextPtr;
+	    break;
+	}
+    }
+    if (consolePtr->writeBuf != NULL) {
+	ckfree(consolePtr->writeBuf);
+	consolePtr->writeBuf = 0;
+    }
+    ckfree((char*) consolePtr);
+
+    return errorCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleInputProc --
+ *
+ *	Reads input from the IO channel into the buffer given. Returns
+ *	count of how many bytes were actually read, and an error indication.
+ *
+ * Results:
+ *	A count of how many bytes were read is returned and an error
+ *	indication is returned in an output argument.
+ *
+ * Side effects:
+ *	Reads input from the actual channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleInputProc(
+    ClientData instanceData,		/* Console state. */
+    char *buf,				/* Where to store data read. */
+    int bufSize,			/* How much space is available
+                                         * in the buffer? */
+    int *errorCode)			/* Where to store error code. */
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+    DWORD count, bytesRead = 0;
+    int result;
+
+    *errorCode = 0;
+
+    /*
+     * Synchronize with the reader thread.
+     */
+    
+    result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
+    
+    /*
+     * If an error occurred, return immediately.
+     */
+    
+    if (result == -1) {
+	*errorCode = errno;
+	return -1;
+    }
+
+    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
+	/*
+	 * Data is stored in the buffer.
+	 */
+
+	if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
+	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
+	    bytesRead = bufSize;
+	    infoPtr->offset += bufSize;
+	} else {
+	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
+	    bytesRead = infoPtr->bytesRead - infoPtr->offset;
+
+	    /*
+	     * Reset the buffer
+	     */
+	    
+	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;
+	    infoPtr->offset = 0;
+	}
+
+	return bytesRead;
+    }
+    
+    /*
+     * Attempt to read bufSize bytes.  The read will return immediately
+     * if there is any data available.  Otherwise it will block until
+     * at least one byte is available or an EOF occurs.
+     */
+
+    if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
+		    (LPOVERLAPPED) NULL) == TRUE) {
+	buf[count] = '\0';
+	return count;
+    }
+
+    return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleOutputProc --
+ *
+ *	Writes the given output on the IO channel. Returns count of how
+ *	many characters were actually written, and an error indication.
+ *
+ * Results:
+ *	A count of how many characters were written is returned and an
+ *	error indication is returned in an output argument.
+ *
+ * Side effects:
+ *	Writes output on the actual channel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleOutputProc(
+    ClientData instanceData,		/* Console state. */
+    CONST char *buf,			/* The data buffer. */
+    int toWrite,			/* How many bytes to write? */
+    int *errorCode)			/* Where to store error code. */
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+    DWORD bytesWritten, timeout;
+    
+    *errorCode = 0;
+    timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
+    if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
+	/*
+	 * The writer thread is blocked waiting for a write to complete
+	 * and the channel is in non-blocking mode.
+	 */
+
+	errno = EAGAIN;
+	goto error;
+    }
+    
+    /*
+     * Check for a background error on the last write.
+     */
+
+    if (infoPtr->writeError) {
+	TclWinConvertError(infoPtr->writeError);
+	infoPtr->writeError = 0;
+	goto error;
+    }
+
+    if (infoPtr->flags & CONSOLE_ASYNC) {
+	/*
+	 * The console is non-blocking, so copy the data into the output
+	 * buffer and restart the writer thread.
+	 */
+
+	if (toWrite > infoPtr->writeBufLen) {
+	    /*
+	     * Reallocate the buffer to be large enough to hold the data.
+	     */
+
+	    if (infoPtr->writeBuf) {
+		ckfree(infoPtr->writeBuf);
+	    }
+	    infoPtr->writeBufLen = toWrite;
+	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
+	}
+	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
+	infoPtr->toWrite = toWrite;
+	ResetEvent(infoPtr->writable);
+	SetEvent(infoPtr->startWriter);
+	bytesWritten = toWrite;
+    } else {
+	/*
+	 * In the blocking case, just try to write the buffer directly.
+	 * This avoids an unnecessary copy.
+	 */
+
+	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
+		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
+	    TclWinConvertError(GetLastError());
+	    goto error;
+	}
+    }
+    return bytesWritten;
+
+    error:
+    *errorCode = errno;
+    return -1;
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleEventProc --
+ *
+ *	This function is invoked by Tcl_ServiceEvent when a file event
+ *	reaches the front of the event queue.  This procedure invokes
+ *	Tcl_NotifyChannel on the console.
+ *
+ * Results:
+ *	Returns 1 if the event was handled, meaning it should be removed
+ *	from the queue.  Returns 0 if the event was not handled, meaning
+ *	it should stay on the queue.  The only time the event isn't
+ *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
+ *
+ * Side effects:
+ *	Whatever the notifier callback does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleEventProc(
+    Tcl_Event *evPtr,		/* Event to service. */
+    int flags)			/* Flags that indicate what events to
+				 * handle, such as TCL_FILE_EVENTS. */
+{
+    ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
+    ConsoleInfo *infoPtr;
+    int mask;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    if (!(flags & TCL_FILE_EVENTS)) {
+	return 0;
+    }
+
+    /*
+     * Search through the list of watched consoles for the one whose handle
+     * matches the event.  We do this rather than simply dereferencing
+     * the handle in the event so that consoles can be deleted while the
+     * event is in the queue.
+     */
+
+    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
+	    infoPtr = infoPtr->nextPtr) {
+	if (consoleEvPtr->infoPtr == infoPtr) {
+	    infoPtr->flags &= ~(CONSOLE_PENDING);
+	    break;
+	}
+    }
+
+    /*
+     * Remove stale events.
+     */
+
+    if (!infoPtr) {
+	return 1;
+    }
+
+    /*
+     * Check to see if the console is readable.  Note
+     * that we can't tell if a console is writable, so we always report it
+     * as being writable unless we have detected EOF.
+     */
+
+    mask = 0;
+    if (infoPtr->watchMask & TCL_WRITABLE) {
+	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
+	    mask = TCL_WRITABLE;
+	}
+    }
+
+    if (infoPtr->watchMask & TCL_READABLE) {
+	if (WaitForRead(infoPtr, 0) >= 0) {
+	    if (infoPtr->readFlags & CONSOLE_EOF) {
+		mask = TCL_READABLE;
+	    } else {
+		mask |= TCL_READABLE;
+	    }
+	} 
+    }
+
+    /*
+     * Inform the channel of the events.
+     */
+
+    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
+    return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleWatchProc --
+ *
+ *	Called by the notifier to set up to watch for events on this
+ *	channel.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleWatchProc(
+    ClientData instanceData,		/* Console state. */
+    int mask)				/* What events to watch for, OR-ed
+                                         * combination of TCL_READABLE,
+                                         * TCL_WRITABLE and TCL_EXCEPTION. */
+{
+    ConsoleInfo **nextPtrPtr, *ptr;
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+    int oldMask = infoPtr->watchMask;
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    /*
+     * Since most of the work is handled by the background threads,
+     * we just need to update the watchMask and then force the notifier
+     * to poll once. 
+     */
+
+    infoPtr->watchMask = mask & infoPtr->validMask;
+    if (infoPtr->watchMask) {
+	Tcl_Time blockTime = { 0, 0 };
+	if (!oldMask) {
+	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;
+	    tsdPtr->firstConsolePtr = infoPtr;
+	}
+	Tcl_SetMaxBlockTime(&blockTime);
+    } else {
+	if (oldMask) {
+	    /*
+	     * Remove the console from the list of watched consoles.
+	     */
+
+	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
+		 ptr != NULL;
+		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
+		if (infoPtr == ptr) {
+		    *nextPtrPtr = ptr->nextPtr;
+		    break;
+		}
+	    }
+	}
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleGetHandleProc --
+ *
+ *	Called from Tcl_GetChannelHandle to retrieve OS handles from
+ *	inside a command consoleline based channel.
+ *
+ * Results:
+ *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
+ *	there is no handle for the specified direction. 
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ConsoleGetHandleProc(
+    ClientData instanceData,	/* The console state. */
+    int direction,		/* TCL_READABLE or TCL_WRITABLE */
+    ClientData *handlePtr)	/* Where to store the handle.  */
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+
+    *handlePtr = (ClientData) infoPtr->handle;
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitForRead --
+ *
+ *	Wait until some data is available, the console is at
+ *	EOF or the reader thread is blocked waiting for data (if the
+ *	channel is in non-blocking mode).
+ *
+ * Results:
+ *	Returns 1 if console is readable.  Returns 0 if there is no data
+ *	on the console, but there is buffered data.  Returns -1 if an
+ *	error occurred.  If an error occurred, the threads may not
+ *	be synchronized.
+ *
+ * Side effects:
+ *	Updates the shared state flags.  If no error occurred,
+ *      the reader thread is blocked waiting for a signal from the 
+ *      main thread.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+WaitForRead(
+    ConsoleInfo *infoPtr,		/* Console state. */
+    int blocking)		/* Indicates whether call should be
+				 * blocking or not. */
+{
+    DWORD timeout, count;
+    HANDLE *handle = infoPtr->handle;
+    INPUT_RECORD input;
+    
+    while (1) {
+	/*
+	 * Synchronize with the reader thread.
+	 */
+       
+	timeout = blocking ? INFINITE : 0;
+	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
+	    /*
+	     * The reader thread is blocked waiting for data and the channel
+	     * is in non-blocking mode.
+	     */
+	    errno = EAGAIN;
+	    return -1;
+	}
+	
+	/*
+	 * At this point, the two threads are synchronized, so it is safe
+	 * to access shared state.
+	 */
+	
+	/*
+	 * If the console has hit EOF, it is always readable.
+	 */
+	
+	if (infoPtr->readFlags & CONSOLE_EOF) {
+	    return 1;
+	}
+	
+	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
+            /*
+	     * Check to see if the peek failed because of EOF.
+	     */
+	    
+	    TclWinConvertError(GetLastError());
+	    
+	    if (errno == EOF) {
+		infoPtr->readFlags |= CONSOLE_EOF;
+		return 1;
+	    }
+
+	    /*
+	     * Ignore errors if there is data in the buffer.
+	     */
+	    
+	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
+		return 0;
+	    } else {
+		return -1;
+	    }
+	}
+
+	/*
+	 * If there is data in the buffer, the console must be
+	 * readable (since it is a line-oriented device).
+	 */
+
+	if (infoPtr->readFlags & CONSOLE_BUFFERED) {
+	    return 1;
+	}
+
+	
+	/*
+	 * There wasn't any data available, so reset the thread and
+	 * try again.
+	 */
+    
+	ResetEvent(infoPtr->readable);
+	SetEvent(infoPtr->startReader);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleReaderThread --
+ *
+ *	This function runs in a separate thread and waits for input
+ *	to become available on a console.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Signals the main thread when input become available.  May
+ *	cause the main thread to wake up by posting a message.  May
+ *	one line from the console for each wait operation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static DWORD WINAPI
+ConsoleReaderThread(LPVOID arg)
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
+    HANDLE *handle = infoPtr->handle;
+    DWORD count, waitResult;
+    HANDLE wEvents[2];
+
+    /* The first event takes precedence. */
+    wEvents[0] = infoPtr->stopReader;
+    wEvents[1] = infoPtr->startReader;
+
+    for (;;) {
+	/*
+	 * Wait for the main thread to signal before attempting to wait.
+	 */
+
+	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+	if (waitResult != (WAIT_OBJECT_0 + 1)) {
+	    /*
+	     * The start event was not signaled.  It must be the stop event
+	     * or an error, so exit this thread.
+	     */
+
+	    break;
+	}
+
+	count = 0;
+
+	/* 
+	 * Look for data on the console, but first ignore any events
+	 * that are not KEY_EVENTs 
+	 */
+	if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
+		(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
+	    /*
+	     * Data was stored in the buffer.
+	     */
+	    
+	    infoPtr->readFlags |= CONSOLE_BUFFERED;
+	} else {
+	    DWORD err;
+	    err = GetLastError();
+	    
+	    if (err == EOF) {
+		infoPtr->readFlags = CONSOLE_EOF;
+	    }
+	}
+
+	/*
+	 * Signal the main thread by signalling the readable event and
+	 * then waking up the notifier thread.
+	 */
+
+	SetEvent(infoPtr->readable);
+
+	/*
+	 * Alert the foreground thread.  Note that we need to treat this like
+	 * a critical section so the foreground thread does not terminate
+	 * this thread while we are holding a mutex in the notifier code.
+	 */
+
+	Tcl_MutexLock(&consoleMutex);
+	if (infoPtr->threadId != NULL) {
+	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
+	    Tcl_ThreadAlert(infoPtr->threadId);
+	}
+	Tcl_MutexUnlock(&consoleMutex);
+    }
+
+    return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleWriterThread --
+ *
+ *	This function runs in a separate thread and writes data
+ *	onto a console.
+ *
+ * Results:
+ *	Always returns 0.
+ *
+ * Side effects:
+ *	Signals the main thread when an output operation is completed.
+ *	May cause the main thread to wake up by posting a message.  
+ *
+ *----------------------------------------------------------------------
+ */
+
+static DWORD WINAPI
+ConsoleWriterThread(LPVOID arg)
+{
+
+    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
+    HANDLE *handle = infoPtr->handle;
+    DWORD count, toWrite, waitResult;
+    char *buf;
+    HANDLE wEvents[2];
+
+    /* The first event takes precedence. */
+    wEvents[0] = infoPtr->stopWriter;
+    wEvents[1] = infoPtr->startWriter;
+
+    for (;;) {
+	/*
+	 * Wait for the main thread to signal before attempting to write.
+	 */
+
+	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+	if (waitResult != (WAIT_OBJECT_0 + 1)) {
+	    /*
+	     * The start event was not signaled.  It must be the stop event
+	     * or an error, so exit this thread.
+	     */
+
+	    break;
+	}
+
+	buf = infoPtr->writeBuf;
+	toWrite = infoPtr->toWrite;
+
+	/*
+	 * Loop until all of the bytes are written or an error occurs.
+	 */
+
+	while (toWrite > 0) {
+	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
+		infoPtr->writeError = GetLastError();
+		break;
+	    } else {
+		toWrite -= count;
+		buf += count;
+	    }
+	}
+
+	/*
+	 * Signal the main thread by signalling the writable event and
+	 * then waking up the notifier thread.
+	 */
+	
+	SetEvent(infoPtr->writable);
+
+	/*
+	 * Alert the foreground thread.  Note that we need to treat this like
+	 * a critical section so the foreground thread does not terminate
+	 * this thread while we are holding a mutex in the notifier code.
+	 */
+
+	Tcl_MutexLock(&consoleMutex);
+	if (infoPtr->threadId != NULL) {
+	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
+	    Tcl_ThreadAlert(infoPtr->threadId);
+	}
+	Tcl_MutexUnlock(&consoleMutex);
+    }
+
+    return 0;
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclWinOpenConsoleChannel --
+ *
+ *	Constructs a Console channel for the specified standard OS handle.
+ *      This is a helper function to break up the construction of 
+ *      channels into File, Console, or Serial.
+ *
+ * Results:
+ *	Returns the new channel, or NULL.
+ *
+ * Side effects:
+ *	May open the channel
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_Channel
+TclWinOpenConsoleChannel(handle, channelName, permissions)
+    HANDLE handle;
+    char *channelName;
+    int permissions;
+{
+    char encoding[4 + TCL_INTEGER_SPACE];
+    ConsoleInfo *infoPtr;
+    DWORD id, modes;
+
+    ConsoleInit();
+
+    /*
+     * See if a channel with this handle already exists.
+     */
+    
+    infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
+    memset(infoPtr, 0, sizeof(ConsoleInfo));
+
+    infoPtr->validMask = permissions;
+    infoPtr->handle = handle;
+    infoPtr->channel = (Tcl_Channel) NULL;
+
+    wsprintfA(encoding, "cp%d", GetConsoleCP());
+
+    infoPtr->threadId = Tcl_GetCurrentThread();
+
+    /*
+     * Use the pointer for the name of the result channel.
+     * This keeps the channel names unique, since some may share
+     * handles (stdin/stdout/stderr for instance).
+     */
+
+    wsprintfA(channelName, "file%lx", (int) infoPtr);
+    
+    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
+            (ClientData) infoPtr, permissions);
+
+    if (permissions & TCL_READABLE) {
+	/*
+	 * Make sure the console input buffer is ready for only character
+	 * input notifications and the buffer is set for line buffering.
+	 * IOW, we only want to catch when complete lines are ready for
+	 * reading.
+	 */
+	GetConsoleMode(infoPtr->handle, &modes);
+	modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
+	modes |= ENABLE_LINE_INPUT;
+	SetConsoleMode(infoPtr->handle, modes);
+
+	infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
+	infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
+	infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
+	infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
+	        infoPtr, 0, &id);
+	SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
+    }
+
+    if (permissions & TCL_WRITABLE) {
+	infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
+	infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+	infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+	infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
+	        infoPtr, 0, &id);
+	SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
+    }
+
+    /*
+     * Files have default translation of AUTO and ^Z eof char, which
+     * means that a ^Z will be accepted as EOF when reading.
+     */
+    
+    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
+    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
+    Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
+
+    return infoPtr->channel;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConsoleThreadActionProc --
+ *
+ *	Insert or remove any thread local refs to this channel.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Changes thread local list of valid channels.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConsoleThreadActionProc (instanceData, action)
+     ClientData instanceData;
+     int action;
+{
+    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
+
+    /* We do not access firstConsolePtr in the thread structures. This is
+     * not for all serials managed by the thread, but only those we are
+     * watching. Removal of the filevent handlers before transfer thus
+     * takes care of this structure.
+     */
+
+    Tcl_MutexLock(&consoleMutex);
+    if (action == TCL_CHANNEL_THREAD_INSERT) {
+        /* We can't copy the thread information from the channel when
+	 * the channel is created. At this time the channel back
+	 * pointer has not been set yet. However in that case the
+	 * threadId has already been set by TclpCreateCommandChannel
+	 * itself, so the structure is still good.
+	 */
+
+        ConsoleInit ();
+        if (infoPtr->channel != NULL) {
+	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
+	}
+    } else {
+	infoPtr->threadId = NULL;
+    }
+    Tcl_MutexUnlock(&consoleMutex);
+}