Porting AT&T ioctl()'s to BSD???

Doug Gwyn gwyn at smoke.BRL.MIL
Tue Jan 3 04:42:28 AEST 1989


In article <7300002 at fthood> egray at 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;
	}



More information about the Comp.unix.questions mailing list