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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25