home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tcltk805.zip / tcl805s.zip / tcl8.0.5 / os2 / tclOS2Chan.c < prev    next >
C/C++ Source or Header  |  2001-02-09  |  60KB  |  1,795 lines

  1. /* 
  2.  * tclOS2Chan.c
  3.  *
  4.  *    Common channel driver for OS/2 channels based on files, command
  5.  *    pipes and TCP sockets (EMX).
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  * Copyright (c) 1999-2001 Illya Vaes
  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.  * SCCS: @(#) tclUnixChan.c 1.203 97/06/20 13:03:18
  14.  */
  15.  
  16.  
  17. /*
  18.  * Serial ports are manipulated via DosDevIOCtl, category 1h ASYNC (RS232-C)
  19.  * Other categories: 03h (Video), 04h (Keyboard), 05h (Parallel Port),
  20.  * 07h (Mouse), 08h (logical Disk), 09h (Physical Disk), 0Ah (Character Device),
  21.  * 0Bh (General Device), 0Ch (Advanced Power Management), 80h (Screen Control,
  22.  * OEMHLP, Adapter Presence-Check Services, Resource Manager and CD-ROM Drive
  23.  * and Disc) and 81h (Touch Device-Dependent Driver).
  24.  * Summary of Category 01h IOCtl Commands (symbol for category IOCTL_ASYNC):
  25.  * Func  Description                                Symbol
  26.  *  14h  Reserved
  27.  *  34h  Reserved
  28.  *  41h  Set Bit Rate                                 ASYNC_SETBAUDRATE
  29.  *  42h  Set Line Characteristics (stop/parity/data)  ASYNC_SETLINECTRL
  30.  *  43h  Extended Set Bit Rate                        ASYNC_EXTSETBAUDRATE
  31.  *  44h  Transmit Byte Immediate                      ASYNC_TRANSMITIMM
  32.  *  45h  Set Break OFF                                ASYNC_SETBREAKOFF
  33.  *  46h  Set Modem Control Signals                    ASYNC_SETMODEMCTRL
  34.  *  47h  Behave as if XOFF received (Stop transmit)   ASYNC_STOPTRANSMIT
  35.  *  48h  Behave as if XON received (Start transmit)   ASYNC_STARTTRANSMIT
  36.  *  49h  Reserved                                        
  37.  *  4Bh  Set Break ON                                 ASYNC_SETBREAKON
  38.  *  53h  Set Device Control Block (DCB) Parameters    ASYNC_SETDCBINFO
  39.  *  54h  Set Enhanced Mode Parameters                 ASYNC_SETENHANCEDMODEPARMS
  40.  *  61h  Query Current Bit Rate                       ASYNC_GETBAUDRATE
  41.  *  62h  Query Line Characteristics                   ASYNC_GETLINECTRL
  42.  *  63h  Extended Query Bit Rate                      ASYNC_EXTGETBAUDRATE
  43.  *  64h  Query COM Status                             ASYNC_GETCOMMSTATUS
  44.  *  65h  Query Transmit Data Status                   ASYNC_GETLINESTATUS
  45.  *  66h  Query Modem Control Output Signals           ASYNC_GETMODEMOUTPUT
  46.  *  67h  Query Current Modem Input Signals            ASYNC_GETMODEMINPUT
  47.  *  68h  Query Nr of Characters in Receive Queue      ASYNC_GETINQUECOUNT
  48.  *  69h  Query Nr of Characters in Transmit Queue     ASYNC_GETOUTQUECOUNT
  49.  *  6Dh  Query COM Error                              ASYNC_GETCOMMERROR
  50.  *  72h  Query COM Event Information                  ASYNC_GETCOMMEVENT
  51.  *  73h  Query Device Control Block (DCB) Parms       ASYNC_GETDCBINFO
  52.  *  74h  Query Enhanced Mode Parameters               ASYNC_GETENHANCEDMODEPARMS
  53.  *
  54.  * To get the DosDevIOCtl declarations, we need to define INCL_DOSDEVIOCTL
  55.  * before including os2.h, ie. before including tclOS2Int.h.
  56.  */
  57.  
  58. #define INCL_DOSDEVIOCTL
  59. #include    "tclOS2Int.h"
  60. #undef INCL_DOSDEVIOCTL
  61.  
  62. /*
  63.  * This is the size of the channel name for File based channels
  64.  */
  65.  
  66. #define CHANNEL_NAME_SIZE       64
  67. static char channelName[CHANNEL_NAME_SIZE+1];
  68.  
  69. /*
  70.  * The following variable is used to tell whether this module has been
  71.  * initialized.
  72.  */
  73.  
  74. static int initialized = 0;
  75.  
  76. /*
  77.  * State flags used in the info structures below.
  78.  */
  79.  
  80. #define FILE_PENDING    (1<<0)  /* Message is pending in the queue. */
  81. #define FILE_ASYNC      (1<<1)  /* Channel is non-blocking. */
  82. #define FILE_APPEND     (1<<2)  /* File is in append mode. */
  83.  
  84. /*
  85.  * The following structure contains per-instance data for a file based channel
  86.  */
  87.  
  88. typedef struct FileInfo {
  89.     Tcl_Channel channel;        /* Pointer to channel structure. */
  90.     int validMask;              /* OR'ed combination of TCL_READABLE,
  91.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  92.                                  * which operations are valid on the file. */
  93.     int watchMask;              /* OR'ed combination of TCL_READABLE,
  94.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  95.                                  * which events should be reported. */
  96.     int flags;                  /* State flags, see above for a list. */
  97.     HFILE handle;               /* Input/output file. */
  98.     struct FileInfo *nextPtr;   /* Pointer to next registered file. */
  99. } FileInfo;
  100.  
  101. /*
  102.  * List of all file channels currently open.
  103.  */
  104.  
  105. static FileInfo *firstFilePtr = NULL;
  106.  
  107. /*
  108.  * The following structure is what is added to the Tcl event queue when
  109.  * file events are generated.
  110.  */
  111.  
  112. typedef struct FileEvent {
  113.     Tcl_Event header;           /* Information that is standard for
  114.                                  * all events. */
  115.     FileInfo *infoPtr;          /* Pointer to file info structure.  Note
  116.                                  * that we still have to verify that the
  117.                                  * file exists before dereferencing this
  118.                                  * pointer. */
  119. } FileEvent;
  120.  
  121. /*
  122.  * Static routines for this file:
  123.  */
  124.  
  125. static int              ComGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  126.                             Tcl_Interp *interp, char *optionName,
  127.                             Tcl_DString *dsPtr));
  128. static int              ComInputProc _ANSI_ARGS_((ClientData instanceData,
  129.                             char *buf, int toRead, int *errorCode));
  130. static int              ComSetOptionProc _ANSI_ARGS_((ClientData instanceData,
  131.                             Tcl_Interp *interp, char *optionName,
  132.                             char *value));
  133. static int        FileBlockModeProc _ANSI_ARGS_((
  134.                     ClientData instanceData, int mode));
  135. static void             FileChannelExitHandler _ANSI_ARGS_((
  136.                             ClientData clientData));
  137. static void             FileCheckProc _ANSI_ARGS_((ClientData clientData,
  138.                             int flags));
  139. static int        FileCloseProc _ANSI_ARGS_((ClientData instanceData,
  140.                 Tcl_Interp *interp));
  141. static int              FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  142.                             int flags));
  143. static int        FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  144.                     int direction, ClientData *handlePtr));
  145. static void             FileInit _ANSI_ARGS_((void));
  146. static int        FileInputProc _ANSI_ARGS_((ClientData instanceData,
  147.                     char *buf, int toRead, int *errorCode));
  148. static int        FileOutputProc _ANSI_ARGS_((
  149.                 ClientData instanceData, char *buf, int toWrite,
  150.                             int *errorCode));
  151. static int        FileSeekProc _ANSI_ARGS_((ClientData instanceData,
  152.                 long offset, int mode, int *errorCode));
  153. static void             FileSetupProc _ANSI_ARGS_((ClientData clientData,
  154.                             int flags));
  155. static void        FileWatchProc _ANSI_ARGS_((ClientData instanceData,
  156.                     int mask));
  157.  
  158. /*
  159.  * This structure describes the channel type structure for file based IO:
  160.  */
  161.  
  162. static Tcl_ChannelType fileChannelType = {
  163.     "file",                /* Type name. */
  164.     FileBlockModeProc,            /* Set blocking/nonblocking mode.*/
  165.     FileCloseProc,            /* Close proc. */
  166.     FileInputProc,            /* Input proc. */
  167.     FileOutputProc,            /* Output proc. */
  168.     FileSeekProc,            /* Seek proc. */
  169.     NULL,                /* Set option proc. */
  170.     NULL,                /* Get option proc. */
  171.     FileWatchProc,            /* Initialize notifier. */
  172.     FileGetHandleProc,            /* Get OS handles out of channel. */
  173. };
  174.  
  175. static Tcl_ChannelType comChannelType = {
  176.     "com",                /* Type name. */
  177.     FileBlockModeProc,            /* Set blocking/nonblocking mode.*/
  178.     FileCloseProc,            /* Close proc. */
  179.     ComInputProc,            /* Input proc. */
  180.     FileOutputProc,            /* Output proc. */
  181.     NULL,                /* Seek proc. */
  182.     ComSetOptionProc,            /* Set option proc. */
  183.     ComGetOptionProc,            /* Get option proc. */
  184.     FileWatchProc,            /* Initialize notifier. */
  185.     FileGetHandleProc,            /* Get OS handles out of channel. */
  186. };
  187.  
  188. /*
  189.  *----------------------------------------------------------------------
  190.  *
  191.  * ComInputProc --
  192.  *
  193.  *      Reads input from the IO channel into the buffer given. Returns
  194.  *      count of how many bytes were actually read, and an error indication.
  195.  *
  196.  * Results:
  197.  *      A count of how many bytes were read is returned and an error
  198.  *      indication is returned in an output argument.
  199.  *
  200.  * Side effects:
  201.  *      Reads input from the actual channel.
  202.  *
  203.  *----------------------------------------------------------------------
  204.  */
  205.  
  206. static int
  207. ComInputProc(instanceData, buf, bufSize, errorCode)
  208.     ClientData instanceData;    /* File state. */
  209.     char *buf;                  /* Where to store data read. */
  210.     int bufSize;                /* How much space is available
  211.                                  * in the buffer? */
  212.     int *errorCode;             /* Where to store error code. */
  213. {
  214.     FileInfo *infoPtr;
  215.     ULONG bytesRead;
  216.     ULONG len;
  217.     USHORT comError;
  218.     RXQUEUE inQueue;
  219.  
  220.     *errorCode = 0;
  221.     infoPtr = (FileInfo *) instanceData;
  222.  
  223.     /* Retrieve and clear COM device error information */
  224.     len = sizeof(comError);
  225.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  226.                      IOCTL_ASYNC,        /* Serial-device control */
  227.                      ASYNC_GETCOMMERROR,    /* Get COM error */
  228.                      NULL,            /* No parameter packet */
  229.                      0L,            /* Max size of parameter list */
  230.                      NULL,            /* Size of parameter packet */
  231.                      (PULONG)&comError,        /* Points at com error */
  232.                      sizeof(comError),        /* Max size of data packet */
  233.                      &len);            /* Size of data packet */
  234.     if (rc == NO_ERROR) {
  235. #ifdef VERBOSE
  236.         printf("Com DosDevIOCtl ASYNC_GETCOMMERROR OK: %x\n", comError);
  237.         fflush(stdout);
  238. #endif
  239.         if ((comError & 0xf) != 0) {
  240.             *errorCode = EIO;
  241.             return -1;
  242.         }
  243.         /* Determine nr. of characters in receive queue */
  244.         len = sizeof(inQueue);
  245.         inQueue.cch = 0;
  246.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  247.                          IOCTL_ASYNC,        /* Serial-device control */
  248.                          ASYNC_GETINQUECOUNT,    /* Get in-queue count */
  249.                          NULL,            /* No parameter packet */
  250.                          0L,            /* Max size of parameter list */
  251.                          NULL,            /* Size of parameter packet */
  252.                          (PULONG)&inQueue,    /* Points at queue structure */
  253.                          sizeof(inQueue),    /* Max size of data packet */
  254.                          &len);            /* Size of data packet */
  255.         if (inQueue.cch != 0) {
  256.             if ((ULONG) bufSize > inQueue.cch) {
  257.                 bufSize = inQueue.cch;
  258.             }
  259.         } else {
  260.             if (infoPtr->flags & FILE_ASYNC) {
  261.                 errno = *errorCode = EAGAIN;
  262.                 return -1;
  263.             } else {
  264.                 bufSize = 1;
  265.             }
  266.         }
  267. #ifdef VERBOSE
  268.     } else {
  269.         printf("Com DosDevIOCtl ASYNC_GETCOMMERROR ERROR %d\n", rc);
  270.         fflush(stdout);
  271. #endif
  272.     }
  273.  
  274.     rc = DosRead(infoPtr->handle, (PVOID) buf, (ULONG) bufSize, &bytesRead);
  275. #ifdef VERBOSE
  276.     printf("ComInputProc DosRead handle [%x] returns [%d], bytes read [%d]\n",
  277.            infoPtr->handle, rc, bytesRead);
  278.     fflush(stdout);
  279. #endif
  280.     if (rc != NO_ERROR) {
  281.         TclOS2ConvertError(rc);
  282.         *errorCode = errno;
  283.         return -1;
  284.     }
  285.  
  286.     return bytesRead;
  287. }
  288.  
  289. /*
  290.  *----------------------------------------------------------------------
  291.  *
  292.  * ComSetOptionProc --
  293.  *
  294.  *      Sets an option on a channel.
  295.  *
  296.  * Results:
  297.  *      A standard Tcl result. Also sets interp->result on error if
  298.  *      interp is not NULL.
  299.  *
  300.  * Side effects:
  301.  *      May modify an option on a device.
  302.  *
  303.  *----------------------------------------------------------------------
  304.  */
  305.  
  306. static int
  307. ComSetOptionProc(instanceData, interp, optionName, value)
  308.     ClientData instanceData;    /* File state. */
  309.     Tcl_Interp *interp;         /* For error reporting - can be NULL. */
  310.     char *optionName;           /* Which option to set? */
  311.     char *value;                /* New value for option. */
  312. {
  313.     FileInfo *infoPtr;
  314.     LINECONTROL line;
  315.     ULONG parmLen;
  316.     struct {
  317.         ULONG lSpeed;
  318.         UCHAR fraction;
  319.     } extSpeed;
  320.     USHORT speed;
  321.     int len, i, end;
  322.     char stop[3] = {'1', '\0', '\0'};
  323.     static char *bad = "bad value for -mode";
  324.  
  325.     infoPtr = (FileInfo *) instanceData;
  326.  
  327.     len = strlen(optionName);
  328.     if ((len <= 1) || (strncmp(optionName, "-mode", len) != 0)) {
  329.         return Tcl_BadChannelOption(interp, optionName, "mode");
  330.     }
  331.  
  332. #ifdef VERBOSE
  333.     /* Get current state of com */
  334.     parmLen = sizeof(line);
  335.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  336.                      IOCTL_ASYNC,        /* Serial-device control */
  337.                      ASYNC_GETLINECTRL,        /* Get line control */
  338.                      NULL,            /* No parameter packet */
  339.                      0L,            /* Max size of parameter list */
  340.                      NULL,            /* Size of parameter packet */
  341.                      (PULONG)&line,        /* Points at line structure */
  342.                      sizeof(line),        /* Max size of data packet */
  343.                      &parmLen            /* Size of data packet */
  344.                     );
  345.     if (rc != NO_ERROR) {
  346.         if (interp) {
  347.             Tcl_AppendResult(interp, "can't get comm line state", NULL);
  348.         }
  349.         return TCL_ERROR;
  350.     }
  351.  
  352.     /* Get current baud rate */
  353.     parmLen = sizeof(speed);
  354.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  355.                      IOCTL_ASYNC,        /* Serial-device control */
  356.                      ASYNC_GETBAUDRATE,        /* Get baud rate */
  357.                      NULL,            /* No parameter packet */
  358.                      0L,            /* Max size of parameter list */
  359.                      NULL,            /* Size of parameter packet */
  360.                      (PULONG)&speed,        /* Points at speed variable */
  361.                      sizeof(speed),        /* Max size of data packet */
  362.                      &parmLen            /* Size of data packet */
  363.                     );
  364.     if (rc != NO_ERROR) {
  365.         if (interp) {
  366.             Tcl_AppendResult(interp, "can't get comm baud rate", NULL);
  367.         }
  368.         return TCL_ERROR;
  369.     }
  370.  
  371.     printf("Com Current comm stats h %x: %d,%d,%d,%d (baud,parity,data,stop)\n",
  372.            infoPtr->handle,speed, line.bParity, line.bDataBits, line.bStopBits);
  373.     fflush(stdout);
  374. #endif
  375.  
  376.     /* Parse mode argument. It is of the form baud,parity,data,stop */
  377.     i = sscanf(value, "%ld,%c,%d,%s%n", &(extSpeed.lSpeed), &(line.bParity),
  378.                (int *)&(line.bDataBits), stop, &end);
  379.  
  380.     /* If not succesful return error */
  381.     if ((i != 4) || (value[end] != '\0')) {
  382.         if (interp) {
  383.             Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
  384.                              NULL);
  385.         }
  386.         return TCL_ERROR;
  387.     }
  388.     if (strchr("noems", line.bParity) == NULL) {
  389.         if (interp != NULL) {
  390.             Tcl_AppendResult(interp, bad, " parity: should be n, o, e, m, or s",
  391.                              NULL);
  392.         }
  393.         return TCL_ERROR;
  394.     }
  395.     switch(line.bParity) {
  396.         case 'n': line.bParity = 0; break;
  397.         case 'o': line.bParity = 1; break;
  398.         case 'e': line.bParity = 2; break;
  399.         case 'm': line.bParity = 3; break;
  400.         case 's': line.bParity = 4; break;
  401.         default:  line.bParity = 2; break;
  402.     }
  403.  
  404.     if ((line.bDataBits < 0x05) || (line.bDataBits > 0x08)) {
  405.         if (interp != NULL) {
  406.             Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
  407.                              NULL);
  408.         }
  409.         return TCL_ERROR;
  410.     }
  411.  
  412.     /*
  413.      * We get a warning if we uncomment the following '< 0' because the
  414.      * bStopBits member is declared as BYTE = *unsigned* char.
  415.      */
  416.     if (/*(line.bStopBits < 0) ||*/ (line.bStopBits > 2)) {
  417.         if (interp != NULL) {
  418.             Tcl_AppendResult(interp, bad, " stop: should be 1, 1.5 or 2", NULL);
  419.         }
  420.         return TCL_ERROR;
  421.     }
  422.     if (stop[0] == '2') {
  423.         line.bStopBits = 2;
  424.     } else {
  425.         if (stop[1] == '.' && stop[2] == '5') {
  426.             line.bStopBits = 1;
  427.         } else {
  428.             /* 1 stopbit, OS/2 default */
  429.             line.bStopBits = 0;
  430.         }
  431.     }
  432.  
  433.     /* Everything OK, set baud rate to new value */
  434.     if (extSpeed.lSpeed > 19200) {
  435.         /* We need to use EXTSETBAURATE for higher baud rates than 19200 */
  436.         parmLen = sizeof(extSpeed);
  437.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  438.                          IOCTL_ASYNC,        /* Serial-device control */
  439.                          ASYNC_SETBAUDRATE,    /* Set baud rate */
  440.                          (PULONG)&extSpeed,    /* Points at speed variable */
  441.                          sizeof(extSpeed),    /* Max size of param packet */
  442.                          &parmLen,        /* Size of param packet */
  443.                          NULL,            /* No data packet */
  444.                          0L,            /* Max size of data list */
  445.                          NULL            /* Size of data packet */
  446.                         );
  447.     } else {
  448.         speed = (USHORT) extSpeed.lSpeed;
  449.         parmLen = sizeof(speed);
  450.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  451.                          IOCTL_ASYNC,        /* Serial-device control */
  452.                          ASYNC_SETBAUDRATE,    /* Set baud rate */
  453.                          (PULONG)&speed,    /* Points at speed variable */
  454.                          sizeof(speed),        /* Max size of param packet */
  455.                          &parmLen,        /* Size of param packet */
  456.                          NULL,            /* No data packet */
  457.                          0L,            /* Max size of data list */
  458.                          NULL            /* Size of data packet */
  459.                         );
  460.     }
  461.     if (rc != NO_ERROR) {
  462. #ifdef VERBOSE
  463.         printf("Com DosDevIOCtl (EXT)SETBAUDRATE handle %x ERROR %d\n",
  464.                infoPtr->handle, rc);
  465.         fflush(stdout);
  466. #endif
  467.         if (interp) {
  468.             Tcl_AppendResult(interp, "can't set comm baud rate", NULL);
  469.         }
  470.         return TCL_ERROR;
  471.     }
  472.  
  473.     /* Everything OK, set line control to new values */
  474.     parmLen =  3 * sizeof(BYTE);
  475.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  476.                      IOCTL_ASYNC,        /* Serial-device control */
  477.                      ASYNC_SETLINECTRL,        /* Set line control */
  478.                      (PULONG)&line,        /* Points at line structure */
  479.                      sizeof(line),        /* Max size of parm packet */
  480.                      &parmLen,            /* Size of parm packet */
  481.                      NULL,            /* No data packet */
  482.                      0L,            /* Max size of data list */
  483.                      NULL            /* Size of data packet */
  484.                     );
  485.  
  486.     if (rc == NO_ERROR) {
  487.         return TCL_OK;
  488.     } else {
  489.         if (interp) {
  490.             Tcl_AppendResult(interp, "can't set comm line control",
  491.                              NULL);
  492.         }
  493.         return TCL_ERROR;
  494.     }
  495. }
  496.  
  497. /*
  498.  *----------------------------------------------------------------------
  499.  *
  500.  * ComGetOptionProc --
  501.  *
  502.  *      Gets a mode associated with an IO channel. If the optionName arg
  503.  *      is non NULL, retrieves the value of that option. If the optionName
  504.  *      arg is NULL, retrieves a list of alternating option names and
  505.  *      values for the given channel.
  506.  *
  507.  * Results:
  508.  *      A standard Tcl result. Also sets the supplied DString to the
  509.  *      string value of the option(s) returned.
  510.  *
  511.  * Side effects:
  512.  *      The string returned by this function is in static storage and
  513.  *      may be reused at any time subsequent to the call.
  514.  *
  515.  *----------------------------------------------------------------------
  516.  */
  517.  
  518. static int
  519. ComGetOptionProc(instanceData, interp, optionName, dsPtr)
  520.     ClientData instanceData;    /* File state. */
  521.     Tcl_Interp *interp;          /* For error reporting - can be NULL. */
  522.     char *optionName;           /* Option to get. */
  523.     Tcl_DString *dsPtr;         /* Where to store value(s). */
  524. {
  525.     FileInfo *infoPtr;
  526.     LINECONTROL line;
  527.     ULONG parmLen;
  528.     struct {
  529.         ULONG lSpeed;
  530.         UCHAR fraction;
  531.         ULONG minRate;
  532.         UCHAR minFraction;
  533.         ULONG maxRate;
  534.         UCHAR maxFraction;
  535.     } extSpeed;
  536.     USHORT speed;
  537.     int len;
  538.     char parity;
  539.     char *stop;
  540.     char buf[32];
  541.  
  542.     infoPtr = (FileInfo *) instanceData;
  543.  
  544.     if (optionName == NULL) {
  545.         Tcl_DStringAppendElement(dsPtr, "-mode");
  546.         len = 0;
  547.     } else {
  548.         len = strlen(optionName);
  549.     }
  550.  
  551.     if ((len != 0) &&
  552.             ((len <= 1) || (strncmp(optionName, "-mode", len) != 0))) {
  553.         return Tcl_BadChannelOption(interp, optionName, "mode");
  554.     }
  555.  
  556.     /* Get line control */
  557.     parmLen = sizeof(line);
  558.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  559.                      IOCTL_ASYNC,        /* Serial-device control */
  560.                      ASYNC_GETLINECTRL,        /* Get line control */
  561.                      NULL,            /* No parameter packet */
  562.                      0L,            /* Max size of parameter list */
  563.                      NULL,            /* Size of parameter packet */
  564.                      (PULONG)&line,        /* Points at line structure */
  565.                      sizeof(line),        /* Max size of data packet */
  566.                      &parmLen            /* Size of data packet */
  567.                     );
  568.     if (rc != NO_ERROR) {
  569.         if (interp) {
  570.             Tcl_AppendResult(interp, "can't get comm line state", NULL);
  571.         }
  572.         return TCL_ERROR;
  573.     }
  574.  
  575.     /* Get baud rate */
  576.     parmLen = sizeof(speed);
  577.     rc = DosDevIOCtl(infoPtr->handle,        /* Device handle */
  578.                      IOCTL_ASYNC,        /* Serial-device control */
  579.                      ASYNC_GETBAUDRATE,        /* Get baud rate */
  580.                      NULL,            /* No parameter packet */
  581.                      0L,            /* Max size of parameter list */
  582.                      NULL,            /* Size of parameter packet */
  583.                      (PULONG)&speed,        /* Points at speed variable */
  584.                      sizeof(speed),        /* Max size of data packet */
  585.                      &parmLen            /* Size of data packet */
  586.                     );
  587.     if (rc != NO_ERROR) {
  588.         if (interp) {
  589.             Tcl_AppendResult(interp, "can't get comm baud rate", NULL);
  590.         }
  591.         return TCL_ERROR;
  592.     }
  593.     /* If we got 1200 baud, we could be having a case of a baud rate > 19200 */
  594.     extSpeed.lSpeed = speed;
  595.     if (speed == 1200) {
  596.         parmLen = sizeof(extSpeed);
  597.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  598.                          IOCTL_ASYNC,        /* Serial-device control */
  599.                          ASYNC_EXTGETBAUDRATE,    /* Get baud rate */
  600.                          NULL,            /* No parameter packet */
  601.                          0L,            /* Max size of parameter list */
  602.                          NULL,            /* Size of parameter packet */
  603.                          (PULONG)&extSpeed,    /* Points at speed variable */
  604.                          sizeof(extSpeed),    /* Max size of data packet */
  605.                          &parmLen        /* Size of data packet */
  606.                         );
  607.         if (rc != NO_ERROR) {
  608.             extSpeed.lSpeed = speed;
  609. #ifdef VERBOSE
  610.             printf("Com DosDevIOCtl ERROR %d, resetting speed to 1200\n", rc);
  611.             fflush(stdout);
  612. #endif
  613.         }
  614.     }
  615.  
  616.     parity = 'e';    /* OS/2 initial value; Windows version says 'n' */
  617.     if (line.bParity < 5) {
  618.         parity = "noems"[line.bParity];
  619.     }
  620.  
  621.     stop = (line.bStopBits == 2) ? "2" :
  622.             (line.bStopBits == 1) ? "1.5" : "1"; /* 1 is OS/2 initial value */
  623.  
  624.     sprintf(buf, "%d,%c,%d,%s", speed, parity, line.bDataBits, stop);
  625.     Tcl_DStringAppendElement(dsPtr, buf);
  626.  
  627.     return TCL_OK;
  628. }
  629.  
  630. /*
  631.  *----------------------------------------------------------------------
  632.  *
  633.  * FileBlockModeProc --
  634.  *
  635.  *    Helper procedure to set blocking and nonblocking modes on a
  636.  *    file based channel. Invoked by generic IO level code.
  637.  *
  638.  * Results:
  639.  *    0 if successful, errno when failed.
  640.  *
  641.  * Side effects:
  642.  *    Sets the device into blocking or non-blocking mode.
  643.  *
  644.  *----------------------------------------------------------------------
  645.  */
  646.  
  647.     /* ARGSUSED */
  648. static int
  649. FileBlockModeProc(instanceData, mode)
  650.     ClientData instanceData;        /* File state. */
  651.     int mode;                /* The mode to set. Can be one of
  652.                                          * TCL_MODE_BLOCKING or
  653.                                          * TCL_MODE_NONBLOCKING. */
  654. {
  655.     FileInfo *infoPtr = (FileInfo *) instanceData;
  656.  
  657.     /*
  658.      * Files on OS/2 can not be switched between blocking and nonblocking,
  659.      * hence we have to emulate the behavior. This is done in the input
  660.      * function by checking against a bit in the state. We set or unset the
  661.      * bit here to cause the input function to emulate the correct behavior.
  662.      */
  663.  
  664.     if (mode == TCL_MODE_NONBLOCKING) {
  665.         infoPtr->flags |= FILE_ASYNC;
  666.     } else {
  667.         infoPtr->flags &= ~(FILE_ASYNC);
  668.     }
  669.     return 0;
  670. }
  671.  
  672. /*
  673.  *----------------------------------------------------------------------
  674.  *
  675.  * FileChannelExitHandler --
  676.  *
  677.  *      This function is called to cleanup the channel driver before
  678.  *      Tcl is unloaded.
  679.  *
  680.  * Results:
  681.  *      None.
  682.  *
  683.  * Side effects:
  684.  *      Destroys the communication window.
  685.  *
  686.  *----------------------------------------------------------------------
  687.  */
  688.  
  689. static void
  690. FileChannelExitHandler(clientData)
  691.     ClientData clientData;      /* Old window proc */
  692. {
  693.     Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
  694.     initialized = 0;
  695. }
  696.  
  697. /*
  698.  *----------------------------------------------------------------------
  699.  *
  700.  * FileCheckProc --
  701.  *
  702.  *      This procedure is called by Tcl_DoOneEvent to check the file
  703.  *      event source for events.
  704.  *
  705.  * Results:
  706.  *      None.
  707.  *
  708.  * Side effects:
  709.  *      May queue an event.
  710.  *
  711.  *----------------------------------------------------------------------
  712.  */
  713.  
  714. static void
  715. FileCheckProc(data, flags)
  716.     ClientData data;            /* Not used. */
  717.     int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
  718. {
  719.     FileEvent *evPtr;
  720.     FileInfo *infoPtr;
  721.  
  722.     if (!(flags & TCL_FILE_EVENTS)) {
  723.         return;
  724.     }
  725.  
  726.     /*
  727.      * Queue events for any ready files that don't already have events
  728.      * queued (caused by persistent states that won't generate WinSock
  729.      * events).
  730.      */
  731.  
  732.     for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  733.         if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
  734.             infoPtr->flags |= FILE_PENDING;
  735.             evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
  736.             evPtr->header.proc = FileEventProc;
  737.             evPtr->infoPtr = infoPtr;
  738.             Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
  739.         }
  740.     }
  741. }
  742.  
  743. /*
  744.  *----------------------------------------------------------------------
  745.  *
  746.  * FileCloseProc --
  747.  *
  748.  *    This procedure is called from the generic IO level to perform
  749.  *    channel-type-specific cleanup when a file based channel is closed.
  750.  *
  751.  * Results:
  752.  *    0 if successful, errno if failed.
  753.  *
  754.  * Side effects:
  755.  *    Closes the device of the channel.
  756.  *
  757.  *----------------------------------------------------------------------
  758.  */
  759.  
  760. static int
  761. FileCloseProc(instanceData, interp)
  762.     ClientData instanceData;    /* File state. */
  763.     Tcl_Interp *interp;        /* For error reporting - unused. */
  764. {
  765.     FileInfo *fileInfoPtr = (FileInfo *) instanceData;
  766.     FileInfo **nextPtrPtr;
  767.     int errorCode = 0;
  768.  
  769.     /*
  770.      * Remove the file from the watch list.
  771.      */
  772.  
  773.     FileWatchProc(instanceData, 0);
  774.  
  775.     rc = DosClose(fileInfoPtr->handle);
  776.     if (rc != NO_ERROR) {
  777.         TclOS2ConvertError(rc);
  778.         errorCode = errno;
  779.     }
  780. #ifdef VERBOSE
  781.       else {
  782.         openedFiles--;
  783.     }
  784. #endif
  785.     for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL;
  786.          nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
  787.         if ((*nextPtrPtr) == fileInfoPtr) {
  788.             (*nextPtrPtr) = fileInfoPtr->nextPtr;
  789.             break;
  790.         }
  791.     }
  792.     ckfree((char *)fileInfoPtr);
  793.     return errorCode;
  794. }
  795.  
  796. /*----------------------------------------------------------------------
  797.  *
  798.  * FileEventProc --
  799.  *
  800.  *      This function is invoked by Tcl_ServiceEvent when a file event
  801.  *      reaches the front of the event queue.  This procedure invokes
  802.  *      Tcl_NotifyChannel on the file.
  803.  *
  804.  * Results:
  805.  *      Returns 1 if the event was handled, meaning it should be removed
  806.  *      from the queue.  Returns 0 if the event was not handled, meaning
  807.  *      it should stay on the queue.  The only time the event isn't
  808.  *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
  809.  *
  810.  * Side effects:
  811.  *      Whatever the notifier callback does.
  812.  *
  813.  *----------------------------------------------------------------------
  814.  */
  815.  
  816. static int
  817. FileEventProc(evPtr, flags)
  818.     Tcl_Event *evPtr;           /* Event to service. */
  819.     int flags;                  /* Flags that indicate what events to
  820.                                  * handle, such as TCL_FILE_EVENTS. */
  821. {
  822.     FileEvent *fileEvPtr = (FileEvent *)evPtr;
  823.     FileInfo *infoPtr;
  824.  
  825.     if (!(flags & TCL_FILE_EVENTS)) {
  826.         return 0;
  827.     }
  828.  
  829.     /*
  830.      * Search through the list of watched files for the one whose handle
  831.      * matches the event.  We do this rather than simply dereferencing
  832.      * the handle in the event so that files can be deleted while the
  833.      * event is in the queue.
  834.      */
  835.  
  836.     for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  837.         if (fileEvPtr->infoPtr == infoPtr) {
  838.             infoPtr->flags &= ~(FILE_PENDING);
  839.             Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
  840.             break;
  841.         }
  842.     }
  843.     return 1;
  844. }
  845.  
  846. /*
  847.  *----------------------------------------------------------------------
  848.  *
  849.  * FileGetHandleProc --
  850.  *
  851.  *    Called from Tcl_GetChannelFile to retrieve OS handles from
  852.  *    a file based channel.
  853.  *
  854.  * Results:
  855.  *    Returns TCL_OK with the handle in handlePtr, or TCL_ERROR if
  856.  *    there is no handle for the specified direction. 
  857.  *
  858.  * Side effects:
  859.  *    None.
  860.  *
  861.  *----------------------------------------------------------------------
  862.  */
  863.  
  864. static int
  865. FileGetHandleProc(instanceData, direction, handlePtr)
  866.     ClientData instanceData;    /* The file state. */
  867.     int direction;        /* TCL_READABLE or TCL_WRITABLE */
  868.     ClientData *handlePtr;    /* Where to store the handle.  */
  869. {
  870.     FileInfo *infoPtr = (FileInfo *) instanceData;
  871.  
  872.     if (direction & infoPtr->validMask) {
  873.         *handlePtr = (ClientData) infoPtr->handle;
  874.         return TCL_OK;
  875.     } else {
  876.         return TCL_ERROR;
  877.     }
  878. }
  879.  
  880. /*
  881.  *----------------------------------------------------------------------
  882.  *
  883.  * FileInit --
  884.  *
  885.  *      This function creates the window used to simulate file events.
  886.  *
  887.  * Results:
  888.  *      None.
  889.  *
  890.  * Side effects:
  891.  *      Creates a new window and creates an exit handler.
  892.  *
  893.  *----------------------------------------------------------------------
  894.  */
  895.  
  896. static void
  897. FileInit()
  898. {
  899.     initialized = 1;
  900.     firstFilePtr = NULL;
  901.     Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
  902.     Tcl_CreateExitHandler(FileChannelExitHandler, NULL);
  903. }
  904.  
  905. /*
  906.  *----------------------------------------------------------------------
  907.  *
  908.  * FileInputProc --
  909.  *
  910.  *    This procedure is invoked from the generic IO level to read
  911.  *    input from a file based channel.
  912.  *
  913.  * Results:
  914.  *    The number of bytes read is returned or -1 on error. An output
  915.  *    argument contains a POSIX error code if an error occurs, or zero.
  916.  *
  917.  * Side effects:
  918.  *    Reads input from the input device of the channel.
  919.  *
  920.  *----------------------------------------------------------------------
  921.  */
  922.  
  923. static int
  924. FileInputProc(instanceData, buf, toRead, errorCodePtr)
  925.     ClientData instanceData;        /* File state. */
  926.     char *buf;                /* Where to store data read. */
  927.     int toRead;                /* How much space is available
  928.                                          * in the buffer? */
  929.     int *errorCodePtr;            /* Where to store error code. */
  930. {
  931.     FileInfo *infoPtr = (FileInfo *) instanceData;
  932.     ULONG bytesRead;            /* How many bytes were actually
  933.                                          * read from the input device? */
  934.  
  935.     *errorCodePtr = 0;
  936.     
  937.     /*
  938.      * Note that we will block on reads from a console buffer until a
  939.      * full line has been entered.  The only way I know of to get
  940.      * around this is to write a console driver.  We should probably
  941.      * do this at some point, but for now, we just block.  The same
  942.      * problem exists for files being read over the network.
  943.      */
  944.  
  945. #ifdef VERBOSE
  946.     printf("FileInputProc to read %d handle %x\n", toRead, infoPtr->handle);
  947.     fflush(stdout);
  948. #endif
  949.     rc = DosRead(infoPtr->handle, (PVOID) buf, (ULONG) toRead, &bytesRead);
  950. #ifdef VERBOSE
  951.     printf("FileInputProc DosRead handle %x returns %d, bytes read [%d]\n",
  952.            infoPtr->handle, rc, bytesRead);
  953.     fflush(stdout);
  954. #endif
  955.     if (rc == NO_ERROR) {
  956.         return bytesRead;
  957.     }
  958.  
  959.     TclOS2ConvertError(rc);
  960.     *errorCodePtr = errno;
  961.     if (errno == EPIPE) {
  962.         return 0;
  963.     }
  964.     return -1;
  965. }
  966.  
  967. /*
  968.  *----------------------------------------------------------------------
  969.  *
  970.  * FileOutputProc--
  971.  *
  972.  *    This procedure is invoked from the generic IO level to write
  973.  *    output to a file channel.
  974.  *
  975.  * Results:
  976.  *    The number of bytes written is returned or -1 on error. An
  977.  *    output argument    contains a POSIX error code if an error occurred,
  978.  *    or zero.
  979.  *
  980.  * Side effects:
  981.  *    Writes output on the output device of the channel.
  982.  *
  983.  *----------------------------------------------------------------------
  984.  */
  985.  
  986. static int
  987. FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
  988.     ClientData instanceData;        /* File state. */
  989.     char *buf;                /* The data buffer. */
  990.     int toWrite;            /* How many bytes to write? */
  991.     int *errorCodePtr;            /* Where to store error code. */
  992. {
  993.     FileInfo *infoPtr = (FileInfo *) instanceData;
  994.     ULONG bytesWritten;
  995.     ULONG newPos;
  996.  
  997.     /*
  998.      * If we are writing to a file that was opened with O_APPEND, we need to
  999.      * seek to the end of the file before writing the current buffer.
  1000.      */
  1001.  
  1002.     if (infoPtr->flags & FILE_APPEND) {
  1003.         DosSetFilePtr(infoPtr->handle, 0, FILE_END, &newPos);
  1004.     }
  1005.  
  1006.     rc = DosWrite(infoPtr->handle, (PVOID) buf, (ULONG) toWrite, &bytesWritten);
  1007.     if (rc != NO_ERROR) {
  1008.         TclOS2ConvertError(rc);
  1009.         if (errno == EPIPE) {
  1010.             return 0;
  1011.         }
  1012.         *errorCodePtr = errno;
  1013.         return -1;
  1014.     }
  1015.     DosResetBuffer(infoPtr->handle);
  1016.     return bytesWritten;
  1017. }
  1018.  
  1019. /*
  1020.  *----------------------------------------------------------------------
  1021.  *
  1022.  * FileSeekProc --
  1023.  *
  1024.  *    This procedure is called by the generic IO level to move the
  1025.  *    access point in a file based channel.
  1026.  *
  1027.  * Results:
  1028.  *    -1 if failed, the new position if successful. An output
  1029.  *    argument contains the POSIX error code if an error occurred,
  1030.  *    or zero.
  1031.  *
  1032.  * Side effects:
  1033.  *    Moves the location at which the channel will be accessed in
  1034.  *    future operations.
  1035.  *
  1036.  *----------------------------------------------------------------------
  1037.  */
  1038.  
  1039. static int
  1040. FileSeekProc(instanceData, offset, mode, errorCodePtr)
  1041.     ClientData instanceData;            /* File state. */
  1042.     long offset;                /* Offset to seek to. */
  1043.     int mode;                    /* Relative to where
  1044.                                                  * should we seek? Can be
  1045.                                                  * one of SEEK_START,
  1046.                                                  * SEEK_SET or SEEK_END. */
  1047.     int *errorCodePtr;                /* To store error code. */
  1048. {
  1049.     FileInfo *infoPtr = (FileInfo *) instanceData;
  1050.     ULONG moveMethod;
  1051.     ULONG newPos;
  1052.  
  1053.     *errorCodePtr = 0;
  1054.     if (mode == SEEK_SET) {
  1055.         moveMethod = FILE_BEGIN;
  1056.     } else if (mode == SEEK_CUR) {
  1057.         moveMethod = FILE_CURRENT;
  1058.     } else {
  1059.         moveMethod = FILE_END;
  1060.     }
  1061.  
  1062.     rc = DosSetFilePtr(infoPtr->handle, offset, moveMethod, &newPos);
  1063.     if (rc != NO_ERROR) {
  1064. #ifdef VERBOSE
  1065.         printf("FileSeekProc: DosSetFilePtr handle [%x] ERROR %d\n",
  1066.                infoPtr->handle, rc);
  1067.         fflush(stdout);
  1068. #endif
  1069.         return -1;
  1070.     }
  1071. #ifdef VERBOSE
  1072.     printf("FileSeekProc: DosSetFilePtr handle [%x] newPos [%d] OK\n",
  1073.            infoPtr->handle, newPos);
  1074.     fflush(stdout);
  1075. #endif
  1076.     return newPos;
  1077. }
  1078.  
  1079. /*
  1080.  *----------------------------------------------------------------------
  1081.  *
  1082.  * FileSetupProc --
  1083.  *
  1084.  *      This procedure is invoked before Tcl_DoOneEvent blocks waiting
  1085.  *      for an event.
  1086.  *
  1087.  * Results:
  1088.  *      None.
  1089.  *
  1090.  * Side effects:
  1091.  *      Adjusts the block time if needed.
  1092.  *
  1093.  *----------------------------------------------------------------------
  1094.  */
  1095.  
  1096. void
  1097. FileSetupProc(data, flags)
  1098.     ClientData data;            /* Not used. */
  1099.     int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
  1100. {
  1101.     FileInfo *infoPtr;
  1102.     Tcl_Time blockTime = { 0, 0 };
  1103.  
  1104.     if (!(flags & TCL_FILE_EVENTS)) {
  1105.         return;
  1106.     }
  1107.  
  1108.     /*
  1109.      * Check to see if there is a ready file.  If so, poll.
  1110.      */
  1111.  
  1112.     for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  1113.         if (infoPtr->watchMask) {
  1114.             Tcl_SetMaxBlockTime(&blockTime);
  1115.             break;
  1116.         }
  1117.     }
  1118. }
  1119.  
  1120. /*
  1121.  *----------------------------------------------------------------------
  1122.  *
  1123.  * FileWatchProc --
  1124.  *
  1125.  *      Called by the notifier to set up to watch for events on this
  1126.  *      channel.
  1127.  *
  1128.  * Results:
  1129.  *    None.
  1130.  *
  1131.  * Side effects:
  1132.  *      None.
  1133.  *
  1134.  *----------------------------------------------------------------------
  1135.  */
  1136.  
  1137. static void
  1138. FileWatchProc(instanceData, mask)
  1139.     ClientData instanceData;        /* The file state. */
  1140.     int mask;                /* Events of interest; an OR-ed
  1141.                                          * combination of TCL_READABLE,
  1142.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  1143. {
  1144.     FileInfo *infoPtr = (FileInfo *) instanceData;
  1145.     Tcl_Time blockTime = { 0, 0 };
  1146.  
  1147.     /*
  1148.      * Since the file is always ready for events, we set the block time
  1149.      * to zero so we will poll.
  1150.      */
  1151.  
  1152.     infoPtr->watchMask = mask & infoPtr->validMask;
  1153.     if (infoPtr->watchMask) {
  1154.         Tcl_SetMaxBlockTime(&blockTime);
  1155.     }
  1156. }
  1157.  
  1158. /*
  1159.  *----------------------------------------------------------------------
  1160.  *
  1161.  * TclpOpenFileChannel --
  1162.  *
  1163.  *    Open an file based channel on OS/2 systems.
  1164.  *
  1165.  * Results:
  1166.  *    The new channel or NULL. If NULL, the output argument
  1167.  *    errorCodePtr is set to a POSIX error and an error message is
  1168.  *    left in interp->result if interp is not NULL.
  1169.  *
  1170.  * Side effects:
  1171.  *    May open the channel and may cause creation of a file on the
  1172.  *    file system.
  1173.  *
  1174.  *----------------------------------------------------------------------
  1175.  */
  1176.  
  1177. Tcl_Channel
  1178. TclpOpenFileChannel(interp, fileName, modeString, permissions)
  1179.     Tcl_Interp *interp;            /* Interpreter for error reporting;
  1180.                                          * can be NULL. */
  1181.     char *fileName;            /* Name of file to open. */
  1182.     char *modeString;            /* A list of POSIX open modes or
  1183.                                          * a string such as "rw". */
  1184.     int permissions;            /* If the open involves creating a
  1185.                                          * file, with what modes to create
  1186.                                          * it? */
  1187. {
  1188.     FileInfo *infoPtr;
  1189.     int seekFlag, mode, channelPermissions = 0;
  1190.     HFILE handle;
  1191.     ULONG accessMode = 0, createMode, flags, exist;
  1192.     BOOL readonly = FALSE;
  1193.     char *nativeName;
  1194.     Tcl_DString buffer;
  1195.     Tcl_ChannelType *channelTypePtr;
  1196.  
  1197.     if (!initialized) {
  1198.         FileInit();
  1199.     }
  1200.  
  1201.     mode = TclGetOpenMode(interp, modeString, &seekFlag);
  1202.     if (mode == -1) {
  1203.         return NULL;
  1204.     }
  1205.  
  1206.     nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
  1207.     if (nativeName == NULL) {
  1208.         return NULL;
  1209.     }
  1210.  
  1211.     /*
  1212.      * Hack for compatibility with Windows-oriented scripts: Windows uses
  1213.      * eg. "COM1:" for the first serial port, while OS/2 uses the reserved
  1214.      * name "COM1" (without ':'). Map the first to the latter.
  1215.      * If people have more than 4 comports they can sure make their script
  1216.      * have a special case for OS/2.
  1217.      */
  1218.     if ((nativeName[0] == 'C' || nativeName[0] == 'c') && 
  1219.         (stricmp(nativeName, "COM1:")== 0 || stricmp(nativeName, "COM2:")== 0 ||
  1220.          stricmp(nativeName, "COM3:")== 0 || stricmp(nativeName, "COM4:")== 0 ||
  1221.          stricmp(nativeName, "COM5:")== 0 || stricmp(nativeName, "COM6:")== 0 ||
  1222.          stricmp(nativeName, "COM7:")== 0 || stricmp(nativeName, "COM8:")== 0 ||
  1223.          stricmp(nativeName, "COM9:")== 0)
  1224.        ) {
  1225. #ifdef VERBOSE
  1226.         printf("Mapping Windows comport %s to OS/2's ", nativeName);
  1227.         fflush(stdout);
  1228. #endif
  1229.         nativeName[4] = '\0';
  1230. #ifdef VERBOSE
  1231.         printf("%s\n", nativeName);
  1232.         fflush(stdout);
  1233. #endif
  1234.     }
  1235.  
  1236.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  1237.         case O_RDONLY:
  1238.             accessMode = OPEN_ACCESS_READONLY;
  1239.             readonly = TRUE; /* Needed because O_A_R is 0 */
  1240.             channelPermissions = TCL_READABLE;
  1241.             break;
  1242.         case O_WRONLY:
  1243.             accessMode = OPEN_ACCESS_WRITEONLY;
  1244.             channelPermissions = TCL_WRITABLE;
  1245.             break;
  1246.         case O_RDWR:
  1247.             accessMode = OPEN_ACCESS_READWRITE;
  1248.             channelPermissions = (TCL_READABLE | TCL_WRITABLE);
  1249.             break;
  1250.         default:
  1251.             panic("TclpOpenFileChannel: invalid mode value");
  1252.             break;
  1253.     }
  1254.  
  1255.     /*
  1256.      * Map the creation flags to the OS/2 open mode.
  1257.      */
  1258.  
  1259.     switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
  1260.         case (O_CREAT | O_EXCL | O_TRUNC):
  1261.             createMode = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
  1262.             break;
  1263.         case (O_CREAT | O_EXCL):
  1264.             createMode = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
  1265.             break;
  1266.         case (O_CREAT | O_TRUNC):
  1267.             createMode = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS;
  1268.             break;
  1269.         case O_CREAT:
  1270.             createMode = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
  1271.             break;
  1272.         case O_TRUNC:
  1273.         case (O_TRUNC | O_EXCL):
  1274.             createMode = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS;
  1275.             break;
  1276.         default:
  1277.             createMode = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
  1278.             break;
  1279.     }
  1280.  
  1281.     /*
  1282.      * If the file is being created, get the file attributes from the
  1283.      * permissions argument, else use the existing file attributes.
  1284.      */
  1285.  
  1286.     if (mode & O_CREAT) {
  1287.         if (permissions & S_IWRITE) {
  1288.             flags = FILE_NORMAL;
  1289.         } else {
  1290.             flags = FILE_READONLY;
  1291.         }
  1292.     } else {
  1293.         FILESTATUS3 infoBuf;
  1294.  
  1295.         if (DosQueryPathInfo(nativeName, FIL_STANDARD, &infoBuf,
  1296.                          sizeof(infoBuf)) == NO_ERROR) {
  1297.             flags = infoBuf.attrFile;
  1298.         } else {
  1299.             flags = 0;
  1300.         }
  1301.     }
  1302.  
  1303.     /*
  1304.      * Set up the file sharing mode.  We want to allow simultaneous access.
  1305.      */
  1306.  
  1307.     accessMode |= OPEN_SHARE_DENYNONE;
  1308.  
  1309.     /*
  1310.      * Now we get to create the file.
  1311.      */
  1312.  
  1313.     rc = DosOpen(nativeName, &handle, &exist, 0, flags, createMode,
  1314.                   accessMode, (PEAOP2)NULL);
  1315. #ifdef VERBOSE
  1316.     if (rc == NO_ERROR) openedFiles++;
  1317.     printf("DosOpen [%s]: handle [%x], rc [%d] (create [%x] access [%x])\n",
  1318.            nativeName, handle, rc, createMode, accessMode);
  1319.     fflush(stdout);
  1320. #endif
  1321.  
  1322.     if (rc != NO_ERROR) {
  1323.         ULONG err = ERROR_SIGNAL_REFUSED;
  1324.  
  1325.         switch (rc) {
  1326.             case ERROR_FILE_NOT_FOUND:
  1327.             case ERROR_PATH_NOT_FOUND:
  1328.                 err = ERROR_FILE_NOT_FOUND;
  1329.                 break;
  1330.             case ERROR_ACCESS_DENIED:
  1331.             case ERROR_INVALID_ACCESS:
  1332.             case ERROR_SHARING_VIOLATION:
  1333.             case ERROR_CANNOT_MAKE:
  1334.             case ERROR_OPEN_FAILED:
  1335.                 err = (mode & O_CREAT) ? ERROR_FILE_EXISTS
  1336.                                        : ERROR_FILE_NOT_FOUND;
  1337.                 break;
  1338.         }
  1339.         TclOS2ConvertError(err);
  1340.         if (interp != (Tcl_Interp *) NULL) {
  1341.             Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
  1342.                     Tcl_PosixError(interp), (char *) NULL);
  1343.         }
  1344.         Tcl_DStringFree(&buffer);
  1345.         return NULL;
  1346.     }
  1347.  
  1348.     infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
  1349.     infoPtr->nextPtr = firstFilePtr;
  1350.     firstFilePtr = infoPtr;
  1351.     infoPtr->validMask = channelPermissions;
  1352.     infoPtr->watchMask = 0;
  1353.     infoPtr->flags = (mode & O_APPEND) ? FILE_APPEND : 0;
  1354.     infoPtr->handle = handle;
  1355.  
  1356.     /*
  1357.      * We can't distinguish between a normal file and a com-port (reserved
  1358.      * file names COM1 through COM4, so just look at the name.
  1359.      */
  1360.     if ((nativeName[0] == 'C' || nativeName[0] == 'c') && 
  1361.         (stricmp(nativeName, "COM1") == 0 || stricmp(nativeName, "COM2") == 0 ||
  1362.          stricmp(nativeName, "COM3") == 0 || stricmp(nativeName, "COM4") == 0 ||
  1363.          stricmp(nativeName, "COM5") == 0 || stricmp(nativeName, "COM6") == 0 ||
  1364.          stricmp(nativeName, "COM7") == 0 || stricmp(nativeName, "COM8") == 0 ||
  1365.          stricmp(nativeName, "COM9") == 0)
  1366.        ) {
  1367.         /*
  1368.          * This is a com port.  Reopen it with the correct modes.
  1369.          */
  1370.         DCBINFO dcb;
  1371.         ULONG parmLen;
  1372.  
  1373. #ifdef VERBOSE
  1374.         printf("handle 0x%x (%s) is a COM port\n", handle, nativeName);
  1375.         fflush(stdout);
  1376. #endif
  1377.         DosClose(handle);
  1378.         rc = DosOpen(nativeName, &handle, &exist, 0, flags,
  1379.                      OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
  1380.                      accessMode, (PEAOP2)NULL);
  1381.         if (rc != NO_ERROR) {
  1382.             TclOS2ConvertError(ERROR_FILE_NOT_FOUND);
  1383.             if (interp != (Tcl_Interp *) NULL) {
  1384.                 Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
  1385.                         Tcl_PosixError(interp), (char *) NULL);
  1386.             }
  1387.             Tcl_DStringFree(&buffer);
  1388.             return NULL;
  1389.         }
  1390.  
  1391.         /*
  1392.          * Initialize the com port.
  1393.          * GETDCBINFO should be used before SETDCBINFO to set presently
  1394.          * reserved fields correctly when the application is not aware of them.
  1395.          */
  1396.  
  1397.         parmLen = sizeof(dcb);
  1398.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  1399.                          IOCTL_ASYNC,        /* Serial-device control */
  1400.                          ASYNC_GETDCBINFO,    /* Get device control block */
  1401.                          NULL,            /* No parameter packet */
  1402.                          0L,            /* Max size of parameter list */
  1403.                          NULL,            /* Size of parameter packet */
  1404.                          (PULONG)&dcb,        /* Points at dcb variable */
  1405.                          sizeof(dcb),        /* Max size of data packet */
  1406.                          &parmLen        /* Size of data packet */
  1407.                         );
  1408.         if (rc != NO_ERROR) {
  1409.             if (interp) {
  1410.                 Tcl_AppendResult(interp, "can't get comm device info", NULL);
  1411.             }
  1412.             Tcl_DStringFree(&buffer);
  1413.             return NULL;
  1414.         }
  1415.  
  1416.         /*
  1417.          * The Windows port set the following:
  1418.          * Monitoring of events: character received and put in input buffer.
  1419.          * Set (request) input and output buffers to 4096 bytes.
  1420.          * Terminate all outstanding read and write operations and clear any
  1421.          * input and output buffer.
  1422.          * Read operation should return immediately with the characters that
  1423.          * have already been received, even if no characters have been received.
  1424.          * No timeout for write.
  1425.          */
  1426. #ifdef VERBOSE
  1427.         printf("previous fbTimeout %x read %d write %d\n", dcb.fbTimeout,
  1428.                dcb.usReadTimeout, dcb.usWriteTimeout);
  1429.         fflush(stdout);
  1430. #endif
  1431. #define W_INFINITE    0x01
  1432. #define R_NORMAL    0x02
  1433. #define R_WAITFOR_S    0x04
  1434. #define R_NOWAIT    0x06
  1435. #define EHB_DISABLE    0x08
  1436. #define EHB_ENABLE    0x10
  1437. #define EHB_AUTO    0x18
  1438. #define R_TRIGGER_4    0x20
  1439. #define R_TRIGGER_8    0x40
  1440. #define R_TRIGGER_14    0x60
  1441. #define T_BUF_LC_16    0x80
  1442.         dcb.fbTimeout &= W_INFINITE | R_NOWAIT;
  1443.  
  1444.         parmLen = sizeof(dcb);
  1445.         rc = DosDevIOCtl(infoPtr->handle,    /* Device handle */
  1446.                          IOCTL_ASYNC,        /* Serial-device control */
  1447.                          ASYNC_SETDCBINFO,    /* Set device control block */
  1448.                          (PULONG)&dcb,        /* Points at dcb variable */
  1449.                          sizeof(dcb),        /* Max size of param packet */
  1450.                          &parmLen,        /* Size of param packet */
  1451.                          NULL,            /* No data packet */
  1452.                          0L,            /* Max size of data list */
  1453.                          NULL            /* Size of data packet */
  1454.                         );
  1455.         if (rc != NO_ERROR) {
  1456.             if (interp) {
  1457.                 Tcl_AppendResult(interp, "can't initialize comm", NULL);
  1458.             }
  1459.             Tcl_DStringFree(&buffer);
  1460.             return NULL;
  1461.         }
  1462.  
  1463.         channelTypePtr = &comChannelType;
  1464.     } else {
  1465. #ifdef VERBOSE
  1466.         printf("handle 0x%x (%s) not a COM port\n", handle, nativeName);
  1467.         fflush(stdout);
  1468. #endif
  1469.         channelTypePtr = &fileChannelType;
  1470.     }
  1471.     Tcl_DStringFree(&buffer);
  1472.  
  1473.     sprintf(channelName, "file%d", (int) handle);
  1474.  
  1475.     infoPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
  1476.             (ClientData) infoPtr, channelPermissions);
  1477.  
  1478.     if (seekFlag) {
  1479.         if (Tcl_Seek(infoPtr->channel, 0, SEEK_END) < 0) {
  1480.             if (interp != (Tcl_Interp *) NULL) {
  1481.                 Tcl_AppendResult(interp, "could not seek to end of file on \"",
  1482.                         channelName, "\": ", Tcl_PosixError(interp),
  1483.                         (char *) NULL);
  1484.             }
  1485.             Tcl_Close(NULL, infoPtr->channel);
  1486.             return NULL;
  1487.         }
  1488.     }
  1489.  
  1490.     /*
  1491.      * Files have default translation of AUTO and ^Z eof char, which
  1492.      * means that a ^Z will be appended to them at close.
  1493.      */
  1494.  
  1495.     if (channelTypePtr == &comChannelType) {
  1496.         /*
  1497.          * Gotcha.  Most modems need a "\r" at the end of the command
  1498.          * sequence.  If you just send "at\n", the modem will not respond
  1499.          * with "OK" because it never got a "\r" to actually invoke the
  1500.          * command.  So, by default, newlines are translated to "\r\n" on
  1501.          * output to avoid "bug" reports that the serial port isn't working.
  1502.          */
  1503.  
  1504.         if (Tcl_SetChannelOption(interp, infoPtr->channel, "-translation",
  1505.                 "auto crlf") != TCL_OK) {
  1506.             Tcl_Close(NULL, infoPtr->channel);
  1507.             return NULL;
  1508.         }
  1509.     } else {
  1510.         Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1511.     }
  1512.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
  1513.     return infoPtr->channel;
  1514. }
  1515.  
  1516. /*
  1517.  *----------------------------------------------------------------------
  1518.  *
  1519.  * Tcl_MakeFileChannel --
  1520.  *
  1521.  *    Makes a Tcl_Channel from an existing OS level file handle.
  1522.  *
  1523.  * Results:
  1524.  *    The Tcl_Channel created around the preexisting OS level file handle.
  1525.  *
  1526.  * Side effects:
  1527.  *    None.
  1528.  *
  1529.  *----------------------------------------------------------------------
  1530.  */
  1531.  
  1532. Tcl_Channel
  1533. Tcl_MakeFileChannel(handle, mode)
  1534.     ClientData handle;        /* OS level handle. */
  1535.     int mode;            /* ORed combination of TCL_READABLE and
  1536.                                  * TCL_WRITABLE to indicate file mode. */
  1537. {
  1538.     char channelName[20];
  1539.     FileInfo *infoPtr;
  1540.  
  1541.     if (!initialized) {
  1542.         FileInit();
  1543.     }
  1544.  
  1545.     if (mode == 0) {
  1546.         return NULL;
  1547.     }
  1548.  
  1549.     sprintf(channelName, "file%d", (int) handle);
  1550.  
  1551.     /*
  1552.      * Look to see if a channel with this handle and the same mode already
  1553.      * exists. If the handle is used, but the mode doesn't match, return NULL.
  1554.      */
  1555.     
  1556.     for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  1557.         if (infoPtr->handle == (HFILE) handle) {
  1558.             return (mode == infoPtr->validMask) ? infoPtr->channel : NULL;
  1559.         }
  1560.     }
  1561.  
  1562.     infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
  1563.     infoPtr->nextPtr = firstFilePtr;
  1564.     firstFilePtr = infoPtr;
  1565.     infoPtr->validMask = mode;
  1566.     infoPtr->watchMask = 0;
  1567.     infoPtr->flags = 0;
  1568.     infoPtr->handle = (HFILE) handle;
  1569.     infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
  1570.             (ClientData) infoPtr, mode);
  1571.  
  1572.     /*
  1573.      * OS/2 files have AUTO translation mode and ^Z eof char on input.
  1574.      */
  1575.  
  1576.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
  1577.     Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
  1578.     return infoPtr->channel;
  1579. }
  1580.  
  1581. /*
  1582.  *----------------------------------------------------------------------
  1583.  *
  1584.  * TclGetDefaultStdChannel --
  1585.  *
  1586.  *    Creates channels for standard input, standard output or standard
  1587.  *    error output if they do not already exist.
  1588.  *
  1589.  * Results:
  1590.  *    Returns the specified default standard channel, or NULL.
  1591.  *
  1592.  * Side effects:
  1593.  *    May cause the creation of a standard channel and the underlying
  1594.  *    file.
  1595.  *
  1596.  *----------------------------------------------------------------------
  1597.  */
  1598.  
  1599. Tcl_Channel
  1600. TclGetDefaultStdChannel(type)
  1601.     int type;            /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
  1602. {
  1603.     Tcl_Channel channel = NULL;
  1604.     HFILE handle = (HFILE)0L;
  1605.     int mode = 0;
  1606.     char *bufMode = NULL;
  1607.  
  1608.     switch (type) {
  1609.         case TCL_STDIN:
  1610.             handle = HF_STDIN;
  1611.             mode = TCL_READABLE;
  1612.             bufMode = "line";
  1613.             break;
  1614.         case TCL_STDOUT:
  1615.             handle = HF_STDOUT;
  1616.             mode = TCL_WRITABLE;
  1617.             bufMode = "line";
  1618.             break;
  1619.         case TCL_STDERR:
  1620.             handle = HF_STDERR;
  1621.             mode = TCL_WRITABLE;
  1622.             bufMode = "none";
  1623.             break;
  1624.         default:
  1625.             panic("TclGetDefaultStdChannel: Unexpected channel type");
  1626.             break;
  1627.     }
  1628.  
  1629.     channel = Tcl_MakeFileChannel((ClientData)handle, mode);
  1630.  
  1631.     /*
  1632.      * Set up the normal channel options for stdio handles.
  1633.      */
  1634.  
  1635.     if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
  1636.             "auto") == TCL_ERROR)
  1637.             || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
  1638.                     "\032 {}") == TCL_ERROR)
  1639.             || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
  1640.                     "-buffering", bufMode) == TCL_ERROR)) {
  1641.         Tcl_Close((Tcl_Interp *) NULL, channel);
  1642.         return (Tcl_Channel) NULL;
  1643.     }
  1644.     return channel;
  1645. }
  1646.  
  1647. /*
  1648.  *----------------------------------------------------------------------
  1649.  *
  1650.  * TclOS2WaitForFile --
  1651.  *
  1652.  *    This procedure waits synchronously for a file to become readable
  1653.  *    or writable, with an optional timeout.
  1654.  *
  1655.  * Results:
  1656.  *    The return value is an OR'ed combination of TCL_READABLE,
  1657.  *    TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
  1658.  *    that are present on file at the time of the return.  This
  1659.  *    procedure will not return until either "timeout" milliseconds
  1660.  *    have elapsed or at least one of the conditions given by mask
  1661.  *    has occurred for file (a return value of 0 means that a timeout
  1662.  *    occurred).  No normal events will be serviced during the
  1663.  *    execution of this procedure.
  1664.  *
  1665.  * Side effects:
  1666.  *    Time passes.
  1667.  *
  1668.  *----------------------------------------------------------------------
  1669.  */
  1670.  
  1671. int
  1672. TclOS2WaitForFile(handle, mask, timeout)
  1673.     HFILE handle;        /* Handle for file on which to wait. */
  1674.     int mask;            /* What to wait for: OR'ed combination of
  1675.                  * TCL_READABLE, TCL_WRITABLE, and
  1676.                  * TCL_EXCEPTION. */
  1677.     int timeout;        /* Maximum amount of time to wait for one
  1678.                  * of the conditions in mask to occur, in
  1679.                  * milliseconds.  A value of 0 means don't
  1680.                  * wait at all, and a value of -1 means
  1681.                  * wait forever. */
  1682. {
  1683.     Tcl_Time abortTime, now;
  1684.     struct timeval blockTime, *timeoutPtr;
  1685.     int numFound, result = 0;
  1686.     static fd_set readyMasks[3];
  1687.                 /* This array reflects the readable/writable
  1688.                  * conditions that were found to exist by the
  1689.                  * last call to select. */
  1690.  
  1691.     /*
  1692.      * If there is a non-zero finite timeout, compute the time when
  1693.      * we give up.
  1694.      */
  1695.  
  1696.     if (timeout > 0) {
  1697.     TclpGetTime(&now);
  1698.     abortTime.sec = now.sec + timeout/1000;
  1699.     abortTime.usec = now.usec + (timeout%1000)*1000;
  1700.     if (abortTime.usec >= 1000000) {
  1701.         abortTime.usec -= 1000000;
  1702.         abortTime.sec += 1;
  1703.     }
  1704.     timeoutPtr = &blockTime;
  1705.     } else if (timeout == 0) {
  1706.     timeoutPtr = &blockTime;
  1707.     blockTime.tv_sec = 0;
  1708.     blockTime.tv_usec = 0;
  1709.     } else {
  1710.     timeoutPtr = NULL;
  1711.     }
  1712.  
  1713.     /*
  1714.      * Initialize the ready masks and compute the mask offsets.
  1715.      */
  1716.  
  1717.     if ((int)handle >= FD_SETSIZE) {
  1718.     panic("TclWaitForFile can't handle file id %d", (int)handle);
  1719.     }
  1720.     FD_ZERO(&readyMasks[0]);
  1721.     FD_ZERO(&readyMasks[1]);
  1722.     FD_ZERO(&readyMasks[2]);
  1723.     
  1724.     /*
  1725.      * Loop in a mini-event loop of our own, waiting for either the
  1726.      * file to become ready or a timeout to occur.
  1727.      */
  1728.  
  1729.     while (1) {
  1730.     if (timeout > 0) {
  1731.         blockTime.tv_sec = abortTime.sec - now.sec;
  1732.         blockTime.tv_usec = abortTime.usec - now.usec;
  1733.         if (blockTime.tv_usec < 0) {
  1734.         blockTime.tv_sec -= 1;
  1735.         blockTime.tv_usec += 1000000;
  1736.         }
  1737.         if (blockTime.tv_sec < 0) {
  1738.         blockTime.tv_sec = 0;
  1739.         blockTime.tv_usec = 0;
  1740.         }
  1741.     }
  1742.     
  1743.     /*
  1744.      * Set the appropriate bit in the ready masks for the handle.
  1745.      */
  1746.  
  1747.     if (mask & TCL_READABLE) {
  1748.         FD_SET((int)handle, &readyMasks[0]);
  1749.     }
  1750.     if (mask & TCL_WRITABLE) {
  1751.         FD_SET((int)handle, &readyMasks[1]);
  1752.     }
  1753.     if (mask & TCL_EXCEPTION) {
  1754.         FD_SET((int)handle, &readyMasks[2]);
  1755.     }
  1756.  
  1757.     /*
  1758.      * Wait for the event or a timeout.
  1759.      */
  1760.  
  1761.     numFound = select(((int)handle)+1, &readyMasks[0], &readyMasks[1],
  1762.                       &readyMasks[2], timeoutPtr);
  1763.     if (numFound == 1) {
  1764.         if (FD_ISSET((int)handle, &readyMasks[0])) {
  1765.         result |= TCL_READABLE;
  1766.         }
  1767.         if (FD_ISSET((int)handle, &readyMasks[1])) {
  1768.         result |= TCL_WRITABLE;
  1769.         }
  1770.         if (FD_ISSET((int)handle, &readyMasks[2])) {
  1771.         result |= TCL_EXCEPTION;
  1772.         }
  1773.         result &= mask;
  1774.         if (result) {
  1775.         break;
  1776.         }
  1777.     }
  1778.     if (timeout == 0) {
  1779.         break;
  1780.     }
  1781.  
  1782.     /*
  1783.      * The select returned early, so we need to recompute the timeout.
  1784.      */
  1785.  
  1786.     TclpGetTime(&now);
  1787.     if ((abortTime.sec < now.sec)
  1788.         || ((abortTime.sec == now.sec)
  1789.         && (abortTime.usec <= now.usec))) {
  1790.         break;
  1791.     }
  1792.     }
  1793.     return result;
  1794. }
  1795.