Path: utzoo!attcan!utgpu!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!ukma!rutgers!cmcl2!adm!smoke!gwyn From: gwyn@smoke.BRL.MIL (Doug Gwyn ) Newsgroups: comp.unix.questions Subject: Re: Porting AT&T ioctl()'s to BSD??? Message-ID: <9265@smoke.BRL.MIL> Date: 2 Jan 89 18:42:28 GMT References: <7300002@fthood> Reply-To: Gwyn@BRL.MIL (Douglas A. Gwyn) Organization: Ballistic Research Lab (BRL), APG, MD. Lines: 605 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 #include #include /* 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 */ /* #define TIOCSETP _IOW( 't', 9, _sgttyb ) /* defined in */ #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; }