[comp.unix.questions] Porting AT&T ioctl

egray@fthood.UUCP (01/02/89)

Howdy.. I've got some problems porting AT&T code to BSD that someone
may have some experience with.

I need the ability to flush the TTY input queue, output queue, or both
similar to what AT&T does with ioctl(fd, TCFLSH, n).  I can't use
BSD's icotl(fd, TCIOFLUSH, 0) since it always flushed both queues.

Also, I'd like a way to emulate AT&T's ioctl(fd, TCSBRK, 1) that waits
for the output of the queue to drain.  I suspect BSD's select() could
be used?

Examples and/or code fragments would be much appreciated!

Emmet P. Gray				US Army, HQ III Corps & Fort Hood
...!uunet!uiucuxc!fthood!egray		Attn: AFZF-DE-ENV
					Directorate of Engineering & Housing
					Environmental Management Office
					Fort Hood, TX 76544-5057

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/03/89)

In article <7300002@fthood> egray@fthood.UUCP writes:
[Why isn't this an Internet address?  Army regulations require that
any computer system purchased with the intention of networking
support the DoD standard Internet protocols, including SMTP.]

>I need the ability to flush the TTY input queue, output queue, or both
>similar to what AT&T does with ioctl(fd, TCFLSH, n).  I can't use
>BSD's icotl(fd, TCIOFLUSH, 0) since it always flushed both queues.

>Also, I'd like a way to emulate AT&T's ioctl(fd, TCSBRK, 1) that waits
>for the output of the queue to drain.  I suspect BSD's select() could
>be used?

No, select() is useless for this.

I am attaching my System V ioctl() emulation for 4.3BSD.  It maps
System V ioctl() requests into calls to _ioctl(), which is the raw
4.3BSD system call interface for ioctl.  (The _ioctl() module has
to be added to your C library for this to work; also, there are
associated System V header files that are needed.  If you want to
try wholesale emulation like this, your best bet is to get a copy
of the BRL UNIX System V emulation for 4.3BSD from me.)  The code
shows the techniques I use to approximate the System V behavior on
4.3BSD; adapt them as you see fit.

/*
	ioctl -- system call emulation for 4.2BSD

	last edit:	01-Sep-1987	D A Gwyn

	Because there is not a 1-1 mapping between Bell and Berkeley
	terminal driver modes, some flag bits have a slightly "adjusted"
	meaning in an attempt to provide improved mapping reversibility.

	Special note:  sg_flags is an int, not a short, and it contains
	both the standard sgttyb flags and Berkeley's added local flags.

	Beware!  Setting NOFLSH in c_flag will set ALL the Berkeley
	local flags unless you have fixed this bug in the tty driver.
	(On 4.1cBSD, there is a similar problem with BSDLY.)
*/

#include	<errno.h>
#include	<sys/termio.h>
#include	<sys/ttold.h>

/*	4.2BSD magic input characters:	*/

struct tchars				/* data for TIOC[GS]ETC */
	{
	char	t_intrc;		/* SIGINT */
	char	t_quitc;		/* SIGQUIT */
	char	t_startc;		/* start output */
	char	t_stopc;		/* stop output */
	char	t_eofc; 		/* end-of-file */
	char	t_brkc; 		/* input delimiter */
	};

struct ltchars				/* data for TIOC[GS]LTC */
	{
	char	t_suspc;		/* SIGTSTP */
	char	t_dsuspc;		/* delayed SIGTSTP */
	char	t_rprntc;		/* reprint input */
	char	t_flushc;		/* flush output */
	char	t_werasc; 		/* word erase */
	char	t_lnextc; 		/* literal next */
	};

/*	4.2BSD _ioctl() requests:	*/

#define FIOCLEX 	_IO( 'f', 1 )
#define FIONCLEX	_IO( 'f', 2 )

#define	TIOCGETD	_IOR( 't', 0, int )
#define	TIOCSETD	_IOW( 't', 1, int )
#define TIOCHPCL	_IO( 't', 2 )
/* #define TIOCGETP	_IOR( 't', 8, _sgttyb )	/* defined in <sys/ttold.h> */
/* #define TIOCSETP	_IOW( 't', 9, _sgttyb )	/* defined in <sys/ttold.h> */
#define TIOCSETN	_IOW( 't', 10, _sgttyb )
#define TIOCFLUSH	_IOW( 't', 16, int )
#define TIOCSETC	_IOW( 't', 17, struct tchars )
#define TIOCGETC	_IOR( 't', 18, struct tchars )
#define TIOCSTART	_IO( 't', 110 )
#define TIOCSTOP	_IO( 't', 111 )
#define	TIOCOUTQ	_IOR( 't', 115, int )
#define	TIOCGLTC	_IOR( 't', 116, struct ltchars )
#define	TIOCSLTC	_IOW( 't', 117, struct ltchars )
#define TIOCCBRK	_IO( 't', 122 )
#define TIOCSBRK	_IO( 't', 123 )
#define TIOCLGET	_IOR( 't', 124, int )
#define TIOCLSET	_IOW( 't', 125, int )

/*	4.2BSD terminal handler line disciplines:	*/

#define	OTTYDISC	0		/* 7th Edition UNIX style */
#define	NTTYDISC	2		/* ditto, with extensions */

/*	differing 4.2BSD sg_flag bits:	*/

#define X_TANDEM	0x00000001	/* automatic flow control */
#define X_CBREAK	0x00000002	/* half-cooked mode */
#define X_TBDELAY	0x00000c00	/* tab delay code: */
#define X_XTABS 	0x00000c00	/* map tabs to spaces */

/*	added 4.2BSD sg_flag bits:	*/

#define X_CRTBS 	0x00010000	/* fancy BS erase */
#define X_PRTERA	0x00020000	/* \.../ erase */
#define X_CRTERA	0x00040000	/* BS-SP-BS erase */
#define X_TILDE 	0x00080000	/* Hazeltine kludge */
#define X_MDMBUF	0x00100000	/* DTR stall kludge */
#define X_LITOUT	0x00200000	/* literal output */
#define X_TOSTOP	0x00400000	/* SIGSTOP on bkgnd output */
#define X_FLUSHO	0x00800000	/* set by ^O */
#define X_NOHANG	0x01000000	/* no SIGHUP on hangup */
#define X_ETXACK	0x02000000	/* ETX->ACK protocol */
#define X_CRTKIL	0x04000000	/* BS-SP-BS kill */
#define X_PASS8		0x08000000	/* used to be X_INTRUP */
#define X_CTLECH	0x10000000	/* echo ctrl-X as "^X" */
#define X_PENDIN	0x20000000	/* reread raw queue */
#define X_DECCTQ	0x40000000	/* strict DC3/DC1 protocol */
#define X_NOFLSH	0x80000000	/* no output flush on signal */

/*	Kludge for accessing "local flags" part of sg_flags:	*/

#ifdef vax
typedef struct
	{
	short	low;			/* low half (standard flags) */
	short	high;			/* high half (local flags) */
	}	word;			/* map onto sg_flags */
#else	/* Accel, Alliant, Gould, Sun, etc. */
typedef struct
	{
	short	high;			/* high half (local flags) */
	short	low;			/* low half (standard flags) */
	}	word;			/* map onto sg_flags */
#endif

extern int	_ioctl(), _nap();

static void	new_tty(), unfudge();
static int	fudge(), get_sgttyb(), pack(), set_sgttyb(), unpack();


int
ioctl( fildes, request, arg )		/* returns 0 if ok, else -1 */
	int		fildes; 	/* file descriptor */
	int		request;	/* command */
	int		arg;		/* command arguments */
	{
	struct sgttyb	tb;		/* [gs]tty values */

	switch ( request )
		{
	case IOCTYPE:
		return TIOC;

	case TIOCGETP:
		new_tty( fildes );
		if ( get_sgttyb( fildes, (struct sgttyb *)arg ) < 0 )
			return -1;	/* errno already set */
		unfudge( (struct sgttyb *)arg );
		return 0;

	case TIOCSETP:
		new_tty( fildes );
		tb = *(struct sgttyb *)arg;	/* local copy */
		if ( fudge( fildes, &tb ) < 0
		  || set_sgttyb( fildes, &tb, 1 ) < 0
		   )
			return -1;	/* errno already set */
		return 0;

	case TCGETA:
		new_tty( fildes );
		if ( get_sgttyb( fildes, &tb ) < 0 )
			return -1;	/* errno already set */
		return unpack( fildes, &tb, (struct termio *)arg );

	case TCSETA:
		new_tty( fildes );
		if ( pack( fildes, (struct termio *)arg, &tb ) != 0 )
			return -1;	/* errno already set */
		return set_sgttyb( fildes, &tb, 0 );

	case TCSETAW:			/* sorry, best we can do */
	case TCSETAF:
		new_tty( fildes );
		if ( pack( fildes, (struct termio *)arg, &tb ) != 0 )
			return -1;	/* errno already set */
		return set_sgttyb( fildes, &tb, 1 );

	case TCSBRK:
		if ( _ioctl( fildes, TIOCGETP, (char *)&tb ) < 0 )
			return -1;	/* errno already set */
		if ( _ioctl( fildes, TIOCSETP, (char *)&tb ) < 0 )
			return -1;	/* errno already set */
		/* output is now drained; too bad input was flushed */

		if ( arg == 0 ) 	/* send break */
			{
			if ( _ioctl( fildes, TIOCSBRK, (char *)0 ) < 0 )
				return -1;	/* errno already set */
			(void)_nap( 250000L );	/* 0.25 second delay */
			return _ioctl( fildes, TIOCCBRK, (char *)0 );
			}
		else
			return 0;

	case TCXONC:
		return _ioctl( fildes, arg == 0 ? TIOCSTOP : TIOCSTART,
			       (char *)0
			     );

	case TCFLSH:
		if ( arg < 0 || arg > 2 )
			{
			errno = EINVAL;
			return -1;
			}
		else	{
			int	rw = arg + 1;	/* stupid call design */

			return _ioctl( fildes, TIOCFLUSH, (char *)&rw );
			}

	case LDOPEN:
	case LDCLOSE:
		new_tty();
		return 0;		/* no-op */

	case LDCHG:
		{
		struct termio	t;

		if ( ioctl( fildes, TCGETA, (int)&t ) != 0 )
			return -1;	/* errno already set */

		t.c_lflag = arg;

		return ioctl( fildes, TCSETA, (int)&t );
		}

	case LDGETT:
	case LDSETT:
		errno = EINVAL;
		return -1;

	default:
		return _ioctl( fildes, request, (char *)arg );
		}
	}


static void
new_tty( fildes )		/* make sure new tty handler is used */
	{
	static int	ldisc = OTTYDISC;	/* line discipline */

	if ( ldisc != NTTYDISC		/* first time this process */
	  && (_ioctl( fildes, TIOCGETD, &ldisc ) != 0	/* unknown */
	   || ldisc != NTTYDISC		/* known but not "new tty" */
	     )
	   )	{
		ldisc = NTTYDISC;	/* force new tty handler */
		(void)_ioctl( fildes, TIOCSETD, &ldisc );
		}
	}


/*	I used to take a really ugly efficiency shortcut in the next
	two routines, but the Gould byte order put a stop to that.   */

static int
get_sgttyb( fildes, tbp )		/* extended gtty */
	int			fildes; /* file descriptor */
	register struct sgttyb	*tbp;	/* -> where to put data */
	{
	int			lf;	/* local flags */
	_sgttyb			xb;	/* native data */

	if ( _ioctl( fildes, TIOCGETP, (char *)&xb ) < 0 )
		return -1;		/* errno already set */
	tbp->sg_ispeed = xb.sg_ispeed;
	tbp->sg_ospeed = xb.sg_ospeed;
	tbp->sg_erase = xb.sg_erase;
	tbp->sg_kill = xb.sg_kill;
	((word *)&tbp->sg_flags)->low = xb.sg_flags;

	if ( _ioctl( fildes, TIOCLGET, (char *)&lf ) < 0 )
		return -1;		/* errno already set */
	((word *)&tbp->sg_flags)->high = (short)lf;

	return 0;
	}


static int
set_sgttyb( fildes, tbp, wait ) 	/* extended stty */
	int			fildes; /* file descriptor */
	register struct sgttyb	*tbp;	/* -> data to be set */
	int			wait;	/* "wait for output to drain" */
	{
	int			lf;	/* local flags */
	_sgttyb			xb;	/* native data */

	xb.sg_ispeed = tbp->sg_ispeed;
	xb.sg_ospeed = tbp->sg_ospeed;
	xb.sg_erase = tbp->sg_erase;
	xb.sg_kill = tbp->sg_kill;
	xb.sg_flags = ((word *)&tbp->sg_flags)->low;
	if ( _ioctl( fildes, wait != 0 ? TIOCSETP : TIOCSETN,
		     (char *)&xb
		   ) < 0
	   )
		return -1;		/* errno already set */

	lf = (int)((word *)&tbp->sg_flags)->high;
	if ( _ioctl( fildes, TIOCLSET, (char *)&lf ) < 0 )
		return -1;		/* errno already set */

	return 0;
	}


static int
fudge( fildes, tbp )			/* map Sys V stty to 4.2BSD */
	int			fildes; /* file descriptor */
	register struct sgttyb	*tbp;	/* -> data about to be set */
	{
	if ( (tbp->sg_flags & O_XTABS) != 0 )
		tbp->sg_flags |= X_XTABS;
	if ( (tbp->sg_flags & O_HUPCL) != 0
	  && _ioctl( fildes, TIOCHPCL, (char *)0 ) < 0
	   )
		return -1;		/* errno already set */
	tbp->sg_flags &= ~(O_XTABS | O_HUPCL);
	return 0;
	}


static void
unfudge( tbp )				/* map 4.2BSD gtty to Sys V */
	register struct sgttyb	*tbp;	/* -> data just gotten */
	{
	if ( (tbp->sg_flags & X_CBREAK) != 0 )
		tbp->sg_flags |= O_RAW; /* approximation */
	tbp->sg_flags &= ~(X_CBREAK | X_TANDEM);
	if ( (tbp->sg_flags & X_TBDELAY) == X_XTABS )
		{
		tbp->sg_flags &= ~X_TBDELAY;
		tbp->sg_flags |= O_XTABS;
		}
	else if ( (tbp->sg_flags & X_TBDELAY) != 0 )
		{
		tbp->sg_flags |= O_TBDELAY;
		tbp->sg_flags &= ~O_NOAL;
		}
	}


static int
pack( fildes, argp, tbp )		/* map termio to 4.2BSD stty */
	int			fildes; /* file descriptor */
	register struct termio	*argp;	/* -> desired state info */
	register struct sgttyb	*tbp;	/* -> stty buffer */
	{
	register int		flag;	/* holds sg_flags */
	struct tchars		tc;	/* 4.2BSD magic characters */
	struct ltchars		ltc;	/* more 4.2BSD magic chars */

	if ( (argp->c_lflag & ICANON) != 0 )	/* no MIN, TIME */
		{
		if ( (tc.t_eofc = argp->c_cc[VEOF]) == CNUL )
			tc.t_eofc = (char)-1;
		if ( (tc.t_brkc = argp->c_cc[VEOL]) == CNUL )
			tc.t_brkc = (char)-1;
		}
	else if ( _ioctl( fildes, TIOCGETC, (char *)&tc ) < 0 )
		return -1;		/* errno already set */
	if ( (argp->c_lflag & (ICANON | ISIG)) == ICANON )
		tc.t_intrc = tc.t_quitc = (char)-1;	/* disable */
	else	{
		if ( (tc.t_intrc = argp->c_cc[VINTR]) == CNUL )
			tc.t_intrc = (char)-1;
		if ( (tc.t_quitc = argp->c_cc[VQUIT]) == CNUL )
			tc.t_quitc = (char)-1;
		}
	if ( (argp->c_iflag & IXON) == 0 )
		tc.t_startc = tc.t_stopc = (char)-1;	/* disable */
	else	{
		tc.t_startc = CSTART;
		tc.t_stopc = CSTOP;
		}
	if ( _ioctl( fildes, TIOCSETC, (char *)&tc ) < 0 )
		return -1;		/* errno already set */

	if ( _ioctl( fildes, TIOCGLTC, (char *)&ltc ) == 0 )
		{			/* new tty handler */		
		if ( (ltc.t_suspc = argp->c_cc[VSWTCH]) == CNSWTCH )
			ltc.t_suspc = (char)-1;
		if ( _ioctl( fildes, TIOCSLTC, (char *)&ltc ) < 0 )
			return -1;	/* errno already set */
		}

	if ( (argp->c_cflag & HUPCL) != 0
	  && _ioctl( fildes, TIOCHPCL, (char *)0 ) < 0
	   )
		return -1;		/* errno already set */

	tbp->sg_erase = argp->c_cc[VERASE];
	tbp->sg_kill = argp->c_cc[VKILL];
	tbp->sg_ispeed = tbp->sg_ospeed = argp->c_cflag & CBAUD;

	flag = X_CTLECH;		/* everybody gets this */

	if ( (argp->c_lflag & ICANON) == 0 )
		if ( (argp->c_lflag & ISIG) == 0 )
			flag |= O_RAW;
		else
			flag |= X_CBREAK;	/* added by Gould */
	if ( (argp->c_lflag & XCASE) != 0 )
		flag |= O_LCASE;
	if ( (argp->c_lflag & ECHO) != 0 )
		flag |= O_ECHO;
	if ( (argp->c_lflag & ECHOE) != 0 )
		{
		flag |= X_CRTBS;
		if ( tbp->sg_ospeed >= B1200 )
			flag |= X_CRTERA | X_CRTKIL;
		}
	else
		flag |= X_PRTERA;
	if ( (argp->c_lflag & NOFLSH) != 0 )
		flag |= X_NOFLSH;
	if ( (argp->c_cflag & PARODD) != 0 )
		flag |= O_ODDP;
	else if ( (argp->c_iflag & INPCK) != 0 )
		flag |= O_EVENP;
	else
		flag |= O_ODDP | O_EVENP;
	if ( (argp->c_cflag & CLOCAL) != 0 )
		flag |= X_MDMBUF | X_NOHANG;
	/* The following is done even if OPOST is off, to keep track: */
	if ( (argp->c_oflag & ONLCR) != 0 )
		{
		flag |= O_CRMOD;
		if ( (argp->c_oflag & CRDLY) == CR1 )
			flag |= O_NL1;	/* sorry `bout that */
		else if ( (argp->c_oflag & CRDLY) == CR2 )
			flag |= O_CR1;	/* approximation */
		else if ( (argp->c_oflag & CRDLY) != 0 )
			flag |= O_CR2;	/* approximation to CR3 */
		}
	else if ( (argp->c_oflag & ONLRET) != 0 )
		{
		if ( (argp->c_oflag & CR2) != 0 )	/* CR2 or CR3 */
			flag |= O_NL2;
		else if ( (argp->c_oflag & CR1) != 0 )	/* CR1 */
			flag |= O_NL1;
		}
	else
		if ( (argp->c_oflag & NLDLY) != 0 )
			flag |= O_NL2;
	flag |= (long)((argp->c_oflag & TABDLY) >> 1);
	if ( (argp->c_oflag & (VTDLY | FFDLY)) != 0 )
		flag |= O_VTDELAY;
	if ( (argp->c_oflag & BSDLY) != 0 )
		flag |= O_BSDELAY;
	if ( (argp->c_iflag & (IXON | IXANY)) == IXON )
		flag |= X_DECCTQ;
	if ( (argp->c_iflag & IXOFF) != 0 )
		flag |= X_TANDEM;

	tbp->sg_flags = flag;

	/* argp->c_line ignored; must not change line discipline! */

	return 0;
	}


static int
unpack( fildes, tbp, argp )		/* map 4.2BSD gtty to termio */
	int			fildes; /* file descriptor */
	struct sgttyb		*tbp;	/* -> gtty buffer */
	register struct termio	*argp;	/* where to put unpacking */
	{
	struct tchars		tc;	/* 4.2BSD magic characters */
	struct ltchars		ltc;	/* more 4.2BSD magic chars */
	register int		flag = tbp->sg_flags;	/* for speed */

	if ( _ioctl( fildes, TIOCGETC, (char *)&tc ) < 0 )
		return -1;		/* errno already set */
	argp->c_cc[VERASE] = tbp->sg_erase;
	argp->c_cc[VKILL] = tbp->sg_kill;
	if ( tc.t_intrc == (char)-1 && tc.t_quitc == (char)-1 )
		{			/* assume defaults */
		argp->c_cc[VINTR] = CINTR;
		argp->c_cc[VQUIT] = CQUIT;
		argp->c_lflag = 0;	/* no ISIG */
		}
	else	{
		if ( (argp->c_cc[VINTR] = tc.t_intrc) == (unsigned char)-1 )
			argp->c_cc[VINTR] = CNUL;
		if ( (argp->c_cc[VQUIT] = tc.t_quitc) == (unsigned char)-1 )
			argp->c_cc[VQUIT] = CNUL;
		argp->c_lflag = ISIG;
		}
	if ( tc.t_startc == (char)-1 && tc.t_stopc == (char)-1 )
		argp->c_iflag = 0;	/* no IXON */
	else
		argp->c_iflag = (flag & X_DECCTQ) != 0 ? IXON
						       : IXON | IXANY;
	if ( (flag & (O_RAW | X_CBREAK)) != 0 )
		{			/* no MIN, TIME (inspired by Sun) */
		argp->c_cc[VEOF] = 1;	/* fake MIN */
		argp->c_cc[VEOL] = 0;	/* fake TIME */
		}
	else	{
		if ( (argp->c_cc[VEOF] = tc.t_eofc) == (unsigned char)-1 )
			argp->c_cc[VEOF] = CNUL;
		if ( (argp->c_cc[VEOL] = tc.t_brkc) == (unsigned char)-1 )
			argp->c_cc[VEOL] = CNUL;
		}
	argp->c_cc[VEOL2] = CNUL;
	if ( _ioctl( fildes, TIOCGLTC, (char *)&ltc ) < 0
					/* old tty handler */
	  || (argp->c_cc[VSWTCH] = ltc.t_suspc) == (unsigned char)-1
	   )
		argp->c_cc[VSWTCH] = CNSWTCH;

	argp->c_oflag = (unsigned short)(flag & X_TBDELAY) << 1;

	if ( (argp->c_cflag = tbp->sg_ispeed & CBAUD | CREAD)
	      == (B110 | CREAD)
	   )
		argp->c_cflag |= CSTOPB;

	if ( (flag & (X_MDMBUF | X_NOHANG)) != 0 )
		argp->c_cflag |= CLOCAL;
	if ( (flag & O_LCASE) != 0 )
		{
		argp->c_iflag |= IUCLC;
		argp->c_oflag |= OLCUC;
		argp->c_lflag |= XCASE;
		}
	if ( (flag & O_ECHO) != 0 )
		argp->c_lflag |= ECHO;
	if ( (flag & X_NOFLSH) != 0 )
		argp->c_lflag |= NOFLSH;
	else
		argp->c_lflag |= ECHOK;
	if ( (flag & O_CRMOD) != 0 )
		{
		argp->c_iflag |= ICRNL;
		argp->c_oflag |= ONLCR;
		if ( (flag & O_NL2) != 0 )	/* O_NL2 or O_NL3 */
			argp->c_oflag |= NL1;
		else if ( (flag & O_NL1) != 0 ) /* O_NL1 */
			argp->c_oflag |= CR1;
		else if ( (flag & O_CR2) != 0 ) /* O_CR2 or O_CR3 */
			argp->c_oflag |= ONOCR | CR3;	/* approx. */
		else if ( (flag & O_CR1) != 0 ) /* O_CR1 */
			argp->c_oflag |= ONOCR | CR2;	/* approx. */
		}
	else	{
		argp->c_oflag |= ONLRET;
		if ( (flag & O_NL1) != 0 )
			argp->c_oflag |= CR1;
		if ( (flag & O_NL2) != 0 )
			argp->c_oflag |= CR2;
		}
	if ( (flag & O_VTDELAY) != 0 )
		argp->c_oflag |= FF1 | VT1;
	if ( (flag & O_BSDELAY) != 0 )
		argp->c_oflag |= BS1;
	if ( (flag & O_RAW) != 0 )
		{
		argp->c_cflag |= CS8;
		argp->c_iflag &= ~(ICRNL | IUCLC);
		argp->c_lflag &= ~ISIG;
		}
	else	{
		argp->c_cflag |= CS7 | PARENB;
		argp->c_iflag |= BRKINT | IGNPAR | INPCK | ISTRIP;
		argp->c_oflag |= OPOST;
		if ( (flag & X_CBREAK) == 0 )	/* added by Gould */
			argp->c_lflag |= ICANON;
		}
	if ( (flag & O_ODDP) != 0 )
		if ( (flag & O_EVENP) != 0 )
			argp->c_iflag &= ~INPCK;
		else
			argp->c_cflag |= PARODD;
	if ( (flag & X_CRTBS) != 0 )
		argp->c_lflag |= ECHOE;
	if ( (flag & X_TANDEM) != 0 )
		argp->c_iflag |= IXOFF;

	argp->c_line = 0;		/* default line discipline */

	return 0;
	}

chris@mimsy.UUCP (Chris Torek) (01/03/89)

In article <7300002@fthood> egray@fthood.UUCP writes:
>I need the ability to flush the TTY input queue, output queue, or both
>similar to what AT&T does with ioctl(fd, TCFLSH, n).  I can't use
>BSD's icotl(fd, TCIOFLUSH, 0) since it always flushed both queues.

TIOCFLUSH actually does either or both, although I am unsure whether
it is documented properly:

	#include <sys/types.h>
	#include <sys/file.h>
	#include <sys/ioctl.h>

	foo() {
		int r = FREAD, w = FWRITE, rw = FREAD|FWRITE;

		... ioctl(fd, TIOCFLUSH, (caddr_t)&r) ...  /* flush input */
		... ioctl(fd, TIOCFLUSH, (caddr_t)&w) ...  /* flush output */
		... ioctl(fd, TIOCFLUSH, (caddr_t)&rw) ... /* flush both */

In fact,

	ioctl(fd, TIOCFLUSH, (caddr_t)0)

(with or without the cast to caddr_t or `char *') is illegal, and
merely `happens to work' on a Vax running stock 4BSD.  You should
always pass the address of an `int' that is set to some combination
of FREAD and FWRITE, or to 0 (which means the same as FREAD|FWRITE).

>Also, I'd like a way to emulate AT&T's ioctl(fd, TCSBRK, 1) that waits
>for the output of the queue to drain.  I suspect BSD's select() could
>be used?

Not directly: select tells you when an I/O operation would not block
(which is why select-for-read is true at EOF on pipes, for instance).

The easiest thing to do is to wait for the POSIX tty driver in 4.4BSD
(or whatever the next BSD will be called).  If you really need to wait
for the tty output queue to drain (which typically has no effect anyway
if, e.g., one is logged in across a network), TIOCSETP does this,
although it then also flushes pending input and sets the terminal
interpretation modes; or, you can use TIOCOUTQ and select() timeouts
together.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris