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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.44  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25