/[dtapublic]/projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinconsole.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tcl_base_7_5_w_mods/tclwinconsole.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.67  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25