tclWinChan.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:38k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclWinChan.c
  3.  *
  4.  * Channel drivers for Windows channels based on files, command
  5.  * pipes and TCP sockets.
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tclWinChan.c,v 1.30.2.5 2006/08/30 17:53:28 hobbs Exp $
  13.  */
  14. #include "tclWinInt.h"
  15. #include "tclIO.h"
  16. /*
  17.  * State flags used in the info structures below.
  18.  */
  19. #define FILE_PENDING (1<<0) /* Message is pending in the queue. */
  20. #define FILE_ASYNC (1<<1) /* Channel is non-blocking. */
  21. #define FILE_APPEND (1<<2) /* File is in append mode. */
  22. #define FILE_TYPE_SERIAL  (FILE_TYPE_PIPE+1)
  23. #define FILE_TYPE_CONSOLE (FILE_TYPE_PIPE+2)
  24. /*
  25.  * The following structure contains per-instance data for a file based channel.
  26.  */
  27. typedef struct FileInfo {
  28.     Tcl_Channel channel; /* Pointer to channel structure. */
  29.     int validMask; /* OR'ed combination of TCL_READABLE,
  30.  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  31.  * which operations are valid on the file. */
  32.     int watchMask; /* OR'ed combination of TCL_READABLE,
  33.  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  34.  * which events should be reported. */
  35.     int flags; /* State flags, see above for a list. */
  36.     HANDLE handle; /* Input/output file. */
  37.     struct FileInfo *nextPtr; /* Pointer to next registered file. */
  38.     int dirty;                  /* Boolean flag. Set if the OS may have data
  39.  * pending on the channel */
  40. } FileInfo;
  41. typedef struct ThreadSpecificData {
  42.     /*
  43.      * List of all file channels currently open.
  44.      */
  45.     FileInfo *firstFilePtr;
  46. } ThreadSpecificData;
  47. static Tcl_ThreadDataKey dataKey;
  48. /*
  49.  * The following structure is what is added to the Tcl event queue when
  50.  * file events are generated.
  51.  */
  52. typedef struct FileEvent {
  53.     Tcl_Event header; /* Information that is standard for
  54.  * all events. */
  55.     FileInfo *infoPtr; /* Pointer to file info structure.  Note
  56.  * that we still have to verify that the
  57.  * file exists before dereferencing this
  58.  * pointer. */
  59. } FileEvent;
  60. /*
  61.  * Static routines for this file:
  62.  */
  63. static int FileBlockProc _ANSI_ARGS_((ClientData instanceData,
  64.     int mode));
  65. static void FileChannelExitHandler _ANSI_ARGS_((
  66.             ClientData clientData));
  67. static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
  68.     int flags));
  69. static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
  70.             Tcl_Interp *interp));
  71. static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr, 
  72.     int flags));
  73. static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  74.             int direction, ClientData *handlePtr));
  75. static ThreadSpecificData *FileInit _ANSI_ARGS_((void));
  76. static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
  77.                  char *buf, int toRead, int *errorCode));
  78. static int FileOutputProc _ANSI_ARGS_((ClientData instanceData,
  79.     CONST char *buf, int toWrite, int *errorCode));
  80. static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
  81.     long offset, int mode, int *errorCode));
  82. static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData,
  83.     Tcl_WideInt offset, int mode, int *errorCode));
  84. static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
  85.     int flags));
  86. static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
  87.             int mask));
  88. static void             FileThreadActionProc _ANSI_ARGS_ ((
  89.    ClientData instanceData, int action));
  90. static DWORD FileGetType _ANSI_ARGS_((HANDLE handle));
  91. /*
  92.  * This structure describes the channel type structure for file based IO.
  93.  */
  94. static Tcl_ChannelType fileChannelType = {
  95.     "file", /* Type name. */
  96.     TCL_CHANNEL_VERSION_4, /* v4 channel */
  97.     FileCloseProc, /* Close proc. */
  98.     FileInputProc, /* Input proc. */
  99.     FileOutputProc, /* Output proc. */
  100.     FileSeekProc, /* Seek proc. */
  101.     NULL, /* Set option proc. */
  102.     NULL, /* Get option proc. */
  103.     FileWatchProc, /* Set up the notifier to watch the channel. */
  104.     FileGetHandleProc, /* Get an OS handle from channel. */
  105.     NULL, /* close2proc. */
  106.     FileBlockProc, /* Set blocking or non-blocking mode.*/
  107.     NULL, /* flush proc. */
  108.     NULL, /* handler proc. */
  109.     FileWideSeekProc, /* Wide seek proc. */
  110.     FileThreadActionProc, /* Thread action proc. */
  111. };
  112. #ifdef HAVE_NO_SEH
  113. /*
  114.  * Unlike Borland and Microsoft, we don't register exception handlers
  115.  * by pushing registration records onto the runtime stack.  Instead, we
  116.  * register them by creating an EXCEPTION_REGISTRATION within the activation
  117.  * record.
  118.  */
  119. typedef struct EXCEPTION_REGISTRATION {
  120.     struct EXCEPTION_REGISTRATION* link;
  121.     EXCEPTION_DISPOSITION (*handler)( struct _EXCEPTION_RECORD*, void*,
  122.       struct _CONTEXT*, void* );
  123.     void* ebp;
  124.     void* esp;
  125.     int status;
  126. } EXCEPTION_REGISTRATION;
  127. #endif
  128. /*
  129.  *----------------------------------------------------------------------
  130.  *
  131.  * FileInit --
  132.  *
  133.  * This function creates the window used to simulate file events.
  134.  *
  135.  * Results:
  136.  * None.
  137.  *
  138.  * Side effects:
  139.  * Creates a new window and creates an exit handler. 
  140.  *
  141.  *----------------------------------------------------------------------
  142.  */
  143. static ThreadSpecificData *
  144. FileInit()
  145. {
  146.     ThreadSpecificData *tsdPtr =
  147. (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
  148.     if (tsdPtr == NULL) {
  149. tsdPtr = TCL_TSD_INIT(&dataKey);
  150. tsdPtr->firstFilePtr = NULL;
  151. Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
  152. Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL);
  153.     }
  154.     return tsdPtr;
  155. }
  156. /*
  157.  *----------------------------------------------------------------------
  158.  *
  159.  * FileChannelExitHandler --
  160.  *
  161.  * This function is called to cleanup the channel driver before
  162.  * Tcl is unloaded.
  163.  *
  164.  * Results:
  165.  * None.
  166.  *
  167.  * Side effects:
  168.  * Destroys the communication window.
  169.  *
  170.  *----------------------------------------------------------------------
  171.  */
  172. static void
  173. FileChannelExitHandler(clientData)
  174.     ClientData clientData; /* Old window proc */
  175. {
  176.     Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
  177. }
  178. /*
  179.  *----------------------------------------------------------------------
  180.  *
  181.  * FileSetupProc --
  182.  *
  183.  * This procedure is invoked before Tcl_DoOneEvent blocks waiting
  184.  * for an event.
  185.  *
  186.  * Results:
  187.  * None.
  188.  *
  189.  * Side effects:
  190.  * Adjusts the block time if needed.
  191.  *
  192.  *----------------------------------------------------------------------
  193.  */
  194. void
  195. FileSetupProc(data, flags)
  196.     ClientData data; /* Not used. */
  197.     int flags; /* Event flags as passed to Tcl_DoOneEvent. */
  198. {
  199.     FileInfo *infoPtr;
  200.     Tcl_Time blockTime = { 0, 0 };
  201.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  202.     if (!(flags & TCL_FILE_EVENTS)) {
  203. return;
  204.     }
  205.     
  206.     /*
  207.      * Check to see if there is a ready file.  If so, poll.
  208.      */
  209.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  210.     infoPtr = infoPtr->nextPtr) {
  211. if (infoPtr->watchMask) {
  212.     Tcl_SetMaxBlockTime(&blockTime);
  213.     break;
  214. }
  215.     }
  216. }
  217. /*
  218.  *----------------------------------------------------------------------
  219.  *
  220.  * FileCheckProc --
  221.  *
  222.  * This procedure is called by Tcl_DoOneEvent to check the file
  223.  * event source for events. 
  224.  *
  225.  * Results:
  226.  * None.
  227.  *
  228.  * Side effects:
  229.  * May queue an event.
  230.  *
  231.  *----------------------------------------------------------------------
  232.  */
  233. static void
  234. FileCheckProc(data, flags)
  235.     ClientData data; /* Not used. */
  236.     int flags; /* Event flags as passed to Tcl_DoOneEvent. */
  237. {
  238.     FileEvent *evPtr;
  239.     FileInfo *infoPtr;
  240.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  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.     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.  * FileEventProc --
  264.  *
  265.  * This function is invoked by Tcl_ServiceEvent when a file event
  266.  * reaches the front of the event queue.  This procedure invokes
  267.  * Tcl_NotifyChannel on the file.
  268.  *
  269.  * Results:
  270.  * Returns 1 if the event was handled, meaning it should be removed
  271.  * from the queue.  Returns 0 if the event was not handled, meaning
  272.  * it should stay on the queue.  The only time the event isn't
  273.  * handled is if the TCL_FILE_EVENTS flag bit isn't set.
  274.  *
  275.  * Side effects:
  276.  * Whatever the notifier callback does.
  277.  *
  278.  *----------------------------------------------------------------------
  279.  */
  280. static int
  281. FileEventProc(evPtr, flags)
  282.     Tcl_Event *evPtr; /* Event to service. */
  283.     int flags; /* Flags that indicate what events to
  284.  * handle, such as TCL_FILE_EVENTS. */
  285. {
  286.     FileEvent *fileEvPtr = (FileEvent *)evPtr;
  287.     FileInfo *infoPtr;
  288.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  289.     if (!(flags & TCL_FILE_EVENTS)) {
  290. return 0;
  291.     }
  292.     /*
  293.      * Search through the list of watched files for the one whose handle
  294.      * matches the event.  We do this rather than simply dereferencing
  295.      * the handle in the event so that files can be deleted while the
  296.      * event is in the queue.
  297.      */
  298.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
  299.     infoPtr = infoPtr->nextPtr) {
  300. if (fileEvPtr->infoPtr == infoPtr) {
  301.     infoPtr->flags &= ~(FILE_PENDING);
  302.     Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
  303.     break;
  304. }
  305.     }
  306.     return 1;
  307. }
  308. /*
  309.  *----------------------------------------------------------------------
  310.  *
  311.  * FileBlockProc --
  312.  *
  313.  * Set blocking or non-blocking mode on channel.
  314.  *
  315.  * Results:
  316.  * 0 if successful, errno when failed.
  317.  *
  318.  * Side effects:
  319.  * Sets the device into blocking or non-blocking mode.
  320.  *
  321.  *----------------------------------------------------------------------
  322.  */
  323. static int
  324. FileBlockProc(instanceData, mode)
  325.     ClientData instanceData; /* Instance data for channel. */
  326.     int mode; /* TCL_MODE_BLOCKING or
  327.                                  * TCL_MODE_NONBLOCKING. */
  328. {
  329.     FileInfo *infoPtr = (FileInfo *) instanceData;
  330.     
  331.     /*
  332.      * Files on Windows can not be switched between blocking and nonblocking,
  333.      * hence we have to emulate the behavior. This is done in the input
  334.      * function by checking against a bit in the state. We set or unset the
  335.      * bit here to cause the input function to emulate the correct behavior.
  336.      */
  337.     if (mode == TCL_MODE_NONBLOCKING) {
  338. infoPtr->flags |= FILE_ASYNC;
  339.     } else {
  340. infoPtr->flags &= ~(FILE_ASYNC);
  341.     }
  342.     return 0;
  343. }
  344. /*
  345.  *----------------------------------------------------------------------
  346.  *
  347.  * FileCloseProc --
  348.  *
  349.  * Closes the IO channel.
  350.  *
  351.  * Results:
  352.  * 0 if successful, the value of errno if failed.
  353.  *
  354.  * Side effects:
  355.  * Closes the physical channel
  356.  *
  357.  *----------------------------------------------------------------------
  358.  */
  359. static int
  360. FileCloseProc(instanceData, interp)
  361.     ClientData instanceData; /* Pointer to FileInfo structure. */
  362.     Tcl_Interp *interp; /* Not used. */
  363. {
  364.     FileInfo *fileInfoPtr = (FileInfo *) instanceData;
  365.     FileInfo *infoPtr;
  366.     ThreadSpecificData *tsdPtr;
  367.     int errorCode = 0;
  368.     /*
  369.      * Remove the file from the watch list.
  370.      */
  371.     FileWatchProc(instanceData, 0);
  372.     /*
  373.      * Don't close the Win32 handle if the handle is a standard channel
  374.      * during the thread exit process.  Otherwise, one thread may kill
  375.      * the stdio of another.
  376.      */
  377.     if (!TclInThreadExit() 
  378.     || ((GetStdHandle(STD_INPUT_HANDLE) != fileInfoPtr->handle)
  379. && (GetStdHandle(STD_OUTPUT_HANDLE) != fileInfoPtr->handle)
  380. && (GetStdHandle(STD_ERROR_HANDLE) != fileInfoPtr->handle))) {
  381. if (CloseHandle(fileInfoPtr->handle) == FALSE) {
  382.     TclWinConvertError(GetLastError());
  383.     errorCode = errno;
  384. }
  385.     }
  386.     /*
  387.      * See if this FileInfo* is still on the thread local list.
  388.      */
  389.     tsdPtr = TCL_TSD_INIT(&dataKey);
  390.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  391.     infoPtr = infoPtr->nextPtr) {
  392. if (infoPtr == fileInfoPtr) {
  393.             /*
  394.              * This channel exists on the thread local list. It should
  395.              * have been removed by an earlier Thread Action call,
  396.              * but do that now since just deallocating fileInfoPtr would
  397.              * leave an deallocated pointer on the thread local list.
  398.              */
  399.     FileThreadActionProc(fileInfoPtr,TCL_CHANNEL_THREAD_REMOVE);
  400.             break;
  401.         }
  402.     }
  403.     ckfree((char *)fileInfoPtr);
  404.     return errorCode;
  405. }
  406. /*
  407.  *----------------------------------------------------------------------
  408.  *
  409.  * FileSeekProc --
  410.  *
  411.  * Seeks on a file-based channel. Returns the new position.
  412.  *
  413.  * Results:
  414.  * -1 if failed, the new position if successful. If failed, it
  415.  * also sets *errorCodePtr to the error code.
  416.  *
  417.  * Side effects:
  418.  * Moves the location at which the channel will be accessed in
  419.  * future operations.
  420.  *
  421.  *----------------------------------------------------------------------
  422.  */
  423. static int
  424. FileSeekProc(instanceData, offset, mode, errorCodePtr)
  425.     ClientData instanceData; /* File state. */
  426.     long offset; /* Offset to seek to. */
  427.     int mode; /* Relative to where should we seek? */
  428.     int *errorCodePtr; /* To store error code. */
  429. {
  430.     FileInfo *infoPtr = (FileInfo *) instanceData;
  431.     DWORD moveMethod;
  432.     DWORD newPos, newPosHigh;
  433.     DWORD oldPos, oldPosHigh;
  434.     *errorCodePtr = 0;
  435.     if (mode == SEEK_SET) {
  436.         moveMethod = FILE_BEGIN;
  437.     } else if (mode == SEEK_CUR) {
  438.         moveMethod = FILE_CURRENT;
  439.     } else {
  440.         moveMethod = FILE_END;
  441.     }
  442.     /*
  443.      * Save our current place in case we need to roll-back the seek.
  444.      */
  445.     oldPosHigh = (DWORD)0;
  446.     oldPos = SetFilePointer(infoPtr->handle, (LONG)0, &oldPosHigh,
  447.     FILE_CURRENT);
  448.     if (oldPos == INVALID_SET_FILE_POINTER) {
  449. DWORD winError = GetLastError();
  450. if (winError != NO_ERROR) {
  451.     TclWinConvertError(winError);
  452.     *errorCodePtr = errno;
  453.     return -1;
  454. }
  455.     }
  456.     newPosHigh = (DWORD)(offset < 0 ? -1 : 0);
  457.     newPos = SetFilePointer(infoPtr->handle, (LONG) offset, &newPosHigh,
  458.     moveMethod);
  459.     if (newPos == INVALID_SET_FILE_POINTER) {
  460. DWORD winError = GetLastError();
  461. if (winError != NO_ERROR) {
  462.     TclWinConvertError(winError);
  463.     *errorCodePtr = errno;
  464.     return -1;
  465. }
  466.     }
  467.     /*
  468.      * Check for expressability in our return type, and roll-back otherwise.
  469.      */
  470.     if (newPosHigh != 0) {
  471. *errorCodePtr = EOVERFLOW;
  472. SetFilePointer(infoPtr->handle, (LONG)oldPos, &oldPosHigh, FILE_BEGIN);
  473. return -1;
  474.     }
  475.     return (int) newPos;
  476. }
  477. /*
  478.  *----------------------------------------------------------------------
  479.  *
  480.  * FileWideSeekProc --
  481.  *
  482.  * Seeks on a file-based channel. Returns the new position.
  483.  *
  484.  * Results:
  485.  * -1 if failed, the new position if successful. If failed, it
  486.  * also sets *errorCodePtr to the error code.
  487.  *
  488.  * Side effects:
  489.  * Moves the location at which the channel will be accessed in
  490.  * future operations.
  491.  *
  492.  *----------------------------------------------------------------------
  493.  */
  494. static Tcl_WideInt
  495. FileWideSeekProc(instanceData, offset, mode, errorCodePtr)
  496.     ClientData instanceData; /* File state. */
  497.     Tcl_WideInt offset; /* Offset to seek to. */
  498.     int mode; /* Relative to where should we seek? */
  499.     int *errorCodePtr; /* To store error code. */
  500. {
  501.     FileInfo *infoPtr = (FileInfo *) instanceData;
  502.     DWORD moveMethod;
  503.     DWORD newPos, newPosHigh;
  504.     *errorCodePtr = 0;
  505.     if (mode == SEEK_SET) {
  506.         moveMethod = FILE_BEGIN;
  507.     } else if (mode == SEEK_CUR) {
  508.         moveMethod = FILE_CURRENT;
  509.     } else {
  510.         moveMethod = FILE_END;
  511.     }
  512.     newPosHigh = (DWORD)(offset >> 32);
  513.     newPos = SetFilePointer(infoPtr->handle, (LONG) offset, &newPosHigh,
  514.     moveMethod);
  515.     if (newPos == INVALID_SET_FILE_POINTER) {
  516. DWORD winError = GetLastError();
  517. if (winError != NO_ERROR) {
  518.     TclWinConvertError(winError);
  519.     *errorCodePtr = errno;
  520.     return -1;
  521. }
  522.     }
  523.     return ((Tcl_WideInt) newPos) | (((Tcl_WideInt) newPosHigh) << 32);
  524. }
  525. /*
  526.  *----------------------------------------------------------------------
  527.  *
  528.  * FileInputProc --
  529.  *
  530.  * Reads input from the IO channel into the buffer given. Returns
  531.  * count of how many bytes were actually read, and an error indication.
  532.  *
  533.  * Results:
  534.  * A count of how many bytes were read is returned and an error
  535.  * indication is returned in an output argument.
  536.  *
  537.  * Side effects:
  538.  * Reads input from the actual channel.
  539.  *
  540.  *----------------------------------------------------------------------
  541.  */
  542. static int
  543. FileInputProc(instanceData, buf, bufSize, errorCode)
  544.     ClientData instanceData; /* File state. */
  545.     char *buf; /* Where to store data read. */
  546.     int bufSize; /* How much space is available
  547.                                          * in the buffer? */
  548.     int *errorCode; /* Where to store error code. */
  549. {
  550.     FileInfo *infoPtr;
  551.     DWORD bytesRead;
  552.     *errorCode = 0;
  553.     infoPtr = (FileInfo *) instanceData;
  554.     /*
  555.      * Note that we will block on reads from a console buffer until a
  556.      * full line has been entered.  The only way I know of to get
  557.      * around this is to write a console driver.  We should probably
  558.      * do this at some point, but for now, we just block.  The same
  559.      * problem exists for files being read over the network.
  560.      */
  561.     if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
  562.             (LPOVERLAPPED) NULL) != FALSE) {
  563. return bytesRead;
  564.     }
  565.     
  566.     TclWinConvertError(GetLastError());
  567.     *errorCode = errno;
  568.     if (errno == EPIPE) {
  569. return 0;
  570.     }
  571.     return -1;
  572. }
  573. /*
  574.  *----------------------------------------------------------------------
  575.  *
  576.  * FileOutputProc --
  577.  *
  578.  * Writes the given output on the IO channel. Returns count of how
  579.  * many characters were actually written, and an error indication.
  580.  *
  581.  * Results:
  582.  * A count of how many characters were written is returned and an
  583.  * error indication is returned in an output argument.
  584.  *
  585.  * Side effects:
  586.  * Writes output on the actual channel.
  587.  *
  588.  *----------------------------------------------------------------------
  589.  */
  590. static int
  591. FileOutputProc(instanceData, buf, toWrite, errorCode)
  592.     ClientData instanceData; /* File state. */
  593.     CONST char *buf; /* The data buffer. */
  594.     int toWrite; /* How many bytes to write? */
  595.     int *errorCode; /* Where to store error code. */
  596. {
  597.     FileInfo *infoPtr = (FileInfo *) instanceData;
  598.     DWORD bytesWritten;
  599.     
  600.     *errorCode = 0;
  601.     /*
  602.      * If we are writing to a file that was opened with O_APPEND, we need to
  603.      * seek to the end of the file before writing the current buffer.
  604.      */
  605.     if (infoPtr->flags & FILE_APPEND) {
  606.         SetFilePointer(infoPtr->handle, 0, NULL, FILE_END);
  607.     }
  608.     if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten,
  609.             (LPOVERLAPPED) NULL) == FALSE) {
  610.         TclWinConvertError(GetLastError());
  611.         *errorCode = errno;
  612.         return -1;
  613.     }
  614.     infoPtr->dirty = 1;
  615.     return bytesWritten;
  616. }
  617. /*
  618.  *----------------------------------------------------------------------
  619.  *
  620.  * FileWatchProc --
  621.  *
  622.  * Called by the notifier to set up to watch for events on this
  623.  * channel.
  624.  *
  625.  * Results:
  626.  * None.
  627.  *
  628.  * Side effects:
  629.  * None.
  630.  *
  631.  *----------------------------------------------------------------------
  632.  */
  633. static void
  634. FileWatchProc(instanceData, mask)
  635.     ClientData instanceData; /* File state. */
  636.     int mask; /* What events to watch for; OR-ed
  637.                                          * combination of TCL_READABLE,
  638.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  639. {
  640.     FileInfo *infoPtr = (FileInfo *) instanceData;
  641.     Tcl_Time blockTime = { 0, 0 };
  642.     /*
  643.      * Since the file is always ready for events, we set the block time
  644.      * to zero so we will poll.
  645.      */
  646.     infoPtr->watchMask = mask & infoPtr->validMask;
  647.     if (infoPtr->watchMask) {
  648. Tcl_SetMaxBlockTime(&blockTime);
  649.     }
  650. }
  651. /*
  652.  *----------------------------------------------------------------------
  653.  *
  654.  * FileGetHandleProc --
  655.  *
  656.  * Called from Tcl_GetChannelHandle to retrieve OS handles from
  657.  * a file based channel.
  658.  *
  659.  * Results:
  660.  * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  661.  * there is no handle for the specified direction. 
  662.  *
  663.  * Side effects:
  664.  * None.
  665.  *
  666.  *----------------------------------------------------------------------
  667.  */
  668. static int
  669. FileGetHandleProc(instanceData, direction, handlePtr)
  670.     ClientData instanceData; /* The file state. */
  671.     int direction; /* TCL_READABLE or TCL_WRITABLE */
  672.     ClientData *handlePtr; /* Where to store the handle.  */
  673. {
  674.     FileInfo *infoPtr = (FileInfo *) instanceData;
  675.     if (direction & infoPtr->validMask) {
  676. *handlePtr = (ClientData) infoPtr->handle;
  677. return TCL_OK;
  678.     } else {
  679. return TCL_ERROR;
  680.     }
  681. }
  682. /*
  683.  *----------------------------------------------------------------------
  684.  *
  685.  * TclpOpenFileChannel --
  686.  *
  687.  * Open an File based channel on Unix systems.
  688.  *
  689.  * Results:
  690.  * The new channel or NULL. If NULL, the output argument
  691.  * errorCodePtr is set to a POSIX error.
  692.  *
  693.  * Side effects:
  694.  * May open the channel and may cause creation of a file on the
  695.  * file system.
  696.  *
  697.  *----------------------------------------------------------------------
  698.  */
  699. Tcl_Channel
  700. TclpOpenFileChannel(interp, pathPtr, mode, permissions)
  701.     Tcl_Interp *interp; /* Interpreter for error reporting;
  702.                                          * can be NULL. */
  703.     Tcl_Obj *pathPtr; /* Name of file to open. */
  704.     int mode; /* POSIX mode. */
  705.     int permissions; /* If the open involves creating a
  706.                                          * file, with what modes to create
  707.                                          * it? */
  708. {
  709.     Tcl_Channel channel = 0;
  710.     int channelPermissions;
  711.     DWORD accessMode, createMode, shareMode, flags;
  712.     CONST TCHAR *nativeName;
  713.     HANDLE handle;
  714.     char channelName[16 + TCL_INTEGER_SPACE];
  715.     TclFile readFile = NULL;
  716.     TclFile writeFile = NULL;
  717.     nativeName = (TCHAR*) Tcl_FSGetNativePath(pathPtr);
  718.     if (nativeName == NULL) {
  719. return NULL;
  720.     }
  721.     
  722.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  723. case O_RDONLY:
  724.     accessMode = GENERIC_READ;
  725.     channelPermissions = TCL_READABLE;
  726.     break;
  727. case O_WRONLY:
  728.     accessMode = GENERIC_WRITE;
  729.     channelPermissions = TCL_WRITABLE;
  730.     break;
  731. case O_RDWR:
  732.     accessMode = (GENERIC_READ | GENERIC_WRITE);
  733.     channelPermissions = (TCL_READABLE | TCL_WRITABLE);
  734.     break;
  735. default:
  736.     panic("TclpOpenFileChannel: invalid mode value");
  737.     break;
  738.     }
  739.     /*
  740.      * Map the creation flags to the NT create mode.
  741.      */
  742.     switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
  743. case (O_CREAT | O_EXCL):
  744. case (O_CREAT | O_EXCL | O_TRUNC):
  745.     createMode = CREATE_NEW;
  746.     break;
  747. case (O_CREAT | O_TRUNC):
  748.     createMode = CREATE_ALWAYS;
  749.     break;
  750. case O_CREAT:
  751.     createMode = OPEN_ALWAYS;
  752.     break;
  753. case O_TRUNC:
  754. case (O_TRUNC | O_EXCL):
  755.     createMode = TRUNCATE_EXISTING;
  756.     break;
  757. default:
  758.     createMode = OPEN_EXISTING;
  759.     break;
  760.     }
  761.     /*
  762.      * If the file is being created, get the file attributes from the
  763.      * permissions argument, else use the existing file attributes.
  764.      */
  765.     if (mode & O_CREAT) {
  766.         if (permissions & S_IWRITE) {
  767.             flags = FILE_ATTRIBUTE_NORMAL;
  768.         } else {
  769.             flags = FILE_ATTRIBUTE_READONLY;
  770.         }
  771.     } else {
  772. flags = (*tclWinProcs->getFileAttributesProc)(nativeName);
  773.         if (flags == 0xFFFFFFFF) {
  774.     flags = 0;
  775. }
  776.     }
  777.     /*
  778.      * Set up the file sharing mode.  We want to allow simultaneous access.
  779.      */
  780.     shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
  781.     /*
  782.      * Now we get to create the file.
  783.      */
  784.     handle = (*tclWinProcs->createFileProc)(nativeName, accessMode, 
  785.     shareMode, NULL, createMode, flags, (HANDLE) NULL);
  786.     if (handle == INVALID_HANDLE_VALUE) {
  787. DWORD err;
  788. err = GetLastError();
  789. if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
  790.     err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
  791. }
  792.         TclWinConvertError(err);
  793. if (interp != (Tcl_Interp *) NULL) {
  794.             Tcl_AppendResult(interp, "couldn't open "", 
  795.      Tcl_GetString(pathPtr), "": ",
  796.      Tcl_PosixError(interp), (char *) NULL);
  797.         }
  798.         return NULL;
  799.     }
  800.     
  801.     channel = NULL;
  802.     switch ( FileGetType(handle) ) {
  803.     case FILE_TYPE_SERIAL:
  804. /*
  805.  * Reopen channel for OVERLAPPED operation
  806.  * Normally this shouldn't fail, because the channel exists
  807.  */
  808. handle = TclWinSerialReopen(handle, nativeName, accessMode);
  809. if (handle == INVALID_HANDLE_VALUE) {
  810.     TclWinConvertError(GetLastError());
  811.     if (interp != (Tcl_Interp *) NULL) {
  812. Tcl_AppendResult(interp, "couldn't reopen serial "",
  813. Tcl_GetString(pathPtr), "": ",
  814. Tcl_PosixError(interp), (char *) NULL);
  815.     }
  816.     return NULL;
  817. }
  818. channel = TclWinOpenSerialChannel(handle, channelName,
  819.         channelPermissions);
  820. break;
  821.     case FILE_TYPE_CONSOLE:
  822. channel = TclWinOpenConsoleChannel(handle, channelName,
  823.         channelPermissions);
  824. break;
  825.     case FILE_TYPE_PIPE:
  826. if (channelPermissions & TCL_READABLE) {
  827.     readFile = TclWinMakeFile(handle);
  828. }
  829. if (channelPermissions & TCL_WRITABLE) {
  830.     writeFile = TclWinMakeFile(handle);
  831. }
  832. channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
  833. break;
  834.     case FILE_TYPE_CHAR:
  835.     case FILE_TYPE_DISK:
  836.     case FILE_TYPE_UNKNOWN:
  837. channel = TclWinOpenFileChannel(handle, channelName,
  838. channelPermissions,
  839. (mode & O_APPEND) ? FILE_APPEND : 0);
  840. break;
  841.     default:
  842. /*
  843.  * The handle is of an unknown type, probably /dev/nul equivalent
  844.  * or possibly a closed handle.  
  845.  */
  846. channel = NULL;
  847. Tcl_AppendResult(interp, "couldn't open "", 
  848.  Tcl_GetString(pathPtr), "": ",
  849.  "bad file type", (char *) NULL);
  850. break;
  851.     }
  852.     return channel;
  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. 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. #ifdef HAVE_NO_SEH
  877.     EXCEPTION_REGISTRATION registration;
  878. #endif
  879.     char channelName[16 + TCL_INTEGER_SPACE];
  880.     Tcl_Channel channel = NULL;
  881.     HANDLE handle = (HANDLE) rawHandle;
  882.     HANDLE dupedHandle;
  883.     TclFile readFile = NULL;
  884.     TclFile writeFile = NULL;
  885.     BOOL result;
  886.     if (mode == 0) {
  887. return NULL;
  888.     }
  889.     switch (FileGetType(handle))
  890.     {
  891.     case FILE_TYPE_SERIAL:
  892. channel = TclWinOpenSerialChannel(handle, channelName, mode);
  893. break;
  894.     case FILE_TYPE_CONSOLE:
  895. channel = TclWinOpenConsoleChannel(handle, channelName, mode);
  896. break;
  897.     case FILE_TYPE_PIPE:
  898. if (mode & TCL_READABLE)
  899. {
  900.     readFile = TclWinMakeFile(handle);
  901. }
  902. if (mode & TCL_WRITABLE)
  903. {
  904.     writeFile = TclWinMakeFile(handle);
  905. }
  906. channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);
  907. break;
  908.     case FILE_TYPE_DISK:
  909.     case FILE_TYPE_CHAR:
  910. channel = TclWinOpenFileChannel(handle, channelName, mode, 0);
  911. break;
  912.     case FILE_TYPE_UNKNOWN:
  913.     default:
  914. /*
  915.  * The handle is of an unknown type.  Test the validity of this OS
  916.  * handle by duplicating it, then closing the dupe.  The Win32 API
  917.  * doesn't provide an IsValidHandle() function, so we have to emulate
  918.  * it here.  This test will not work on a console handle reliably,
  919.  * which is why we can't test every handle that comes into this
  920.  * function in this way.
  921.  */
  922. result = DuplicateHandle(GetCurrentProcess(), handle,
  923. GetCurrentProcess(), &dupedHandle, 0, FALSE,
  924. DUPLICATE_SAME_ACCESS);
  925. if (result == 0) {
  926.     /* 
  927.      * Unable to make a duplicate. It's definately invalid at this
  928.      * point.
  929.      */
  930.     return NULL;
  931. }
  932. /*
  933.  * Use structured exception handling (Win32 SEH) to protect the close
  934.  * of this duped handle which might throw EXCEPTION_INVALID_HANDLE.
  935.  */
  936. result = 0;
  937. #ifndef HAVE_NO_SEH
  938. __try {
  939.     CloseHandle(dupedHandle);
  940.     result = 1;
  941. } __except (EXCEPTION_EXECUTE_HANDLER) {}
  942. #else
  943. /*
  944.  * Don't have SEH available, do things the hard way.
  945.  * Note that this needs to be one block of asm, to avoid stack
  946.  * imbalance; also, it is illegal for one asm block to contain 
  947.  * a jump to another.
  948.  */
  949. __asm__ __volatile__ (
  950.     /*
  951.      * Pick up parameters before messing with the stack
  952.      */
  953.     "movl       %[dupedHandle], %%ebx"          "nt"
  954.     /*
  955.      * Construct an EXCEPTION_REGISTRATION to protect the
  956.      * call to CloseHandle
  957.      */
  958.     "leal       %[registration], %%edx"         "nt"
  959.     "movl       %%fs:0,         %%eax"          "nt"
  960.     "movl       %%eax,          0x0(%%edx)"     "nt" /* link */
  961.     "leal       1f,             %%eax"          "nt"
  962.     "movl       %%eax,          0x4(%%edx)"     "nt" /* handler */
  963.     "movl       %%ebp,          0x8(%%edx)"     "nt" /* ebp */
  964.     "movl       %%esp,          0xc(%%edx)"     "nt" /* esp */
  965.     "movl       $0,             0x10(%%edx)"    "nt" /* status */
  966.     /* Link the EXCEPTION_REGISTRATION on the chain */
  967.     
  968.     "movl       %%edx,          %%fs:0"         "nt"
  969.     
  970.     /* Call CloseHandle( dupedHandle ) */
  971.     
  972.     "pushl      %%ebx"                          "nt"
  973.     "call       _CloseHandle@4"                 "nt"
  974.     
  975.     /* 
  976.      * Come here on normal exit.  Recover the EXCEPTION_REGISTRATION
  977.      * and put a TRUE status return into it.
  978.      */
  979.     
  980.     "movl       %%fs:0,         %%edx"          "nt"
  981.     "movl $1, %%eax" "nt"
  982.     "movl       %%eax,          0x10(%%edx)"    "nt"
  983.     "jmp        2f"                             "n"
  984.     
  985.     /*
  986.      * Come here on an exception.  Recover the EXCEPTION_REGISTRATION
  987.      */
  988.     
  989.     "1:"                                        "t"
  990.     "movl       %%fs:0,         %%edx"          "nt"
  991.     "movl       0x8(%%edx),     %%edx"          "nt"
  992.     
  993.     /* 
  994.      * Come here however we exited.  Restore context from the
  995.      * EXCEPTION_REGISTRATION in case the stack is unbalanced.
  996.      */
  997.     
  998.     "2:"                                        "t"
  999.     "movl       0xc(%%edx),     %%esp"          "nt"
  1000.     "movl       0x8(%%edx),     %%ebp"          "nt"
  1001.     "movl       0x0(%%edx),     %%eax"          "nt"
  1002.     "movl       %%eax,          %%fs:0"         "nt"
  1003.     
  1004.     :
  1005.     /* No outputs */
  1006.     :
  1007.     [registration]  "m"     (registration),
  1008.     [dupedHandle]   "m"     (dupedHandle)
  1009.     :
  1010.     "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"
  1011.     );
  1012. result = registration.status;
  1013. #endif
  1014. if (result == FALSE) {
  1015.     return NULL;
  1016. }
  1017. /* Fall through, the handle is valid. */
  1018. /*
  1019.  * Create the undefined channel, anyways, because we know the handle
  1020.  * is valid to something.
  1021.  */
  1022. channel = TclWinOpenFileChannel(handle, channelName, mode, 0);
  1023.     }
  1024.     return channel;
  1025. }
  1026. /*
  1027.  *----------------------------------------------------------------------
  1028.  *
  1029.  * TclpGetDefaultStdChannel --
  1030.  *
  1031.  * Constructs a channel for the specified standard OS handle.
  1032.  *
  1033.  * Results:
  1034.  * Returns the specified default standard channel, or NULL.
  1035.  *
  1036.  * Side effects:
  1037.  * May cause the creation of a standard channel and the underlying
  1038.  * file.
  1039.  *
  1040.  *----------------------------------------------------------------------
  1041.  */
  1042. Tcl_Channel
  1043. TclpGetDefaultStdChannel(type)
  1044.     int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
  1045. {
  1046.     Tcl_Channel channel;
  1047.     HANDLE handle;
  1048.     int mode;
  1049.     char *bufMode;
  1050.     DWORD handleId; /* Standard handle to retrieve. */
  1051.     switch (type) {
  1052. case TCL_STDIN:
  1053.     handleId = STD_INPUT_HANDLE;
  1054.     mode = TCL_READABLE;
  1055.     bufMode = "line";
  1056.     break;
  1057. case TCL_STDOUT:
  1058.     handleId = STD_OUTPUT_HANDLE;
  1059.     mode = TCL_WRITABLE;
  1060.     bufMode = "line";
  1061.     break;
  1062. case TCL_STDERR:
  1063.     handleId = STD_ERROR_HANDLE;
  1064.     mode = TCL_WRITABLE;
  1065.     bufMode = "none";
  1066.     break;
  1067. default:
  1068.     panic("TclGetDefaultStdChannel: Unexpected channel type");
  1069.     break;
  1070.     }
  1071.     handle = GetStdHandle(handleId);
  1072.     /*
  1073.      * Note that we need to check for 0 because Windows may return 0 if this
  1074.      * is not a console mode application, even though this is not a valid
  1075.      * handle.
  1076.      */
  1077.     if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
  1078. return (Tcl_Channel) NULL;
  1079.     }
  1080.     channel = Tcl_MakeFileChannel(handle, mode);
  1081.     if (channel == NULL) {
  1082. return (Tcl_Channel) NULL;
  1083.     }
  1084.     /*
  1085.      * Set up the normal channel options for stdio handles.
  1086.      */
  1087.     if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
  1088.             "auto") == TCL_ERROR)
  1089.     || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
  1090.     "32 {}") == TCL_ERROR)
  1091.     || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
  1092.     "-buffering", bufMode) == TCL_ERROR)) {
  1093.         Tcl_Close((Tcl_Interp *) NULL, channel);
  1094.         return (Tcl_Channel) NULL;
  1095.     }
  1096.     return channel;
  1097. }
  1098. /*
  1099.  *----------------------------------------------------------------------
  1100.  *
  1101.  * TclWinOpenFileChannel --
  1102.  *
  1103.  * Constructs a File channel for the specified standard OS handle.
  1104.  *      This is a helper function to break up the construction of 
  1105.  *      channels into File, Console, or Serial.
  1106.  *
  1107.  * Results:
  1108.  * Returns the new channel, or NULL.
  1109.  *
  1110.  * Side effects:
  1111.  * May open the channel and may cause creation of a file on the
  1112.  * file system.
  1113.  *
  1114.  *----------------------------------------------------------------------
  1115.  */
  1116. Tcl_Channel
  1117. TclWinOpenFileChannel(handle, channelName, permissions, appendMode)
  1118.     HANDLE handle;
  1119.     char *channelName;
  1120.     int permissions;
  1121.     int appendMode;
  1122. {
  1123.     FileInfo *infoPtr;
  1124.     ThreadSpecificData *tsdPtr;
  1125.     tsdPtr = FileInit();
  1126.     /*
  1127.      * See if a channel with this handle already exists.
  1128.      */
  1129.     
  1130.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  1131.  infoPtr = infoPtr->nextPtr) {
  1132. if (infoPtr->handle == (HANDLE) handle) {
  1133.     return (permissions == infoPtr->validMask) ? infoPtr->channel : NULL;
  1134. }
  1135.     }
  1136.     infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
  1137.     /* TIP #218. Removed the code inserting the new structure
  1138.      * into the global list. This is now handled in the thread
  1139.      * action callbacks, and only there.
  1140.      */
  1141.     infoPtr->nextPtr = NULL;
  1142.     infoPtr->validMask = permissions;
  1143.     infoPtr->watchMask = 0;
  1144.     infoPtr->flags = appendMode;
  1145.     infoPtr->handle = handle;
  1146.     infoPtr->dirty = 0;
  1147.     wsprintfA(channelName, "file%lx", (int) infoPtr);
  1148.     
  1149.     infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
  1150.     (ClientData) infoPtr, permissions);
  1151.     
  1152.     /*
  1153.      * Files have default translation of AUTO and ^Z eof char, which
  1154.      * means that a ^Z will be accepted as EOF when reading.
  1155.      */
  1156.     
  1157.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1158.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "32 {}");
  1159.     return infoPtr->channel;
  1160. }
  1161. /*
  1162.  *----------------------------------------------------------------------
  1163.  *
  1164.  * TclWinFlushDirtyChannels --
  1165.  *
  1166.  * Flush all dirty channels to disk, so that requesting the
  1167.  * size of any file returns the correct value.
  1168.  *
  1169.  * Results:
  1170.  * None.
  1171.  *
  1172.  * Side effects:
  1173.  * Information is actually written to disk now, rather than
  1174.  * later.  Don't call this too often, or there will be a 
  1175.  * performance hit (i.e. only call when we need to ask for
  1176.  * the size of a file).
  1177.  *
  1178.  *----------------------------------------------------------------------
  1179.  */
  1180. void
  1181. TclWinFlushDirtyChannels ()
  1182. {
  1183.     FileInfo *infoPtr;
  1184.     ThreadSpecificData *tsdPtr;
  1185.     tsdPtr = FileInit();
  1186.     /*
  1187.      * Flush all channels which are dirty, i.e. may have data pending
  1188.      * in the OS
  1189.      */
  1190.     
  1191.     for (infoPtr = tsdPtr->firstFilePtr;
  1192.  infoPtr != NULL; 
  1193.  infoPtr = infoPtr->nextPtr) {
  1194. if (infoPtr->dirty) {
  1195.     FlushFileBuffers(infoPtr->handle);
  1196.     infoPtr->dirty = 0;
  1197. }
  1198.     }
  1199. }
  1200. /*
  1201.  *----------------------------------------------------------------------
  1202.  *
  1203.  * FileThreadActionProc --
  1204.  *
  1205.  * Insert or remove any thread local refs to this channel.
  1206.  *
  1207.  * Results:
  1208.  * None.
  1209.  *
  1210.  * Side effects:
  1211.  * Changes thread local list of valid channels.
  1212.  *
  1213.  *----------------------------------------------------------------------
  1214.  */
  1215. static void
  1216. FileThreadActionProc (instanceData, action)
  1217.      ClientData instanceData;
  1218.      int action;
  1219. {
  1220.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1221.     FileInfo *infoPtr = (FileInfo *) instanceData;
  1222.     if (action == TCL_CHANNEL_THREAD_INSERT) {
  1223.         infoPtr->nextPtr = tsdPtr->firstFilePtr;
  1224. tsdPtr->firstFilePtr = infoPtr;
  1225.     } else {
  1226.         FileInfo **nextPtrPtr;
  1227. int removed = 0;
  1228. for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
  1229.      nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
  1230.     if ((*nextPtrPtr) == infoPtr) {
  1231.         (*nextPtrPtr) = infoPtr->nextPtr;
  1232. removed = 1;
  1233. break;
  1234.     }
  1235. }
  1236. /*
  1237.  * This could happen if the channel was created in one thread
  1238.  * and then moved to another without updating the thread
  1239.  * local data in each thread.
  1240.  */
  1241. if (!removed) {
  1242.     panic("file info ptr not on thread channel list");
  1243. }
  1244.     }
  1245. }
  1246. /*
  1247.  *----------------------------------------------------------------------
  1248.  *
  1249.  * FileGetType --
  1250.  *
  1251.  * Given a file handle, return its type
  1252.  *
  1253.  * Results:
  1254.  * None.
  1255.  *
  1256.  * Side effects:
  1257.  * None.
  1258.  *
  1259.  *----------------------------------------------------------------------
  1260.  */
  1261. DWORD
  1262. FileGetType(handle)
  1263.     HANDLE handle; /* Opened file handle */
  1264.     DWORD type;
  1265.     DWORD consoleParams;
  1266.     DCB dcb;
  1267.     type = GetFileType(handle);
  1268.     /*
  1269.      * If the file is a character device, we need to try to figure out
  1270.      * whether it is a serial port, a console, or something else.  We
  1271.      * test for the console case first because this is more common.
  1272.      */
  1273.     
  1274.     if (type == FILE_TYPE_CHAR || (type == FILE_TYPE_UNKNOWN && !GetLastError())) {
  1275.     if (GetConsoleMode(handle, &consoleParams)) {
  1276.       type = FILE_TYPE_CONSOLE;
  1277.       } else {
  1278.       dcb.DCBlength = sizeof( DCB ) ;
  1279.       if (GetCommState(handle, &dcb)) {
  1280.       type = FILE_TYPE_SERIAL;
  1281.         }
  1282.       }
  1283.     }
  1284.     return type;
  1285. }