os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclWinConsole.c --
     3  *
     4  *	This file implements the Windows-specific console functions,
     5  *	and the "console" channel driver.
     6  *
     7  * Copyright (c) 1999 by Scriptics Corp.
     8  *
     9  * See the file "license.terms" for information on usage and redistribution
    10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11  *
    12  * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
    13  */
    14 
    15 #include "tclWinInt.h"
    16 
    17 #include <fcntl.h>
    18 #include <io.h>
    19 #include <sys/stat.h>
    20 
    21 /*
    22  * The following variable is used to tell whether this module has been
    23  * initialized.
    24  */
    25 
    26 static int initialized = 0;
    27 
    28 /*
    29  * The consoleMutex locks around access to the initialized variable, and it is
    30  * used to protect background threads from being terminated while they are
    31  * using APIs that hold locks.
    32  */
    33 
    34 TCL_DECLARE_MUTEX(consoleMutex)
    35 
    36 /*
    37  * Bit masks used in the flags field of the ConsoleInfo structure below.
    38  */
    39 
    40 #define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
    41 #define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
    42 
    43 /*
    44  * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
    45  */
    46 
    47 #define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */
    48 #define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader
    49 				     thread */
    50 
    51 #define CONSOLE_BUFFER_SIZE (8*1024)
    52 /*
    53  * This structure describes per-instance data for a console based channel.
    54  */
    55 
    56 typedef struct ConsoleInfo {
    57     HANDLE handle;
    58     int type;
    59     struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
    60     Tcl_Channel channel;	/* Pointer to channel structure. */
    61     int validMask;		/* OR'ed combination of TCL_READABLE,
    62 				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    63 				 * which operations are valid on the file. */
    64     int watchMask;		/* OR'ed combination of TCL_READABLE,
    65 				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
    66 				 * which events should be reported. */
    67     int flags;			/* State flags, see above for a list. */
    68     Tcl_ThreadId threadId;	/* Thread to which events should be reported.
    69 				 * This value is used by the reader/writer
    70 				 * threads. */
    71     HANDLE writeThread;		/* Handle to writer thread. */
    72     HANDLE readThread;		/* Handle to reader thread. */
    73     HANDLE writable;		/* Manual-reset event to signal when the
    74 				 * writer thread has finished waiting for
    75 				 * the current buffer to be written. */
    76     HANDLE readable;		/* Manual-reset event to signal when the
    77 				 * reader thread has finished waiting for
    78 				 * input. */
    79     HANDLE startWriter;		/* Auto-reset event used by the main thread to
    80 				 * signal when the writer thread should attempt
    81 				 * to write to the console. */
    82     HANDLE stopWriter;		/* Auto-reset event used by the main thread to
    83 				 * signal when the writer thread should exit.
    84 				 */
    85     HANDLE startReader;		/* Auto-reset event used by the main thread to
    86 				 * signal when the reader thread should attempt
    87 				 * to read from the console. */
    88     HANDLE stopReader;		/* Auto-reset event used by the main thread to
    89 				 * signal when the reader thread should exit.
    90 				 */
    91     DWORD writeError;		/* An error caused by the last background
    92 				 * write.  Set to 0 if no error has been
    93 				 * detected.  This word is shared with the
    94 				 * writer thread so access must be
    95 				 * synchronized with the writable object.
    96 				 */
    97     char *writeBuf;		/* Current background output buffer.
    98 				 * Access is synchronized with the writable
    99 				 * object. */
   100     int writeBufLen;		/* Size of write buffer.  Access is
   101 				 * synchronized with the writable
   102 				 * object. */
   103     int toWrite;		/* Current amount to be written.  Access is
   104 				 * synchronized with the writable object. */
   105     int readFlags;		/* Flags that are shared with the reader
   106 				 * thread.  Access is synchronized with the
   107 				 * readable object.  */
   108     int bytesRead;              /* number of bytes in the buffer */
   109     int offset;                 /* number of bytes read out of the buffer */
   110     char buffer[CONSOLE_BUFFER_SIZE];
   111                                 /* Data consumed by reader thread. */
   112 } ConsoleInfo;
   113 
   114 typedef struct ThreadSpecificData {
   115     /*
   116      * The following pointer refers to the head of the list of consoles
   117      * that are being watched for file events.
   118      */
   119     
   120     ConsoleInfo *firstConsolePtr;
   121 } ThreadSpecificData;
   122 
   123 static Tcl_ThreadDataKey dataKey;
   124 
   125 /*
   126  * The following structure is what is added to the Tcl event queue when
   127  * console events are generated.
   128  */
   129 
   130 typedef struct ConsoleEvent {
   131     Tcl_Event header;		/* Information that is standard for
   132 				 * all events. */
   133     ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note
   134 				 * that we still have to verify that the
   135 				 * console exists before dereferencing this
   136 				 * pointer. */
   137 } ConsoleEvent;
   138 
   139 /*
   140  * Declarations for functions used only in this file.
   141  */
   142 
   143 static int		ConsoleBlockModeProc(ClientData instanceData, int mode);
   144 static void		ConsoleCheckProc(ClientData clientData, int flags);
   145 static int		ConsoleCloseProc(ClientData instanceData,
   146 			    Tcl_Interp *interp);
   147 static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);
   148 static void		ConsoleExitHandler(ClientData clientData);
   149 static int		ConsoleGetHandleProc(ClientData instanceData,
   150 			    int direction, ClientData *handlePtr);
   151 static void             ConsoleInit(void);
   152 static int		ConsoleInputProc(ClientData instanceData, char *buf,
   153 			    int toRead, int *errorCode);
   154 static int		ConsoleOutputProc(ClientData instanceData,
   155 			    CONST char *buf, int toWrite, int *errorCode);
   156 static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
   157 static void		ConsoleSetupProc(ClientData clientData, int flags);
   158 static void		ConsoleWatchProc(ClientData instanceData, int mask);
   159 static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
   160 static void		ProcExitHandler(ClientData clientData);
   161 static int		WaitForRead(ConsoleInfo *infoPtr, int blocking);
   162 
   163 static void             ConsoleThreadActionProc _ANSI_ARGS_ ((
   164 			   ClientData instanceData, int action));
   165 
   166 /*
   167  * This structure describes the channel type structure for command console
   168  * based IO.
   169  */
   170 
   171 static Tcl_ChannelType consoleChannelType = {
   172     "console",			/* Type name. */
   173     TCL_CHANNEL_VERSION_4,	/* v4 channel */
   174     ConsoleCloseProc,		/* Close proc. */
   175     ConsoleInputProc,		/* Input proc. */
   176     ConsoleOutputProc,		/* Output proc. */
   177     NULL,			/* Seek proc. */
   178     NULL,			/* Set option proc. */
   179     NULL,			/* Get option proc. */
   180     ConsoleWatchProc,		/* Set up notifier to watch the channel. */
   181     ConsoleGetHandleProc,	/* Get an OS handle from channel. */
   182     NULL,			/* close2proc. */
   183     ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/
   184     NULL,			/* flush proc. */
   185     NULL,			/* handler proc. */
   186     NULL,                       /* wide seek proc */
   187     ConsoleThreadActionProc,    /* thread action proc */
   188 };
   189 
   190 /*
   191  *----------------------------------------------------------------------
   192  *
   193  * ConsoleInit --
   194  *
   195  *	This function initializes the static variables for this file.
   196  *
   197  * Results:
   198  *	None.
   199  *
   200  * Side effects:
   201  *	Creates a new event source.
   202  *
   203  *----------------------------------------------------------------------
   204  */
   205 
   206 static void
   207 ConsoleInit()
   208 {
   209     ThreadSpecificData *tsdPtr;
   210 
   211     /*
   212      * Check the initialized flag first, then check again in the mutex.
   213      * This is a speed enhancement.
   214      */
   215 
   216     if (!initialized) {
   217 	Tcl_MutexLock(&consoleMutex);
   218 	if (!initialized) {
   219 	    initialized = 1;
   220 	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
   221 	}
   222 	Tcl_MutexUnlock(&consoleMutex);
   223     }
   224 
   225     tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   226     if (tsdPtr == NULL) {
   227 	tsdPtr = TCL_TSD_INIT(&dataKey);
   228 	tsdPtr->firstConsolePtr = NULL;
   229 	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
   230 	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
   231     }
   232 }
   233 
   234 /*
   235  *----------------------------------------------------------------------
   236  *
   237  * ConsoleExitHandler --
   238  *
   239  *	This function is called to cleanup the console module before
   240  *	Tcl is unloaded.
   241  *
   242  * Results:
   243  *	None.
   244  *
   245  * Side effects:
   246  *	Removes the console event source.
   247  *
   248  *----------------------------------------------------------------------
   249  */
   250 
   251 static void
   252 ConsoleExitHandler(
   253     ClientData clientData)	/* Old window proc */
   254 {
   255     Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
   256 }
   257 
   258 /*
   259  *----------------------------------------------------------------------
   260  *
   261  * ProcExitHandler --
   262  *
   263  *	This function is called to cleanup the process list before
   264  *	Tcl is unloaded.
   265  *
   266  * Results:
   267  *	None.
   268  *
   269  * Side effects:
   270  *	Resets the process list.
   271  *
   272  *----------------------------------------------------------------------
   273  */
   274 
   275 static void
   276 ProcExitHandler(
   277     ClientData clientData)	/* Old window proc */
   278 {
   279     Tcl_MutexLock(&consoleMutex);
   280     initialized = 0;
   281     Tcl_MutexUnlock(&consoleMutex);
   282 }
   283 
   284 /*
   285  *----------------------------------------------------------------------
   286  *
   287  * ConsoleSetupProc --
   288  *
   289  *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
   290  *	for an event.
   291  *
   292  * Results:
   293  *	None.
   294  *
   295  * Side effects:
   296  *	Adjusts the block time if needed.
   297  *
   298  *----------------------------------------------------------------------
   299  */
   300 
   301 void
   302 ConsoleSetupProc(
   303     ClientData data,		/* Not used. */
   304     int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   305 {
   306     ConsoleInfo *infoPtr;
   307     Tcl_Time blockTime = { 0, 0 };
   308     int block = 1;
   309     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   310 
   311     if (!(flags & TCL_FILE_EVENTS)) {
   312 	return;
   313     }
   314     
   315     /*
   316      * Look to see if any events are already pending.  If they are, poll.
   317      */
   318 
   319     for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
   320 	    infoPtr = infoPtr->nextPtr) {
   321 	if (infoPtr->watchMask & TCL_WRITABLE) {
   322 	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   323 		block = 0;
   324 	    }
   325 	}
   326 	if (infoPtr->watchMask & TCL_READABLE) {
   327 	    if (WaitForRead(infoPtr, 0) >= 0) {
   328 		block = 0;
   329 	    }
   330 	}
   331     }
   332     if (!block) {
   333 	Tcl_SetMaxBlockTime(&blockTime);
   334     }
   335 }
   336 
   337 /*
   338  *----------------------------------------------------------------------
   339  *
   340  * ConsoleCheckProc --
   341  *
   342  *	This procedure is called by Tcl_DoOneEvent to check the console
   343  *	event source for events. 
   344  *
   345  * Results:
   346  *	None.
   347  *
   348  * Side effects:
   349  *	May queue an event.
   350  *
   351  *----------------------------------------------------------------------
   352  */
   353 
   354 static void
   355 ConsoleCheckProc(
   356     ClientData data,		/* Not used. */
   357     int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   358 {
   359     ConsoleInfo *infoPtr;
   360     ConsoleEvent *evPtr;
   361     int needEvent;
   362     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   363 
   364     if (!(flags & TCL_FILE_EVENTS)) {
   365 	return;
   366     }
   367     
   368     /*
   369      * Queue events for any ready consoles that don't already have events
   370      * queued.
   371      */
   372 
   373     for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
   374 	    infoPtr = infoPtr->nextPtr) {
   375 	if (infoPtr->flags & CONSOLE_PENDING) {
   376 	    continue;
   377 	}
   378 	
   379 	/*
   380 	 * Queue an event if the console is signaled for reading or writing.
   381 	 */
   382 
   383 	needEvent = 0;
   384 	if (infoPtr->watchMask & TCL_WRITABLE) {
   385 	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   386 		needEvent = 1;
   387 	    }
   388 	}
   389 	
   390 	if (infoPtr->watchMask & TCL_READABLE) {
   391 	    if (WaitForRead(infoPtr, 0) >= 0) {
   392 		needEvent = 1;
   393 	    }
   394 	}
   395 
   396 	if (needEvent) {
   397 	    infoPtr->flags |= CONSOLE_PENDING;
   398 	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
   399 	    evPtr->header.proc = ConsoleEventProc;
   400 	    evPtr->infoPtr = infoPtr;
   401 	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
   402 	}
   403     }
   404 }
   405 
   406 
   407 /*
   408  *----------------------------------------------------------------------
   409  *
   410  * ConsoleBlockModeProc --
   411  *
   412  *	Set blocking or non-blocking mode on channel.
   413  *
   414  * Results:
   415  *	0 if successful, errno when failed.
   416  *
   417  * Side effects:
   418  *	Sets the device into blocking or non-blocking mode.
   419  *
   420  *----------------------------------------------------------------------
   421  */
   422 
   423 static int
   424 ConsoleBlockModeProc(
   425     ClientData instanceData,	/* Instance data for channel. */
   426     int mode)			/* TCL_MODE_BLOCKING or
   427                                  * TCL_MODE_NONBLOCKING. */
   428 {
   429     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   430     
   431     /*
   432      * Consoles on Windows can not be switched between blocking and nonblocking,
   433      * hence we have to emulate the behavior. This is done in the input
   434      * function by checking against a bit in the state. We set or unset the
   435      * bit here to cause the input function to emulate the correct behavior.
   436      */
   437 
   438     if (mode == TCL_MODE_NONBLOCKING) {
   439 	infoPtr->flags |= CONSOLE_ASYNC;
   440     } else {
   441 	infoPtr->flags &= ~(CONSOLE_ASYNC);
   442     }
   443     return 0;
   444 }
   445 
   446 /*
   447  *----------------------------------------------------------------------
   448  *
   449  * ConsoleCloseProc --
   450  *
   451  *	Closes a console based IO channel.
   452  *
   453  * Results:
   454  *	0 on success, errno otherwise.
   455  *
   456  * Side effects:
   457  *	Closes the physical channel.
   458  *
   459  *----------------------------------------------------------------------
   460  */
   461 
   462 static int
   463 ConsoleCloseProc(
   464     ClientData instanceData,	/* Pointer to ConsoleInfo structure. */
   465     Tcl_Interp *interp)		/* For error reporting. */
   466 {
   467     ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
   468     int errorCode;
   469     ConsoleInfo *infoPtr, **nextPtrPtr;
   470     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   471     DWORD exitCode;
   472 
   473     errorCode = 0;
   474     
   475     /*
   476      * Clean up the background thread if necessary.  Note that this
   477      * must be done before we can close the file, since the 
   478      * thread may be blocking trying to read from the console.
   479      */
   480     
   481     if (consolePtr->readThread) {
   482 
   483 	/*
   484 	 * The thread may already have closed on it's own.  Check it's
   485 	 * exit code.
   486 	 */
   487 
   488 	GetExitCodeThread(consolePtr->readThread, &exitCode);
   489 
   490 	if (exitCode == STILL_ACTIVE) {
   491 
   492 	    /*
   493 	     * Set the stop event so that if the reader thread is blocked
   494 	     * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
   495 	     * cleanly.
   496 	     */
   497 
   498 	    SetEvent(consolePtr->stopReader);
   499 
   500 	    /*
   501 	     * Wait at most 20 milliseconds for the reader thread to close.
   502 	     */
   503 
   504 	    if (WaitForSingleObject(consolePtr->readThread, 20)
   505 		    == WAIT_TIMEOUT) {
   506 		/*
   507 		 * Forcibly terminate the background thread as a last
   508 		 * resort.  Note that we need to guard against
   509 		 * terminating the thread while it is in the middle of
   510 		 * Tcl_ThreadAlert because it won't be able to release
   511 		 * the notifier lock.
   512 		 */
   513 
   514 		Tcl_MutexLock(&consoleMutex);
   515 
   516 		/* BUG: this leaks memory. */
   517 		TerminateThread(consolePtr->readThread, 0);
   518 		Tcl_MutexUnlock(&consoleMutex);
   519 	    }
   520 	}
   521 
   522 	CloseHandle(consolePtr->readThread);
   523 	CloseHandle(consolePtr->readable);
   524 	CloseHandle(consolePtr->startReader);
   525 	CloseHandle(consolePtr->stopReader);
   526 	consolePtr->readThread = NULL;
   527     }
   528     consolePtr->validMask &= ~TCL_READABLE;
   529 
   530     /*
   531      * Wait for the writer thread to finish the current buffer, then
   532      * terminate the thread and close the handles.  If the channel is
   533      * nonblocking, there should be no pending write operations.
   534      */
   535     
   536     if (consolePtr->writeThread) {
   537 	if (consolePtr->toWrite) {
   538 	    /*
   539 	     * We only need to wait if there is something to write.
   540 	     * This may prevent infinite wait on exit. [python bug 216289]
   541 	     */
   542 	    WaitForSingleObject(consolePtr->writable, INFINITE);
   543 	}
   544 
   545 	/*
   546 	 * The thread may already have closed on it's own.  Check it's
   547 	 * exit code.
   548 	 */
   549 
   550 	GetExitCodeThread(consolePtr->writeThread, &exitCode);
   551 
   552 	if (exitCode == STILL_ACTIVE) {
   553 	    /*
   554 	     * Set the stop event so that if the reader thread is blocked
   555 	     * in ConsoleWriterThread on WaitForMultipleEvents, it will
   556 	     * exit cleanly.
   557 	     */
   558 
   559 	    SetEvent(consolePtr->stopWriter);
   560 
   561 	    /*
   562 	     * Wait at most 20 milliseconds for the writer thread to close.
   563 	     */
   564 
   565 	    if (WaitForSingleObject(consolePtr->writeThread, 20)
   566 		    == WAIT_TIMEOUT) {
   567 		/*
   568 		 * Forcibly terminate the background thread as a last
   569 		 * resort.  Note that we need to guard against
   570 		 * terminating the thread while it is in the middle of
   571 		 * Tcl_ThreadAlert because it won't be able to release
   572 		 * the notifier lock.
   573 		 */
   574 
   575 		Tcl_MutexLock(&consoleMutex);
   576 
   577 		/* BUG: this leaks memory. */
   578 		TerminateThread(consolePtr->writeThread, 0);
   579 		Tcl_MutexUnlock(&consoleMutex);
   580 	    }
   581 	}
   582 
   583 	CloseHandle(consolePtr->writeThread);
   584 	CloseHandle(consolePtr->writable);
   585 	CloseHandle(consolePtr->startWriter);
   586 	CloseHandle(consolePtr->stopWriter);
   587 	consolePtr->writeThread = NULL;
   588     }
   589     consolePtr->validMask &= ~TCL_WRITABLE;
   590 
   591 
   592     /*
   593      * Don't close the Win32 handle if the handle is a standard channel
   594      * during the thread exit process.  Otherwise, one thread may kill
   595      * the stdio of another.
   596      */
   597 
   598     if (!TclInThreadExit() 
   599 	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
   600 		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
   601 		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
   602 	if (CloseHandle(consolePtr->handle) == FALSE) {
   603 	    TclWinConvertError(GetLastError());
   604 	    errorCode = errno;
   605 	}
   606     }
   607     
   608     consolePtr->watchMask &= consolePtr->validMask;
   609 
   610     /*
   611      * Remove the file from the list of watched files.
   612      */
   613 
   614     for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
   615 	    infoPtr != NULL;
   616 	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
   617 	if (infoPtr == (ConsoleInfo *)consolePtr) {
   618 	    *nextPtrPtr = infoPtr->nextPtr;
   619 	    break;
   620 	}
   621     }
   622     if (consolePtr->writeBuf != NULL) {
   623 	ckfree(consolePtr->writeBuf);
   624 	consolePtr->writeBuf = 0;
   625     }
   626     ckfree((char*) consolePtr);
   627 
   628     return errorCode;
   629 }
   630 
   631 /*
   632  *----------------------------------------------------------------------
   633  *
   634  * ConsoleInputProc --
   635  *
   636  *	Reads input from the IO channel into the buffer given. Returns
   637  *	count of how many bytes were actually read, and an error indication.
   638  *
   639  * Results:
   640  *	A count of how many bytes were read is returned and an error
   641  *	indication is returned in an output argument.
   642  *
   643  * Side effects:
   644  *	Reads input from the actual channel.
   645  *
   646  *----------------------------------------------------------------------
   647  */
   648 
   649 static int
   650 ConsoleInputProc(
   651     ClientData instanceData,		/* Console state. */
   652     char *buf,				/* Where to store data read. */
   653     int bufSize,			/* How much space is available
   654                                          * in the buffer? */
   655     int *errorCode)			/* Where to store error code. */
   656 {
   657     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   658     DWORD count, bytesRead = 0;
   659     int result;
   660 
   661     *errorCode = 0;
   662 
   663     /*
   664      * Synchronize with the reader thread.
   665      */
   666     
   667     result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
   668     
   669     /*
   670      * If an error occurred, return immediately.
   671      */
   672     
   673     if (result == -1) {
   674 	*errorCode = errno;
   675 	return -1;
   676     }
   677 
   678     if (infoPtr->readFlags & CONSOLE_BUFFERED) {
   679 	/*
   680 	 * Data is stored in the buffer.
   681 	 */
   682 
   683 	if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
   684 	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
   685 	    bytesRead = bufSize;
   686 	    infoPtr->offset += bufSize;
   687 	} else {
   688 	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
   689 	    bytesRead = infoPtr->bytesRead - infoPtr->offset;
   690 
   691 	    /*
   692 	     * Reset the buffer
   693 	     */
   694 	    
   695 	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;
   696 	    infoPtr->offset = 0;
   697 	}
   698 
   699 	return bytesRead;
   700     }
   701     
   702     /*
   703      * Attempt to read bufSize bytes.  The read will return immediately
   704      * if there is any data available.  Otherwise it will block until
   705      * at least one byte is available or an EOF occurs.
   706      */
   707 
   708     if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
   709 		    (LPOVERLAPPED) NULL) == TRUE) {
   710 	buf[count] = '\0';
   711 	return count;
   712     }
   713 
   714     return -1;
   715 }
   716 
   717 /*
   718  *----------------------------------------------------------------------
   719  *
   720  * ConsoleOutputProc --
   721  *
   722  *	Writes the given output on the IO channel. Returns count of how
   723  *	many characters were actually written, and an error indication.
   724  *
   725  * Results:
   726  *	A count of how many characters were written is returned and an
   727  *	error indication is returned in an output argument.
   728  *
   729  * Side effects:
   730  *	Writes output on the actual channel.
   731  *
   732  *----------------------------------------------------------------------
   733  */
   734 
   735 static int
   736 ConsoleOutputProc(
   737     ClientData instanceData,		/* Console state. */
   738     CONST char *buf,			/* The data buffer. */
   739     int toWrite,			/* How many bytes to write? */
   740     int *errorCode)			/* Where to store error code. */
   741 {
   742     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   743     DWORD bytesWritten, timeout;
   744     
   745     *errorCode = 0;
   746     timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
   747     if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
   748 	/*
   749 	 * The writer thread is blocked waiting for a write to complete
   750 	 * and the channel is in non-blocking mode.
   751 	 */
   752 
   753 	errno = EAGAIN;
   754 	goto error;
   755     }
   756     
   757     /*
   758      * Check for a background error on the last write.
   759      */
   760 
   761     if (infoPtr->writeError) {
   762 	TclWinConvertError(infoPtr->writeError);
   763 	infoPtr->writeError = 0;
   764 	goto error;
   765     }
   766 
   767     if (infoPtr->flags & CONSOLE_ASYNC) {
   768 	/*
   769 	 * The console is non-blocking, so copy the data into the output
   770 	 * buffer and restart the writer thread.
   771 	 */
   772 
   773 	if (toWrite > infoPtr->writeBufLen) {
   774 	    /*
   775 	     * Reallocate the buffer to be large enough to hold the data.
   776 	     */
   777 
   778 	    if (infoPtr->writeBuf) {
   779 		ckfree(infoPtr->writeBuf);
   780 	    }
   781 	    infoPtr->writeBufLen = toWrite;
   782 	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
   783 	}
   784 	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
   785 	infoPtr->toWrite = toWrite;
   786 	ResetEvent(infoPtr->writable);
   787 	SetEvent(infoPtr->startWriter);
   788 	bytesWritten = toWrite;
   789     } else {
   790 	/*
   791 	 * In the blocking case, just try to write the buffer directly.
   792 	 * This avoids an unnecessary copy.
   793 	 */
   794 
   795 	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
   796 		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
   797 	    TclWinConvertError(GetLastError());
   798 	    goto error;
   799 	}
   800     }
   801     return bytesWritten;
   802 
   803     error:
   804     *errorCode = errno;
   805     return -1;
   806 
   807 }
   808 
   809 /*
   810  *----------------------------------------------------------------------
   811  *
   812  * ConsoleEventProc --
   813  *
   814  *	This function is invoked by Tcl_ServiceEvent when a file event
   815  *	reaches the front of the event queue.  This procedure invokes
   816  *	Tcl_NotifyChannel on the console.
   817  *
   818  * Results:
   819  *	Returns 1 if the event was handled, meaning it should be removed
   820  *	from the queue.  Returns 0 if the event was not handled, meaning
   821  *	it should stay on the queue.  The only time the event isn't
   822  *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
   823  *
   824  * Side effects:
   825  *	Whatever the notifier callback does.
   826  *
   827  *----------------------------------------------------------------------
   828  */
   829 
   830 static int
   831 ConsoleEventProc(
   832     Tcl_Event *evPtr,		/* Event to service. */
   833     int flags)			/* Flags that indicate what events to
   834 				 * handle, such as TCL_FILE_EVENTS. */
   835 {
   836     ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
   837     ConsoleInfo *infoPtr;
   838     int mask;
   839     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   840 
   841     if (!(flags & TCL_FILE_EVENTS)) {
   842 	return 0;
   843     }
   844 
   845     /*
   846      * Search through the list of watched consoles for the one whose handle
   847      * matches the event.  We do this rather than simply dereferencing
   848      * the handle in the event so that consoles can be deleted while the
   849      * event is in the queue.
   850      */
   851 
   852     for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
   853 	    infoPtr = infoPtr->nextPtr) {
   854 	if (consoleEvPtr->infoPtr == infoPtr) {
   855 	    infoPtr->flags &= ~(CONSOLE_PENDING);
   856 	    break;
   857 	}
   858     }
   859 
   860     /*
   861      * Remove stale events.
   862      */
   863 
   864     if (!infoPtr) {
   865 	return 1;
   866     }
   867 
   868     /*
   869      * Check to see if the console is readable.  Note
   870      * that we can't tell if a console is writable, so we always report it
   871      * as being writable unless we have detected EOF.
   872      */
   873 
   874     mask = 0;
   875     if (infoPtr->watchMask & TCL_WRITABLE) {
   876 	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
   877 	    mask = TCL_WRITABLE;
   878 	}
   879     }
   880 
   881     if (infoPtr->watchMask & TCL_READABLE) {
   882 	if (WaitForRead(infoPtr, 0) >= 0) {
   883 	    if (infoPtr->readFlags & CONSOLE_EOF) {
   884 		mask = TCL_READABLE;
   885 	    } else {
   886 		mask |= TCL_READABLE;
   887 	    }
   888 	} 
   889     }
   890 
   891     /*
   892      * Inform the channel of the events.
   893      */
   894 
   895     Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
   896     return 1;
   897 }
   898 
   899 /*
   900  *----------------------------------------------------------------------
   901  *
   902  * ConsoleWatchProc --
   903  *
   904  *	Called by the notifier to set up to watch for events on this
   905  *	channel.
   906  *
   907  * Results:
   908  *	None.
   909  *
   910  * Side effects:
   911  *	None.
   912  *
   913  *----------------------------------------------------------------------
   914  */
   915 
   916 static void
   917 ConsoleWatchProc(
   918     ClientData instanceData,		/* Console state. */
   919     int mask)				/* What events to watch for, OR-ed
   920                                          * combination of TCL_READABLE,
   921                                          * TCL_WRITABLE and TCL_EXCEPTION. */
   922 {
   923     ConsoleInfo **nextPtrPtr, *ptr;
   924     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   925     int oldMask = infoPtr->watchMask;
   926     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   927 
   928     /*
   929      * Since most of the work is handled by the background threads,
   930      * we just need to update the watchMask and then force the notifier
   931      * to poll once. 
   932      */
   933 
   934     infoPtr->watchMask = mask & infoPtr->validMask;
   935     if (infoPtr->watchMask) {
   936 	Tcl_Time blockTime = { 0, 0 };
   937 	if (!oldMask) {
   938 	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;
   939 	    tsdPtr->firstConsolePtr = infoPtr;
   940 	}
   941 	Tcl_SetMaxBlockTime(&blockTime);
   942     } else {
   943 	if (oldMask) {
   944 	    /*
   945 	     * Remove the console from the list of watched consoles.
   946 	     */
   947 
   948 	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
   949 		 ptr != NULL;
   950 		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
   951 		if (infoPtr == ptr) {
   952 		    *nextPtrPtr = ptr->nextPtr;
   953 		    break;
   954 		}
   955 	    }
   956 	}
   957     }
   958 }
   959 
   960 /*
   961  *----------------------------------------------------------------------
   962  *
   963  * ConsoleGetHandleProc --
   964  *
   965  *	Called from Tcl_GetChannelHandle to retrieve OS handles from
   966  *	inside a command consoleline based channel.
   967  *
   968  * Results:
   969  *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
   970  *	there is no handle for the specified direction. 
   971  *
   972  * Side effects:
   973  *	None.
   974  *
   975  *----------------------------------------------------------------------
   976  */
   977 
   978 static int
   979 ConsoleGetHandleProc(
   980     ClientData instanceData,	/* The console state. */
   981     int direction,		/* TCL_READABLE or TCL_WRITABLE */
   982     ClientData *handlePtr)	/* Where to store the handle.  */
   983 {
   984     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
   985 
   986     *handlePtr = (ClientData) infoPtr->handle;
   987     return TCL_OK;
   988 }
   989 
   990 /*
   991  *----------------------------------------------------------------------
   992  *
   993  * WaitForRead --
   994  *
   995  *	Wait until some data is available, the console is at
   996  *	EOF or the reader thread is blocked waiting for data (if the
   997  *	channel is in non-blocking mode).
   998  *
   999  * Results:
  1000  *	Returns 1 if console is readable.  Returns 0 if there is no data
  1001  *	on the console, but there is buffered data.  Returns -1 if an
  1002  *	error occurred.  If an error occurred, the threads may not
  1003  *	be synchronized.
  1004  *
  1005  * Side effects:
  1006  *	Updates the shared state flags.  If no error occurred,
  1007  *      the reader thread is blocked waiting for a signal from the 
  1008  *      main thread.
  1009  *
  1010  *----------------------------------------------------------------------
  1011  */
  1012 
  1013 static int
  1014 WaitForRead(
  1015     ConsoleInfo *infoPtr,		/* Console state. */
  1016     int blocking)		/* Indicates whether call should be
  1017 				 * blocking or not. */
  1018 {
  1019     DWORD timeout, count;
  1020     HANDLE *handle = infoPtr->handle;
  1021     INPUT_RECORD input;
  1022     
  1023     while (1) {
  1024 	/*
  1025 	 * Synchronize with the reader thread.
  1026 	 */
  1027        
  1028 	timeout = blocking ? INFINITE : 0;
  1029 	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
  1030 	    /*
  1031 	     * The reader thread is blocked waiting for data and the channel
  1032 	     * is in non-blocking mode.
  1033 	     */
  1034 	    errno = EAGAIN;
  1035 	    return -1;
  1036 	}
  1037 	
  1038 	/*
  1039 	 * At this point, the two threads are synchronized, so it is safe
  1040 	 * to access shared state.
  1041 	 */
  1042 	
  1043 	/*
  1044 	 * If the console has hit EOF, it is always readable.
  1045 	 */
  1046 	
  1047 	if (infoPtr->readFlags & CONSOLE_EOF) {
  1048 	    return 1;
  1049 	}
  1050 	
  1051 	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
  1052             /*
  1053 	     * Check to see if the peek failed because of EOF.
  1054 	     */
  1055 	    
  1056 	    TclWinConvertError(GetLastError());
  1057 	    
  1058 	    if (errno == EOF) {
  1059 		infoPtr->readFlags |= CONSOLE_EOF;
  1060 		return 1;
  1061 	    }
  1062 
  1063 	    /*
  1064 	     * Ignore errors if there is data in the buffer.
  1065 	     */
  1066 	    
  1067 	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
  1068 		return 0;
  1069 	    } else {
  1070 		return -1;
  1071 	    }
  1072 	}
  1073 
  1074 	/*
  1075 	 * If there is data in the buffer, the console must be
  1076 	 * readable (since it is a line-oriented device).
  1077 	 */
  1078 
  1079 	if (infoPtr->readFlags & CONSOLE_BUFFERED) {
  1080 	    return 1;
  1081 	}
  1082 
  1083 	
  1084 	/*
  1085 	 * There wasn't any data available, so reset the thread and
  1086 	 * try again.
  1087 	 */
  1088     
  1089 	ResetEvent(infoPtr->readable);
  1090 	SetEvent(infoPtr->startReader);
  1091     }
  1092 }
  1093 
  1094 /*
  1095  *----------------------------------------------------------------------
  1096  *
  1097  * ConsoleReaderThread --
  1098  *
  1099  *	This function runs in a separate thread and waits for input
  1100  *	to become available on a console.
  1101  *
  1102  * Results:
  1103  *	None.
  1104  *
  1105  * Side effects:
  1106  *	Signals the main thread when input become available.  May
  1107  *	cause the main thread to wake up by posting a message.  May
  1108  *	one line from the console for each wait operation.
  1109  *
  1110  *----------------------------------------------------------------------
  1111  */
  1112 
  1113 static DWORD WINAPI
  1114 ConsoleReaderThread(LPVOID arg)
  1115 {
  1116     ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
  1117     HANDLE *handle = infoPtr->handle;
  1118     DWORD count, waitResult;
  1119     HANDLE wEvents[2];
  1120 
  1121     /* The first event takes precedence. */
  1122     wEvents[0] = infoPtr->stopReader;
  1123     wEvents[1] = infoPtr->startReader;
  1124 
  1125     for (;;) {
  1126 	/*
  1127 	 * Wait for the main thread to signal before attempting to wait.
  1128 	 */
  1129 
  1130 	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1131 
  1132 	if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1133 	    /*
  1134 	     * The start event was not signaled.  It must be the stop event
  1135 	     * or an error, so exit this thread.
  1136 	     */
  1137 
  1138 	    break;
  1139 	}
  1140 
  1141 	count = 0;
  1142 
  1143 	/* 
  1144 	 * Look for data on the console, but first ignore any events
  1145 	 * that are not KEY_EVENTs 
  1146 	 */
  1147 	if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
  1148 		(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
  1149 	    /*
  1150 	     * Data was stored in the buffer.
  1151 	     */
  1152 	    
  1153 	    infoPtr->readFlags |= CONSOLE_BUFFERED;
  1154 	} else {
  1155 	    DWORD err;
  1156 	    err = GetLastError();
  1157 	    
  1158 	    if (err == EOF) {
  1159 		infoPtr->readFlags = CONSOLE_EOF;
  1160 	    }
  1161 	}
  1162 
  1163 	/*
  1164 	 * Signal the main thread by signalling the readable event and
  1165 	 * then waking up the notifier thread.
  1166 	 */
  1167 
  1168 	SetEvent(infoPtr->readable);
  1169 
  1170 	/*
  1171 	 * Alert the foreground thread.  Note that we need to treat this like
  1172 	 * a critical section so the foreground thread does not terminate
  1173 	 * this thread while we are holding a mutex in the notifier code.
  1174 	 */
  1175 
  1176 	Tcl_MutexLock(&consoleMutex);
  1177 	if (infoPtr->threadId != NULL) {
  1178 	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1179 	    Tcl_ThreadAlert(infoPtr->threadId);
  1180 	}
  1181 	Tcl_MutexUnlock(&consoleMutex);
  1182     }
  1183 
  1184     return 0;
  1185 }
  1186 
  1187 /*
  1188  *----------------------------------------------------------------------
  1189  *
  1190  * ConsoleWriterThread --
  1191  *
  1192  *	This function runs in a separate thread and writes data
  1193  *	onto a console.
  1194  *
  1195  * Results:
  1196  *	Always returns 0.
  1197  *
  1198  * Side effects:
  1199  *	Signals the main thread when an output operation is completed.
  1200  *	May cause the main thread to wake up by posting a message.  
  1201  *
  1202  *----------------------------------------------------------------------
  1203  */
  1204 
  1205 static DWORD WINAPI
  1206 ConsoleWriterThread(LPVOID arg)
  1207 {
  1208 
  1209     ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
  1210     HANDLE *handle = infoPtr->handle;
  1211     DWORD count, toWrite, waitResult;
  1212     char *buf;
  1213     HANDLE wEvents[2];
  1214 
  1215     /* The first event takes precedence. */
  1216     wEvents[0] = infoPtr->stopWriter;
  1217     wEvents[1] = infoPtr->startWriter;
  1218 
  1219     for (;;) {
  1220 	/*
  1221 	 * Wait for the main thread to signal before attempting to write.
  1222 	 */
  1223 
  1224 	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
  1225 
  1226 	if (waitResult != (WAIT_OBJECT_0 + 1)) {
  1227 	    /*
  1228 	     * The start event was not signaled.  It must be the stop event
  1229 	     * or an error, so exit this thread.
  1230 	     */
  1231 
  1232 	    break;
  1233 	}
  1234 
  1235 	buf = infoPtr->writeBuf;
  1236 	toWrite = infoPtr->toWrite;
  1237 
  1238 	/*
  1239 	 * Loop until all of the bytes are written or an error occurs.
  1240 	 */
  1241 
  1242 	while (toWrite > 0) {
  1243 	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
  1244 		infoPtr->writeError = GetLastError();
  1245 		break;
  1246 	    } else {
  1247 		toWrite -= count;
  1248 		buf += count;
  1249 	    }
  1250 	}
  1251 
  1252 	/*
  1253 	 * Signal the main thread by signalling the writable event and
  1254 	 * then waking up the notifier thread.
  1255 	 */
  1256 	
  1257 	SetEvent(infoPtr->writable);
  1258 
  1259 	/*
  1260 	 * Alert the foreground thread.  Note that we need to treat this like
  1261 	 * a critical section so the foreground thread does not terminate
  1262 	 * this thread while we are holding a mutex in the notifier code.
  1263 	 */
  1264 
  1265 	Tcl_MutexLock(&consoleMutex);
  1266 	if (infoPtr->threadId != NULL) {
  1267 	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
  1268 	    Tcl_ThreadAlert(infoPtr->threadId);
  1269 	}
  1270 	Tcl_MutexUnlock(&consoleMutex);
  1271     }
  1272 
  1273     return 0;
  1274 }
  1275 
  1276 
  1277 
  1278 /*
  1279  *----------------------------------------------------------------------
  1280  *
  1281  * TclWinOpenConsoleChannel --
  1282  *
  1283  *	Constructs a Console channel for the specified standard OS handle.
  1284  *      This is a helper function to break up the construction of 
  1285  *      channels into File, Console, or Serial.
  1286  *
  1287  * Results:
  1288  *	Returns the new channel, or NULL.
  1289  *
  1290  * Side effects:
  1291  *	May open the channel
  1292  *
  1293  *----------------------------------------------------------------------
  1294  */
  1295 
  1296 Tcl_Channel
  1297 TclWinOpenConsoleChannel(handle, channelName, permissions)
  1298     HANDLE handle;
  1299     char *channelName;
  1300     int permissions;
  1301 {
  1302     char encoding[4 + TCL_INTEGER_SPACE];
  1303     ConsoleInfo *infoPtr;
  1304     DWORD id, modes;
  1305 
  1306     ConsoleInit();
  1307 
  1308     /*
  1309      * See if a channel with this handle already exists.
  1310      */
  1311     
  1312     infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
  1313     memset(infoPtr, 0, sizeof(ConsoleInfo));
  1314 
  1315     infoPtr->validMask = permissions;
  1316     infoPtr->handle = handle;
  1317     infoPtr->channel = (Tcl_Channel) NULL;
  1318 
  1319     wsprintfA(encoding, "cp%d", GetConsoleCP());
  1320 
  1321     infoPtr->threadId = Tcl_GetCurrentThread();
  1322 
  1323     /*
  1324      * Use the pointer for the name of the result channel.
  1325      * This keeps the channel names unique, since some may share
  1326      * handles (stdin/stdout/stderr for instance).
  1327      */
  1328 
  1329     wsprintfA(channelName, "file%lx", (int) infoPtr);
  1330     
  1331     infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
  1332             (ClientData) infoPtr, permissions);
  1333 
  1334     if (permissions & TCL_READABLE) {
  1335 	/*
  1336 	 * Make sure the console input buffer is ready for only character
  1337 	 * input notifications and the buffer is set for line buffering.
  1338 	 * IOW, we only want to catch when complete lines are ready for
  1339 	 * reading.
  1340 	 */
  1341 	GetConsoleMode(infoPtr->handle, &modes);
  1342 	modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  1343 	modes |= ENABLE_LINE_INPUT;
  1344 	SetConsoleMode(infoPtr->handle, modes);
  1345 
  1346 	infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1347 	infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
  1348 	infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
  1349 	infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
  1350 	        infoPtr, 0, &id);
  1351 	SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
  1352     }
  1353 
  1354     if (permissions & TCL_WRITABLE) {
  1355 	infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
  1356 	infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1357 	infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
  1358 	infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
  1359 	        infoPtr, 0, &id);
  1360 	SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
  1361     }
  1362 
  1363     /*
  1364      * Files have default translation of AUTO and ^Z eof char, which
  1365      * means that a ^Z will be accepted as EOF when reading.
  1366      */
  1367     
  1368     Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1369     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
  1370     Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
  1371 
  1372     return infoPtr->channel;
  1373 }
  1374 
  1375 /*
  1376  *----------------------------------------------------------------------
  1377  *
  1378  * ConsoleThreadActionProc --
  1379  *
  1380  *	Insert or remove any thread local refs to this channel.
  1381  *
  1382  * Results:
  1383  *	None.
  1384  *
  1385  * Side effects:
  1386  *	Changes thread local list of valid channels.
  1387  *
  1388  *----------------------------------------------------------------------
  1389  */
  1390 
  1391 static void
  1392 ConsoleThreadActionProc (instanceData, action)
  1393      ClientData instanceData;
  1394      int action;
  1395 {
  1396     ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
  1397 
  1398     /* We do not access firstConsolePtr in the thread structures. This is
  1399      * not for all serials managed by the thread, but only those we are
  1400      * watching. Removal of the filevent handlers before transfer thus
  1401      * takes care of this structure.
  1402      */
  1403 
  1404     Tcl_MutexLock(&consoleMutex);
  1405     if (action == TCL_CHANNEL_THREAD_INSERT) {
  1406         /* We can't copy the thread information from the channel when
  1407 	 * the channel is created. At this time the channel back
  1408 	 * pointer has not been set yet. However in that case the
  1409 	 * threadId has already been set by TclpCreateCommandChannel
  1410 	 * itself, so the structure is still good.
  1411 	 */
  1412 
  1413         ConsoleInit ();
  1414         if (infoPtr->channel != NULL) {
  1415 	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
  1416 	}
  1417     } else {
  1418 	infoPtr->threadId = NULL;
  1419     }
  1420     Tcl_MutexUnlock(&consoleMutex);
  1421 }