summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixChan.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixChan.c')
-rw-r--r--unix/tclUnixChan.c583
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 */
-
/*
*----------------------------------------------------------------------
*