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

Annotation of /projs/emts/trunk/src/c_tcl_base_7_5_w_mods/tclwinchan.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 269 - (hide annotations) (download)
Sat Jun 1 21:29:58 2019 UTC (5 years, 4 months ago) by dashley
File MIME type: text/plain
File size: 29388 byte(s)
Rename from ETS to EMTS.
1 dashley 71 /* $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