OSDN Git Service

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