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