*** tclUnixChan.c.old	Tue Feb 26 08:05:26 2002
--- tclUnixChan.c	Tue Feb 26 14:34:07 2002
***************
*** 45,60 ****
--- 45,81 ----
  
  #ifdef USE_TERMIOS
  #   include <termios.h>
+ #   include <sys/ioctl.h>
  #   define IOSTATE			struct termios
  #   define GETIOSTATE(fd, statePtr)	tcgetattr((fd), (statePtr))
  #   define SETIOSTATE(fd, statePtr)	tcsetattr((fd), TCSADRAIN, (statePtr))
+ #   define GETCONTROL(fd, intPtr)	ioctl((fd), TIOCMGET, (intPtr))
+ #   define SETCONTROL(fd, intPtr)	ioctl((fd), TIOCMSET, (intPtr))
+ #   define TTYFLUSH(fd)			tcflush((fd), TCIOFLUSH);
+ #   ifdef FIONREAD
+ #	define GETREADQUEUE(fd, int)	ioctl((fd), FIONREAD, &(int))
+ #   elif defined(FIORDCHK)
+ #	define GETREADQUEUE(fd, int)	int = ioctl((fd), FIORDCHK, NULL)
+ #   endif
+ #   ifdef TIOCOUTQ
+ #	define GETWRITEQUEUE(fd, int)	ioctl((fd), TIOCOUTQ, &(int))
+ #   endif
+ #   if defined(TIOCSBRK) && defined(TIOCCBRK)
+ #	define SETBREAK(fd, flag)		\
+ 		ioctl((fd), (unsigned) ((flag) ? TIOCSBRK:TIOCCBRK), NULL)
+ #   endif
+ #   if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
+ #	define CRTSCTS CNEW_RTSCTS
+ #   endif
  #else	/* !USE_TERMIOS */
+ 
  #ifdef USE_TERMIO
  #   include <termio.h>
  #   define IOSTATE			struct termio
  #   define GETIOSTATE(fd, statePtr)	ioctl((fd), TCGETA, (statePtr))
  #   define SETIOSTATE(fd, statePtr)	ioctl((fd), TCSETAW, (statePtr))
  #else	/* !USE_TERMIO */
+ 
  #ifdef USE_SGTTY
  #   include <sgtty.h>
  #   define IOSTATE			struct sgttyb
***************
*** 63,68 ****
--- 84,90 ----
  #else	/* !USE_SGTTY */
  #   undef SUPPORTS_TTY
  #endif	/* !USE_SGTTY */
+ 
  #endif	/* !USE_TERMIO */
  #endif	/* !USE_TERMIOS */
  
***************
*** 220,225 ****
--- 242,249 ----
  			    Tcl_Interp *interp, CONST char *optionName,
  			    Tcl_DString *dsPtr));
  static FileState *	TtyInit _ANSI_ARGS_((int fd, int initialize));
+ static int		TtyOutputProc _ANSI_ARGS_((ClientData instanceData,
+ 			    CONST char *buf, int toWrite, int *errorCode));
  static int		TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp,
  			    CONST char *mode, int *speedPtr, int *parityPtr,
  			    int *dataPtr, int *stopPtr));
***************
*** 264,270 ****
      TCL_CHANNEL_VERSION_2,	/* v2 channel */
      TtyCloseProc,		/* Close proc. */
      FileInputProc,		/* Input proc. */
!     FileOutputProc,		/* Output proc. */
      NULL,			/* Seek proc. */
      TtySetOptionProc,		/* Set option proc. */
      TtyGetOptionProc,		/* Get option proc. */
--- 288,294 ----
      TCL_CHANNEL_VERSION_2,	/* v2 channel */
      TtyCloseProc,		/* Close proc. */
      FileInputProc,		/* Input proc. */
!     TtyOutputProc,		/* Output proc. */
      NULL,			/* Seek proc. */
      TtySetOptionProc,		/* Set option proc. */
      TtyGetOptionProc,		/* Get option proc. */
***************
*** 631,641 ****
   *	0 if successful, errno if failed.
   *
   * Side effects:
!  *	Restores the settings and closes the device of the channel.
   *
   *----------------------------------------------------------------------
   */
- 
  static int
  TtyCloseProc(instanceData, interp)
      ClientData instanceData;	/* Tty state. */
--- 655,664 ----
   *	0 if successful, errno if failed.
   *
   * Side effects:
!  *	Closes the device of the channel.
   *
   *----------------------------------------------------------------------
   */
  static int
  TtyCloseProc(instanceData, interp)
      ClientData instanceData;	/* Tty state. */
***************
*** 644,658 ****
--- 667,761 ----
      TtyState *ttyPtr;
  
      ttyPtr = (TtyState *) instanceData;
+ #ifdef TTYFLUSH
+     TTYFLUSH(ttyPtr->fs.fd);
+ #endif
+     /* Don't unsave TTY state, tcl may be used as a simpler 'stty' */
+ #if 0
      if (ttyPtr->stateUpdated) {
  	SETIOSTATE(ttyPtr->fs.fd, &ttyPtr->savedState);
      }
+ #endif
      return FileCloseProc(instanceData, interp);
  }
  
  /*
   *----------------------------------------------------------------------
   *
+  * TtyOutputProc--
+  *
+  *	This procedure is invoked from the generic IO level to write
+  *	output to a TTY channel.
+  *
+  * Results:
+  *	The number of bytes written is returned or -1 on error. An
+  *	output argument	contains a POSIX error code if an error occurred,
+  *	or zero.
+  *
+  * Side effects:
+  *	Writes output on the output device of the channel
+  *	if the channel is not designated to be closed.
+  *
+  *----------------------------------------------------------------------
+  */
+ 
+ static int
+ TtyOutputProc(instanceData, buf, toWrite, errorCodePtr)
+     ClientData instanceData;		/* File state. */
+     CONST char *buf;			/* The data buffer. */
+     int toWrite;			/* How many bytes to write? */
+     int *errorCodePtr;			/* Where to store error code. */
+ {
+     if ( TclInExit() ) {
+ 	/*
+ 	 * Do not write data during Tcl exit.
+ 	 * Serial port may block preventing Tcl from exit.
+ 	 */
+ 	return toWrite;
+     } else {
+ 	return FileOutputProc(instanceData, buf, toWrite, errorCodePtr);
+     }
+ }
+ 
+ #ifdef USE_TERMIOS
+ 
+ /*
+  *----------------------------------------------------------------------
+  *
+  * TtyModemStatusStr --
+  *
+  *  Converts a RS232 modem status list of readable flags
+  *
+  *----------------------------------------------------------------------
+  */
+ static void
+ TtyModemStatusStr(status, dsPtr)
+     int status;            /* RS232 modem status */
+     Tcl_DString *dsPtr;    /* Where to store string */
+ {
+ #ifdef TIOCM_CTS
+     Tcl_DStringAppendElement(dsPtr, "CTS");
+     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0" );
+ #endif
+ #ifdef TIOCM_DSR
+     Tcl_DStringAppendElement(dsPtr, "DSR");
+     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0" );
+ #endif
+ #ifdef TIOCM_RNG
+     Tcl_DStringAppendElement(dsPtr, "RING");
+     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0" );
+ #endif
+ #ifdef TIOCM_CD
+     Tcl_DStringAppendElement(dsPtr, "DCD");
+     Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0" );
+ #endif
+ }
+ 
+ #endif
+ 
+ /*
+  *----------------------------------------------------------------------
+  *
   * TtySetOptionProc --
   *
   *	Sets an option on a channel.
***************
*** 676,686 ****
      CONST char *value;		/* New value for option. */
  {
      FileState *fsPtr = (FileState *) instanceData;
!     unsigned int len;
      TtyAttrs tty;
  
      len = strlen(optionName);
!     if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
  	if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
  		&tty.stop) != TCL_OK) {
  	    return TCL_ERROR;
--- 779,799 ----
      CONST char *value;		/* New value for option. */
  {
      FileState *fsPtr = (FileState *) instanceData;
!     unsigned int len, vlen;
      TtyAttrs tty;
+ #ifdef USE_TERMIOS
+     int flag, control, argc;
+     CONST char **argv;
+     IOSTATE iostate;
+ #endif
  
      len = strlen(optionName);
!     vlen = strlen(value);
! 
!     /*
!      * Option -mode baud,parity,databits,stopbits
!      */
!     if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
  	if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
  		&tty.stop) != TCL_OK) {
  	    return TCL_ERROR;
***************
*** 692,700 ****
  	TtySetAttributes(fsPtr->fd, &tty);
  	((TtyState *) fsPtr)->stateUpdated = 1;
  	return TCL_OK;
      } else {
! 	return Tcl_BadChannelOption(interp, optionName, "mode");
      }
  }
  
  /*
--- 805,985 ----
  	TtySetAttributes(fsPtr->fd, &tty);
  	((TtyState *) fsPtr)->stateUpdated = 1;
  	return TCL_OK;
+     }
+ 
+ #ifdef USE_TERMIOS
+ 
+     /*
+      * Option -handshake none|xonxoff|rtscts|dtrdsr
+      */
+     if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
+ 	/*
+ 	 * Reset all handshake options
+ 	 * DTR and RTS are ON by default
+ 	 */
+ 	GETIOSTATE(fsPtr->fd, &iostate);
+ 	iostate.c_iflag &= ~(IXON | IXOFF | IXANY);
+ #ifdef CRTSCTS
+ 	iostate.c_cflag &= ~CRTSCTS;
+ #endif
+ 	if (strncasecmp(value, "NONE", vlen) == 0) {
+ 	    /* leave all handshake options disabled */
+ 	} else if (strncasecmp(value, "XONXOFF", vlen) == 0) {
+ 	    iostate.c_iflag |= (IXON | IXOFF | IXANY);
+ 	} else if (strncasecmp(value, "RTSCTS", vlen) == 0) {
+ #ifdef CRTSCTS
+ 	    iostate.c_cflag |= CRTSCTS;
+ #else
+ 	    if (interp) {
+ 		Tcl_AppendResult(interp, "-handshake RTSCTS ",
+ 		    "not supported for this platform",
+ 		     (char *) NULL);
+ 		}
+ 	    return TCL_ERROR;
+ #endif
+ 	} else if (strncasecmp(value, "DTRDSR", vlen) == 0) {
+ 	    if (interp) {
+ 		Tcl_AppendResult(interp, "-handshake DTRDSR ",
+ 		    "not supported for this platform",
+ 		     (char *) NULL);
+ 		}
+ 	    return TCL_ERROR;
  	} else {
! 	    if (interp) {
! 		Tcl_AppendResult(interp, "bad value for -handshake: ",
! 		    "must be one of xonxoff, rtscts, dtrdsr or none",
! 		    (char *) NULL);
! 		return TCL_ERROR;
! 	    }
! 	}
! 	SETIOSTATE(fsPtr->fd, &iostate);
! 	return TCL_OK;
!     }
! 
!     /*
!      * Option -xchar {\x11 \x13}
!      */
!     if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
! 
! 	GETIOSTATE(fsPtr->fd, &iostate);
! 	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
! 	    return TCL_ERROR;
! 	}
! 	if (argc == 2) {
! 	    iostate.c_cc[VSTART] = argv[0][0];
! 	    iostate.c_cc[VSTOP]  = argv[1][0];
! 	} else {
! 	    if (interp) {
! 		Tcl_AppendResult(interp,
! 		    "bad value for -xchar: should be a list of two elements",
! 		    (char *) NULL);
! 	    }
! 	    return TCL_ERROR;
  	}
+ 	SETIOSTATE(fsPtr->fd, &iostate);
+ 	return TCL_OK;
+     }
+ 
+     /*
+     * Option -timeout msec
+     */
+     if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
+ 	int msec;
+ 
+ 	GETIOSTATE(fsPtr->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;
+ 	SETIOSTATE(fsPtr->fd, &iostate);
+ 	return TCL_OK;
+     }
+ 
+     /*
+      * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
+      */
+     if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
+ 
+ 	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
+ 	    return TCL_ERROR;
+ 	}
+ 	if ((argc % 2) == 1) {
+ 	    if (interp) {
+ 		Tcl_AppendResult(interp,
+ 		    "bad value for -ttycontrol: should be a list of",
+ 		    "signal,value pairs", (char *) NULL);
+ 	    }
+ 	    return TCL_ERROR;
+ 	}
+ 
+ 	GETCONTROL(fsPtr->fd, &control);
+ 	while (argc > 1) {
+ 	    if (Tcl_GetBoolean(interp, argv[1], &flag) == TCL_ERROR) {
+ 		return TCL_ERROR;
+ 	    }
+ 	    if (strncasecmp(argv[0], "DTR", strlen(argv[0])) == 0) {
+ #ifdef TIOCM_DTR
+ 		if (flag ) {
+ 		    control |= TIOCM_DTR;
+ 		} else {
+ 		    control &= ~TIOCM_DTR;
+ 		}
+ #else
+ 		if (interp) {
+ 		    Tcl_AppendResult(interp, "-ttycontrol DTR ",
+ 			"not supported for this platform",
+ 			(char *) NULL);
+ 		}
+ 		return TCL_ERROR;
+ #endif
+ 	    } else if (strncasecmp(argv[0], "RTS", strlen(argv[0])) == 0) {
+ #ifdef TIOCM_RTS
+ 		if (flag ) {
+ 		    control |= TIOCM_RTS;
+ 		} else {
+ 		    control &= ~TIOCM_RTS;
+ 		}
+ #else
+ 		if (interp) {
+ 		    Tcl_AppendResult(interp, "-ttycontrol RTS ",
+ 			"not supported for this platform",
+ 			(char *) NULL);
+ 		}
+ 		return TCL_ERROR;
+ #endif
+ 	    } else if (strncasecmp(argv[0], "BREAK", strlen(argv[0])) == 0) {
+ #ifdef SETBREAK
+ 		SETBREAK(fsPtr->fd, flag);
+ #else
+ 		if (interp) {
+ 		    Tcl_AppendResult(interp, "-ttycontrol BREAK ",
+ 			"not supported for this platform",
+ 			(char *) NULL);
+ 		}
+ 		return TCL_ERROR;
+ #endif
+ 	    } else {
+ 		if (interp) {
+ 		    Tcl_AppendResult(interp,
+ 			"bad signal for -ttycontrol: must be ",
+ 			"DTR, RTS or BREAK", (char *) NULL);
+ 		}
+ 		return TCL_ERROR;
+ 	    }
+ 	    argc -= 2, argv += 2;
+ 	} /* while (argc > 1) */
+ 
+ 	SETCONTROL(fsPtr->fd, &control);
+ 	return TCL_OK;
+      }
+ 
+     return Tcl_BadChannelOption(interp, optionName,
+ 	"mode handshake timeout ttycontrol xchar ");
+ 
+ #else    /* not USE_TERMIOS */
+     return Tcl_BadChannelOption(interp, optionName, "mode");
+ #endif
  }
  
  /*
***************
*** 730,750 ****
      unsigned int len;
      char buf[3 * TCL_INTEGER_SPACE + 16];
      TtyAttrs tty;
  
      if (optionName == NULL) {
- 	Tcl_DStringAppendElement(dsPtr, "-mode");
  	len = 0;
      } else {
  	len = strlen(optionName);
      }
      if ((len == 0) || 
! 	    ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
  	TtyGetAttributes(fsPtr->fd, &tty);
  	sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
  	Tcl_DStringAppendElement(dsPtr, buf);
  	return TCL_OK;
      } else {
! 	return Tcl_BadChannelOption(interp, optionName, "mode");
      }
  }
  
--- 1015,1108 ----
      unsigned int len;
      char buf[3 * TCL_INTEGER_SPACE + 16];
      TtyAttrs tty;
+     int valid = 0;  /* flag if valid option parsed */
  
      if (optionName == NULL) {
  	len = 0;
      } else {
  	len = strlen(optionName);
      }
+     if (len == 0) {
+ 	Tcl_DStringAppendElement(dsPtr, "-mode");
+     }
      if ((len == 0) || 
! 	    ((len > 2) && (strncmp(optionName, "-mode", len) == 0))) {
! 	valid = 1;
  	TtyGetAttributes(fsPtr->fd, &tty);
  	sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
  	Tcl_DStringAppendElement(dsPtr, buf);
+     }
+ 
+ #ifdef USE_TERMIOS
+     /*
+      * get option -xchar
+      */
+     if (len == 0) {
+ 	Tcl_DStringAppendElement(dsPtr, "-xchar");
+ 	Tcl_DStringStartSublist(dsPtr);
+     }
+     if ((len == 0) ||
+ 	    ((len > 1) && (strncmp(optionName, "-xchar", len) == 0))) {
+ 
+ 	IOSTATE iostate;
+ 	valid = 1;
+ 
+ 	GETIOSTATE(fsPtr->fd, &iostate);
+ 	sprintf(buf, "%c", iostate.c_cc[VSTART]);
+ 	Tcl_DStringAppendElement(dsPtr, buf);
+ 	sprintf(buf, "%c", iostate.c_cc[VSTOP]);
+ 	Tcl_DStringAppendElement(dsPtr, buf);
+     }
+     if (len == 0) {
+ 	Tcl_DStringEndSublist(dsPtr);
+     }
+ 
+     /*
+      * get option -queue
+      * option is readonly and returned by [fconfigure chan -queue]
+      * but not returned by unnamed [fconfigure chan]
+      */
+     if ( (len > 1) && (strncmp(optionName, "-queue", len) == 0) ) {
+ 	int inQueue=0, outQueue=0;
+ 	int inBuffered, outBuffered;
+         valid = 1;
+ #ifdef GETREADQUEUE
+ 	GETREADQUEUE(fsPtr->fd, inQueue);
+ #endif
+ #ifdef GETWRITEQUEUE
+ 	GETWRITEQUEUE(fsPtr->fd, outQueue);
+ #endif
+ 	inBuffered  = Tcl_InputBuffered(fsPtr->channel);
+ 	outBuffered = Tcl_OutputBuffered(fsPtr->channel);
+ 
+ 	sprintf(buf, "%d", inBuffered+inQueue);
+ 	Tcl_DStringAppendElement(dsPtr, buf);
+ 	sprintf(buf, "%d", outBuffered+outQueue);
+ 	Tcl_DStringAppendElement(dsPtr, buf);
+     }
+ 
+     /*
+      * get option -ttystatus
+      * 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;
+ 	GETCONTROL(fsPtr->fd, &status);
+ 	TtyModemStatusStr(status, dsPtr);
+     }
+ #endif
+ 
+     if (valid) {
  	return TCL_OK;
      } else {
! 	return Tcl_BadChannelOption(interp, optionName,
! #ifdef USE_TERMIOS
! 	    "mode queue ttystatus xchar");
! #else
! 	    "mode");
! #endif
      }
  }
  
