/[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 42 - (hide annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (8 years ago) by dashley
Original Path: projs/trunk/shared_source/tcl_base/tclwinchan.c
File MIME type: text/plain
File size: 30818 byte(s)
Move shared source code to commonize.
1 dashley 25 /* $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