diff options
Diffstat (limited to 'unix/tclUnixChan.c')
-rw-r--r-- | unix/tclUnixChan.c | 583 |
1 files changed, 349 insertions, 234 deletions
diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c index b4b2739..be49c95 100644 --- a/unix/tclUnixChan.c +++ b/unix/tclUnixChan.c @@ -49,6 +49,16 @@ #endif /* HAVE_TERMIOS_H */ /* + * The bits supported for describing the closeMode field of TtyState. + */ + +enum CloseModeBits { + CLOSE_DEFAULT, + CLOSE_DRAIN, + CLOSE_DISCARD +}; + +/* * Helper macros to make parts of this file clearer. The macros do exactly * what they say on the tin. :-) They also only ever refer to their arguments * once, and so can be used without regard to side effects. @@ -58,10 +68,11 @@ #define CLEAR_BITS(var, bits) ((var) &= ~(bits)) /* - * This structure describes per-instance state of a file based channel. + * These structures describe per-instance state of file-based and serial-based + * channels. */ -typedef struct FileState { +typedef struct { Tcl_Channel channel; /* Channel associated with this file. */ int fd; /* File handle. */ int validMask; /* OR'ed combination of TCL_READABLE, @@ -69,6 +80,18 @@ typedef struct FileState { * which operations are valid on the file. */ } FileState; +typedef struct { + FileState fileState; +#ifdef SUPPORTS_TTY + int closeMode; /* One of CLOSE_DEFAULT, CLOSE_DRAIN or + * CLOSE_DISCARD. */ + int doReset; /* Whether we should do a terminal reset on + * close. */ + struct termios initState; /* The state of the terminal when it was + * opened. */ +#endif /* SUPPORTS_TTY */ +} TtyState; + #ifdef SUPPORTS_TTY /* @@ -76,14 +99,14 @@ typedef struct FileState { * a platform-independant manner. */ -typedef struct TtyAttrs { +typedef struct { int baud; int parity; int data; int stop; } TtyAttrs; -#endif /* !SUPPORTS_TTY */ +#endif /* SUPPORTS_TTY */ #define UNSUPPORTED_OPTION(detail) \ if (interp) { \ @@ -113,6 +136,8 @@ static Tcl_WideInt FileWideSeekProc(ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCode); static void FileWatchProc(ClientData instanceData, int mask); #ifdef SUPPORTS_TTY +static int TtyCloseProc(ClientData instanceData, + Tcl_Interp *interp); static void TtyGetAttributes(int fd, TtyAttrs *ttyPtr); static int TtyGetOptionProc(ClientData instanceData, Tcl_Interp *interp, const char *optionName, @@ -162,7 +187,7 @@ static const Tcl_ChannelType fileChannelType = { static const Tcl_ChannelType ttyChannelType = { "tty", /* Type name. */ TCL_CHANNEL_VERSION_5, /* v5 channel */ - FileCloseProc, /* Close proc. */ + TtyCloseProc, /* Close proc. */ FileInputProc, /* Input proc. */ FileOutputProc, /* Output proc. */ NULL, /* Seek proc. */ @@ -310,10 +335,11 @@ FileOutputProc( /* *---------------------------------------------------------------------- * - * FileCloseProc -- + * FileCloseProc, TtyCloseProc -- * - * This function is called from the generic IO level to perform - * channel-type-specific cleanup when a file based channel is closed. + * These functions are called from the generic IO level to perform + * channel-type-specific cleanup when a file- or tty-based channel is + * closed. * * Results: * 0 if successful, errno if failed. @@ -347,6 +373,46 @@ FileCloseProc( ckfree(fsPtr); return errorCode; } + +#ifdef SUPPORTS_TTY +static int +TtyCloseProc( + ClientData instanceData, + Tcl_Interp *interp) +{ + TtyState *ttyPtr = instanceData; + + /* + * If we've been asked by the user to drain or flush, do so now. + */ + + switch (ttyPtr->closeMode) { + case CLOSE_DRAIN: + tcdrain(ttyPtr->fileState.fd); + break; + case CLOSE_DISCARD: + tcflush(ttyPtr->fileState.fd, TCIOFLUSH); + break; + default: + /* Do nothing */ + break; + } + + /* + * If we've had our state changed from the default, reset now. + */ + + if (ttyPtr->doReset) { + tcsetattr(ttyPtr->fileState.fd, TCSANOW, &ttyPtr->initState); + } + + /* + * Delegate to close for files. + */ + + return FileCloseProc(instanceData, interp); +} +#endif /* SUPPORTS_TTY */ /* *---------------------------------------------------------------------- @@ -383,7 +449,7 @@ FileSeekProc( */ oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR); - if (oldLoc == Tcl_LongAsWide(-1)) { + if (oldLoc == -1) { /* * Bad things are happening. Error out... */ @@ -398,14 +464,14 @@ FileSeekProc( * Check for expressability in our return type, and roll-back otherwise. */ - if (newLoc > Tcl_LongAsWide(INT_MAX)) { + if (newLoc > INT_MAX) { *errorCodePtr = EOVERFLOW; TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET); return -1; } else { - *errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0; + *errorCodePtr = (newLoc == -1) ? errno : 0; } - return (int) Tcl_WideAsLong(newLoc); + return (int) newLoc; } /* @@ -578,7 +644,7 @@ TtySetOptionProc( const char *optionName, /* Which option to set? */ const char *value) /* New value for option. */ { - FileState *fsPtr = instanceData; + TtyState *fsPtr = instanceData; unsigned int len, vlen; TtyAttrs tty; int argc; @@ -601,11 +667,10 @@ TtySetOptionProc( * system calls results should be checked there. - dl */ - TtySetAttributes(fsPtr->fd, &tty); + TtySetAttributes(fsPtr->fileState.fd, &tty); return TCL_OK; } - /* * Option -handshake none|xonxoff|rtscts|dtrdsr */ @@ -615,7 +680,7 @@ TtySetOptionProc( * Reset all handshake options. DTR and RTS are ON by default. */ - tcgetattr(fsPtr->fd, &iostate); + tcgetattr(fsPtr->fileState.fd, &iostate); CLEAR_BITS(iostate.c_iflag, IXON | IXOFF | IXANY); #ifdef CRTSCTS CLEAR_BITS(iostate.c_cflag, CRTSCTS); @@ -646,7 +711,7 @@ TtySetOptionProc( } return TCL_ERROR; } - tcsetattr(fsPtr->fd, TCSADRAIN, &iostate); + tcsetattr(fsPtr->fileState.fd, TCSADRAIN, &iostate); return TCL_OK; } @@ -655,34 +720,42 @@ TtySetOptionProc( */ if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) { - Tcl_DString ds; - if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) { return TCL_ERROR; } else if (argc != 2) { + badXchar: if (interp) { Tcl_SetObjResult(interp, Tcl_NewStringObj( "bad value for -xchar: should be a list of" - " two elements", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE", - "VALUE", NULL); + " two elements with each a single 8-bit character", -1)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "XCHAR", NULL); } ckfree(argv); return TCL_ERROR; } - tcgetattr(fsPtr->fd, &iostate); + tcgetattr(fsPtr->fileState.fd, &iostate); - Tcl_UtfToExternalDString(NULL, argv[0], -1, &ds); - iostate.c_cc[VSTART] = *(const cc_t *) Tcl_DStringValue(&ds); - TclDStringClear(&ds); + iostate.c_cc[VSTART] = argv[0][0]; + iostate.c_cc[VSTOP] = argv[1][0]; + if (argv[0][0] & 0x80 || argv[1][0] & 0x80) { + Tcl_UniChar character = 0; + int charLen; - Tcl_UtfToExternalDString(NULL, argv[1], -1, &ds); - iostate.c_cc[VSTOP] = *(const cc_t *) Tcl_DStringValue(&ds); - Tcl_DStringFree(&ds); + charLen = Tcl_UtfToUniChar(argv[0], &character); + if ((character > 0xFF) || argv[0][charLen]) { + goto badXchar; + } + iostate.c_cc[VSTART] = character; + charLen = Tcl_UtfToUniChar(argv[1], &character); + if ((character > 0xFF) || argv[1][charLen]) { + goto badXchar; + } + iostate.c_cc[VSTOP] = character; + } ckfree(argv); - tcsetattr(fsPtr->fd, TCSADRAIN, &iostate); + tcsetattr(fsPtr->fileState.fd, TCSADRAIN, &iostate); return TCL_OK; } @@ -693,19 +766,20 @@ TtySetOptionProc( if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) { int msec; - tcgetattr(fsPtr->fd, &iostate); + tcgetattr(fsPtr->fileState.fd, &iostate); if (Tcl_GetInt(interp, value, &msec) != TCL_OK) { return TCL_ERROR; } iostate.c_cc[VMIN] = 0; iostate.c_cc[VTIME] = (msec==0) ? 0 : (msec<100) ? 1 : (msec+50)/100; - tcsetattr(fsPtr->fd, TCSADRAIN, &iostate); + tcsetattr(fsPtr->fileState.fd, TCSADRAIN, &iostate); return TCL_OK; } /* * Option -ttycontrol {DTR 1 RTS 0 BREAK 0} */ + if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) { #if defined(TIOCMGET) && defined(TIOCMSET) int i, control, flag; @@ -725,7 +799,7 @@ TtySetOptionProc( return TCL_ERROR; } - ioctl(fsPtr->fd, TIOCMGET, &control); + ioctl(fsPtr->fileState.fd, TIOCMGET, &control); for (i = 0; i < argc-1; i += 2) { if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) { ckfree(argv); @@ -746,9 +820,9 @@ TtySetOptionProc( } else if (Tcl_UtfNcasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) { #if defined(TIOCSBRK) && defined(TIOCCBRK) if (flag) { - ioctl(fsPtr->fd, TIOCSBRK, NULL); + ioctl(fsPtr->fileState.fd, TIOCSBRK, NULL); } else { - ioctl(fsPtr->fd, TIOCCBRK, NULL); + ioctl(fsPtr->fileState.fd, TIOCCBRK, NULL); } #else /* TIOCSBRK & TIOCCBRK */ UNSUPPORTED_OPTION("-ttycontrol BREAK"); @@ -768,7 +842,7 @@ TtySetOptionProc( } } /* -ttycontrol options loop */ - ioctl(fsPtr->fd, TIOCMSET, &control); + ioctl(fsPtr->fileState.fd, TIOCMSET, &control); ckfree(argv); return TCL_OK; #else /* TIOCMGET&TIOCMSET */ @@ -776,8 +850,112 @@ TtySetOptionProc( #endif /* TIOCMGET&TIOCMSET */ } + /* + * Option -closemode drain|discard + */ + + if ((len > 2) && (strncmp(optionName, "-closemode", len) == 0)) { + if (Tcl_UtfNcasecmp(value, "DEFAULT", vlen) == 0) { + fsPtr->closeMode = CLOSE_DEFAULT; + } else if (Tcl_UtfNcasecmp(value, "DRAIN", vlen) == 0) { + fsPtr->closeMode = CLOSE_DRAIN; + } else if (Tcl_UtfNcasecmp(value, "DISCARD", vlen) == 0) { + fsPtr->closeMode = CLOSE_DISCARD; + } else { + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad mode \"%s\" for -closemode: must be" + " default, discard, or drain", value)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE", + "VALUE", NULL); + } + return TCL_ERROR; + } + return TCL_OK; + } + + /* + * Option -inputmode normal|password|raw + */ + + if ((len > 2) && (strncmp(optionName, "-inputmode", len) == 0)) { + if (tcgetattr(fsPtr->fileState.fd, &iostate) < 0) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't read serial terminal control state: %s", + Tcl_PosixError(interp))); + } + return TCL_ERROR; + } + if (Tcl_UtfNcasecmp(value, "NORMAL", vlen) == 0) { + SET_BITS(iostate.c_iflag, BRKINT | IGNPAR | ISTRIP | ICRNL | IXON); + SET_BITS(iostate.c_oflag, OPOST); + SET_BITS(iostate.c_lflag, ECHO | ECHONL | ICANON | ISIG); + } else if (Tcl_UtfNcasecmp(value, "PASSWORD", vlen) == 0) { + SET_BITS(iostate.c_iflag, BRKINT | IGNPAR | ISTRIP | ICRNL | IXON); + SET_BITS(iostate.c_oflag, OPOST); + CLEAR_BITS(iostate.c_lflag, ECHO); + /* + * Note: password input turns out to be best if you echo the + * newline that the user types. Theoretically we could get users + * to do the processing of this in their scripts, but it always + * feels highly unnatural to do so in practice. + */ + SET_BITS(iostate.c_lflag, ECHONL | ICANON | ISIG); + } else if (Tcl_UtfNcasecmp(value, "RAW", vlen) == 0) { +#ifdef HAVE_CFMAKERAW + cfmakeraw(&iostate); +#else /* !HAVE_CFMAKERAW */ + CLEAR_BITS(iostate.c_iflag, IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + CLEAR_BITS(iostate.c_oflag, OPOST); + CLEAR_BITS(iostate.c_lflag, ECHO | ECHONL | ICANON | ISIG | IEXTEN); + CLEAR_BITS(iostate.c_cflag, CSIZE | PARENB); + SET_BITS(iostate.c_cflag, CS8); +#endif /* HAVE_CFMAKERAW */ + } else if (Tcl_UtfNcasecmp(value, "RESET", vlen) == 0) { + /* + * Reset to the initial state, whatever that is. + */ + + memcpy(&iostate, &fsPtr->initState, sizeof(struct termios)); + } else { + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad mode \"%s\" for -inputmode: must be" + " normal, password, raw, or reset", value)); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE", + "VALUE", NULL); + } + return TCL_ERROR; + } + if (tcsetattr(fsPtr->fileState.fd, TCSADRAIN, &iostate) < 0) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't update serial terminal control state: %s", + Tcl_PosixError(interp))); + } + return TCL_ERROR; + } + + /* + * If we've changed the state from default, schedule a reset later. + * Note that this specifically does not detect changes made by calling + * an external stty program; that is deliberate, as it maintains + * compatibility with existing code! + * + * This mechanism in Tcl is not intended to be a full replacement for + * what stty does; it just handles a few common cases and tries not to + * leave things in a broken state. + */ + + fsPtr->doReset = (memcmp(&iostate, &fsPtr->initState, + sizeof(struct termios)) != 0); + return TCL_OK; + } + return Tcl_BadChannelOption(interp, optionName, - "mode handshake timeout ttycontrol xchar"); + "closemode inputmode mode handshake timeout ttycontrol xchar"); } /* @@ -805,16 +983,74 @@ TtyGetOptionProc( const char *optionName, /* Option to get. */ Tcl_DString *dsPtr) /* Where to store value(s). */ { - FileState *fsPtr = instanceData; + TtyState *fsPtr = instanceData; unsigned int len; char buf[3*TCL_INTEGER_SPACE + 16]; int valid = 0; /* Flag if valid option parsed. */ + struct termios iostate; if (optionName == NULL) { len = 0; } else { len = strlen(optionName); } + + /* + * Get option -closemode + */ + + if (len == 0) { + Tcl_DStringAppendElement(dsPtr, "-closemode"); + } + if (len==0 || (len>1 && strncmp(optionName, "-closemode", len)==0)) { + switch (fsPtr->closeMode) { + case CLOSE_DRAIN: + Tcl_DStringAppendElement(dsPtr, "drain"); + break; + case CLOSE_DISCARD: + Tcl_DStringAppendElement(dsPtr, "discard"); + break; + default: + Tcl_DStringAppendElement(dsPtr, "default"); + break; + } + } + + /* + * Get option -inputmode + * + * This is a great simplification of the underlying reality, but actually + * represents what almost all scripts really want to know. + */ + + if (len == 0) { + Tcl_DStringAppendElement(dsPtr, "-inputmode"); + } + if (len==0 || (len>1 && strncmp(optionName, "-inputmode", len)==0)) { + valid = 1; + if (tcgetattr(fsPtr->fileState.fd, &iostate) < 0) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't read serial terminal control state: %s", + Tcl_PosixError(interp))); + } + return TCL_ERROR; + } + if (iostate.c_lflag & ICANON) { + if (iostate.c_lflag & ECHO) { + Tcl_DStringAppendElement(dsPtr, "normal"); + } else { + Tcl_DStringAppendElement(dsPtr, "password"); + } + } else { + Tcl_DStringAppendElement(dsPtr, "raw"); + } + } + + /* + * Get option -mode + */ + if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-mode"); } @@ -822,7 +1058,7 @@ TtyGetOptionProc( TtyAttrs tty; valid = 1; - TtyGetAttributes(fsPtr->fd, &tty); + TtyGetAttributes(fsPtr->fileState.fd, &tty); sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop); Tcl_DStringAppendElement(dsPtr, buf); } @@ -836,11 +1072,10 @@ TtyGetOptionProc( Tcl_DStringStartSublist(dsPtr); } if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) { - struct termios iostate; Tcl_DString ds; valid = 1; - tcgetattr(fsPtr->fd, &iostate); + tcgetattr(fsPtr->fileState.fd, &iostate); Tcl_DStringInit(&ds); Tcl_ExternalToUtfDString(NULL, (char *) &iostate.c_cc[VSTART], 1, &ds); @@ -865,10 +1100,10 @@ TtyGetOptionProc( int inQueue=0, outQueue=0, inBuffered, outBuffered; valid = 1; - GETREADQUEUE(fsPtr->fd, inQueue); - GETWRITEQUEUE(fsPtr->fd, outQueue); - inBuffered = Tcl_InputBuffered(fsPtr->channel); - outBuffered = Tcl_OutputBuffered(fsPtr->channel); + GETREADQUEUE(fsPtr->fileState.fd, inQueue); + GETWRITEQUEUE(fsPtr->fileState.fd, outQueue); + inBuffered = Tcl_InputBuffered(fsPtr->fileState.channel); + outBuffered = Tcl_OutputBuffered(fsPtr->fileState.channel); sprintf(buf, "%d", inBuffered+inQueue); Tcl_DStringAppendElement(dsPtr, buf); @@ -882,24 +1117,49 @@ TtyGetOptionProc( * Option is readonly and returned by [fconfigure chan -ttystatus] but not * returned by unnamed [fconfigure chan]. */ + if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) { int status; valid = 1; - ioctl(fsPtr->fd, TIOCMGET, &status); + ioctl(fsPtr->fileState.fd, TIOCMGET, &status); TtyModemStatusStr(status, dsPtr); } #endif /* TIOCMGET */ +#if defined(TIOCGWINSZ) + /* + * Get option -winsize + * Option is readonly and returned by [fconfigure chan -winsize] but not + * returned by [fconfigure chan] without explicit option name. + */ + + if ((len > 1) && (strncmp(optionName, "-winsize", len) == 0)) { + struct winsize ws; + + valid = 1; + if (ioctl(fsPtr->fileState.fd, TIOCGWINSZ, &ws) < 0) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't read terminal size: %s", + Tcl_PosixError(interp))); + } + return TCL_ERROR; + } + sprintf(buf, "%d", ws.ws_col); + Tcl_DStringAppendElement(dsPtr, buf); + sprintf(buf, "%d", ws.ws_row); + Tcl_DStringAppendElement(dsPtr, buf); + } +#endif /* TIOCGWINSZ */ + if (valid) { return TCL_OK; } - return Tcl_BadChannelOption(interp, optionName, "mode" - " queue ttystatus xchar" - ); + return Tcl_BadChannelOption(interp, optionName, + "closemode inputmode mode queue ttystatus winsize xchar"); } - static const struct {int baud; speed_t speed;} speeds[] = { #ifdef B0 {0, B0}, @@ -1023,7 +1283,7 @@ static const struct {int baud; speed_t speed;} speeds[] = { #endif {-1, 0} }; - + /* *--------------------------------------------------------------------------- * @@ -1315,7 +1575,8 @@ TtyParseMode( static void TtyInit( - int fd) /* Open file descriptor for serial port to be initialized. */ + int fd) /* Open file descriptor for serial port to be + * initialized. */ { struct termios iostate; tcgetattr(fd, &iostate); @@ -1325,8 +1586,7 @@ TtyInit( || iostate.c_lflag != 0 || iostate.c_cflag & CREAD || iostate.c_cc[VMIN] != 1 - || iostate.c_cc[VTIME] != 0) - { + || iostate.c_cc[VTIME] != 0) { iostate.c_iflag = IGNBRK; iostate.c_oflag = 0; iostate.c_lflag = 0; @@ -1368,7 +1628,7 @@ TclpOpenFileChannel( * what modes to create it? */ { int fd, channelPermissions; - FileState *fsPtr; + TtyState *fsPtr; const char *native, *translation; char channelName[16 + TCL_INTEGER_SPACE]; const Tcl_ChannelType *channelTypePtr; @@ -1424,8 +1684,6 @@ TclpOpenFileChannel( fcntl(fd, F_SETFD, FD_CLOEXEC); - sprintf(channelName, "file%d", fd); - #ifdef SUPPORTS_TTY if (strcmp(native, "/dev/tty") != 0 && isatty(fd)) { /* @@ -1445,18 +1703,27 @@ TclpOpenFileChannel( translation = "auto crlf"; channelTypePtr = &ttyChannelType; TtyInit(fd); + sprintf(channelName, "serial%d", fd); } else #endif /* SUPPORTS_TTY */ { translation = NULL; channelTypePtr = &fileChannelType; + sprintf(channelName, "file%d", fd); } - fsPtr = ckalloc(sizeof(FileState)); - fsPtr->validMask = channelPermissions | TCL_EXCEPTION; - fsPtr->fd = fd; + fsPtr = ckalloc(sizeof(TtyState)); + fsPtr->fileState.validMask = channelPermissions | TCL_EXCEPTION; + fsPtr->fileState.fd = fd; +#ifdef SUPPORTS_TTY + if (channelTypePtr == &ttyChannelType) { + fsPtr->closeMode = CLOSE_DEFAULT; + fsPtr->doReset = 0; + tcgetattr(fsPtr->fileState.fd, &fsPtr->initState); + } +#endif /* SUPPORTS_TTY */ - fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, + fsPtr->fileState.channel = Tcl_CreateChannel(channelTypePtr, channelName, fsPtr, channelPermissions); if (translation != NULL) { @@ -1468,14 +1735,14 @@ TclpOpenFileChannel( * reports that the serial port isn't working. */ - if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation", - translation) != TCL_OK) { - Tcl_Close(NULL, fsPtr->channel); + if (Tcl_SetChannelOption(interp, fsPtr->fileState.channel, + "-translation", translation) != TCL_OK) { + Tcl_Close(NULL, fsPtr->fileState.channel); return NULL; } } - return fsPtr->channel; + return fsPtr->fileState.channel; } /* @@ -1500,7 +1767,7 @@ Tcl_MakeFileChannel( int mode) /* ORed combination of TCL_READABLE and * TCL_WRITABLE to indicate file mode. */ { - FileState *fsPtr; + TtyState *fsPtr; char channelName[16 + TCL_INTEGER_SPACE]; int fd = PTR2INT(handle); const Tcl_ChannelType *channelTypePtr; @@ -1519,22 +1786,30 @@ Tcl_MakeFileChannel( sprintf(channelName, "serial%d", fd); } else #endif /* SUPPORTS_TTY */ - if ((getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0) - && (sockaddrLen > 0) - && (sockaddr.sa_family == AF_INET || sockaddr.sa_family == AF_INET6)) { + if ((getsockname(fd, (struct sockaddr *) &sockaddr, &sockaddrLen) == 0) + && (sockaddrLen > 0) + && (sockaddr.sa_family == AF_INET + || sockaddr.sa_family == AF_INET6)) { return TclpMakeTcpClientChannelMode(INT2PTR(fd), mode); } else { channelTypePtr = &fileChannelType; sprintf(channelName, "file%d", fd); } - fsPtr = ckalloc(sizeof(FileState)); - fsPtr->fd = fd; - fsPtr->validMask = mode | TCL_EXCEPTION; - fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName, + fsPtr = ckalloc(sizeof(TtyState)); + fsPtr->fileState.fd = fd; + fsPtr->fileState.validMask = mode | TCL_EXCEPTION; + fsPtr->fileState.channel = Tcl_CreateChannel(channelTypePtr, channelName, fsPtr, mode); +#ifdef SUPPORTS_TTY + if (channelTypePtr == &ttyChannelType) { + fsPtr->closeMode = CLOSE_DEFAULT; + fsPtr->doReset = 0; + tcgetattr(fsPtr->fileState.fd, &fsPtr->initState); + } +#endif /* SUPPORTS_TTY */ - return fsPtr->channel; + return fsPtr->fileState.channel; } /* @@ -1726,166 +2001,6 @@ Tcl_GetOpenFile( return TCL_ERROR; } -#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is - * in tclMacOSXNotify.c */ -/* - *---------------------------------------------------------------------- - * - * TclUnixWaitForFile -- - * - * This function waits synchronously for a file to become readable or - * writable, with an optional timeout. - * - * Results: - * The return value is an OR'ed combination of TCL_READABLE, - * TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are - * present on file at the time of the return. This function will not - * return until either "timeout" milliseconds have elapsed or at least - * one of the conditions given by mask has occurred for file (a return - * value of 0 means that a timeout occurred). No normal events will be - * serviced during the execution of this function. - * - * Side effects: - * Time passes. - * - *---------------------------------------------------------------------- - */ - -int -TclUnixWaitForFile( - int fd, /* Handle for file on which to wait. */ - int mask, /* What to wait for: OR'ed combination of - * TCL_READABLE, TCL_WRITABLE, and - * TCL_EXCEPTION. */ - int timeout) /* Maximum amount of time to wait for one of - * the conditions in mask to occur, in - * milliseconds. A value of 0 means don't wait - * at all, and a value of -1 means wait - * forever. */ -{ - Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */ - struct timeval blockTime, *timeoutPtr; - int numFound, result = 0; - fd_set readableMask; - fd_set writableMask; - fd_set exceptionMask; - -#ifndef _DARWIN_C_SOURCE - /* - * Sanity check fd. - */ - - if (fd >= FD_SETSIZE) { - Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd); - /* must never get here, or select masks overrun will occur below */ - } -#endif - - /* - * If there is a non-zero finite timeout, compute the time when we give - * up. - */ - - if (timeout > 0) { - Tcl_GetTime(&now); - abortTime.sec = now.sec + timeout/1000; - abortTime.usec = now.usec + (timeout%1000)*1000; - if (abortTime.usec >= 1000000) { - abortTime.usec -= 1000000; - abortTime.sec += 1; - } - timeoutPtr = &blockTime; - } else if (timeout == 0) { - timeoutPtr = &blockTime; - blockTime.tv_sec = 0; - blockTime.tv_usec = 0; - } else { - timeoutPtr = NULL; - } - - /* - * Initialize the select masks. - */ - - FD_ZERO(&readableMask); - FD_ZERO(&writableMask); - FD_ZERO(&exceptionMask); - - /* - * Loop in a mini-event loop of our own, waiting for either the file to - * become ready or a timeout to occur. - */ - - while (1) { - if (timeout > 0) { - blockTime.tv_sec = abortTime.sec - now.sec; - blockTime.tv_usec = abortTime.usec - now.usec; - if (blockTime.tv_usec < 0) { - blockTime.tv_sec -= 1; - blockTime.tv_usec += 1000000; - } - if (blockTime.tv_sec < 0) { - blockTime.tv_sec = 0; - blockTime.tv_usec = 0; - } - } - - /* - * Setup the select masks for the fd. - */ - - if (mask & TCL_READABLE) { - FD_SET(fd, &readableMask); - } - if (mask & TCL_WRITABLE) { - FD_SET(fd, &writableMask); - } - if (mask & TCL_EXCEPTION) { - FD_SET(fd, &exceptionMask); - } - - /* - * Wait for the event or a timeout. - */ - - numFound = select(fd + 1, &readableMask, &writableMask, - &exceptionMask, timeoutPtr); - if (numFound == 1) { - if (FD_ISSET(fd, &readableMask)) { - SET_BITS(result, TCL_READABLE); - } - if (FD_ISSET(fd, &writableMask)) { - SET_BITS(result, TCL_WRITABLE); - } - if (FD_ISSET(fd, &exceptionMask)) { - SET_BITS(result, TCL_EXCEPTION); - } - result &= mask; - if (result) { - break; - } - } - if (timeout == 0) { - break; - } - if (timeout < 0) { - continue; - } - - /* - * The select returned early, so we need to recompute the timeout. - */ - - Tcl_GetTime(&now); - if ((abortTime.sec < now.sec) - || (abortTime.sec==now.sec && abortTime.usec<=now.usec)) { - break; - } - } - return result; -} -#endif /* HAVE_COREFOUNDATION */ - /* *---------------------------------------------------------------------- * |