/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinchan.c
ViewVC logotype

Contents of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinchan.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (show annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (8 years, 1 month ago) by dashley
File MIME type: text/plain
File size: 29388 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 /* $Header$ */
2 /*
3 * tclWinChan.c
4 *
5 * Channel drivers for Windows channels based on files, command
6 * pipes and TCP sockets.
7 *
8 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclwinchan.c,v 1.1.1.1 2001/06/13 04:48:23 dtashley Exp $
14 */
15
16 #include "tclWinInt.h"
17
18 /*
19 * State flags used in the info structures below.
20 */
21
22 #define FILE_PENDING (1<<0) /* Message is pending in the queue. */
23 #define FILE_ASYNC (1<<1) /* Channel is non-blocking. */
24 #define FILE_APPEND (1<<2) /* File is in append mode. */
25
26 #define FILE_TYPE_SERIAL (FILE_TYPE_PIPE+1)
27 #define FILE_TYPE_CONSOLE (FILE_TYPE_PIPE+2)
28
29 /*
30 * The following structure contains per-instance data for a file based channel.
31 */
32
33 typedef struct FileInfo {
34 Tcl_Channel channel; /* Pointer to channel structure. */
35 int validMask; /* OR'ed combination of TCL_READABLE,
36 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
37 * which operations are valid on the file. */
38 int watchMask; /* OR'ed combination of TCL_READABLE,
39 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
40 * which events should be reported. */
41 int flags; /* State flags, see above for a list. */
42 HANDLE handle; /* Input/output file. */
43 struct FileInfo *nextPtr; /* Pointer to next registered file. */
44 } FileInfo;
45
46 typedef struct ThreadSpecificData {
47 /*
48 * List of all file channels currently open.
49 */
50
51 FileInfo *firstFilePtr;
52 } ThreadSpecificData;
53
54 static Tcl_ThreadDataKey dataKey;
55
56 /*
57 * The following structure is what is added to the Tcl event queue when
58 * file events are generated.
59 */
60
61 typedef struct FileEvent {
62 Tcl_Event header; /* Information that is standard for
63 * all events. */
64 FileInfo *infoPtr; /* Pointer to file info structure. Note
65 * that we still have to verify that the
66 * file exists before dereferencing this
67 * pointer. */
68 } FileEvent;
69
70 /*
71 * Static routines for this file:
72 */
73
74 static int FileBlockProc _ANSI_ARGS_((ClientData instanceData,
75 int mode));
76 static void FileChannelExitHandler _ANSI_ARGS_((
77 ClientData clientData));
78 static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
79 int flags));
80 static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
81 Tcl_Interp *interp));
82 static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
83 int flags));
84 static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
85 int direction, ClientData *handlePtr));
86 static ThreadSpecificData *FileInit _ANSI_ARGS_((void));
87 static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
88 char *buf, int toRead, int *errorCode));
89 static int FileOutputProc _ANSI_ARGS_((ClientData instanceData,
90 char *buf, int toWrite, int *errorCode));
91 static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
92 long offset, int mode, int *errorCode));
93 static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
94 int flags));
95 static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
96 int mask));
97
98
99 /*
100 * This structure describes the channel type structure for file based IO.
101 */
102
103 static Tcl_ChannelType fileChannelType = {
104 "file", /* Type name. */
105 FileBlockProc, /* Set blocking or non-blocking mode.*/
106 FileCloseProc, /* Close proc. */
107 FileInputProc, /* Input proc. */
108 FileOutputProc, /* Output proc. */
109 FileSeekProc, /* Seek proc. */
110 NULL, /* Set option proc. */
111 NULL, /* Get option proc. */
112 FileWatchProc, /* Set up the notifier to watch the channel. */
113 FileGetHandleProc, /* Get an OS handle from channel. */
114 };
115
116
117 /*
118 *----------------------------------------------------------------------
119 *
120 * FileInit --
121 *
122 * This function creates the window used to simulate file events.
123 *
124 * Results:
125 * None.
126 *
127 * Side effects:
128 * Creates a new window and creates an exit handler.
129 *
130 *----------------------------------------------------------------------
131 */
132
133 static ThreadSpecificData *
134 FileInit()
135 {
136 ThreadSpecificData *tsdPtr =
137 (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
138 if (tsdPtr == NULL) {
139 tsdPtr = TCL_TSD_INIT(&dataKey);
140 tsdPtr->firstFilePtr = NULL;
141 Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
142 Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL);
143 }
144 return tsdPtr;
145 }
146
147 /*
148 *----------------------------------------------------------------------
149 *
150 * FileChannelExitHandler --
151 *
152 * This function is called to cleanup the channel driver before
153 * Tcl is unloaded.
154 *
155 * Results:
156 * None.
157 *
158 * Side effects:
159 * Destroys the communication window.
160 *
161 *----------------------------------------------------------------------
162 */
163
164 static void
165 FileChannelExitHandler(clientData)
166 ClientData clientData; /* Old window proc */
167 {
168 Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
169 }
170
171 /*
172 *----------------------------------------------------------------------
173 *
174 * FileSetupProc --
175 *
176 * This procedure is invoked before Tcl_DoOneEvent blocks waiting
177 * for an event.
178 *
179 * Results:
180 * None.
181 *
182 * Side effects:
183 * Adjusts the block time if needed.
184 *
185 *----------------------------------------------------------------------
186 */
187
188 void
189 FileSetupProc(data, flags)
190 ClientData data; /* Not used. */
191 int flags; /* Event flags as passed to Tcl_DoOneEvent. */
192 {
193 FileInfo *infoPtr;
194 Tcl_Time blockTime = { 0, 0 };
195 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
196
197 if (!(flags & TCL_FILE_EVENTS)) {
198 return;
199 }
200
201 /*
202 * Check to see if there is a ready file. If so, poll.
203 */
204
205 for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
206 infoPtr = infoPtr->nextPtr) {
207 if (infoPtr->watchMask) {
208 Tcl_SetMaxBlockTime(&blockTime);
209 break;
210 }
211 }
212 }
213
214 /*
215 *----------------------------------------------------------------------
216 *
217 * FileCheckProc --
218 *
219 * This procedure is called by Tcl_DoOneEvent to check the file
220 * event source for events.
221 *
222 * Results:
223 * None.
224 *
225 * Side effects:
226 * May queue an event.
227 *
228 *----------------------------------------------------------------------
229 */
230
231 static void
232 FileCheckProc(data, flags)
233 ClientData data; /* Not used. */
234 int flags; /* Event flags as passed to Tcl_DoOneEvent. */
235 {
236 FileEvent *evPtr;
237 FileInfo *infoPtr;
238 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
239
240 if (!(flags & TCL_FILE_EVENTS)) {
241 return;
242 }
243
244 /*
245 * Queue events for any ready files that don't already have events
246 * queued (caused by persistent states that won't generate WinSock
247 * events).
248 */
249
250 for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
251 infoPtr = infoPtr->nextPtr) {
252 if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
253 infoPtr->flags |= FILE_PENDING;
254 evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
255 evPtr->header.proc = FileEventProc;
256 evPtr->infoPtr = infoPtr;
257 Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
258 }
259 }
260 }
261
262 /*----------------------------------------------------------------------
263 *
264 * FileEventProc --
265 *
266 * This function is invoked by Tcl_ServiceEvent when a file event
267 * reaches the front of the event queue. This procedure invokes
268 * Tcl_NotifyChannel on the file.
269 *
270 * Results:
271 * Returns 1 if the event was handled, meaning it should be removed
272 * from the queue. Returns 0 if the event was not handled, meaning
273 * it should stay on the queue. The only time the event isn't
274 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
275 *
276 * Side effects:
277 * Whatever the notifier callback does.
278 *
279 *----------------------------------------------------------------------
280 */
281
282 static int
283 FileEventProc(evPtr, flags)
284 Tcl_Event *evPtr; /* Event to service. */
285 int flags; /* Flags that indicate what events to
286 * handle, such as TCL_FILE_EVENTS. */
287 {
288 FileEvent *fileEvPtr = (FileEvent *)evPtr;
289 FileInfo *infoPtr;
290 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
291
292 if (!(flags & TCL_FILE_EVENTS)) {
293 return 0;
294 }
295
296 /*
297 * Search through the list of watched files for the one whose handle
298 * matches the event. We do this rather than simply dereferencing
299 * the handle in the event so that files can be deleted while the
300 * event is in the queue.
301 */
302
303 for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
304 infoPtr = infoPtr->nextPtr) {
305 if (fileEvPtr->infoPtr == infoPtr) {
306 infoPtr->flags &= ~(FILE_PENDING);
307 Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
308 break;
309 }
310 }
311 return 1;
312 }
313
314 /*
315 *----------------------------------------------------------------------
316 *
317 * FileBlockProc --
318 *
319 * Set blocking or non-blocking mode on channel.
320 *
321 * Results:
322 * 0 if successful, errno when failed.
323 *
324 * Side effects:
325 * Sets the device into blocking or non-blocking mode.
326 *
327 *----------------------------------------------------------------------
328 */
329
330 static int
331 FileBlockProc(instanceData, mode)
332 ClientData instanceData; /* Instance data for channel. */
333 int mode; /* TCL_MODE_BLOCKING or
334 * TCL_MODE_NONBLOCKING. */
335 {
336 FileInfo *infoPtr = (FileInfo *) instanceData;
337
338 /*
339 * Files on Windows can not be switched between blocking and nonblocking,
340 * hence we have to emulate the behavior. This is done in the input
341 * function by checking against a bit in the state. We set or unset the
342 * bit here to cause the input function to emulate the correct behavior.
343 */
344
345 if (mode == TCL_MODE_NONBLOCKING) {
346 infoPtr->flags |= FILE_ASYNC;
347 } else {
348 infoPtr->flags &= ~(FILE_ASYNC);
349 }
350 return 0;
351 }
352
353 /*
354 *----------------------------------------------------------------------
355 *
356 * FileCloseProc --
357 *
358 * Closes the IO channel.
359 *
360 * Results:
361 * 0 if successful, the value of errno if failed.
362 *
363 * Side effects:
364 * Closes the physical channel
365 *
366 *----------------------------------------------------------------------
367 */
368
369 static int
370 FileCloseProc(instanceData, interp)
371 ClientData instanceData; /* Pointer to FileInfo structure. */
372 Tcl_Interp *interp; /* Not used. */
373 {
374 FileInfo *fileInfoPtr = (FileInfo *) instanceData;
375 FileInfo **nextPtrPtr;
376 int errorCode = 0;
377 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
378
379 /*
380 * Remove the file from the watch list.
381 */
382
383 FileWatchProc(instanceData, 0);
384
385 /*
386 * Don't close the Win32 handle if the handle is a standard channel
387 * during the exit process. Otherwise, one thread may kill the stdio
388 * of another.
389 */
390
391 if (!TclInExit()
392 || ((GetStdHandle(STD_INPUT_HANDLE) != fileInfoPtr->handle)
393 && (GetStdHandle(STD_OUTPUT_HANDLE) != fileInfoPtr->handle)
394 && (GetStdHandle(STD_ERROR_HANDLE) != fileInfoPtr->handle))) {
395 if (CloseHandle(fileInfoPtr->handle) == FALSE) {
396 TclWinConvertError(GetLastError());
397 errorCode = errno;
398 }
399 }
400 for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
401 nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
402 if ((*nextPtrPtr) == fileInfoPtr) {
403 (*nextPtrPtr) = fileInfoPtr->nextPtr;
404 break;
405 }
406 }
407 ckfree((char *)fileInfoPtr);
408 return errorCode;
409 }
410
411 /*
412 *----------------------------------------------------------------------
413 *
414 * FileSeekProc --
415 *
416 * Seeks on a file-based channel. Returns the new position.
417 *
418 * Results:
419 * -1 if failed, the new position if successful. If failed, it
420 * also sets *errorCodePtr to the error code.
421 *
422 * Side effects:
423 * Moves the location at which the channel will be accessed in
424 * future operations.
425 *
426 *----------------------------------------------------------------------
427 */
428
429 static int
430 FileSeekProc(instanceData, offset, mode, errorCodePtr)
431 ClientData instanceData; /* File state. */
432 long offset; /* Offset to seek to. */
433 int mode; /* Relative to where
434 * should we seek? */
435 int *errorCodePtr; /* To store error code. */
436 {
437 FileInfo *infoPtr = (FileInfo *) instanceData;
438 DWORD moveMethod;
439 DWORD newPos;
440
441 *errorCodePtr = 0;
442 if (mode == SEEK_SET) {
443 moveMethod = FILE_BEGIN;
444 } else if (mode == SEEK_CUR) {
445 moveMethod = FILE_CURRENT;
446 } else {
447 moveMethod = FILE_END;
448 }
449
450 newPos = SetFilePointer(infoPtr->handle, offset, NULL, moveMethod);
451 if (newPos == 0xFFFFFFFF) {
452 TclWinConvertError(GetLastError());
453 *errorCodePtr = errno;
454 return -1;
455 }
456 return newPos;
457 }
458
459 /*
460 *----------------------------------------------------------------------
461 *
462 * FileInputProc --
463 *
464 * Reads input from the IO channel into the buffer given. Returns
465 * count of how many bytes were actually read, and an error indication.
466 *
467 * Results:
468 * A count of how many bytes were read is returned and an error
469 * indication is returned in an output argument.
470 *
471 * Side effects:
472 * Reads input from the actual channel.
473 *
474 *----------------------------------------------------------------------
475 */
476
477 static int
478 FileInputProc(instanceData, buf, bufSize, errorCode)
479 ClientData instanceData; /* File state. */
480 char *buf; /* Where to store data read. */
481 int bufSize; /* How much space is available
482 * in the buffer? */
483 int *errorCode; /* Where to store error code. */
484 {
485 FileInfo *infoPtr;
486 DWORD bytesRead;
487
488 *errorCode = 0;
489 infoPtr = (FileInfo *) instanceData;
490
491 /*
492 * Note that we will block on reads from a console buffer until a
493 * full line has been entered. The only way I know of to get
494 * around this is to write a console driver. We should probably
495 * do this at some point, but for now, we just block. The same
496 * problem exists for files being read over the network.
497 */
498
499 if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
500 (LPOVERLAPPED) NULL) != FALSE) {
501 return bytesRead;
502 }
503
504 TclWinConvertError(GetLastError());
505 *errorCode = errno;
506 if (errno == EPIPE) {
507 return 0;
508 }
509 return -1;
510 }
511
512 /*
513 *----------------------------------------------------------------------
514 *
515 * FileOutputProc --
516 *
517 * Writes the given output on the IO channel. Returns count of how
518 * many characters were actually written, and an error indication.
519 *
520 * Results:
521 * A count of how many characters were written is returned and an
522 * error indication is returned in an output argument.
523 *
524 * Side effects:
525 * Writes output on the actual channel.
526 *
527 *----------------------------------------------------------------------
528 */
529
530 static int
531 FileOutputProc(instanceData, buf, toWrite, errorCode)
532 ClientData instanceData; /* File state. */
533 char *buf; /* The data buffer. */
534 int toWrite; /* How many bytes to write? */
535 int *errorCode; /* Where to store error code. */
536 {
537 FileInfo *infoPtr = (FileInfo *) instanceData;
538 DWORD bytesWritten;
539
540 *errorCode = 0;
541
542 /*
543 * If we are writing to a file that was opened with O_APPEND, we need to
544 * seek to the end of the file before writing the current buffer.
545 */
546
547 if (infoPtr->flags & FILE_APPEND) {
548 SetFilePointer(infoPtr->handle, 0, NULL, FILE_END);
549 }
550
551 if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten,
552 (LPOVERLAPPED) NULL) == FALSE) {
553 TclWinConvertError(GetLastError());
554 *errorCode = errno;
555 return -1;
556 }
557 FlushFileBuffers(infoPtr->handle);
558 return bytesWritten;
559 }
560
561 /*
562 *----------------------------------------------------------------------
563 *
564 * FileWatchProc --
565 *
566 * Called by the notifier to set up to watch for events on this
567 * channel.
568 *
569 * Results:
570 * None.
571 *
572 * Side effects:
573 * None.
574 *
575 *----------------------------------------------------------------------
576 */
577
578 static void
579 FileWatchProc(instanceData, mask)
580 ClientData instanceData; /* File state. */
581 int mask; /* What events to watch for; OR-ed
582 * combination of TCL_READABLE,
583 * TCL_WRITABLE and TCL_EXCEPTION. */
584 {
585 FileInfo *infoPtr = (FileInfo *) instanceData;
586 Tcl_Time blockTime = { 0, 0 };
587
588 /*
589 * Since the file is always ready for events, we set the block time
590 * to zero so we will poll.
591 */
592
593 infoPtr->watchMask = mask & infoPtr->validMask;
594 if (infoPtr->watchMask) {
595 Tcl_SetMaxBlockTime(&blockTime);
596 }
597 }
598
599 /*
600 *----------------------------------------------------------------------
601 *
602 * FileGetHandleProc --
603 *
604 * Called from Tcl_GetChannelHandle to retrieve OS handles from
605 * a file based channel.
606 *
607 * Results:
608 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
609 * there is no handle for the specified direction.
610 *
611 * Side effects:
612 * None.
613 *
614 *----------------------------------------------------------------------
615 */
616
617 static int
618 FileGetHandleProc(instanceData, direction, handlePtr)
619 ClientData instanceData; /* The file state. */
620 int direction; /* TCL_READABLE or TCL_WRITABLE */
621 ClientData *handlePtr; /* Where to store the handle. */
622 {
623 FileInfo *infoPtr = (FileInfo *) instanceData;
624
625 if (direction & infoPtr->validMask) {
626 *handlePtr = (ClientData) infoPtr->handle;
627 return TCL_OK;
628 } else {
629 return TCL_ERROR;
630 }
631 }
632
633
634 /*
635 *----------------------------------------------------------------------
636 *
637 * TclpOpenFileChannel --
638 *
639 * Open an File based channel on Unix systems.
640 *
641 * Results:
642 * The new channel or NULL. If NULL, the output argument
643 * errorCodePtr is set to a POSIX error.
644 *
645 * Side effects:
646 * May open the channel and may cause creation of a file on the
647 * file system.
648 *
649 *----------------------------------------------------------------------
650 */
651
652 Tcl_Channel
653 TclpOpenFileChannel(interp, fileName, modeString, permissions)
654 Tcl_Interp *interp; /* Interpreter for error reporting;
655 * can be NULL. */
656 char *fileName; /* Name of file to open. */
657 char *modeString; /* A list of POSIX open modes or
658 * a string such as "rw". */
659 int permissions; /* If the open involves creating a
660 * file, with what modes to create
661 * it? */
662 {
663 Tcl_Channel channel = 0;
664 int seekFlag, mode, channelPermissions;
665 DWORD accessMode, createMode, shareMode, flags, consoleParams, type;
666 TCHAR *nativeName;
667 Tcl_DString ds, buffer;
668 DCB dcb;
669 HANDLE handle;
670 char channelName[16 + TCL_INTEGER_SPACE];
671 TclFile readFile = NULL;
672 TclFile writeFile = NULL;
673
674 mode = TclGetOpenMode(interp, modeString, &seekFlag);
675 if (mode == -1) {
676 return NULL;
677 }
678
679 if (Tcl_TranslateFileName(interp, fileName, &ds) == NULL) {
680 return NULL;
681 }
682 nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&ds),
683 Tcl_DStringLength(&ds), &buffer);
684
685 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
686 case O_RDONLY:
687 accessMode = GENERIC_READ;
688 channelPermissions = TCL_READABLE;
689 break;
690 case O_WRONLY:
691 accessMode = GENERIC_WRITE;
692 channelPermissions = TCL_WRITABLE;
693 break;
694 case O_RDWR:
695 accessMode = (GENERIC_READ | GENERIC_WRITE);
696 channelPermissions = (TCL_READABLE | TCL_WRITABLE);
697 break;
698 default:
699 panic("TclpOpenFileChannel: invalid mode value");
700 break;
701 }
702
703 /*
704 * Map the creation flags to the NT create mode.
705 */
706
707 switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
708 case (O_CREAT | O_EXCL):
709 case (O_CREAT | O_EXCL | O_TRUNC):
710 createMode = CREATE_NEW;
711 break;
712 case (O_CREAT | O_TRUNC):
713 createMode = CREATE_ALWAYS;
714 break;
715 case O_CREAT:
716 createMode = OPEN_ALWAYS;
717 break;
718 case O_TRUNC:
719 case (O_TRUNC | O_EXCL):
720 createMode = TRUNCATE_EXISTING;
721 break;
722 default:
723 createMode = OPEN_EXISTING;
724 break;
725 }
726
727 /*
728 * If the file is being created, get the file attributes from the
729 * permissions argument, else use the existing file attributes.
730 */
731
732 if (mode & O_CREAT) {
733 if (permissions & S_IWRITE) {
734 flags = FILE_ATTRIBUTE_NORMAL;
735 } else {
736 flags = FILE_ATTRIBUTE_READONLY;
737 }
738 } else {
739 flags = (*tclWinProcs->getFileAttributesProc)(nativeName);
740 if (flags == 0xFFFFFFFF) {
741 flags = 0;
742 }
743 }
744
745 /*
746 * Set up the file sharing mode. We want to allow simultaneous access.
747 */
748
749 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
750
751 /*
752 * Now we get to create the file.
753 */
754
755 handle = (*tclWinProcs->createFileProc)(nativeName, accessMode,
756 shareMode, NULL, createMode, flags, (HANDLE) NULL);
757
758 if (handle == INVALID_HANDLE_VALUE) {
759 DWORD err;
760 err = GetLastError();
761 if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
762 err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
763 }
764 TclWinConvertError(err);
765 if (interp != (Tcl_Interp *) NULL) {
766 Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
767 Tcl_PosixError(interp), (char *) NULL);
768 }
769 Tcl_DStringFree(&buffer);
770 return NULL;
771 }
772
773 type = GetFileType(handle);
774
775 /*
776 * If the file is a character device, we need to try to figure out
777 * whether it is a serial port, a console, or something else. We
778 * test for the console case first because this is more common.
779 */
780
781 if (type == FILE_TYPE_CHAR) {
782 if (GetConsoleMode(handle, &consoleParams)) {
783 type = FILE_TYPE_CONSOLE;
784 } else {
785 dcb.DCBlength = sizeof( DCB ) ;
786 if (GetCommState(handle, &dcb)) {
787 type = FILE_TYPE_SERIAL;
788 }
789
790 }
791 }
792
793 channel = NULL;
794
795 switch (type) {
796 case FILE_TYPE_SERIAL:
797 channel = TclWinOpenSerialChannel(handle, channelName,
798 channelPermissions);
799 break;
800 case FILE_TYPE_CONSOLE:
801 channel = TclWinOpenConsoleChannel(handle, channelName,
802 channelPermissions);
803 break;
804 case FILE_TYPE_PIPE:
805 if (channelPermissions & TCL_READABLE) {
806 readFile = TclWinMakeFile(handle);
807 }
808 if (channelPermissions & TCL_WRITABLE) {
809 writeFile = TclWinMakeFile(handle);
810 }
811 channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
812 break;
813 case FILE_TYPE_CHAR:
814 case FILE_TYPE_DISK:
815 case FILE_TYPE_UNKNOWN:
816 channel = TclWinOpenFileChannel(handle, channelName,
817 channelPermissions,
818 (mode & O_APPEND) ? FILE_APPEND : 0);
819 break;
820
821 default:
822 /*
823 * The handle is of an unknown type, probably /dev/nul equivalent
824 * or possibly a closed handle.
825 */
826
827 channel = NULL;
828 Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
829 "bad file type", (char *) NULL);
830 break;
831 }
832
833 Tcl_DStringFree(&buffer);
834 Tcl_DStringFree(&ds);
835
836 if (channel != NULL) {
837 if (seekFlag) {
838 if (Tcl_Seek(channel, 0, SEEK_END) < 0) {
839 if (interp != (Tcl_Interp *) NULL) {
840 Tcl_AppendResult(interp,
841 "could not seek to end of file on \"",
842 channelName, "\": ", Tcl_PosixError(interp),
843 (char *) NULL);
844 }
845 Tcl_Close(NULL, channel);
846 return NULL;
847 }
848 }
849 }
850 return channel;
851 }
852
853 /*
854 *----------------------------------------------------------------------
855 *
856 * Tcl_MakeFileChannel --
857 *
858 * Creates a Tcl_Channel from an existing platform specific file
859 * handle.
860 *
861 * Results:
862 * The Tcl_Channel created around the preexisting file.
863 *
864 * Side effects:
865 * None.
866 *
867 *----------------------------------------------------------------------
868 */
869
870 Tcl_Channel
871 Tcl_MakeFileChannel(rawHandle, mode)
872 ClientData rawHandle; /* OS level handle */
873 int mode; /* ORed combination of TCL_READABLE and
874 * TCL_WRITABLE to indicate file mode. */
875 {
876 char channelName[16 + TCL_INTEGER_SPACE];
877 Tcl_Channel channel = NULL;
878 HANDLE handle = (HANDLE) rawHandle;
879 DCB dcb;
880 DWORD consoleParams;
881 DWORD type;
882 TclFile readFile = NULL;
883 TclFile writeFile = NULL;
884
885 if (mode == 0) {
886 return NULL;
887 }
888
889 type = GetFileType(handle);
890
891 /*
892 * If the file is a character device, we need to try to figure out
893 * whether it is a serial port, a console, or something else. We
894 * test for the console case first because this is more common.
895 */
896
897 if (type == FILE_TYPE_CHAR) {
898 if (GetConsoleMode(handle, &consoleParams)) {
899 type = FILE_TYPE_CONSOLE;
900 } else {
901 dcb.DCBlength = sizeof( DCB ) ;
902 if (GetCommState(handle, &dcb)) {
903 type = FILE_TYPE_SERIAL;
904 }
905 }
906 }
907
908 switch (type)
909 {
910 case FILE_TYPE_SERIAL:
911 channel = TclWinOpenSerialChannel(handle, channelName, mode);
912 break;
913 case FILE_TYPE_CONSOLE:
914 channel = TclWinOpenConsoleChannel(handle, channelName, mode);
915 break;
916 case FILE_TYPE_PIPE:
917 if (mode & TCL_READABLE)
918 {
919 readFile = TclWinMakeFile(handle);
920 }
921 if (mode & TCL_WRITABLE)
922 {
923 writeFile = TclWinMakeFile(handle);
924 }
925 channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
926 break;
927
928 case FILE_TYPE_DISK:
929 case FILE_TYPE_CHAR:
930 case FILE_TYPE_UNKNOWN:
931 channel = TclWinOpenFileChannel(handle, channelName, mode, 0);
932 break;
933
934 default:
935 /*
936 * The handle is of an unknown type, probably /dev/nul equivalent
937 * or possibly a closed handle.
938 */
939
940 channel = NULL;
941 break;
942
943 }
944
945 return channel;
946 }
947
948 /*
949 *----------------------------------------------------------------------
950 *
951 * TclpGetDefaultStdChannel --
952 *
953 * Constructs a channel for the specified standard OS handle.
954 *
955 * Results:
956 * Returns the specified default standard channel, or NULL.
957 *
958 * Side effects:
959 * May cause the creation of a standard channel and the underlying
960 * file.
961 *
962 *----------------------------------------------------------------------
963 */
964
965 Tcl_Channel
966 TclpGetDefaultStdChannel(type)
967 int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
968 {
969 Tcl_Channel channel;
970 HANDLE handle;
971 int mode;
972 char *bufMode;
973 DWORD handleId; /* Standard handle to retrieve. */
974
975 switch (type) {
976 case TCL_STDIN:
977 handleId = STD_INPUT_HANDLE;
978 mode = TCL_READABLE;
979 bufMode = "line";
980 break;
981 case TCL_STDOUT:
982 handleId = STD_OUTPUT_HANDLE;
983 mode = TCL_WRITABLE;
984 bufMode = "line";
985 break;
986 case TCL_STDERR:
987 handleId = STD_ERROR_HANDLE;
988 mode = TCL_WRITABLE;
989 bufMode = "none";
990 break;
991 default:
992 panic("TclGetDefaultStdChannel: Unexpected channel type");
993 break;
994 }
995
996 handle = GetStdHandle(handleId);
997
998 /*
999 * Note that we need to check for 0 because Windows may return 0 if this
1000 * is not a console mode application, even though this is not a valid
1001 * handle.
1002 */
1003
1004 if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
1005 return NULL;
1006 }
1007
1008 channel = Tcl_MakeFileChannel(handle, mode);
1009
1010 if (channel == NULL) {
1011 return NULL;
1012 }
1013
1014 /*
1015 * Set up the normal channel options for stdio handles.
1016 */
1017
1018 if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
1019 "auto") == TCL_ERROR)
1020 || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
1021 "\032 {}") == TCL_ERROR)
1022 || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
1023 "-buffering", bufMode) == TCL_ERROR)) {
1024 Tcl_Close((Tcl_Interp *) NULL, channel);
1025 return (Tcl_Channel) NULL;
1026 }
1027 return channel;
1028 }
1029
1030
1031
1032 /*
1033 *----------------------------------------------------------------------
1034 *
1035 * TclWinOpenFileChannel --
1036 *
1037 * Constructs a File channel for the specified standard OS handle.
1038 * This is a helper function to break up the construction of
1039 * channels into File, Console, or Serial.
1040 *
1041 * Results:
1042 * Returns the new channel, or NULL.
1043 *
1044 * Side effects:
1045 * May open the channel and may cause creation of a file on the
1046 * file system.
1047 *
1048 *----------------------------------------------------------------------
1049 */
1050
1051 Tcl_Channel
1052 TclWinOpenFileChannel(handle, channelName, permissions, appendMode)
1053 HANDLE handle;
1054 char *channelName;
1055 int permissions;
1056 int appendMode;
1057 {
1058 FileInfo *infoPtr;
1059 ThreadSpecificData *tsdPtr;
1060
1061 tsdPtr = FileInit();
1062
1063 /*
1064 * See if a channel with this handle already exists.
1065 */
1066
1067 for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1068 infoPtr = infoPtr->nextPtr) {
1069 if (infoPtr->handle == (HANDLE) handle) {
1070 return (permissions == infoPtr->validMask) ? infoPtr->channel : NULL;
1071 }
1072 }
1073
1074 infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
1075 infoPtr->nextPtr = tsdPtr->firstFilePtr;
1076 tsdPtr->firstFilePtr = infoPtr;
1077 infoPtr->validMask = permissions;
1078 infoPtr->watchMask = 0;
1079 infoPtr->flags = appendMode;
1080 infoPtr->handle = handle;
1081
1082 wsprintfA(channelName, "file%lx", (int) infoPtr);
1083
1084 infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
1085 (ClientData) infoPtr, permissions);
1086
1087 /*
1088 * Files have default translation of AUTO and ^Z eof char, which
1089 * means that a ^Z will be accepted as EOF when reading.
1090 */
1091
1092 Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
1093 Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
1094
1095 return infoPtr->channel;
1096 }
1097
1098 /* End of tclwinchan.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25