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 *)<c ) == 0 ) { /* new tty handler */ if ( (ltc.t_suspc = argp->c_cc[VSWTCH]) == CNSWTCH ) ltc.t_suspc = (char)-1; if ( _ioctl( fildes, TIOCSLTC, (char *)<c ) < 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 *)<c ) < 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