tclUnixChan.c

Go to the documentation of this file.
00001 /*
00002  * tclUnixChan.c
00003  *
00004  *      Common channel driver for Unix channels based on files, command pipes
00005  *      and TCP sockets.
00006  *
00007  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00008  * Copyright (c) 1998-1999 by Scriptics Corporation.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclUnixChan.c,v 1.89 2007/12/13 15:28:42 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"     /* Internal definitions for Tcl. */
00017 #include "tclIO.h"      /* To get Channel type declaration. */
00018 
00019 /*
00020  * sys/ioctl.h has already been included by tclPort.h. Including termios.h or
00021  * termio.h causes a bunch of warning messages because some duplicate (but not
00022  * contradictory) #defines exist in termios.h and/or termio.h
00023  */
00024 
00025 #undef NL0
00026 #undef NL1
00027 #undef CR0
00028 #undef CR1
00029 #undef CR2
00030 #undef CR3
00031 #undef TAB0
00032 #undef TAB1
00033 #undef TAB2
00034 #undef XTABS
00035 #undef BS0
00036 #undef BS1
00037 #undef FF0
00038 #undef FF1
00039 #undef ECHO
00040 #undef NOFLSH
00041 #undef TOSTOP
00042 #undef FLUSHO
00043 #undef PENDIN
00044 
00045 #define SUPPORTS_TTY
00046 
00047 #undef DIRECT_BAUD
00048 #ifdef B4800
00049 #   if (B4800 == 4800)
00050 #       define DIRECT_BAUD
00051 #   endif /* B4800 == 4800 */
00052 #endif /* B4800 */
00053 
00054 #ifdef USE_TERMIOS
00055 #   include <termios.h>
00056 #   ifdef HAVE_SYS_IOCTL_H
00057 #       include <sys/ioctl.h>
00058 #   endif /* HAVE_SYS_IOCTL_H */
00059 #   ifdef HAVE_SYS_MODEM_H
00060 #       include <sys/modem.h>
00061 #   endif /* HAVE_SYS_MODEM_H */
00062 #   define IOSTATE                      struct termios
00063 #   define GETIOSTATE(fd, statePtr)     tcgetattr((fd), (statePtr))
00064 #   define SETIOSTATE(fd, statePtr)     tcsetattr((fd), TCSADRAIN, (statePtr))
00065 #   define GETCONTROL(fd, intPtr)       ioctl((fd), TIOCMGET, (intPtr))
00066 #   define SETCONTROL(fd, intPtr)       ioctl((fd), TIOCMSET, (intPtr))
00067 
00068     /*
00069      * TIP #35 introduced a different on exit flush/close behavior that does
00070      * not work correctly with standard channels on all systems. The problem
00071      * is tcflush throws away waiting channel data. This may be necessary for
00072      * true serial channels that may block, but isn't correct in the standard
00073      * case. This might be replaced with tcdrain instead, but that can block.
00074      * For now, we revert to making this do nothing, and TtyOutputProc being
00075      * the same old FileOutputProc. - hobbs [Bug #525783]
00076      */
00077 
00078 #   define BAD_TIP35_FLUSH 0
00079 #   if BAD_TIP35_FLUSH
00080 #       define TTYFLUSH(fd)             tcflush((fd), TCIOFLUSH);
00081 #   else
00082 #       define TTYFLUSH(fd)
00083 #   endif /* BAD_TIP35_FLUSH */
00084 #   ifdef FIONREAD
00085 #       define GETREADQUEUE(fd, int)    ioctl((fd), FIONREAD, &(int))
00086 #   elif defined(FIORDCHK)
00087 #       define GETREADQUEUE(fd, int)    int = ioctl((fd), FIORDCHK, NULL)
00088 #   endif /* FIONREAD */
00089 #   ifdef TIOCOUTQ
00090 #       define GETWRITEQUEUE(fd, int)   ioctl((fd), TIOCOUTQ, &(int))
00091 #   endif /* TIOCOUTQ */
00092 #   if defined(TIOCSBRK) && defined(TIOCCBRK)
00093 
00094 /*
00095  * Can't use ?: operator below because that messes up types on either Linux or
00096  * Solaris (the two are mutually exclusive!)
00097  */
00098 
00099 #       define SETBREAK(fd, flag) \
00100                 if (flag) {                             \
00101                     ioctl((fd), TIOCSBRK, NULL);        \
00102                 } else {                                \
00103                     ioctl((fd), TIOCCBRK, NULL);        \
00104                 }
00105 #   endif /* TIOCSBRK&TIOCCBRK */
00106 #   if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
00107 #       define CRTSCTS CNEW_RTSCTS
00108 #   endif /* !CRTSCTS&CNEW_RTSCTS */
00109 #else   /* !USE_TERMIOS */
00110 
00111 #ifdef USE_TERMIO
00112 #   include <termio.h>
00113 #   define IOSTATE                      struct termio
00114 #   define GETIOSTATE(fd, statePtr)     ioctl((fd), TCGETA, (statePtr))
00115 #   define SETIOSTATE(fd, statePtr)     ioctl((fd), TCSETAW, (statePtr))
00116 #else   /* !USE_TERMIO */
00117 
00118 #ifdef USE_SGTTY
00119 #   include <sgtty.h>
00120 #   define IOSTATE                      struct sgttyb
00121 #   define GETIOSTATE(fd, statePtr)     ioctl((fd), TIOCGETP, (statePtr))
00122 #   define SETIOSTATE(fd, statePtr)     ioctl((fd), TIOCSETP, (statePtr))
00123 #else   /* !USE_SGTTY */
00124 #   undef SUPPORTS_TTY
00125 #endif  /* !USE_SGTTY */
00126 
00127 #endif  /* !USE_TERMIO */
00128 #endif  /* !USE_TERMIOS */
00129 
00130 /*
00131  * Helper macros to make parts of this file clearer. The macros do exactly
00132  * what they say on the tin. :-) They also only ever refer to their arguments
00133  * once, and so can be used without regard to side effects.
00134  */
00135 
00136 #define SET_BITS(var, bits)     ((var) |= (bits))
00137 #define CLEAR_BITS(var, bits)   ((var) &= ~(bits))
00138 
00139 /*
00140  * This structure describes per-instance state of a file based channel.
00141  */
00142 
00143 typedef struct FileState {
00144     Tcl_Channel channel;        /* Channel associated with this file. */
00145     int fd;                     /* File handle. */
00146     int validMask;              /* OR'ed combination of TCL_READABLE,
00147                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
00148                                  * which operations are valid on the file. */
00149 } FileState;
00150 
00151 #ifdef SUPPORTS_TTY
00152 
00153 /*
00154  * The following structure describes per-instance state of a tty-based
00155  * channel.
00156  */
00157 
00158 typedef struct TtyState {
00159     FileState fs;               /* Per-instance state of the file descriptor.
00160                                  * Must be the first field. */
00161     int stateUpdated;           /* Flag to say if the state has been modified
00162                                  * and needs resetting. */
00163     IOSTATE savedState;         /* Initial state of device. Used to reset
00164                                  * state when device closed. */
00165 } TtyState;
00166 
00167 /*
00168  * The following structure is used to set or get the serial port attributes in
00169  * a platform-independant manner.
00170  */
00171 
00172 typedef struct TtyAttrs {
00173     int baud;
00174     int parity;
00175     int data;
00176     int stop;
00177 } TtyAttrs;
00178 
00179 #endif  /* !SUPPORTS_TTY */
00180 
00181 #define UNSUPPORTED_OPTION(detail) \
00182     if (interp) {                                               \
00183         Tcl_AppendResult(interp, (detail),                      \
00184                 " not supported for this platform", NULL);      \
00185     }
00186 
00187 /*
00188  * This structure describes per-instance state of a tcp based channel.
00189  */
00190 
00191 typedef struct TcpState {
00192     Tcl_Channel channel;        /* Channel associated with this file. */
00193     int fd;                     /* The socket itself. */
00194     int flags;                  /* ORed combination of the bitfields defined
00195                                  * below. */
00196     Tcl_TcpAcceptProc *acceptProc;
00197                                 /* Proc to call on accept. */
00198     ClientData acceptProcData;  /* The data for the accept proc. */
00199 } TcpState;
00200 
00201 /*
00202  * These bits may be ORed together into the "flags" field of a TcpState
00203  * structure.
00204  */
00205 
00206 #define TCP_ASYNC_SOCKET        (1<<0)  /* Asynchronous socket. */
00207 #define TCP_ASYNC_CONNECT       (1<<1)  /* Async connect in progress. */
00208 
00209 /*
00210  * The following defines the maximum length of the listen queue. This is the
00211  * number of outstanding yet-to-be-serviced requests for a connection on a
00212  * server socket, more than this number of outstanding requests and the
00213  * connection request will fail.
00214  */
00215 
00216 #ifndef SOMAXCONN
00217 #   define SOMAXCONN    100
00218 #endif /* SOMAXCONN */
00219 
00220 #if (SOMAXCONN < 100)
00221 #   undef  SOMAXCONN
00222 #   define SOMAXCONN    100
00223 #endif /* SOMAXCONN < 100 */
00224 
00225 /*
00226  * The following defines how much buffer space the kernel should maintain for
00227  * a socket.
00228  */
00229 
00230 #define SOCKET_BUFSIZE  4096
00231 
00232 /*
00233  * Static routines for this file:
00234  */
00235 
00236 static TcpState *       CreateSocket(Tcl_Interp *interp, int port,
00237                             const char *host, int server, const char *myaddr,
00238                             int myport, int async);
00239 static int              CreateSocketAddress(struct sockaddr_in *sockaddrPtr,
00240                             const char *host, int port, int willBind,
00241                             const char **errorMsgPtr);
00242 static int              FileBlockModeProc(ClientData instanceData, int mode);
00243 static int              FileCloseProc(ClientData instanceData,
00244                             Tcl_Interp *interp);
00245 static int              FileGetHandleProc(ClientData instanceData,
00246                             int direction, ClientData *handlePtr);
00247 static int              FileInputProc(ClientData instanceData, char *buf,
00248                             int toRead, int *errorCode);
00249 static int              FileOutputProc(ClientData instanceData,
00250                             const char *buf, int toWrite, int *errorCode);
00251 static int              FileSeekProc(ClientData instanceData, long offset,
00252                             int mode, int *errorCode);
00253 static int              FileTruncateProc(ClientData instanceData,
00254                             Tcl_WideInt length);
00255 static Tcl_WideInt      FileWideSeekProc(ClientData instanceData,
00256                             Tcl_WideInt offset, int mode, int *errorCode);
00257 static void             FileWatchProc(ClientData instanceData, int mask);
00258 static void             TcpAccept(ClientData data, int mask);
00259 static int              TcpBlockModeProc(ClientData data, int mode);
00260 static int              TcpCloseProc(ClientData instanceData,
00261                             Tcl_Interp *interp);
00262 static int              TcpGetHandleProc(ClientData instanceData,
00263                             int direction, ClientData *handlePtr);
00264 static int              TcpGetOptionProc(ClientData instanceData,
00265                             Tcl_Interp *interp, const char *optionName,
00266                             Tcl_DString *dsPtr);
00267 static int              TcpInputProc(ClientData instanceData, char *buf,
00268                             int toRead, int *errorCode);
00269 static int              TcpOutputProc(ClientData instanceData,
00270                             const char *buf, int toWrite, int *errorCode);
00271 static void             TcpWatchProc(ClientData instanceData, int mask);
00272 #ifdef SUPPORTS_TTY
00273 static int              TtyCloseProc(ClientData instanceData,
00274                             Tcl_Interp *interp);
00275 static void             TtyGetAttributes(int fd, TtyAttrs *ttyPtr);
00276 static int              TtyGetOptionProc(ClientData instanceData,
00277                             Tcl_Interp *interp, const char *optionName,
00278                             Tcl_DString *dsPtr);
00279 #ifndef DIRECT_BAUD
00280 static int              TtyGetBaud(unsigned long speed);
00281 static unsigned long    TtyGetSpeed(int baud);
00282 #endif /* DIRECT_BAUD */
00283 static FileState *      TtyInit(int fd, int initialize);
00284 static void             TtyModemStatusStr(int status, Tcl_DString *dsPtr);
00285 #if BAD_TIP35_FLUSH
00286 static int              TtyOutputProc(ClientData instanceData,
00287                             const char *buf, int toWrite, int *errorCode);
00288 #endif /* BAD_TIP35_FLUSH */
00289 static int              TtyParseMode(Tcl_Interp *interp, const char *mode,
00290                             int *speedPtr, int *parityPtr, int *dataPtr,
00291                             int *stopPtr);
00292 static void             TtySetAttributes(int fd, TtyAttrs *ttyPtr);
00293 static int              TtySetOptionProc(ClientData instanceData,
00294                             Tcl_Interp *interp, const char *optionName,
00295                             const char *value);
00296 #endif  /* SUPPORTS_TTY */
00297 static int              WaitForConnect(TcpState *statePtr, int *errorCodePtr);
00298 static Tcl_Channel      MakeTcpClientChannelMode(ClientData tcpSocket,
00299                             int mode);
00300 
00301 /*
00302  * This structure describes the channel type structure for file based IO:
00303  */
00304 
00305 static Tcl_ChannelType fileChannelType = {
00306     "file",                     /* Type name. */
00307     TCL_CHANNEL_VERSION_5,      /* v5 channel */
00308     FileCloseProc,              /* Close proc. */
00309     FileInputProc,              /* Input proc. */
00310     FileOutputProc,             /* Output proc. */
00311     FileSeekProc,               /* Seek proc. */
00312     NULL,                       /* Set option proc. */
00313     NULL,                       /* Get option proc. */
00314     FileWatchProc,              /* Initialize notifier. */
00315     FileGetHandleProc,          /* Get OS handles out of channel. */
00316     NULL,                       /* close2proc. */
00317     FileBlockModeProc,          /* Set blocking or non-blocking mode.*/
00318     NULL,                       /* flush proc. */
00319     NULL,                       /* handler proc. */
00320     FileWideSeekProc,           /* wide seek proc. */
00321     NULL,
00322     FileTruncateProc,           /* truncate proc. */
00323 };
00324 
00325 #ifdef SUPPORTS_TTY
00326 /*
00327  * This structure describes the channel type structure for serial IO.
00328  * Note that this type is a subclass of the "file" type.
00329  */
00330 
00331 static Tcl_ChannelType ttyChannelType = {
00332     "tty",                      /* Type name. */
00333     TCL_CHANNEL_VERSION_5,      /* v5 channel */
00334     TtyCloseProc,               /* Close proc. */
00335     FileInputProc,              /* Input proc. */
00336 #if BAD_TIP35_FLUSH
00337     TtyOutputProc,              /* Output proc. */
00338 #else /* !BAD_TIP35_FLUSH */
00339     FileOutputProc,             /* Output proc. */
00340 #endif /* BAD_TIP35_FLUSH */
00341     NULL,                       /* Seek proc. */
00342     TtySetOptionProc,           /* Set option proc. */
00343     TtyGetOptionProc,           /* Get option proc. */
00344     FileWatchProc,              /* Initialize notifier. */
00345     FileGetHandleProc,          /* Get OS handles out of channel. */
00346     NULL,                       /* close2proc. */
00347     FileBlockModeProc,          /* Set blocking or non-blocking mode.*/
00348     NULL,                       /* flush proc. */
00349     NULL,                       /* handler proc. */
00350     NULL,                       /* wide seek proc. */
00351     NULL,                       /* thread action proc. */
00352     NULL,                       /* truncate proc. */
00353 };
00354 #endif  /* SUPPORTS_TTY */
00355 
00356 /*
00357  * This structure describes the channel type structure for TCP socket
00358  * based IO:
00359  */
00360 
00361 static Tcl_ChannelType tcpChannelType = {
00362     "tcp",                      /* Type name. */
00363     TCL_CHANNEL_VERSION_5,      /* v5 channel */
00364     TcpCloseProc,               /* Close proc. */
00365     TcpInputProc,               /* Input proc. */
00366     TcpOutputProc,              /* Output proc. */
00367     NULL,                       /* Seek proc. */
00368     NULL,                       /* Set option proc. */
00369     TcpGetOptionProc,           /* Get option proc. */
00370     TcpWatchProc,               /* Initialize notifier. */
00371     TcpGetHandleProc,           /* Get OS handles out of channel. */
00372     NULL,                       /* close2proc. */
00373     TcpBlockModeProc,           /* Set blocking or non-blocking mode.*/
00374     NULL,                       /* flush proc. */
00375     NULL,                       /* handler proc. */
00376     NULL,                       /* wide seek proc. */
00377     NULL,                       /* thread action proc. */
00378     NULL,                       /* truncate proc. */
00379 };
00380 
00381 /*
00382  *----------------------------------------------------------------------
00383  *
00384  * FileBlockModeProc --
00385  *
00386  *      Helper function to set blocking and nonblocking modes on a file based
00387  *      channel. Invoked by generic IO level code.
00388  *
00389  * Results:
00390  *      0 if successful, errno when failed.
00391  *
00392  * Side effects:
00393  *      Sets the device into blocking or non-blocking mode.
00394  *
00395  *----------------------------------------------------------------------
00396  */
00397 
00398         /* ARGSUSED */
00399 static int
00400 FileBlockModeProc(
00401     ClientData instanceData,    /* File state. */
00402     int mode)                   /* The mode to set. Can be one of
00403                                  * TCL_MODE_BLOCKING or
00404                                  * TCL_MODE_NONBLOCKING. */
00405 {
00406     FileState *fsPtr = (FileState *) instanceData;
00407     int curStatus;
00408 
00409 #ifndef USE_FIONBIO
00410     curStatus = fcntl(fsPtr->fd, F_GETFL);
00411     if (mode == TCL_MODE_BLOCKING) {
00412         CLEAR_BITS(curStatus, O_NONBLOCK);
00413     } else {
00414         SET_BITS(curStatus, O_NONBLOCK);
00415     }
00416     if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) {
00417         return errno;
00418     }
00419     curStatus = fcntl(fsPtr->fd, F_GETFL);
00420 #else /* USE_FIONBIO */
00421     if (mode == TCL_MODE_BLOCKING) {
00422         curStatus = 0;
00423     } else {
00424         curStatus = 1;
00425     }
00426     if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) {
00427         return errno;
00428     }
00429 #endif /* !USE_FIONBIO */
00430     return 0;
00431 }
00432 
00433 /*
00434  *----------------------------------------------------------------------
00435  *
00436  * FileInputProc --
00437  *
00438  *      This function is invoked from the generic IO level to read input from
00439  *      a file based channel.
00440  *
00441  * Results:
00442  *      The number of bytes read is returned or -1 on error. An output
00443  *      argument contains a POSIX error code if an error occurs, or zero.
00444  *
00445  * Side effects:
00446  *      Reads input from the input device of the channel.
00447  *
00448  *----------------------------------------------------------------------
00449  */
00450 
00451 static int
00452 FileInputProc(
00453     ClientData instanceData,    /* File state. */
00454     char *buf,                  /* Where to store data read. */
00455     int toRead,                 /* How much space is available in the
00456                                  * buffer? */
00457     int *errorCodePtr)          /* Where to store error code. */
00458 {
00459     FileState *fsPtr = (FileState *) instanceData;
00460     int bytesRead;              /* How many bytes were actually read from the
00461                                  * input device? */
00462 
00463     *errorCodePtr = 0;
00464 
00465     /*
00466      * Assume there is always enough input available. This will block
00467      * appropriately, and read will unblock as soon as a short read is
00468      * possible, if the channel is in blocking mode. If the channel is
00469      * nonblocking, the read will never block.
00470      */
00471 
00472     bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
00473     if (bytesRead > -1) {
00474         return bytesRead;
00475     }
00476     *errorCodePtr = errno;
00477     return -1;
00478 }
00479 
00480 /*
00481  *----------------------------------------------------------------------
00482  *
00483  * FileOutputProc--
00484  *
00485  *      This function is invoked from the generic IO level to write output to
00486  *      a file channel.
00487  *
00488  * Results:
00489  *      The number of bytes written is returned or -1 on error. An output
00490  *      argument contains a POSIX error code if an error occurred, or zero.
00491  *
00492  * Side effects:
00493  *      Writes output on the output device of the channel.
00494  *
00495  *----------------------------------------------------------------------
00496  */
00497 
00498 static int
00499 FileOutputProc(
00500     ClientData instanceData,    /* File state. */
00501     const char *buf,            /* The data buffer. */
00502     int toWrite,                /* How many bytes to write? */
00503     int *errorCodePtr)          /* Where to store error code. */
00504 {
00505     FileState *fsPtr = (FileState *) instanceData;
00506     int written;
00507 
00508     *errorCodePtr = 0;
00509 
00510     if (toWrite == 0) {
00511         /*
00512          * SF Tcl Bug 465765. Do not try to write nothing into a file. STREAM
00513          * based implementations will considers this as EOF (if there is a
00514          * pipe behind the file).
00515          */
00516 
00517         return 0;
00518     }
00519     written = write(fsPtr->fd, buf, (size_t) toWrite);
00520     if (written > -1) {
00521         return written;
00522     }
00523     *errorCodePtr = errno;
00524     return -1;
00525 }
00526 
00527 /*
00528  *----------------------------------------------------------------------
00529  *
00530  * FileCloseProc --
00531  *
00532  *      This function is called from the generic IO level to perform
00533  *      channel-type-specific cleanup when a file based channel is closed.
00534  *
00535  * Results:
00536  *      0 if successful, errno if failed.
00537  *
00538  * Side effects:
00539  *      Closes the device of the channel.
00540  *
00541  *----------------------------------------------------------------------
00542  */
00543 
00544 static int
00545 FileCloseProc(
00546     ClientData instanceData,    /* File state. */
00547     Tcl_Interp *interp)         /* For error reporting - unused. */
00548 {
00549     FileState *fsPtr = (FileState *) instanceData;
00550     int errorCode = 0;
00551 
00552     Tcl_DeleteFileHandler(fsPtr->fd);
00553 
00554     /*
00555      * Do not close standard channels while in thread-exit.
00556      */
00557 
00558     if (!TclInThreadExit()
00559             || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
00560         if (close(fsPtr->fd) < 0) {
00561             errorCode = errno;
00562         }
00563     }
00564     ckfree((char *) fsPtr);
00565     return errorCode;
00566 }
00567 
00568 /*
00569  *----------------------------------------------------------------------
00570  *
00571  * FileSeekProc --
00572  *
00573  *      This function is called by the generic IO level to move the access
00574  *      point in a file based channel.
00575  *
00576  * Results:
00577  *      -1 if failed, the new position if successful. An output argument
00578  *      contains the POSIX error code if an error occurred, or zero.
00579  *
00580  * Side effects:
00581  *      Moves the location at which the channel will be accessed in future
00582  *      operations.
00583  *
00584  *----------------------------------------------------------------------
00585  */
00586 
00587 static int
00588 FileSeekProc(
00589     ClientData instanceData,    /* File state. */
00590     long offset,                /* Offset to seek to. */
00591     int mode,                   /* Relative to where should we seek? Can be
00592                                  * one of SEEK_START, SEEK_SET or SEEK_END. */
00593     int *errorCodePtr)          /* To store error code. */
00594 {
00595     FileState *fsPtr = (FileState *) instanceData;
00596     Tcl_WideInt oldLoc, newLoc;
00597 
00598     /*
00599      * Save our current place in case we need to roll-back the seek.
00600      */
00601 
00602     oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR);
00603     if (oldLoc == Tcl_LongAsWide(-1)) {
00604         /*
00605          * Bad things are happening. Error out...
00606          */
00607 
00608         *errorCodePtr = errno;
00609         return -1;
00610     }
00611 
00612     newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
00613 
00614     /*
00615      * Check for expressability in our return type, and roll-back otherwise.
00616      */
00617 
00618     if (newLoc > Tcl_LongAsWide(INT_MAX)) {
00619         *errorCodePtr = EOVERFLOW;
00620         TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET);
00621         return -1;
00622     } else {
00623         *errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0;
00624     }
00625     return (int) Tcl_WideAsLong(newLoc);
00626 }
00627 
00628 /*
00629  *----------------------------------------------------------------------
00630  *
00631  * FileWideSeekProc --
00632  *
00633  *      This function is called by the generic IO level to move the access
00634  *      point in a file based channel, with offsets expressed as wide
00635  *      integers.
00636  *
00637  * Results:
00638  *      -1 if failed, the new position if successful. An output argument
00639  *      contains the POSIX error code if an error occurred, or zero.
00640  *
00641  * Side effects:
00642  *      Moves the location at which the channel will be accessed in future
00643  *      operations.
00644  *
00645  *----------------------------------------------------------------------
00646  */
00647 
00648 static Tcl_WideInt
00649 FileWideSeekProc(
00650     ClientData instanceData,    /* File state. */
00651     Tcl_WideInt offset,         /* Offset to seek to. */
00652     int mode,                   /* Relative to where should we seek? Can be
00653                                  * one of SEEK_START, SEEK_CUR or SEEK_END. */
00654     int *errorCodePtr)          /* To store error code. */
00655 {
00656     FileState *fsPtr = (FileState *) instanceData;
00657     Tcl_WideInt newLoc;
00658 
00659     newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
00660 
00661     *errorCodePtr = (newLoc == -1) ? errno : 0;
00662     return newLoc;
00663 }
00664 
00665 /*
00666  *----------------------------------------------------------------------
00667  *
00668  * FileWatchProc --
00669  *
00670  *      Initialize the notifier to watch the fd from this channel.
00671  *
00672  * Results:
00673  *      None.
00674  *
00675  * Side effects:
00676  *      Sets up the notifier so that a future event on the channel will
00677  *      be seen by Tcl.
00678  *
00679  *----------------------------------------------------------------------
00680  */
00681 
00682 static void
00683 FileWatchProc(
00684     ClientData instanceData,    /* The file state. */
00685     int mask)                   /* Events of interest; an OR-ed combination of
00686                                  * TCL_READABLE, TCL_WRITABLE and
00687                                  * TCL_EXCEPTION. */
00688 {
00689     FileState *fsPtr = (FileState *) instanceData;
00690 
00691     /*
00692      * Make sure we only register for events that are valid on this file. Note
00693      * that we are passing Tcl_NotifyChannel directly to Tcl_CreateFileHandler
00694      * with the channel pointer as the client data.
00695      */
00696 
00697     mask &= fsPtr->validMask;
00698     if (mask) {
00699         Tcl_CreateFileHandler(fsPtr->fd, mask,
00700                 (Tcl_FileProc *) Tcl_NotifyChannel,
00701                 (ClientData) fsPtr->channel);
00702     } else {
00703         Tcl_DeleteFileHandler(fsPtr->fd);
00704     }
00705 }
00706 
00707 /*
00708  *----------------------------------------------------------------------
00709  *
00710  * FileGetHandleProc --
00711  *
00712  *      Called from Tcl_GetChannelHandle to retrieve OS handles from a file
00713  *      based channel.
00714  *
00715  * Results:
00716  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
00717  *      handle for the specified direction.
00718  *
00719  * Side effects:
00720  *      None.
00721  *
00722  *----------------------------------------------------------------------
00723  */
00724 
00725 static int
00726 FileGetHandleProc(
00727     ClientData instanceData,    /* The file state. */
00728     int direction,              /* TCL_READABLE or TCL_WRITABLE */
00729     ClientData *handlePtr)      /* Where to store the handle. */
00730 {
00731     FileState *fsPtr = (FileState *) instanceData;
00732 
00733     if (direction & fsPtr->validMask) {
00734         *handlePtr = (ClientData) INT2PTR(fsPtr->fd);
00735         return TCL_OK;
00736     }
00737     return TCL_ERROR;
00738 }
00739 
00740 #ifdef SUPPORTS_TTY
00741 /*
00742  *----------------------------------------------------------------------
00743  *
00744  * TtyCloseProc --
00745  *
00746  *      This function is called from the generic IO level to perform
00747  *      channel-type-specific cleanup when a tty based channel is closed.
00748  *
00749  * Results:
00750  *      0 if successful, errno if failed.
00751  *
00752  * Side effects:
00753  *      Closes the device of the channel.
00754  *
00755  *----------------------------------------------------------------------
00756  */
00757 
00758 static int
00759 TtyCloseProc(
00760     ClientData instanceData,    /* Tty state. */
00761     Tcl_Interp *interp)         /* For error reporting - unused. */
00762 {
00763 #if BAD_TIP35_FLUSH
00764     TtyState *ttyPtr = (TtyState *) instanceData;
00765 #endif /* BAD_TIP35_FLUSH */
00766 
00767 #ifdef TTYFLUSH
00768     TTYFLUSH(ttyPtr->fs.fd);
00769 #endif /* TTYFLUSH */
00770 
00771 #if 0
00772     /*
00773      * TIP#35 agreed to remove the unsave so that TCL could be used as a
00774      * simple stty. It would be cleaner to remove all the stuff related to
00775      *    TtyState.stateUpdated
00776      *    TtyState.savedState
00777      * Then the structure TtyState would be the same as FileState. IMO this
00778      * cleanup could better be done for the final 8.4 release after nobody
00779      * complained about the missing unsave. - schroedter
00780      */
00781     if (ttyPtr->stateUpdated) {
00782         SETIOSTATE(ttyPtr->fs.fd, &ttyPtr->savedState);
00783     }
00784 #endif
00785 
00786     return FileCloseProc(instanceData, interp);
00787 }
00788 
00789 /*
00790  *----------------------------------------------------------------------
00791  *
00792  * TtyOutputProc--
00793  *
00794  *      This function is invoked from the generic IO level to write output to
00795  *      a TTY channel.
00796  *
00797  * Results:
00798  *      The number of bytes written is returned or -1 on error. An output
00799  *      argument contains a POSIX error code if an error occurred, or zero.
00800  *
00801  * Side effects:
00802  *      Writes output on the output device of the channel if the channel is
00803  *      not designated to be closed.
00804  *
00805  *----------------------------------------------------------------------
00806  */
00807 
00808 #if BAD_TIP35_FLUSH
00809 static int
00810 TtyOutputProc(
00811     ClientData instanceData,    /* File state. */
00812     const char *buf,            /* The data buffer. */
00813     int toWrite,                /* How many bytes to write? */
00814     int *errorCodePtr)          /* Where to store error code. */
00815 {
00816     if (TclInExit()) {
00817         /*
00818          * Do not write data during Tcl exit. Serial port may block preventing
00819          * Tcl from exit.
00820          */
00821 
00822         return toWrite;
00823     }
00824 
00825     return FileOutputProc(instanceData, buf, toWrite, errorCodePtr);
00826 }
00827 #endif /* BAD_TIP35_FLUSH */
00828 
00829 #ifdef USE_TERMIOS
00830 /*
00831  *----------------------------------------------------------------------
00832  *
00833  * TtyModemStatusStr --
00834  *
00835  *      Converts a RS232 modem status list of readable flags
00836  *
00837  *----------------------------------------------------------------------
00838  */
00839 
00840 static void
00841 TtyModemStatusStr(
00842     int status,                 /* RS232 modem status */
00843     Tcl_DString *dsPtr)         /* Where to store string */
00844 {
00845 #ifdef TIOCM_CTS
00846     Tcl_DStringAppendElement(dsPtr, "CTS");
00847     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0");
00848 #endif /* TIOCM_CTS */
00849 #ifdef TIOCM_DSR
00850     Tcl_DStringAppendElement(dsPtr, "DSR");
00851     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0");
00852 #endif /* TIOCM_DSR */
00853 #ifdef TIOCM_RNG
00854     Tcl_DStringAppendElement(dsPtr, "RING");
00855     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0");
00856 #endif /* TIOCM_RNG */
00857 #ifdef TIOCM_CD
00858     Tcl_DStringAppendElement(dsPtr, "DCD");
00859     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0");
00860 #endif /* TIOCM_CD */
00861 }
00862 #endif /* USE_TERMIOS */
00863 
00864 /*
00865  *----------------------------------------------------------------------
00866  *
00867  * TtySetOptionProc --
00868  *
00869  *      Sets an option on a channel.
00870  *
00871  * Results:
00872  *      A standard Tcl result. Also sets the interp's result on error if
00873  *      interp is not NULL.
00874  *
00875  * Side effects:
00876  *      May modify an option on a device. Sets Error message if needed (by
00877  *      calling Tcl_BadChannelOption).
00878  *
00879  *----------------------------------------------------------------------
00880  */
00881 
00882 static int
00883 TtySetOptionProc(
00884     ClientData instanceData,    /* File state. */
00885     Tcl_Interp *interp,         /* For error reporting - can be NULL. */
00886     const char *optionName,     /* Which option to set? */
00887     const char *value)          /* New value for option. */
00888 {
00889     FileState *fsPtr = (FileState *) instanceData;
00890     unsigned int len, vlen;
00891     TtyAttrs tty;
00892 #ifdef USE_TERMIOS
00893     int flag, control, argc;
00894     const char **argv;
00895     IOSTATE iostate;
00896 #endif /* USE_TERMIOS */
00897 
00898     len = strlen(optionName);
00899     vlen = strlen(value);
00900 
00901     /*
00902      * Option -mode baud,parity,databits,stopbits
00903      */
00904     if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
00905         if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
00906                 &tty.stop) != TCL_OK) {
00907             return TCL_ERROR;
00908         }
00909 
00910         /*
00911          * system calls results should be checked there. - dl
00912          */
00913 
00914         TtySetAttributes(fsPtr->fd, &tty);
00915         ((TtyState *) fsPtr)->stateUpdated = 1;
00916         return TCL_OK;
00917     }
00918 
00919 #ifdef USE_TERMIOS
00920 
00921     /*
00922      * Option -handshake none|xonxoff|rtscts|dtrdsr
00923      */
00924 
00925     if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
00926         /*
00927          * Reset all handshake options. DTR and RTS are ON by default.
00928          */
00929 
00930         GETIOSTATE(fsPtr->fd, &iostate);
00931         CLEAR_BITS(iostate.c_iflag, IXON | IXOFF | IXANY);
00932 #ifdef CRTSCTS
00933         CLEAR_BITS(iostate.c_cflag, CRTSCTS);
00934 #endif /* CRTSCTS */
00935         if (strncasecmp(value, "NONE", vlen) == 0) {
00936             /* leave all handshake options disabled */
00937         } else if (strncasecmp(value, "XONXOFF", vlen) == 0) {
00938             SET_BITS(iostate.c_iflag, IXON | IXOFF | IXANY);
00939         } else if (strncasecmp(value, "RTSCTS", vlen) == 0) {
00940 #ifdef CRTSCTS
00941             SET_BITS(iostate.c_cflag, CRTSCTS);
00942 #else /* !CRTSTS */
00943             UNSUPPORTED_OPTION("-handshake RTSCTS");
00944             return TCL_ERROR;
00945 #endif /* CRTSCTS */
00946         } else if (strncasecmp(value, "DTRDSR", vlen) == 0) {
00947             UNSUPPORTED_OPTION("-handshake DTRDSR");
00948             return TCL_ERROR;
00949         } else {
00950             if (interp) {
00951                 Tcl_AppendResult(interp, "bad value for -handshake: "
00952                         "must be one of xonxoff, rtscts, dtrdsr or none",
00953                         NULL);
00954             }
00955             return TCL_ERROR;
00956         }
00957         SETIOSTATE(fsPtr->fd, &iostate);
00958         return TCL_OK;
00959     }
00960 
00961     /*
00962      * Option -xchar {\x11 \x13}
00963      */
00964 
00965     if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
00966         GETIOSTATE(fsPtr->fd, &iostate);
00967         if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
00968             return TCL_ERROR;
00969         }
00970         if (argc == 2) {
00971             Tcl_DString ds;
00972             Tcl_DStringInit(&ds);
00973 
00974             Tcl_UtfToExternalDString(NULL, argv[0], -1, &ds);
00975             iostate.c_cc[VSTART] = *(const cc_t *) Tcl_DStringValue(&ds);
00976             Tcl_DStringSetLength(&ds, 0);
00977 
00978             Tcl_UtfToExternalDString(NULL, argv[1], -1, &ds);
00979             iostate.c_cc[VSTOP] = *(const cc_t *) Tcl_DStringValue(&ds);
00980             Tcl_DStringFree(&ds);
00981         } else {
00982             if (interp) {
00983                 Tcl_AppendResult(interp, "bad value for -xchar: "
00984                         "should be a list of two elements", NULL);
00985             }
00986             ckfree((char *) argv);
00987             return TCL_ERROR;
00988         }
00989         SETIOSTATE(fsPtr->fd, &iostate);
00990         ckfree((char *) argv);
00991         return TCL_OK;
00992     }
00993 
00994     /*
00995      * Option -timeout msec
00996      */
00997 
00998     if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
00999         int msec;
01000 
01001         GETIOSTATE(fsPtr->fd, &iostate);
01002         if (Tcl_GetInt(interp, value, &msec) != TCL_OK) {
01003             return TCL_ERROR;
01004         }
01005         iostate.c_cc[VMIN] = 0;
01006         iostate.c_cc[VTIME] = (msec==0) ? 0 : (msec<100) ? 1 : (msec+50)/100;
01007         SETIOSTATE(fsPtr->fd, &iostate);
01008         return TCL_OK;
01009     }
01010 
01011     /*
01012      * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
01013      */
01014 
01015     if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
01016         int i;
01017 
01018         if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
01019             return TCL_ERROR;
01020         }
01021         if ((argc % 2) == 1) {
01022             if (interp) {
01023                 Tcl_AppendResult(interp, "bad value for -ttycontrol: "
01024                         "should be a list of signal,value pairs", NULL);
01025             }
01026             ckfree((char *) argv);
01027             return TCL_ERROR;
01028         }
01029 
01030         GETCONTROL(fsPtr->fd, &control);
01031         for (i = 0; i < argc-1; i += 2) {
01032             if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
01033                 ckfree((char *) argv);
01034                 return TCL_ERROR;
01035             }
01036             if (strncasecmp(argv[i], "DTR", strlen(argv[i])) == 0) {
01037 #ifdef TIOCM_DTR
01038                 if (flag) {
01039                     SET_BITS(control, TIOCM_DTR);
01040                 } else {
01041                     CLEAR_BITS(control, TIOCM_DTR);
01042                 }
01043 #else /* !TIOCM_DTR */
01044                 UNSUPPORTED_OPTION("-ttycontrol DTR");
01045                 ckfree((char *) argv);
01046                 return TCL_ERROR;
01047 #endif /* TIOCM_DTR */
01048             } else if (strncasecmp(argv[i], "RTS", strlen(argv[i])) == 0) {
01049 #ifdef TIOCM_RTS
01050                 if (flag) {
01051                     SET_BITS(control, TIOCM_RTS);
01052                 } else {
01053                     CLEAR_BITS(control, TIOCM_RTS);
01054                 }
01055 #else /* !TIOCM_RTS*/
01056                 UNSUPPORTED_OPTION("-ttycontrol RTS");
01057                 ckfree((char *) argv);
01058                 return TCL_ERROR;
01059 #endif /* TIOCM_RTS*/
01060             } else if (strncasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
01061 #ifdef SETBREAK
01062                 SETBREAK(fsPtr->fd, flag);
01063 #else /* !SETBREAK */
01064                 UNSUPPORTED_OPTION("-ttycontrol BREAK");
01065                 ckfree((char *) argv);
01066                 return TCL_ERROR;
01067 #endif /* SETBREAK */
01068             } else {
01069                 if (interp) {
01070                     Tcl_AppendResult(interp, "bad signal \"", argv[i],
01071                             "\" for -ttycontrol: must be "
01072                             "DTR, RTS or BREAK", NULL);
01073                 }
01074                 ckfree((char *) argv);
01075                 return TCL_ERROR;
01076             }
01077         } /* -ttycontrol options loop */
01078 
01079         SETCONTROL(fsPtr->fd, &control);
01080         ckfree((char *) argv);
01081         return TCL_OK;
01082     }
01083 
01084     return Tcl_BadChannelOption(interp, optionName,
01085             "mode handshake timeout ttycontrol xchar");
01086 
01087 #else /* !USE_TERMIOS */
01088     return Tcl_BadChannelOption(interp, optionName, "mode");
01089 #endif /* USE_TERMIOS */
01090 }
01091 
01092 /*
01093  *----------------------------------------------------------------------
01094  *
01095  * TtyGetOptionProc --
01096  *
01097  *      Gets a mode associated with an IO channel. If the optionName arg is
01098  *      non-NULL, retrieves the value of that option. If the optionName arg is
01099  *      NULL, retrieves a list of alternating option names and values for the
01100  *      given channel.
01101  *
01102  * Results:
01103  *      A standard Tcl result. Also sets the supplied DString to the string
01104  *      value of the option(s) returned.
01105  *
01106  * Side effects:
01107  *      The string returned by this function is in static storage and may be
01108  *      reused at any time subsequent to the call. Sets error message if
01109  *      needed (by calling Tcl_BadChannelOption).
01110  *
01111  *----------------------------------------------------------------------
01112  */
01113 
01114 static int
01115 TtyGetOptionProc(
01116     ClientData instanceData,    /* File state. */
01117     Tcl_Interp *interp,         /* For error reporting - can be NULL. */
01118     const char *optionName,     /* Option to get. */
01119     Tcl_DString *dsPtr)         /* Where to store value(s). */
01120 {
01121     FileState *fsPtr = (FileState *) instanceData;
01122     unsigned int len;
01123     char buf[3*TCL_INTEGER_SPACE + 16];
01124     int valid = 0;              /* Flag if valid option parsed. */
01125 
01126     if (optionName == NULL) {
01127         len = 0;
01128     } else {
01129         len = strlen(optionName);
01130     }
01131     if (len == 0) {
01132         Tcl_DStringAppendElement(dsPtr, "-mode");
01133     }
01134     if (len==0 || (len>2 && strncmp(optionName, "-mode", len)==0)) {
01135         TtyAttrs tty;
01136 
01137         valid = 1;
01138         TtyGetAttributes(fsPtr->fd, &tty);
01139         sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
01140         Tcl_DStringAppendElement(dsPtr, buf);
01141     }
01142 
01143 #ifdef USE_TERMIOS
01144     /*
01145      * Get option -xchar
01146      */
01147 
01148     if (len == 0) {
01149         Tcl_DStringAppendElement(dsPtr, "-xchar");
01150         Tcl_DStringStartSublist(dsPtr);
01151     }
01152     if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) {
01153         IOSTATE iostate;
01154         Tcl_DString ds;
01155         valid = 1;
01156 
01157         GETIOSTATE(fsPtr->fd, &iostate);
01158         Tcl_DStringInit(&ds);
01159 
01160         Tcl_ExternalToUtfDString(NULL,  (const char *) &iostate.c_cc[VSTART], 1, &ds);
01161         Tcl_DStringAppendElement(dsPtr, (const char *) Tcl_DStringValue(&ds));
01162         Tcl_DStringSetLength(&ds, 0);
01163 
01164         Tcl_ExternalToUtfDString(NULL,  (const char *) &iostate.c_cc[VSTOP], 1, &ds);
01165         Tcl_DStringAppendElement(dsPtr, (const char *) Tcl_DStringValue(&ds));
01166         Tcl_DStringFree(&ds);
01167     }
01168     if (len == 0) {
01169         Tcl_DStringEndSublist(dsPtr);
01170     }
01171 
01172     /*
01173      * Get option -queue
01174      * Option is readonly and returned by [fconfigure chan -queue] but not
01175      * returned by unnamed [fconfigure chan].
01176      */
01177 
01178     if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
01179         int inQueue=0, outQueue=0, inBuffered, outBuffered;
01180 
01181         valid = 1;
01182 #ifdef GETREADQUEUE
01183         GETREADQUEUE(fsPtr->fd, inQueue);
01184 #endif /* GETREADQUEUE */
01185 #ifdef GETWRITEQUEUE
01186         GETWRITEQUEUE(fsPtr->fd, outQueue);
01187 #endif /* GETWRITEQUEUE */
01188         inBuffered = Tcl_InputBuffered(fsPtr->channel);
01189         outBuffered = Tcl_OutputBuffered(fsPtr->channel);
01190 
01191         sprintf(buf, "%d", inBuffered+inQueue);
01192         Tcl_DStringAppendElement(dsPtr, buf);
01193         sprintf(buf, "%d", outBuffered+outQueue);
01194         Tcl_DStringAppendElement(dsPtr, buf);
01195     }
01196 
01197     /*
01198      * Get option -ttystatus
01199      * Option is readonly and returned by [fconfigure chan -ttystatus] but not
01200      * returned by unnamed [fconfigure chan].
01201      */
01202     if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
01203         int status;
01204 
01205         valid = 1;
01206         GETCONTROL(fsPtr->fd, &status);
01207         TtyModemStatusStr(status, dsPtr);
01208     }
01209 #endif /* USE_TERMIOS */
01210 
01211     if (valid) {
01212         return TCL_OK;
01213     }
01214     return Tcl_BadChannelOption(interp, optionName, "mode"
01215 #ifdef USE_TERMIOS
01216             " queue ttystatus xchar"
01217 #endif /* USE_TERMIOS */
01218             );
01219 }
01220 
01221 #ifdef DIRECT_BAUD
01222 #   define TtyGetSpeed(baud)    ((unsigned) (baud))
01223 #   define TtyGetBaud(speed)    ((int) (speed))
01224 #else /* !DIRECT_BAUD */
01225 
01226 static struct {int baud; unsigned long speed;} speeds[] = {
01227 #ifdef B0
01228     {0, B0},
01229 #endif
01230 #ifdef B50
01231     {50, B50},
01232 #endif
01233 #ifdef B75
01234     {75, B75},
01235 #endif
01236 #ifdef B110
01237     {110, B110},
01238 #endif
01239 #ifdef B134
01240     {134, B134},
01241 #endif
01242 #ifdef B150
01243     {150, B150},
01244 #endif
01245 #ifdef B200
01246     {200, B200},
01247 #endif
01248 #ifdef B300
01249     {300, B300},
01250 #endif
01251 #ifdef B600
01252     {600, B600},
01253 #endif
01254 #ifdef B1200
01255     {1200, B1200},
01256 #endif
01257 #ifdef B1800
01258     {1800, B1800},
01259 #endif
01260 #ifdef B2400
01261     {2400, B2400},
01262 #endif
01263 #ifdef B4800
01264     {4800, B4800},
01265 #endif
01266 #ifdef B9600
01267     {9600, B9600},
01268 #endif
01269 #ifdef B14400
01270     {14400, B14400},
01271 #endif
01272 #ifdef B19200
01273     {19200, B19200},
01274 #endif
01275 #ifdef EXTA
01276     {19200, EXTA},
01277 #endif
01278 #ifdef B28800
01279     {28800, B28800},
01280 #endif
01281 #ifdef B38400
01282     {38400, B38400},
01283 #endif
01284 #ifdef EXTB
01285     {38400, EXTB},
01286 #endif
01287 #ifdef B57600
01288     {57600, B57600},
01289 #endif
01290 #ifdef _B57600
01291     {57600, _B57600},
01292 #endif
01293 #ifdef B76800
01294     {76800, B76800},
01295 #endif
01296 #ifdef B115200
01297     {115200, B115200},
01298 #endif
01299 #ifdef _B115200
01300     {115200, _B115200},
01301 #endif
01302 #ifdef B153600
01303     {153600, B153600},
01304 #endif
01305 #ifdef B230400
01306     {230400, B230400},
01307 #endif
01308 #ifdef B307200
01309     {307200, B307200},
01310 #endif
01311 #ifdef B460800
01312     {460800, B460800},
01313 #endif
01314     {-1, 0}
01315 };
01316 
01317 /*
01318  *---------------------------------------------------------------------------
01319  *
01320  * TtyGetSpeed --
01321  *
01322  *      Given a baud rate, get the mask value that should be stored in the
01323  *      termios, termio, or sgttyb structure in order to select that baud
01324  *      rate.
01325  *
01326  * Results:
01327  *      As above.
01328  *
01329  * Side effects:
01330  *      None.
01331  *
01332  *---------------------------------------------------------------------------
01333  */
01334 
01335 static unsigned long
01336 TtyGetSpeed(
01337     int baud)                   /* The baud rate to look up. */
01338 {
01339     int bestIdx, bestDiff, i, diff;
01340 
01341     bestIdx = 0;
01342     bestDiff = 1000000;
01343 
01344     /*
01345      * If the baud rate does not correspond to one of the known mask values,
01346      * choose the mask value whose baud rate is closest to the specified baud
01347      * rate.
01348      */
01349 
01350     for (i = 0; speeds[i].baud >= 0; i++) {
01351         diff = speeds[i].baud - baud;
01352         if (diff < 0) {
01353             diff = -diff;
01354         }
01355         if (diff < bestDiff) {
01356             bestIdx = i;
01357             bestDiff = diff;
01358         }
01359     }
01360     return speeds[bestIdx].speed;
01361 }
01362 
01363 /*
01364  *---------------------------------------------------------------------------
01365  *
01366  * TtyGetBaud --
01367  *
01368  *      Given a speed mask value from a termios, termio, or sgttyb structure,
01369  *      get the baus rate that corresponds to that mask value.
01370  *
01371  * Results:
01372  *      As above. If the mask value was not recognized, 0 is returned.
01373  *
01374  * Side effects:
01375  *      None.
01376  *
01377  *---------------------------------------------------------------------------
01378  */
01379 
01380 static int
01381 TtyGetBaud(
01382     unsigned long speed)        /* Speed mask value to look up. */
01383 {
01384     int i;
01385 
01386     for (i = 0; speeds[i].baud >= 0; i++) {
01387         if (speeds[i].speed == speed) {
01388             return speeds[i].baud;
01389         }
01390     }
01391     return 0;
01392 }
01393 #endif /* !DIRECT_BAUD */
01394 
01395 /*
01396  *---------------------------------------------------------------------------
01397  *
01398  * TtyGetAttributes --
01399  *
01400  *      Get the current attributes of the specified serial device.
01401  *
01402  * Results:
01403  *      None.
01404  *
01405  * Side effects:
01406  *      None.
01407  *
01408  *---------------------------------------------------------------------------
01409  */
01410 
01411 static void
01412 TtyGetAttributes(
01413     int fd,                     /* Open file descriptor for serial port to be
01414                                  * queried. */
01415     TtyAttrs *ttyPtr)           /* Buffer filled with serial port
01416                                  * attributes. */
01417 {
01418     IOSTATE iostate;
01419     int baud, parity, data, stop;
01420 
01421     GETIOSTATE(fd, &iostate);
01422 
01423 #ifdef USE_TERMIOS
01424     baud = TtyGetBaud(cfgetospeed(&iostate));
01425 
01426     parity = 'n';
01427 #ifdef PAREXT
01428     switch ((int) (iostate.c_cflag & (PARENB | PARODD | PAREXT))) {
01429     case PARENB                   : parity = 'e'; break;
01430     case PARENB | PARODD          : parity = 'o'; break;
01431     case PARENB |          PAREXT : parity = 's'; break;
01432     case PARENB | PARODD | PAREXT : parity = 'm'; break;
01433     }
01434 #else /* !PAREXT */
01435     switch ((int) (iostate.c_cflag & (PARENB | PARODD))) {
01436     case PARENB          : parity = 'e'; break;
01437     case PARENB | PARODD : parity = 'o'; break;
01438     }
01439 #endif /* !PAREXT */
01440 
01441     data = iostate.c_cflag & CSIZE;
01442     data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
01443 
01444     stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
01445 #endif /* USE_TERMIOS */
01446 
01447 #ifdef USE_TERMIO
01448     baud = TtyGetBaud(iostate.c_cflag & CBAUD);
01449 
01450     parity = 'n';
01451     switch (iostate.c_cflag & (PARENB | PARODD | PAREXT)) {
01452     case PARENB                   : parity = 'e'; break;
01453     case PARENB | PARODD          : parity = 'o'; break;
01454     case PARENB |          PAREXT : parity = 's'; break;
01455     case PARENB | PARODD | PAREXT : parity = 'm'; break;
01456     }
01457 
01458     data = iostate.c_cflag & CSIZE;
01459     data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
01460 
01461     stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
01462 #endif /* USE_TERMIO */
01463 
01464 #ifdef USE_SGTTY
01465     baud = TtyGetBaud(iostate.sg_ospeed);
01466 
01467     parity = 'n';
01468     if (iostate.sg_flags & EVENP) {
01469         parity = 'e';
01470     } else if (iostate.sg_flags & ODDP) {
01471         parity = 'o';
01472     }
01473 
01474     data = (iostate.sg_flags & (EVENP | ODDP)) ? 7 : 8;
01475 
01476     stop = 1;
01477 #endif /* USE_SGTTY */
01478 
01479     ttyPtr->baud    = baud;
01480     ttyPtr->parity  = parity;
01481     ttyPtr->data    = data;
01482     ttyPtr->stop    = stop;
01483 }
01484 
01485 /*
01486  *---------------------------------------------------------------------------
01487  *
01488  * TtySetAttributes --
01489  *
01490  *      Set the current attributes of the specified serial device.
01491  *
01492  * Results:
01493  *      None.
01494  *
01495  * Side effects:
01496  *      None.
01497  *
01498  *---------------------------------------------------------------------------
01499  */
01500 
01501 static void
01502 TtySetAttributes(
01503     int fd,                     /* Open file descriptor for serial port to be
01504                                  * modified. */
01505     TtyAttrs *ttyPtr)           /* Buffer containing new attributes for serial
01506                                  * port. */
01507 {
01508     IOSTATE iostate;
01509 
01510 #ifdef USE_TERMIOS
01511     int parity, data, flag;
01512 
01513     GETIOSTATE(fd, &iostate);
01514     cfsetospeed(&iostate, TtyGetSpeed(ttyPtr->baud));
01515     cfsetispeed(&iostate, TtyGetSpeed(ttyPtr->baud));
01516 
01517     flag = 0;
01518     parity = ttyPtr->parity;
01519     if (parity != 'n') {
01520         SET_BITS(flag, PARENB);
01521 #ifdef PAREXT
01522         CLEAR_BITS(iostate.c_cflag, PAREXT);
01523         if ((parity == 'm') || (parity == 's')) {
01524             SET_BITS(flag, PAREXT);
01525         }
01526 #endif /* PAREXT */
01527         if ((parity == 'm') || (parity == 'o')) {
01528             SET_BITS(flag, PARODD);
01529         }
01530     }
01531     data = ttyPtr->data;
01532     SET_BITS(flag,
01533             (data == 5) ? CS5 :
01534             (data == 6) ? CS6 :
01535             (data == 7) ? CS7 : CS8);
01536     if (ttyPtr->stop == 2) {
01537         SET_BITS(flag, CSTOPB);
01538     }
01539 
01540     CLEAR_BITS(iostate.c_cflag, PARENB | PARODD | CSIZE | CSTOPB);
01541     SET_BITS(iostate.c_cflag, flag);
01542 
01543 #endif  /* USE_TERMIOS */
01544 
01545 #ifdef USE_TERMIO
01546     int parity, data, flag;
01547 
01548     GETIOSTATE(fd, &iostate);
01549     CLEAR_BITS(iostate.c_cflag, CBAUD);
01550     SET_BITS(iostate.c_cflag, TtyGetSpeed(ttyPtr->baud));
01551 
01552     flag = 0;
01553     parity = ttyPtr->parity;
01554     if (parity != 'n') {
01555         SET_BITS(flag, PARENB);
01556         if ((parity == 'm') || (parity == 's')) {
01557             SET_BITS(flag, PAREXT);
01558         }
01559         if ((parity == 'm') || (parity == 'o')) {
01560             SET_BITS(flag, PARODD);
01561         }
01562     }
01563     data = ttyPtr->data;
01564     SET_BITS(flag,
01565             (data == 5) ? CS5 :
01566             (data == 6) ? CS6 :
01567             (data == 7) ? CS7 : CS8);
01568     if (ttyPtr->stop == 2) {
01569         SET_BITS(flag, CSTOPB);
01570     }
01571 
01572     CLEAR_BITS(iostate.c_cflag, PARENB | PARODD | PAREXT | CSIZE | CSTOPB);
01573     SET_BITS(iostate.c_cflag, flag);
01574 
01575 #endif  /* USE_TERMIO */
01576 
01577 #ifdef USE_SGTTY
01578     int parity;
01579 
01580     GETIOSTATE(fd, &iostate);
01581     iostate.sg_ospeed = TtyGetSpeed(ttyPtr->baud);
01582     iostate.sg_ispeed = TtyGetSpeed(ttyPtr->baud);
01583 
01584     parity = ttyPtr->parity;
01585     if (parity == 'e') {
01586         CLEAR_BITS(iostate.sg_flags, ODDP);
01587         SET_BITS(iostate.sg_flags, EVENP);
01588     } else if (parity == 'o') {
01589         CLEAR_BITS(iostate.sg_flags, EVENP);
01590         SET_BITS(iostate.sg_flags, ODDP);
01591     }
01592 #endif  /* USE_SGTTY */
01593 
01594     SETIOSTATE(fd, &iostate);
01595 }
01596 
01597 /*
01598  *---------------------------------------------------------------------------
01599  *
01600  * TtyParseMode --
01601  *
01602  *      Parse the "-mode" argument to the fconfigure command. The argument is
01603  *      of the form baud,parity,data,stop.
01604  *
01605  * Results:
01606  *      The return value is TCL_OK if the argument was successfully parsed,
01607  *      TCL_ERROR otherwise. If TCL_ERROR is returned, an error message is
01608  *      left in the interp's result (if interp is non-NULL).
01609  *
01610  * Side effects:
01611  *      None.
01612  *
01613  *---------------------------------------------------------------------------
01614  */
01615 
01616 static int
01617 TtyParseMode(
01618     Tcl_Interp *interp,         /* If non-NULL, interp for error return. */
01619     const char *mode,           /* Mode string to be parsed. */
01620     int *speedPtr,              /* Filled with baud rate from mode string. */
01621     int *parityPtr,             /* Filled with parity from mode string. */
01622     int *dataPtr,               /* Filled with data bits from mode string. */
01623     int *stopPtr)               /* Filled with stop bits from mode string. */
01624 {
01625     int i, end;
01626     char parity;
01627     static const char *bad = "bad value for -mode";
01628 
01629     i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr,
01630             stopPtr, &end);
01631     if ((i != 4) || (mode[end] != '\0')) {
01632         if (interp != NULL) {
01633             Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
01634                     NULL);
01635         }
01636         return TCL_ERROR;
01637     }
01638 
01639     /*
01640      * Only allow setting mark/space parity on platforms that support it Make
01641      * sure to allow for the case where strchr is a macro. [Bug: 5089]
01642      */
01643 
01644 #if defined(PAREXT) || defined(USE_TERMIO)
01645     if (strchr("noems", parity) == NULL) {
01646 #else
01647     if (strchr("noe", parity) == NULL) {
01648 #endif /* PAREXT|USE_TERMIO */
01649         if (interp != NULL) {
01650             Tcl_AppendResult(interp, bad, " parity: should be ",
01651 #if defined(PAREXT) || defined(USE_TERMIO)
01652                     "n, o, e, m, or s",
01653 #else
01654                     "n, o, or e",
01655 #endif /* PAREXT|USE_TERMIO */
01656                     NULL);
01657         }
01658         return TCL_ERROR;
01659     }
01660     *parityPtr = parity;
01661     if ((*dataPtr < 5) || (*dataPtr > 8)) {
01662         if (interp != NULL) {
01663             Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
01664                     NULL);
01665         }
01666         return TCL_ERROR;
01667     }
01668     if ((*stopPtr < 0) || (*stopPtr > 2)) {
01669         if (interp != NULL) {
01670             Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL);
01671         }
01672         return TCL_ERROR;
01673     }
01674     return TCL_OK;
01675 }
01676 
01677 /*
01678  *---------------------------------------------------------------------------
01679  *
01680  * TtyInit --
01681  *
01682  *      Given file descriptor that refers to a serial port, initialize the
01683  *      serial port to a set of sane values so that Tcl can talk to a device
01684  *      located on the serial port. Note that no initialization happens if the
01685  *      initialize flag is not set; this is necessary for the correct handling
01686  *      of UNIX console TTYs at startup.
01687  *
01688  * Results:
01689  *      A pointer to a FileState suitable for use with Tcl_CreateChannel and
01690  *      the ttyChannelType structure.
01691  *
01692  * Side effects:
01693  *      Serial device initialized to non-blocking raw mode, similar to sockets
01694  *      (if initialize flag is non-zero.) All other modes can be simulated on
01695  *      top of this in Tcl.
01696  *
01697  *---------------------------------------------------------------------------
01698  */
01699 
01700 static FileState *
01701 TtyInit(
01702     int fd,                     /* Open file descriptor for serial port to be
01703                                  * initialized. */
01704     int initialize)
01705 {
01706     TtyState *ttyPtr;
01707 
01708     ttyPtr = (TtyState *) ckalloc((unsigned) sizeof(TtyState));
01709     GETIOSTATE(fd, &ttyPtr->savedState);
01710     ttyPtr->stateUpdated = 0;
01711     if (initialize) {
01712         IOSTATE iostate = ttyPtr->savedState;
01713 
01714 #if defined(USE_TERMIOS) || defined(USE_TERMIO)
01715         if (iostate.c_iflag != IGNBRK ||
01716                 iostate.c_oflag != 0 ||
01717                 iostate.c_lflag != 0 ||
01718                 iostate.c_cflag & CREAD ||
01719                 iostate.c_cc[VMIN] != 1 ||
01720                 iostate.c_cc[VTIME] != 0) {
01721             ttyPtr->stateUpdated = 1;
01722         }
01723         iostate.c_iflag = IGNBRK;
01724         iostate.c_oflag = 0;
01725         iostate.c_lflag = 0;
01726         SET_BITS(iostate.c_cflag, CREAD);
01727         iostate.c_cc[VMIN] = 1;
01728         iostate.c_cc[VTIME] = 0;
01729 #endif  /* USE_TERMIOS|USE_TERMIO */
01730 
01731 #ifdef USE_SGTTY
01732         if ((iostate.sg_flags & (EVENP | ODDP)) ||
01733                 !(iostate.sg_flags & RAW)) {
01734             ttyPtr->stateUpdated = 1;
01735         }
01736         iostate.sg_flags &= EVENP | ODDP;
01737         SET_BITS(iostate.sg_flags, RAW);
01738 #endif  /* USE_SGTTY */
01739 
01740         /*
01741          * Only update if we're changing anything to avoid possible blocking.
01742          */
01743 
01744         if (ttyPtr->stateUpdated) {
01745             SETIOSTATE(fd, &iostate);
01746         }
01747     }
01748 
01749     return &ttyPtr->fs;
01750 }
01751 #endif  /* SUPPORTS_TTY */
01752 
01753 /*
01754  *----------------------------------------------------------------------
01755  *
01756  * TclpOpenFileChannel --
01757  *
01758  *      Open an file based channel on Unix systems.
01759  *
01760  * Results:
01761  *      The new channel or NULL. If NULL, the output argument errorCodePtr is
01762  *      set to a POSIX error and an error message is left in the interp's
01763  *      result if interp is not NULL.
01764  *
01765  * Side effects:
01766  *      May open the channel and may cause creation of a file on the file
01767  *      system.
01768  *
01769  *----------------------------------------------------------------------
01770  */
01771 
01772 Tcl_Channel
01773 TclpOpenFileChannel(
01774     Tcl_Interp *interp,         /* Interpreter for error reporting; can be
01775                                  * NULL. */
01776     Tcl_Obj *pathPtr,           /* Name of file to open. */
01777     int mode,                   /* POSIX open mode. */
01778     int permissions)            /* If the open involves creating a file, with
01779                                  * what modes to create it? */
01780 {
01781     int fd, channelPermissions;
01782     FileState *fsPtr;
01783     const char *native, *translation;
01784     char channelName[16 + TCL_INTEGER_SPACE];
01785     Tcl_ChannelType *channelTypePtr;
01786 
01787     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
01788     case O_RDONLY:
01789         channelPermissions = TCL_READABLE;
01790         break;
01791     case O_WRONLY:
01792         channelPermissions = TCL_WRITABLE;
01793         break;
01794     case O_RDWR:
01795         channelPermissions = (TCL_READABLE | TCL_WRITABLE);
01796         break;
01797     default:
01798         /*
01799          * This may occurr if modeString was "", for example.
01800          */
01801 
01802         Tcl_Panic("TclpOpenFileChannel: invalid mode value");
01803         return NULL;
01804     }
01805 
01806     native = Tcl_FSGetNativePath(pathPtr);
01807     if (native == NULL) {
01808         return NULL;
01809     }
01810 
01811 #ifdef DJGPP
01812     SET_BITS(mode, O_BINARY);
01813 #endif
01814 
01815     fd = TclOSopen(native, mode, permissions);
01816 
01817     if (fd < 0) {
01818         if (interp != NULL) {
01819             Tcl_AppendResult(interp, "couldn't open \"", TclGetString(pathPtr),
01820                     "\": ", Tcl_PosixError(interp), NULL);
01821         }
01822         return NULL;
01823     }
01824 
01825     /*
01826      * Set close-on-exec flag on the fd so that child processes will not
01827      * inherit this fd.
01828      */
01829 
01830     fcntl(fd, F_SETFD, FD_CLOEXEC);
01831 
01832     sprintf(channelName, "file%d", fd);
01833 
01834 #ifdef SUPPORTS_TTY
01835     if (strcmp(native, "/dev/tty") != 0 && isatty(fd)) {
01836         /*
01837          * Initialize the serial port to a set of sane parameters. Especially
01838          * important if the remote device is set to echo and the serial port
01839          * driver was also set to echo -- as soon as a char were sent to the
01840          * serial port, the remote device would echo it, then the serial
01841          * driver would echo it back to the device, etc.
01842          *
01843          * Note that we do not do this if we're dealing with /dev/tty itself,
01844          * as that tends to cause Bad Things To Happen when you're working
01845          * interactively. Strictly a better check would be to see if the FD
01846          * being set up is a device and has the same major/minor as the
01847          * initial std FDs (beware reopening!) but that's nearly as messy.
01848          */
01849 
01850         translation = "auto crlf";
01851         channelTypePtr = &ttyChannelType;
01852         fsPtr = TtyInit(fd, 1);
01853     } else
01854 #endif  /* SUPPORTS_TTY */
01855     {
01856         translation = NULL;
01857         channelTypePtr = &fileChannelType;
01858         fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
01859     }
01860 
01861     fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
01862     fsPtr->fd = fd;
01863 
01864     fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
01865             (ClientData) fsPtr, channelPermissions);
01866 
01867     if (translation != NULL) {
01868         /*
01869          * Gotcha. Most modems need a "\r" at the end of the command sequence.
01870          * If you just send "at\n", the modem will not respond with "OK"
01871          * because it never got a "\r" to actually invoke the command. So, by
01872          * default, newlines are translated to "\r\n" on output to avoid "bug"
01873          * reports that the serial port isn't working.
01874          */
01875 
01876         if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
01877                 translation) != TCL_OK) {
01878             Tcl_Close(NULL, fsPtr->channel);
01879             return NULL;
01880         }
01881     }
01882 
01883     return fsPtr->channel;
01884 }
01885 
01886 /*
01887  *----------------------------------------------------------------------
01888  *
01889  * Tcl_MakeFileChannel --
01890  *
01891  *      Makes a Tcl_Channel from an existing OS level file handle.
01892  *
01893  * Results:
01894  *      The Tcl_Channel created around the preexisting OS level file handle.
01895  *
01896  * Side effects:
01897  *      None.
01898  *
01899  *----------------------------------------------------------------------
01900  */
01901 
01902 Tcl_Channel
01903 Tcl_MakeFileChannel(
01904     ClientData handle,          /* OS level handle. */
01905     int mode)                   /* ORed combination of TCL_READABLE and
01906                                  * TCL_WRITABLE to indicate file mode. */
01907 {
01908     FileState *fsPtr;
01909     char channelName[16 + TCL_INTEGER_SPACE];
01910     int fd = PTR2INT(handle);
01911     Tcl_ChannelType *channelTypePtr;
01912     struct sockaddr sockaddr;
01913     socklen_t sockaddrLen = sizeof(sockaddr);
01914 
01915     if (mode == 0) {
01916         return NULL;
01917     }
01918 
01919     sockaddr.sa_family = AF_UNSPEC;
01920 
01921 #ifdef SUPPORTS_TTY
01922     if (isatty(fd)) {
01923         fsPtr = TtyInit(fd, 0);
01924         channelTypePtr = &ttyChannelType;
01925         sprintf(channelName, "serial%d", fd);
01926     } else
01927 #endif /* SUPPORTS_TTY */
01928     if (getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0
01929             && sockaddrLen > 0
01930             && sockaddr.sa_family == AF_INET) {
01931         return MakeTcpClientChannelMode((ClientData) INT2PTR(fd), mode);
01932     } else {
01933         channelTypePtr = &fileChannelType;
01934         fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
01935         sprintf(channelName, "file%d", fd);
01936     }
01937 
01938     fsPtr->fd = fd;
01939     fsPtr->validMask = mode | TCL_EXCEPTION;
01940     fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
01941             (ClientData) fsPtr, mode);
01942 
01943     return fsPtr->channel;
01944 }
01945 
01946 /*
01947  *----------------------------------------------------------------------
01948  *
01949  * TcpBlockModeProc --
01950  *
01951  *      This function is invoked by the generic IO level to set blocking and
01952  *      nonblocking mode on a TCP socket based channel.
01953  *
01954  * Results:
01955  *      0 if successful, errno when failed.
01956  *
01957  * Side effects:
01958  *      Sets the device into blocking or nonblocking mode.
01959  *
01960  *----------------------------------------------------------------------
01961  */
01962 
01963         /* ARGSUSED */
01964 static int
01965 TcpBlockModeProc(
01966     ClientData instanceData,    /* Socket state. */
01967     int mode)                   /* The mode to set. Can be one of
01968                                  * TCL_MODE_BLOCKING or
01969                                  * TCL_MODE_NONBLOCKING. */
01970 {
01971     TcpState *statePtr = (TcpState *) instanceData;
01972     int setting;
01973 
01974 #ifndef USE_FIONBIO
01975     setting = fcntl(statePtr->fd, F_GETFL);
01976     if (mode == TCL_MODE_BLOCKING) {
01977         CLEAR_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
01978         CLEAR_BITS(setting, O_NONBLOCK);
01979     } else {
01980         SET_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
01981         SET_BITS(setting, O_NONBLOCK);
01982     }
01983     if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
01984         return errno;
01985     }
01986 #else /* USE_FIONBIO */
01987     if (mode == TCL_MODE_BLOCKING) {
01988         CLEAR_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
01989         setting = 0;
01990         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
01991             return errno;
01992         }
01993     } else {
01994         SET_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
01995         setting = 1;
01996         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
01997             return errno;
01998         }
01999     }
02000 #endif /* !USE_FIONBIO */
02001 
02002     return 0;
02003 }
02004 
02005 /*
02006  *----------------------------------------------------------------------
02007  *
02008  * WaitForConnect --
02009  *
02010  *      Waits for a connection on an asynchronously opened socket to be
02011  *      completed.
02012  *
02013  * Results:
02014  *      None.
02015  *
02016  * Side effects:
02017  *      The socket is connected after this function returns.
02018  *
02019  *----------------------------------------------------------------------
02020  */
02021 
02022 static int
02023 WaitForConnect(
02024     TcpState *statePtr,         /* State of the socket. */
02025     int *errorCodePtr)          /* Where to store errors? */
02026 {
02027     int timeOut;                /* How long to wait. */
02028     int state;                  /* Of calling TclWaitForFile. */
02029     int flags;                  /* fcntl flags for the socket. */
02030 
02031     /*
02032      * If an asynchronous connect is in progress, attempt to wait for it to
02033      * complete before reading.
02034      */
02035 
02036     if (statePtr->flags & TCP_ASYNC_CONNECT) {
02037         if (statePtr->flags & TCP_ASYNC_SOCKET) {
02038             timeOut = 0;
02039         } else {
02040             timeOut = -1;
02041         }
02042         errno = 0;
02043         state = TclUnixWaitForFile(statePtr->fd,
02044                 TCL_WRITABLE | TCL_EXCEPTION, timeOut);
02045         if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
02046 #ifndef USE_FIONBIO
02047             flags = fcntl(statePtr->fd, F_GETFL);
02048             CLEAR_BITS(flags, O_NONBLOCK);
02049             (void) fcntl(statePtr->fd, F_SETFL, flags);
02050 #else /* USE_FIONBIO */
02051             flags = 0;
02052             (void) ioctl(statePtr->fd, FIONBIO, &flags);
02053 #endif /* !USE_FIONBIO */
02054         }
02055         if (state & TCL_EXCEPTION) {
02056             return -1;
02057         }
02058         if (state & TCL_WRITABLE) {
02059             CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
02060         } else if (timeOut == 0) {
02061             *errorCodePtr = errno = EWOULDBLOCK;
02062             return -1;
02063         }
02064     }
02065     return 0;
02066 }
02067 
02068 /*
02069  *----------------------------------------------------------------------
02070  *
02071  * TcpInputProc --
02072  *
02073  *      This function is invoked by the generic IO level to read input from a
02074  *      TCP socket based channel.
02075  *
02076  *      NOTE: We cannot share code with FilePipeInputProc because here we must
02077  *      use recv to obtain the input from the channel, not read.
02078  *
02079  * Results:
02080  *      The number of bytes read is returned or -1 on error. An output
02081  *      argument contains the POSIX error code on error, or zero if no error
02082  *      occurred.
02083  *
02084  * Side effects:
02085  *      Reads input from the input device of the channel.
02086  *
02087  *----------------------------------------------------------------------
02088  */
02089 
02090         /* ARGSUSED */
02091 static int
02092 TcpInputProc(
02093     ClientData instanceData,    /* Socket state. */
02094     char *buf,                  /* Where to store data read. */
02095     int bufSize,                /* How much space is available in the
02096                                  * buffer? */
02097     int *errorCodePtr)          /* Where to store error code. */
02098 {
02099     TcpState *statePtr = (TcpState *) instanceData;
02100     int bytesRead, state;
02101 
02102     *errorCodePtr = 0;
02103     state = WaitForConnect(statePtr, errorCodePtr);
02104     if (state != 0) {
02105         return -1;
02106     }
02107     bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0);
02108     if (bytesRead > -1) {
02109         return bytesRead;
02110     }
02111     if (errno == ECONNRESET) {
02112         /*
02113          * Turn ECONNRESET into a soft EOF condition.
02114          */
02115 
02116         return 0;
02117     }
02118     *errorCodePtr = errno;
02119     return -1;
02120 }
02121 
02122 /*
02123  *----------------------------------------------------------------------
02124  *
02125  * TcpOutputProc --
02126  *
02127  *      This function is invoked by the generic IO level to write output to a
02128  *      TCP socket based channel.
02129  *
02130  *      NOTE: We cannot share code with FilePipeOutputProc because here we
02131  *      must use send, not write, to get reliable error reporting.
02132  *
02133  * Results:
02134  *      The number of bytes written is returned. An output argument is set to
02135  *      a POSIX error code if an error occurred, or zero.
02136  *
02137  * Side effects:
02138  *      Writes output on the output device of the channel.
02139  *
02140  *----------------------------------------------------------------------
02141  */
02142 
02143 static int
02144 TcpOutputProc(
02145     ClientData instanceData,    /* Socket state. */
02146     const char *buf,            /* The data buffer. */
02147     int toWrite,                /* How many bytes to write? */
02148     int *errorCodePtr)          /* Where to store error code. */
02149 {
02150     TcpState *statePtr = (TcpState *) instanceData;
02151     int written;
02152     int state;                          /* Of waiting for connection. */
02153 
02154     *errorCodePtr = 0;
02155     state = WaitForConnect(statePtr, errorCodePtr);
02156     if (state != 0) {
02157         return -1;
02158     }
02159     written = send(statePtr->fd, buf, (size_t) toWrite, 0);
02160     if (written > -1) {
02161         return written;
02162     }
02163     *errorCodePtr = errno;
02164     return -1;
02165 }
02166 
02167 /*
02168  *----------------------------------------------------------------------
02169  *
02170  * TcpCloseProc --
02171  *
02172  *      This function is invoked by the generic IO level to perform
02173  *      channel-type-specific cleanup when a TCP socket based channel is
02174  *      closed.
02175  *
02176  * Results:
02177  *      0 if successful, the value of errno if failed.
02178  *
02179  * Side effects:
02180  *      Closes the socket of the channel.
02181  *
02182  *----------------------------------------------------------------------
02183  */
02184 
02185         /* ARGSUSED */
02186 static int
02187 TcpCloseProc(
02188     ClientData instanceData,    /* The socket to close. */
02189     Tcl_Interp *interp)         /* For error reporting - unused. */
02190 {
02191     TcpState *statePtr = (TcpState *) instanceData;
02192     int errorCode = 0;
02193 
02194     /*
02195      * Delete a file handler that may be active for this socket if this is a
02196      * server socket - the file handler was created automatically by Tcl as
02197      * part of the mechanism to accept new client connections. Channel
02198      * handlers are already deleted in the generic IO channel closing code
02199      * that called this function, so we do not have to delete them here.
02200      */
02201 
02202     Tcl_DeleteFileHandler(statePtr->fd);
02203 
02204     if (close(statePtr->fd) < 0) {
02205         errorCode = errno;
02206     }
02207     ckfree((char *) statePtr);
02208 
02209     return errorCode;
02210 }
02211 
02212 /*
02213  *----------------------------------------------------------------------
02214  *
02215  * TcpGetOptionProc --
02216  *
02217  *      Computes an option value for a TCP socket based channel, or a list of
02218  *      all options and their values.
02219  *
02220  *      Note: This code is based on code contributed by John Haxby.
02221  *
02222  * Results:
02223  *      A standard Tcl result. The value of the specified option or a list of
02224  *      all options and their values is returned in the supplied DString. Sets
02225  *      Error message if needed.
02226  *
02227  * Side effects:
02228  *      None.
02229  *
02230  *----------------------------------------------------------------------
02231  */
02232 
02233 static int
02234 TcpGetOptionProc(
02235     ClientData instanceData,    /* Socket state. */
02236     Tcl_Interp *interp,         /* For error reporting - can be NULL. */
02237     const char *optionName,     /* Name of the option to retrieve the value
02238                                  * for, or NULL to get all options and their
02239                                  * values. */
02240     Tcl_DString *dsPtr)         /* Where to store the computed value;
02241                                  * initialized by caller. */
02242 {
02243     TcpState *statePtr = (TcpState *) instanceData;
02244     struct sockaddr_in sockname;
02245     struct sockaddr_in peername;
02246     struct hostent *hostEntPtr;
02247     socklen_t size = sizeof(struct sockaddr_in);
02248     size_t len = 0;
02249     char buf[TCL_INTEGER_SPACE];
02250 
02251     if (optionName != NULL) {
02252         len = strlen(optionName);
02253     }
02254 
02255     if ((len > 1) && (optionName[1] == 'e') &&
02256             (strncmp(optionName, "-error", len) == 0)) {
02257         socklen_t optlen = sizeof(int);
02258         int err, ret;
02259 
02260         ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR,
02261                 (char *)&err, &optlen);
02262         if (ret < 0) {
02263             err = errno;
02264         }
02265         if (err != 0) {
02266             Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1);
02267         }
02268         return TCL_OK;
02269     }
02270 
02271     if ((len == 0) ||
02272             ((len > 1) && (optionName[1] == 'p') &&
02273                     (strncmp(optionName, "-peername", len) == 0))) {
02274         if (getpeername(statePtr->fd, (struct sockaddr *) &peername,
02275                 &size) >= 0) {
02276             if (len == 0) {
02277                 Tcl_DStringAppendElement(dsPtr, "-peername");
02278                 Tcl_DStringStartSublist(dsPtr);
02279             }
02280             Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
02281             hostEntPtr = TclpGetHostByAddr(                     /* INTL: Native. */
02282                     (char *) &peername.sin_addr,
02283                     sizeof(peername.sin_addr), AF_INET);
02284             if (hostEntPtr != NULL) {
02285                 Tcl_DString ds;
02286 
02287                 Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
02288                 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
02289                 Tcl_DStringFree(&ds);
02290             } else {
02291                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
02292             }
02293             TclFormatInt(buf, ntohs(peername.sin_port));
02294             Tcl_DStringAppendElement(dsPtr, buf);
02295             if (len == 0) {
02296                 Tcl_DStringEndSublist(dsPtr);
02297             } else {
02298                 return TCL_OK;
02299             }
02300         } else {
02301             /*
02302              * getpeername failed - but if we were asked for all the options
02303              * (len==0), don't flag an error at that point because it could be
02304              * an fconfigure request on a server socket (which have no peer).
02305              * Same must be done on win&mac.
02306              */
02307 
02308             if (len) {
02309                 if (interp) {
02310                     Tcl_AppendResult(interp, "can't get peername: ",
02311                             Tcl_PosixError(interp), NULL);
02312                 }
02313                 return TCL_ERROR;
02314             }
02315         }
02316     }
02317 
02318     if ((len == 0) ||
02319             ((len > 1) && (optionName[1] == 's') &&
02320             (strncmp(optionName, "-sockname", len) == 0))) {
02321         if (getsockname(statePtr->fd, (struct sockaddr *) &sockname,
02322                 &size) >= 0) {
02323             if (len == 0) {
02324                 Tcl_DStringAppendElement(dsPtr, "-sockname");
02325                 Tcl_DStringStartSublist(dsPtr);
02326             }
02327             Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
02328             hostEntPtr = TclpGetHostByAddr(                     /* INTL: Native. */
02329                     (char *) &sockname.sin_addr,
02330                     sizeof(sockname.sin_addr), AF_INET);
02331             if (hostEntPtr != NULL) {
02332                 Tcl_DString ds;
02333 
02334                 Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
02335                 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
02336                 Tcl_DStringFree(&ds);
02337             } else {
02338                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
02339             }
02340             TclFormatInt(buf, ntohs(sockname.sin_port));
02341             Tcl_DStringAppendElement(dsPtr, buf);
02342             if (len == 0) {
02343                 Tcl_DStringEndSublist(dsPtr);
02344             } else {
02345                 return TCL_OK;
02346             }
02347         } else {
02348             if (interp) {
02349                 Tcl_AppendResult(interp, "can't get sockname: ",
02350                         Tcl_PosixError(interp), NULL);
02351             }
02352             return TCL_ERROR;
02353         }
02354     }
02355 
02356     if (len > 0) {
02357         return Tcl_BadChannelOption(interp, optionName, "peername sockname");
02358     }
02359 
02360     return TCL_OK;
02361 }
02362 
02363 /*
02364  *----------------------------------------------------------------------
02365  *
02366  * TcpWatchProc --
02367  *
02368  *      Initialize the notifier to watch the fd from this channel.
02369  *
02370  * Results:
02371  *      None.
02372  *
02373  * Side effects:
02374  *      Sets up the notifier so that a future event on the channel will be
02375  *      seen by Tcl.
02376  *
02377  *----------------------------------------------------------------------
02378  */
02379 
02380 static void
02381 TcpWatchProc(
02382     ClientData instanceData,    /* The socket state. */
02383     int mask)                   /* Events of interest; an OR-ed combination of
02384                                  * TCL_READABLE, TCL_WRITABLE and
02385                                  * TCL_EXCEPTION. */
02386 {
02387     TcpState *statePtr = (TcpState *) instanceData;
02388 
02389     /*
02390      * Make sure we don't mess with server sockets since they will never be
02391      * readable or writable at the Tcl level. This keeps Tcl scripts from
02392      * interfering with the -accept behavior.
02393      */
02394 
02395     if (!statePtr->acceptProc) {
02396         if (mask) {
02397             Tcl_CreateFileHandler(statePtr->fd, mask,
02398                     (Tcl_FileProc *) Tcl_NotifyChannel,
02399                     (ClientData) statePtr->channel);
02400         } else {
02401             Tcl_DeleteFileHandler(statePtr->fd);
02402         }
02403     }
02404 }
02405 
02406 /*
02407  *----------------------------------------------------------------------
02408  *
02409  * TcpGetHandleProc --
02410  *
02411  *      Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
02412  *      TCP socket based channel.
02413  *
02414  * Results:
02415  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
02416  *      handle for the specified direction.
02417  *
02418  * Side effects:
02419  *      None.
02420  *
02421  *----------------------------------------------------------------------
02422  */
02423 
02424         /* ARGSUSED */
02425 static int
02426 TcpGetHandleProc(
02427     ClientData instanceData,    /* The socket state. */
02428     int direction,              /* Not used. */
02429     ClientData *handlePtr)      /* Where to store the handle. */
02430 {
02431     TcpState *statePtr = (TcpState *) instanceData;
02432 
02433     *handlePtr = (ClientData) INT2PTR(statePtr->fd);
02434     return TCL_OK;
02435 }
02436 
02437 /*
02438  *----------------------------------------------------------------------
02439  *
02440  * CreateSocket --
02441  *
02442  *      This function opens a new socket in client or server mode and
02443  *      initializes the TcpState structure.
02444  *
02445  * Results:
02446  *      Returns a new TcpState, or NULL with an error in the interp's result,
02447  *      if interp is not NULL.
02448  *
02449  * Side effects:
02450  *      Opens a socket.
02451  *
02452  *----------------------------------------------------------------------
02453  */
02454 
02455 static TcpState *
02456 CreateSocket(
02457     Tcl_Interp *interp,         /* For error reporting; can be NULL. */
02458     int port,                   /* Port number to open. */
02459     const char *host,           /* Name of host on which to open port. NULL
02460                                  * implies INADDR_ANY */
02461     int server,                 /* 1 if socket should be a server socket, else
02462                                  * 0 for a client socket. */
02463     const char *myaddr,         /* Optional client-side address */
02464     int myport,                 /* Optional client-side port */
02465     int async)                  /* If nonzero and creating a client socket,
02466                                  * attempt to do an async connect. Otherwise
02467                                  * do a synchronous connect or bind. */
02468 {
02469     int status, sock, asyncConnect, curState, origState;
02470     struct sockaddr_in sockaddr;        /* socket address */
02471     struct sockaddr_in mysockaddr;      /* Socket address for client */
02472     TcpState *statePtr;
02473     const char *errorMsg = NULL;
02474 
02475     sock = -1;
02476     origState = 0;
02477     if (!CreateSocketAddress(&sockaddr, host, port, 0, &errorMsg)) {
02478         goto addressError;
02479     }
02480     if ((myaddr != NULL || myport != 0) &&
02481             !CreateSocketAddress(&mysockaddr, myaddr, myport, 1, &errorMsg)) {
02482         goto addressError;
02483     }
02484 
02485     sock = socket(AF_INET, SOCK_STREAM, 0);
02486     if (sock < 0) {
02487         goto addressError;
02488     }
02489 
02490     /*
02491      * Set the close-on-exec flag so that the socket will not get inherited by
02492      * child processes.
02493      */
02494 
02495     fcntl(sock, F_SETFD, FD_CLOEXEC);
02496 
02497     /*
02498      * Set kernel space buffering
02499      */
02500 
02501     TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
02502 
02503     asyncConnect = 0;
02504     status = 0;
02505     if (server) {
02506         /*
02507          * Set up to reuse server addresses automatically and bind to the
02508          * specified port.
02509          */
02510 
02511         status = 1;
02512         (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
02513                 sizeof(status));
02514         status = bind(sock, (struct sockaddr *) &sockaddr,
02515                 sizeof(struct sockaddr));
02516         if (status != -1) {
02517             status = listen(sock, SOMAXCONN);
02518         }
02519     } else {
02520         if (myaddr != NULL || myport != 0) {
02521             curState = 1;
02522             (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
02523                     (char *) &curState, sizeof(curState));
02524             status = bind(sock, (struct sockaddr *) &mysockaddr,
02525                     sizeof(struct sockaddr));
02526             if (status < 0) {
02527                 goto bindError;
02528             }
02529         }
02530 
02531         /*
02532          * Attempt to connect. The connect may fail at present with an
02533          * EINPROGRESS but at a later time it will complete. The caller will
02534          * set up a file handler on the socket if she is interested in being
02535          * informed when the connect completes.
02536          */
02537 
02538         if (async) {
02539 #ifndef USE_FIONBIO
02540             curState = fcntl(sock, F_GETFL);
02541             SET_BITS(curState, O_NONBLOCK);
02542             status = fcntl(sock, F_SETFL, curState);
02543 #else /* USE_FIONBIO */
02544             curState = 1;
02545             status = ioctl(sock, FIONBIO, &curState);
02546 #endif /* !USE_FIONBIO */
02547         } else {
02548             status = 0;
02549         }
02550         if (status > -1) {
02551             status = connect(sock, (struct sockaddr *) &sockaddr,
02552                     sizeof(sockaddr));
02553             if (status < 0) {
02554                 if (errno == EINPROGRESS) {
02555                     asyncConnect = 1;
02556                     status = 0;
02557                 }
02558             } else {
02559                 /*
02560                  * Here we are if the connect succeeds. In case of an
02561                  * asynchronous connect we have to reset the channel to
02562                  * blocking mode. This appears to happen not very often, but
02563                  * e.g. on a HP 9000/800 under HP-UX B.11.00 we enter this
02564                  * stage. [Bug: 4388]
02565                  */
02566 
02567                 if (async) {
02568 #ifndef USE_FIONBIO
02569                     curState = fcntl(sock, F_GETFL);
02570                     CLEAR_BITS(curState, O_NONBLOCK);
02571                     status = fcntl(sock, F_SETFL, curState);
02572 #else /* USE_FIONBIO */
02573                     curState = 0;
02574                     status = ioctl(sock, FIONBIO, &curState);
02575 #endif /* !USE_FIONBIO */
02576                 }
02577             }
02578         }
02579     }
02580 
02581   bindError:
02582     if (status < 0) {
02583         if (interp != NULL) {
02584             Tcl_AppendResult(interp, "couldn't open socket: ",
02585                     Tcl_PosixError(interp), NULL);
02586         }
02587         if (sock != -1) {
02588             close(sock);
02589         }
02590         return NULL;
02591     }
02592 
02593     /*
02594      * Allocate a new TcpState for this socket.
02595      */
02596 
02597     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
02598     statePtr->flags = 0;
02599     if (asyncConnect) {
02600         statePtr->flags = TCP_ASYNC_CONNECT;
02601     }
02602     statePtr->fd = sock;
02603 
02604     return statePtr;
02605 
02606   addressError:
02607     if (sock != -1) {
02608         close(sock);
02609     }
02610     if (interp != NULL) {
02611         Tcl_AppendResult(interp, "couldn't open socket: ",
02612                 Tcl_PosixError(interp), NULL);
02613         if (errorMsg != NULL) {
02614             Tcl_AppendResult(interp, " (", errorMsg, ")", NULL);
02615         }
02616     }
02617     return NULL;
02618 }
02619 
02620 /*
02621  *----------------------------------------------------------------------
02622  *
02623  * CreateSocketAddress --
02624  *
02625  *      This function initializes a sockaddr structure for a host and port.
02626  *
02627  * Results:
02628  *      1 if the host was valid, 0 if the host could not be converted to an IP
02629  *      address.
02630  *
02631  * Side effects:
02632  *      Fills in the *sockaddrPtr structure.
02633  *
02634  *----------------------------------------------------------------------
02635  */
02636 
02637 static int
02638 CreateSocketAddress(
02639     struct sockaddr_in *sockaddrPtr,    /* Socket address */
02640     const char *host,                   /* Host. NULL implies INADDR_ANY */
02641     int port,                           /* Port number */
02642     int willBind,                       /* Is this an address to bind() to or
02643                                          * to connect() to? */
02644     const char **errorMsgPtr)           /* Place to store the error message
02645                                          * detail, if available. */
02646 {
02647 #ifdef HAVE_GETADDRINFO
02648     struct addrinfo hints, *resPtr = NULL;
02649     char *native;
02650     Tcl_DString ds;
02651     int result;
02652 
02653     if (host == NULL) {
02654         sockaddrPtr->sin_family = AF_INET;
02655         sockaddrPtr->sin_addr.s_addr = INADDR_ANY;
02656     addPort:
02657         sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
02658         return 1;
02659     }
02660 
02661     (void) memset(&hints, 0, sizeof(struct addrinfo));
02662     hints.ai_family = AF_INET;
02663     hints.ai_socktype = SOCK_STREAM;
02664     if (willBind) {
02665         hints.ai_flags |= AI_PASSIVE;
02666     }
02667 
02668     /*
02669      * Note that getaddrinfo() *is* thread-safe. If a platform doesn't get
02670      * that right, it shouldn't use this part of the code.
02671      */
02672 
02673     native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
02674     result = getaddrinfo(native, NULL, &hints, &resPtr);
02675     Tcl_DStringFree(&ds);
02676     if (result == 0) {
02677         memcpy(sockaddrPtr, resPtr->ai_addr, sizeof(struct sockaddr_in));
02678         freeaddrinfo(resPtr);
02679         goto addPort;
02680     }
02681 
02682     /*
02683      * Ought to use gai_strerror() here...
02684      */
02685 
02686     switch (result) {
02687     case EAI_NONAME:
02688     case EAI_SERVICE:
02689 #if defined(EAI_ADDRFAMILY) && EAI_ADDRFAMILY != EAI_NONAME
02690     case EAI_ADDRFAMILY:
02691 #endif
02692 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
02693     case EAI_NODATA:
02694 #endif
02695         *errorMsgPtr = gai_strerror(result);
02696         errno = EHOSTUNREACH;
02697         return 0;
02698     case EAI_SYSTEM:
02699         return 0;
02700     default:
02701         *errorMsgPtr = gai_strerror(result);
02702         errno = ENXIO;
02703         return 0;
02704     }
02705 #else /* !HAVE_GETADDRINFO */
02706     struct in_addr addr;                /* For 64/32 bit madness */
02707 
02708     (void) memset(sockaddrPtr, '\0', sizeof(struct sockaddr_in));
02709     sockaddrPtr->sin_family = AF_INET;
02710     sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
02711     if (host == NULL) {
02712         addr.s_addr = INADDR_ANY;
02713     } else {
02714         struct hostent *hostent;        /* Host database entry */
02715         Tcl_DString ds;
02716         const char *native;
02717 
02718         if (host == NULL) {
02719             native = NULL;
02720         } else {
02721             native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
02722         }
02723         addr.s_addr = inet_addr(native);                /* INTL: Native. */
02724 
02725         /*
02726          * This is 0xFFFFFFFF to ensure that it compares as a 32bit -1 on
02727          * either 32 or 64 bits systems.
02728          */
02729 
02730         if (addr.s_addr == 0xFFFFFFFF) {
02731             hostent = TclpGetHostByName(native);        /* INTL: Native. */
02732             if (hostent != NULL) {
02733                 memcpy(&addr, hostent->h_addr_list[0],
02734                         (size_t) hostent->h_length);
02735             } else {
02736 #ifdef  EHOSTUNREACH
02737                 errno = EHOSTUNREACH;
02738 #else /* !EHOSTUNREACH */
02739 #ifdef ENXIO
02740                 errno = ENXIO;
02741 #endif /* ENXIO */
02742 #endif /* EHOSTUNREACH */
02743                 if (native != NULL) {
02744                     Tcl_DStringFree(&ds);
02745                 }
02746                 return 0;       /* Error. */
02747             }
02748         }
02749         if (native != NULL) {
02750             Tcl_DStringFree(&ds);
02751         }
02752     }
02753 
02754     /*
02755      * NOTE: On 64 bit machines the assignment below is rumored to not do the
02756      * right thing. Please report errors related to this if you observe
02757      * incorrect behavior on 64 bit machines such as DEC Alphas. Should we
02758      * modify this code to do an explicit memcpy?
02759      */
02760 
02761     sockaddrPtr->sin_addr.s_addr = addr.s_addr;
02762     return 1;                   /* Success. */
02763 #endif /* HAVE_GETADDRINFO */
02764 }
02765 
02766 /*
02767  *----------------------------------------------------------------------
02768  *
02769  * Tcl_OpenTcpClient --
02770  *
02771  *      Opens a TCP client socket and creates a channel around it.
02772  *
02773  * Results:
02774  *      The channel or NULL if failed. An error message is returned in the
02775  *      interpreter on failure.
02776  *
02777  * Side effects:
02778  *      Opens a client socket and creates a new channel.
02779  *
02780  *----------------------------------------------------------------------
02781  */
02782 
02783 Tcl_Channel
02784 Tcl_OpenTcpClient(
02785     Tcl_Interp *interp,         /* For error reporting; can be NULL. */
02786     int port,                   /* Port number to open. */
02787     const char *host,           /* Host on which to open port. */
02788     const char *myaddr,         /* Client-side address */
02789     int myport,                 /* Client-side port */
02790     int async)                  /* If nonzero, attempt to do an asynchronous
02791                                  * connect. Otherwise we do a blocking
02792                                  * connect. */
02793 {
02794     TcpState *statePtr;
02795     char channelName[16 + TCL_INTEGER_SPACE];
02796 
02797     /*
02798      * Create a new client socket and wrap it in a channel.
02799      */
02800 
02801     statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
02802     if (statePtr == NULL) {
02803         return NULL;
02804     }
02805 
02806     statePtr->acceptProc = NULL;
02807     statePtr->acceptProcData = NULL;
02808 
02809     sprintf(channelName, "sock%d", statePtr->fd);
02810 
02811     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
02812             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
02813     if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
02814             "auto crlf") == TCL_ERROR) {
02815         Tcl_Close(NULL, statePtr->channel);
02816         return NULL;
02817     }
02818     return statePtr->channel;
02819 }
02820 
02821 /*
02822  *----------------------------------------------------------------------
02823  *
02824  * Tcl_MakeTcpClientChannel --
02825  *
02826  *      Creates a Tcl_Channel from an existing client TCP socket.
02827  *
02828  * Results:
02829  *      The Tcl_Channel wrapped around the preexisting TCP socket.
02830  *
02831  * Side effects:
02832  *      None.
02833  *
02834  *----------------------------------------------------------------------
02835  */
02836 
02837 Tcl_Channel
02838 Tcl_MakeTcpClientChannel(
02839     ClientData sock)            /* The socket to wrap up into a channel. */
02840 {
02841     return MakeTcpClientChannelMode(sock, (TCL_READABLE | TCL_WRITABLE));
02842 }
02843 
02844 /*
02845  *----------------------------------------------------------------------
02846  *
02847  * MakeTcpClientChannelMode --
02848  *
02849  *      Creates a Tcl_Channel from an existing client TCP socket
02850  *      with given mode.
02851  *
02852  * Results:
02853  *      The Tcl_Channel wrapped around the preexisting TCP socket.
02854  *
02855  * Side effects:
02856  *      None.
02857  *
02858  *----------------------------------------------------------------------
02859  */
02860 
02861 static Tcl_Channel
02862 MakeTcpClientChannelMode(
02863     ClientData sock,            /* The socket to wrap up into a channel. */
02864     int mode)                   /* ORed combination of TCL_READABLE and
02865                                  * TCL_WRITABLE to indicate file mode. */
02866 {
02867     TcpState *statePtr;
02868     char channelName[16 + TCL_INTEGER_SPACE];
02869 
02870     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
02871     statePtr->fd = PTR2INT(sock);
02872     statePtr->flags = 0;
02873     statePtr->acceptProc = NULL;
02874     statePtr->acceptProcData = NULL;
02875 
02876     sprintf(channelName, "sock%d", statePtr->fd);
02877 
02878     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
02879             (ClientData) statePtr, mode);
02880     if (Tcl_SetChannelOption(NULL, statePtr->channel, "-translation",
02881             "auto crlf") == TCL_ERROR) {
02882         Tcl_Close(NULL, statePtr->channel);
02883         return NULL;
02884     }
02885     return statePtr->channel;
02886 }
02887 
02888 /*
02889  *----------------------------------------------------------------------
02890  *
02891  * Tcl_OpenTcpServer --
02892  *
02893  *      Opens a TCP server socket and creates a channel around it.
02894  *
02895  * Results:
02896  *      The channel or NULL if failed. If an error occurred, an error message
02897  *      is left in the interp's result if interp is not NULL.
02898  *
02899  * Side effects:
02900  *      Opens a server socket and creates a new channel.
02901  *
02902  *----------------------------------------------------------------------
02903  */
02904 
02905 Tcl_Channel
02906 Tcl_OpenTcpServer(
02907     Tcl_Interp *interp,         /* For error reporting - may be NULL. */
02908     int port,                   /* Port number to open. */
02909     const char *myHost,         /* Name of local host. */
02910     Tcl_TcpAcceptProc *acceptProc,
02911                                 /* Callback for accepting connections from new
02912                                  * clients. */
02913     ClientData acceptProcData)  /* Data for the callback. */
02914 {
02915     TcpState *statePtr;
02916     char channelName[16 + TCL_INTEGER_SPACE];
02917 
02918     /*
02919      * Create a new client socket and wrap it in a channel.
02920      */
02921 
02922     statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
02923     if (statePtr == NULL) {
02924         return NULL;
02925     }
02926 
02927     statePtr->acceptProc = acceptProc;
02928     statePtr->acceptProcData = acceptProcData;
02929 
02930     /*
02931      * Set up the callback mechanism for accepting connections from new
02932      * clients.
02933      */
02934 
02935     Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
02936             (ClientData) statePtr);
02937     sprintf(channelName, "sock%d", statePtr->fd);
02938     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
02939             (ClientData) statePtr, 0);
02940     return statePtr->channel;
02941 }
02942 
02943 /*
02944  *----------------------------------------------------------------------
02945  *
02946  * TcpAccept --
02947  *      Accept a TCP socket connection.  This is called by the event loop.
02948  *
02949  * Results:
02950  *      None.
02951  *
02952  * Side effects:
02953  *      Creates a new connection socket. Calls the registered callback for the
02954  *      connection acceptance mechanism.
02955  *
02956  *----------------------------------------------------------------------
02957  */
02958 
02959         /* ARGSUSED */
02960 static void
02961 TcpAccept(
02962     ClientData data,            /* Callback token. */
02963     int mask)                   /* Not used. */
02964 {
02965     TcpState *sockState;        /* Client data of server socket. */
02966     int newsock;                /* The new client socket */
02967     TcpState *newSockState;     /* State for new socket. */
02968     struct sockaddr_in addr;    /* The remote address */
02969     socklen_t len;              /* For accept interface */
02970     char channelName[16 + TCL_INTEGER_SPACE];
02971 
02972     sockState = (TcpState *) data;
02973 
02974     len = sizeof(struct sockaddr_in);
02975     newsock = accept(sockState->fd, (struct sockaddr *) &addr, &len);
02976     if (newsock < 0) {
02977         return;
02978     }
02979 
02980     /*
02981      * Set close-on-exec flag to prevent the newly accepted socket from being
02982      * inherited by child processes.
02983      */
02984 
02985     (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
02986 
02987     newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
02988 
02989     newSockState->flags = 0;
02990     newSockState->fd = newsock;
02991     newSockState->acceptProc = NULL;
02992     newSockState->acceptProcData = NULL;
02993 
02994     sprintf(channelName, "sock%d", newsock);
02995     newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
02996             (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
02997 
02998     Tcl_SetChannelOption(NULL, newSockState->channel, "-translation",
02999             "auto crlf");
03000 
03001     if (sockState->acceptProc != NULL) {
03002         (*sockState->acceptProc)(sockState->acceptProcData,
03003                 newSockState->channel, inet_ntoa(addr.sin_addr),
03004                 ntohs(addr.sin_port));
03005     }
03006 }
03007 
03008 /*
03009  *----------------------------------------------------------------------
03010  *
03011  * TclpGetDefaultStdChannel --
03012  *
03013  *      Creates channels for standard input, standard output or standard error
03014  *      output if they do not already exist.
03015  *
03016  * Results:
03017  *      Returns the specified default standard channel, or NULL.
03018  *
03019  * Side effects:
03020  *      May cause the creation of a standard channel and the underlying file.
03021  *
03022  *----------------------------------------------------------------------
03023  */
03024 
03025 Tcl_Channel
03026 TclpGetDefaultStdChannel(
03027     int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
03028 {
03029     Tcl_Channel channel = NULL;
03030     int fd = 0;                 /* Initializations needed to prevent */
03031     int mode = 0;               /* compiler warning (used before set). */
03032     char *bufMode = NULL;
03033 
03034     /*
03035      * Some #def's to make the code a little clearer!
03036      */
03037 
03038 #define ZERO_OFFSET     ((Tcl_SeekOffset) 0)
03039 #define ERROR_OFFSET    ((Tcl_SeekOffset) -1)
03040 
03041     switch (type) {
03042     case TCL_STDIN:
03043         if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
03044                 && (errno == EBADF)) {
03045             return NULL;
03046         }
03047         fd = 0;
03048         mode = TCL_READABLE;
03049         bufMode = "line";
03050         break;
03051     case TCL_STDOUT:
03052         if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
03053                 && (errno == EBADF)) {
03054             return NULL;
03055         }
03056         fd = 1;
03057         mode = TCL_WRITABLE;
03058         bufMode = "line";
03059         break;
03060     case TCL_STDERR:
03061         if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
03062                 && (errno == EBADF)) {
03063             return NULL;
03064         }
03065         fd = 2;
03066         mode = TCL_WRITABLE;
03067         bufMode = "none";
03068         break;
03069     default:
03070         Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type");
03071         break;
03072     }
03073 
03074 #undef ZERO_OFFSET
03075 #undef ERROR_OFFSET
03076 
03077     channel = Tcl_MakeFileChannel((ClientData) INT2PTR(fd), mode);
03078     if (channel == NULL) {
03079         return NULL;
03080     }
03081 
03082     /*
03083      * Set up the normal channel options for stdio handles.
03084      */
03085 
03086     if (Tcl_GetChannelType(channel) == &fileChannelType) {
03087         Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
03088     } else {
03089         Tcl_SetChannelOption(NULL, channel, "-translation", "auto crlf");
03090     }
03091     Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
03092     return channel;
03093 }
03094 
03095 /*
03096  *----------------------------------------------------------------------
03097  *
03098  * Tcl_GetOpenFile --
03099  *
03100  *      Given a name of a channel registered in the given interpreter, returns
03101  *      a FILE * for it.
03102  *
03103  * Results:
03104  *      A standard Tcl result. If the channel is registered in the given
03105  *      interpreter and it is managed by the "file" channel driver, and it is
03106  *      open for the requested mode, then the output parameter filePtr is set
03107  *      to a FILE * for the underlying file. On error, the filePtr is not set,
03108  *      TCL_ERROR is returned and an error message is left in the interp's
03109  *      result.
03110  *
03111  * Side effects:
03112  *      May invoke fdopen to create the FILE * for the requested file.
03113  *
03114  *----------------------------------------------------------------------
03115  */
03116 
03117 int
03118 Tcl_GetOpenFile(
03119     Tcl_Interp *interp,         /* Interpreter in which to find file. */
03120     const char *chanID,         /* String that identifies file. */
03121     int forWriting,             /* 1 means the file is going to be used for
03122                                  * writing, 0 means for reading. */
03123     int checkUsage,             /* 1 means verify that the file was opened in
03124                                  * a mode that allows the access specified by
03125                                  * "forWriting". Ignored, we always check that
03126                                  * the channel is open for the requested
03127                                  * mode. */
03128     ClientData *filePtr)        /* Store pointer to FILE structure here. */
03129 {
03130     Tcl_Channel chan;
03131     int chanMode, fd;
03132     const Tcl_ChannelType *chanTypePtr;
03133     ClientData data;
03134     FILE *f;
03135 
03136     chan = Tcl_GetChannel(interp, chanID, &chanMode);
03137     if (chan == (Tcl_Channel) NULL) {
03138         return TCL_ERROR;
03139     }
03140     if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
03141         Tcl_AppendResult(interp, "\"", chanID, "\" wasn't opened for writing",
03142                 NULL);
03143         return TCL_ERROR;
03144     } else if ((!forWriting) && ((chanMode & TCL_READABLE) == 0)) {
03145         Tcl_AppendResult(interp, "\"", chanID, "\" wasn't opened for reading",
03146                 NULL);
03147         return TCL_ERROR;
03148     }
03149 
03150     /*
03151      * We allow creating a FILE * out of file based, pipe based and socket
03152      * based channels. We currently do not allow any other channel types,
03153      * because it is likely that stdio will not know what to do with them.
03154      */
03155 
03156     chanTypePtr = Tcl_GetChannelType(chan);
03157     if ((chanTypePtr == &fileChannelType)
03158 #ifdef SUPPORTS_TTY
03159             || (chanTypePtr == &ttyChannelType)
03160 #endif /* SUPPORTS_TTY */
03161             || (chanTypePtr == &tcpChannelType)
03162             || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
03163         if (Tcl_GetChannelHandle(chan,
03164                 (forWriting ? TCL_WRITABLE : TCL_READABLE),
03165                 (ClientData*) &data) == TCL_OK) {
03166             fd = PTR2INT(data);
03167 
03168             /*
03169              * The call to fdopen below is probably dangerous, since it will
03170              * truncate an existing file if the file is being opened for
03171              * writing....
03172              */
03173 
03174             f = fdopen(fd, (forWriting ? "w" : "r"));
03175             if (f == NULL) {
03176                 Tcl_AppendResult(interp, "cannot get a FILE * for \"", chanID,
03177                         "\"", NULL);
03178                 return TCL_ERROR;
03179             }
03180             *filePtr = (ClientData) f;
03181             return TCL_OK;
03182         }
03183     }
03184 
03185     Tcl_AppendResult(interp, "\"", chanID,
03186             "\" cannot be used to get a FILE *", NULL);
03187     return TCL_ERROR;
03188 }
03189 
03190 /*
03191  *----------------------------------------------------------------------
03192  *
03193  * TclUnixWaitForFile --
03194  *
03195  *      This function waits synchronously for a file to become readable or
03196  *      writable, with an optional timeout.
03197  *
03198  * Results:
03199  *      The return value is an OR'ed combination of TCL_READABLE,
03200  *      TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are
03201  *      present on file at the time of the return. This function will not
03202  *      return until either "timeout" milliseconds have elapsed or at least
03203  *      one of the conditions given by mask has occurred for file (a return
03204  *      value of 0 means that a timeout occurred). No normal events will be
03205  *      serviced during the execution of this function.
03206  *
03207  * Side effects:
03208  *      Time passes.
03209  *
03210  *----------------------------------------------------------------------
03211  */
03212 
03213 int
03214 TclUnixWaitForFile(
03215     int fd,                     /* Handle for file on which to wait. */
03216     int mask,                   /* What to wait for: OR'ed combination of
03217                                  * TCL_READABLE, TCL_WRITABLE, and
03218                                  * TCL_EXCEPTION. */
03219     int timeout)                /* Maximum amount of time to wait for one of
03220                                  * the conditions in mask to occur, in
03221                                  * milliseconds. A value of 0 means don't wait
03222                                  * at all, and a value of -1 means wait
03223                                  * forever. */
03224 {
03225     Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
03226     struct timeval blockTime, *timeoutPtr;
03227     int index, numFound, result = 0;
03228     fd_mask bit;
03229     fd_mask readyMasks[3*MASK_SIZE];
03230     fd_mask *maskp[3];          /* This array reflects the readable/writable
03231                                  * conditions that were found to exist by the
03232                                  * last call to select. */
03233 
03234     /*
03235      * If there is a non-zero finite timeout, compute the time when we give
03236      * up.
03237      */
03238 
03239     if (timeout > 0) {
03240         Tcl_GetTime(&now);
03241         abortTime.sec = now.sec + timeout/1000;
03242         abortTime.usec = now.usec + (timeout%1000)*1000;
03243         if (abortTime.usec >= 1000000) {
03244             abortTime.usec -= 1000000;
03245             abortTime.sec += 1;
03246         }
03247         timeoutPtr = &blockTime;
03248     } else if (timeout == 0) {
03249         timeoutPtr = &blockTime;
03250         blockTime.tv_sec = 0;
03251         blockTime.tv_usec = 0;
03252     } else {
03253         timeoutPtr = NULL;
03254     }
03255 
03256     /*
03257      * Initialize the ready masks and compute the mask offsets.
03258      */
03259 
03260     if (fd >= FD_SETSIZE) {
03261         Tcl_Panic("TclWaitForFile can't handle file id %d", fd);
03262         /* must never get here, or readyMasks overrun will occur below */
03263     }
03264     memset(readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
03265     index = fd / (NBBY*sizeof(fd_mask));
03266     bit = ((fd_mask)1) << (fd % (NBBY*sizeof(fd_mask)));
03267 
03268     /*
03269      * Loop in a mini-event loop of our own, waiting for either the file to
03270      * become ready or a timeout to occur.
03271      */
03272 
03273     while (1) {
03274         if (timeout > 0) {
03275             blockTime.tv_sec = abortTime.sec - now.sec;
03276             blockTime.tv_usec = abortTime.usec - now.usec;
03277             if (blockTime.tv_usec < 0) {
03278                 blockTime.tv_sec -= 1;
03279                 blockTime.tv_usec += 1000000;
03280             }
03281             if (blockTime.tv_sec < 0) {
03282                 blockTime.tv_sec = 0;
03283                 blockTime.tv_usec = 0;
03284             }
03285         }
03286 
03287         /*
03288          * Set the appropriate bit in the ready masks for the fd.
03289          */
03290 
03291         if (mask & TCL_READABLE) {
03292             readyMasks[index] |= bit;
03293         }
03294         if (mask & TCL_WRITABLE) {
03295             (readyMasks+MASK_SIZE)[index] |= bit;
03296         }
03297         if (mask & TCL_EXCEPTION) {
03298             (readyMasks+2*(MASK_SIZE))[index] |= bit;
03299         }
03300 
03301         /*
03302          * Wait for the event or a timeout.
03303          */
03304 
03305         /*
03306          * This is needed to satisfy GCC 3.3's strict aliasing rules.
03307          */
03308 
03309         maskp[0] = &readyMasks[0];
03310         maskp[1] = &readyMasks[MASK_SIZE];
03311         maskp[2] = &readyMasks[2*MASK_SIZE];
03312         numFound = select(fd+1, (SELECT_MASK *) maskp[0],
03313                 (SELECT_MASK *) maskp[1],
03314                 (SELECT_MASK *) maskp[2], timeoutPtr);
03315         if (numFound == 1) {
03316             if (readyMasks[index] & bit) {
03317                 SET_BITS(result, TCL_READABLE);
03318             }
03319             if ((readyMasks+MASK_SIZE)[index] & bit) {
03320                 SET_BITS(result, TCL_WRITABLE);
03321             }
03322             if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
03323                 SET_BITS(result, TCL_EXCEPTION);
03324             }
03325             result &= mask;
03326             if (result) {
03327                 break;
03328             }
03329         }
03330         if (timeout == 0) {
03331             break;
03332         }
03333         if (timeout < 0) {
03334             continue;
03335         }
03336 
03337         /*
03338          * The select returned early, so we need to recompute the timeout.
03339          */
03340 
03341         Tcl_GetTime(&now);
03342         if ((abortTime.sec < now.sec)
03343                 || (abortTime.sec==now.sec && abortTime.usec<=now.usec)) {
03344             break;
03345         }
03346     }
03347     return result;
03348 }
03349 
03350 /*
03351  *----------------------------------------------------------------------
03352  *
03353  * FileTruncateProc --
03354  *
03355  *      Truncates a file to a given length.
03356  *
03357  * Results:
03358  *      0 if the operation succeeded, and -1 if it failed (in which case
03359  *      *errorCodePtr will be set to errno).
03360  *
03361  * Side effects:
03362  *      The underlying file is potentially truncated. This can have a wide
03363  *      variety of side effects, including moving file pointers that point at
03364  *      places later in the file than the truncate point.
03365  *
03366  *----------------------------------------------------------------------
03367  */
03368 
03369 static int
03370 FileTruncateProc(
03371     ClientData instanceData,
03372     Tcl_WideInt length)
03373 {
03374     FileState *fsPtr = (FileState *) instanceData;
03375     int result;
03376 
03377 #ifdef HAVE_TYPE_OFF64_T
03378     /*
03379      * We assume this goes with the type for now...
03380      */
03381 
03382     result = ftruncate64(fsPtr->fd, (off64_t) length);
03383 #else
03384     result = ftruncate(fsPtr->fd, (off_t) length);
03385 #endif
03386     if (result) {
03387         return errno;
03388     }
03389     return 0;
03390 }
03391 
03392 /*
03393  * Local Variables:
03394  * mode: c
03395  * c-basic-offset: 4
03396  * fill-column: 78
03397  * End:
03398  */



Generated on Wed Mar 12 12:18:26 2008 by  doxygen 1.5.1