BBN-V6/dmr/xtty.c

Compare this file to the similar file:
Show the results in this format:

#
/*
 * general TTY subroutines
 * Added ttycap (general tty capac routine) 9/28/78 Dan Franklin
 * Added XON/XOFF handling - 11/20/78 Alan Nemeth
 * Modified for extended tty driver -- 4/6/79 and on Dan Franklin
 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/user.h"
#include "../h/xty.h"
#include "../h/proc.h"
#include "../h/inode.h"
#include "../h/file.h"
#include "../h/reg.h"
#include "../h/conf.h"

/*
 * The actual structure of a clist block manipulated by
 * getc and putc (mch.s)
 */
struct cblock {
	struct cblock *c_next;
	char info[6];
};

/* The character lists-- space for 6*NCLIST characters */
struct cblock cfree[NCLIST];
/* List head for unused character blocks. */
struct cblock *cfreelist;


/* -------------------------- C I N I T ----------------------------- */
/*
 * Initialize clist by freeing all character blocks, then count
 * number of character devices. (Once-only routine)
 */
cinit()
{
	register int ccp;
	register struct cblock *cp;
	register struct cdevsw *cdp;

	ccp = cfree;
	for (cp=(ccp+07)&~07; cp <= &cfree[NCLIST-1]; cp++) {
		cp->c_next = cfreelist;
		cfreelist = cp;
	}
	ccp = 0;
	for(cdp = cdevsw; cdp->d_open; cdp++)
		ccp++;
	nchrdev = ccp;
}

/* -------------------------- T T Y O P E N ------------------------- */
/*
 * routine called on first teletype open.
 * establishes a process group for distribution
 * of quits and interrupts from the tty.
 *
 * Modified 12/29/77 to initialize the t_itp pointer (for await) (BBN: jfh)
 *
 */
ttyopen(dev, atp)
struct tty *atp;
{
	register struct proc *pp;
	register struct tty *tp;
	extern int pgrpct;

	pp = u.u_procp;
	tp = atp;
	if(pp->p_pgrp == 0) {
		pp->p_pgrp = ++pgrpct;
		u.u_ttyp = tp;
		u.u_ttyd = dev;
		tp->t_pgrp = pgrpct;
	}
	tp->t_itp = 0;		/* initialize to no awaiters (BBN: jfh) */
	tp->t_state =& ~TS_WOPEN;
	tp->t_state =| TS_ISOPEN;
}

/* -------------------------- M O D T T Y ------------------------------- */
/*
 * The routine implementing the modtty system call.
 * first arg is type: lsb indicates set (1) or get (0).
 * Remaining bits are interpreted by the device driver routine.
 * Second arg is pointer to user's buffer; third arg is length.
 */
modtty()
{
        struct modes v;     /* Actually either modes or cursor structure */
        register char *up, *vp;
        register int ulen;
        int flag;

        vp = &v;
        flag = u.u_arg[0] & 01? 1 : 0;
        up = u.u_arg[1];
        ulen = u.u_arg[2];
        if (ulen > sizeof(v))
            ulen = sizeof(v);

        if (flag)
            while (ulen-- > 0)
                *vp++ = fubyte(up++);

        sgtty(u.u_arg[0], &v);

        if (u.u_error)
                return;
        if (flag == 0)
            while (ulen-- > 0)
                subyte(up++, *vp++);
}

/* -------------------------- S G T T Y ----------------------------- */
/*
 * Stuff common to stty and gtty.
 * Check legality and switch out to individual device routine.
 * The file descriptor is taken from u.u_ar0[R0].
 *
 * Device routines are called with three arguments:
 *      the major/minor device,
 *      the value array,
 *      and the flag.
 */
sgtty(flag, v)
int flag;
char *v;
{
        register struct file *fp;
        register struct inode *ip;

        if ((fp = getf(u.u_ar0[R0])) == NULL)
                return;
        ip = fp->f_inode;
        if ((ip->i_mode&IFMT) != IFCHR) {
                u.u_error = ENOTTY;
                return;
        }
        (*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v, flag);
}

/* -------------------------- T T Y S T T Y ------------------------- */
/*
 * ttystty -- device driver utility function
 * Called from device's sgtty routine with the address of the
 * device's tty structure.
 * If flag's lsb is zero, gtty is being done and the information is
 * passed back in v;
 * if it is one, stty is being done and v contains the input information.
 *
 * The value returned is 0 if stty was done, nonzero otherwise.
 */
ttystty(tp, av, flag)
struct tty *tp;
char *av;
int flag;
{
    register char *p;
    register char *v;
    register struct modes *modes_p;

    v = av;
    if (flag & ~01)
    {
        u.u_error = EINVAL;
        return;
    }

    modes_p = &tp->t_modes; /* Get base */
    p = modes_p;            /* Assign to p */
    modes_p++;              /* Get top */

    if ((flag & 01) == 0)
    {
        while (p < modes_p)
            *v++ = *p++;
        return(1);
    }
    else
    {
        wflushtty(tp);
        while (p < modes_p)
            *p++ = *v++;
        return(0);
    }
}

/* -------------------------- W F L U S H T T Y --------------------- */
/*
 * Wait for output to drain, then flush input waiting.
 */
wflushtty(atp)
struct tty *atp;
{
	register struct tty *tp;

	tp = atp;
	spl5();
	tp->t_state =& ~TSO_XOFFHNG;        /* agn - XON/XOFF */
	ttstart(tp);                    /* agn - XON/XOFF */
	while (tp->t_outq.c_cc) {
		tp->t_state =| TS_ASLEEP;
		sleep(&tp->t_outq, TTOPRI);
	}
	flushtty(tp);
	spl0();
}

/* -------------------------- F L U S H T T Y ----------------------- */
/*
 * flush all TTY queues
 */
flushtty(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int sps;

	tp = atp;
        while (getc(&tp->t_inq) >= 0);
	while (getc(&tp->t_outq) >= 0);
	wakeup(&tp->t_inq);
	wakeup(&tp->t_outq);
	sps = PS->integ;
	spl5();
	while (getc(&tp->t_inq) >= 0);
	tp->t_delct = 0;
	PS->integ = sps;
}

/* -------------------------- T T R E A D --------------------------- */
/*
 * ttread -- move chars from input queue to user's buffer
 * Called from device's read routine after it has
 * calculated the tty-structure pointer.
 *
 * Sleep until a break char entered (delct nonzero).
 * Read chars and copy to user until T_DELIM hit or passc returns negative.
 *      Handle mapping of T_USR_DELIM back to T_DELIM
 *      If deferred echo, echo here according to t_modes.t_iflags
 * Return count of chars copied.
 */
ttread(atp)
struct tty *atp;
{
    register struct tty *tp;
    int cnt;
    register int c, defecho;
    int lastc;

    tp = atp;
    if ((tp->t_state & TS_CARR_ON) == 0)
        return(0);

    spl5();
    while (tp->t_delct == 0)
    {
        if ((tp->t_state & TS_CARR_ON) == 0)
        {
            spl0();
            return(0);
        }
        sleep(&tp->t_inq, TTIPRI);
    }
    spl0();

    if (tp->t_modes.t_iflags & TI_DEFER_ECHO)
        defecho = tp->t_modes.t_iflags & (TI_BRK_ECHO|TI_NONBRK_ECHO);
    else
        defecho = 0;

    cnt = 0;
    lastc = -1;
    for (;;)
    {
        c = getc(&tp->t_inq);
        if (c == T_DELIM)
        {
            if (defecho & TI_BRK_ECHO)
                ttyoutput(lastc, tp);
            tp->t_delct--;
            break;
        }
        else
        {
            if (defecho & TI_NONBRK_ECHO)
                if (lastc != -1)
                    ttyoutput(lastc, tp);
            lastc = c;
            if (c == T_USR_DELIM)
                c = T_DELIM;
            if (passc(c) >= 0)
                cnt++;
            else
                break;
        }
    }

    if (tp->t_state & TSI_XOFFHNG)
        if (tp->t_inq.c_cc <= TTYWARN)
            {
            ttyoutput(CXON, tp);
            tp->t_state =& ~TSI_XOFFHNG;
            }

    return(cnt);
}

/* -------------------------- M A P T A B   A R R A Y --------------- */
/*
 * Input mapping table-- if an entry is non-zero, when the
 * corresponding character is typed preceded by "\" the escape
 * sequence is replaced by the table value.  Used for
 * upper-case only terminals.
 */
char	maptab[]
{
        000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
        000,'|',000,000,000,000,000,'`',
	'{','}',000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
        000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,000,000,
	000,000,000,000,000,000,'~',000,
	000,'A','B','C','D','E','F','G',
	'H','I','J','K','L','M','N','O',
	'P','Q','R','S','T','U','V','W',
	'X','Y','Z',000,000,000,000,000,
};

/* -------------------------- C L A S S   A R R A Y ----------------- */

int class[128]
{
TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,
/* BS */  /* HT */  /* LF */  /* VT */  /* FF */  /* CR */
TB_FORMAT,TB_FORMAT,TB_LF,    TB_FORMAT,TB_FORMAT,TB_CR,    TB_CTRL,  TB_CTRL,
TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,
TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_ESC,   TB_CTRL,  TB_CTRL,  TB_CTRL,  TB_CTRL,

TB_SPACE, TB_TERM,  TB_OTHER, TB_OTHER, TB_OTHER, TB_OTHER, TB_OTHER, TB_OTHER,
TB_BAL,   TB_BAL,   TB_OTHER, TB_OTHER, TB_TERM,  TB_OTHER, TB_TERM,  TB_OTHER,

TB_DIGIT, TB_DIGIT, TB_DIGIT, TB_DIGIT, TB_DIGIT, TB_DIGIT, TB_DIGIT, TB_DIGIT,
TB_DIGIT, TB_DIGIT, TB_TERM,  TB_TERM,  TB_BAL,   TB_OTHER, TB_BAL,   TB_TERM,

TB_OTHER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER,
TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER,
TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER, TB_UPPER,
TB_UPPER, TB_UPPER, TB_UPPER, TB_BAL,   TB_OTHER, TB_BAL,   TB_OTHER, TB_UNDER,

TB_OTHER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER,
TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER,
TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER, TB_LOWER,
TB_LOWER, TB_LOWER, TB_LOWER, TB_BAL,   TB_OTHER, TB_BAL,   TB_OTHER, TB_DEL
};

/* -------------------------- T T Y I N P U T ----------------------- */
/*
 * Handle one terminal input character.
 * XON/XOFF, special chars, and immediate echoing are all handled here.
 * The top half is awakened as required.
 * The arguments are the character and the appropriate
 * tty structure.
 *
 * TI_LOCAL_ECHO is not handled yet, although TI_TAB_ECHO and TI_CR_ECHO are.
 */
ttyinput (ac, atp)
int ac;
struct tty *atp;
{
    register int t_flags, c;
    register struct tty *tp;
    int ch;

    tp = atp;
    c = ac;
    t_flags = tp->t_modes.t_iflags;

    if (t_flags & TI_CLR_MSB)
        c =& 0177;

    if (tp->t_modes.t_oflags & TO_XONXOFF)
    {
        if (c == CXOFF)
        {
            tp->t_state =| TSO_XOFFHNG;
            return;
        }
        if (c == CXON)
        {
            tp->t_state =& ~TSO_XOFFHNG;
            ttstart(tp);
            return;
        }
    }

    if (c == '\r' && (t_flags & TI_CRMOD))
        c = '\n';
/*
 * Special character processing
 * Note that if t_modes.t_intr, t_modes.t_quit, etc. is set to a value >= 0200, its test will
 * always fail. Thus, setting them to such values turns off their function.
 */
    if (c == tp->t_modes.t_intr)
    {
        signal(tp->t_pgrp, SIGINT);
        flushtty(tp);
    }
    else if (c == tp->t_modes.t_quit)
    {
        signal(tp->t_pgrp, SIGQIT);
        flushtty(tp);
    }
    else if (c == tp->t_modes.t_erase)
    {
        ch = unputc(&tp->t_inq);
        if (ch == tp->t_modes.t_esc)
            goto NORMAL;
        if (ch == T_DELIM)
            putc(ch, &tp->t_inq);
        else if (ch != -1)    /* Avoid case where unputc returned emptyness */
            tterase(ch, tp);
    }
    else if (c == tp->t_modes.t_kill)
    {
        ch = unputc(&tp->t_inq);
        if (ch == tp->t_modes.t_esc)
            goto NORMAL;
        while (ch != -1 && ch != T_DELIM)
            ch = unputc(&tp->t_inq);
        if (ch != -1)
            putc(ch, &tp->t_inq);
        ttkill(tp);
    }
    else if (c == tp->t_modes.t_replay)
        ttreplay(tp);
    else
    {
NORMAL:
        if (tp->t_inq.c_cc >= TTYHOG)
        {
            ttyoutput('\7', tp);    /* Bell */
            return;
        }

        if ((t_flags & TI_XONXOFF) == TI_XONXOFF)
            if (tp->t_inq.c_cc >= TTYHOG - TTYWARN)
            {
                if (!(tp->t_state & TSI_XOFFHNG))
                {
                    ttyoutput(CXOFF, tp);
                    tp->t_state =| TSI_XOFFHNG;
                }
            }

        if (t_flags & TI_ONECASE)
        {
            if (c >= 'A' && c <= 'Z')   /* Lowercase it */
                c =+ 'a' - 'A';

            ch = unputc(&tp->t_inq);
            if (ch == tp->t_modes.t_esc && !(c & ~0177) && maptab[c])
                c = maptab[c];
            else if (ch != -1)
                putc(ch, &tp->t_inq);
        }

        if (c != tp->t_modes.t_eot)
        {
            if (c == T_DELIM)
                putc(T_USR_DELIM, &tp->t_inq);
            else
                putc(c, &tp->t_inq);
        }

        if (c == tp->t_modes.t_eot || tp->t_modes.t_breaks
            & (c&~0177? TB_NON_ASCII : class[c]))
        {
            wakeup(&tp->t_inq);
            if (tp->t_itp != 0)
                awake(tp->t_itp, 0);
            if (putc(T_DELIM, &tp->t_inq) == 0)
                tp->t_delct++;
            ch = t_flags & TI_BRK_ECHO;
        }
        else
            ch = t_flags & TI_NONBRK_ECHO;

        if (!(t_flags & TI_DEFER_ECHO)
        && (ch || (c == '\t' && t_flags & TI_TAB_ECHO)
               || (c == '\n' && t_flags & TI_CR_ECHO)))
        {
            ttyoutput (c, tp);
            ttstart (tp);
        }
    }
}

/* -------------------------- T T E R A S E ------------------------- */
/*
 * tterase(c, tp) erases c from the user's terminal. If the user is
 * on a printing terminal, it just prints <backslash><char>. If the
 * user is on a CRT, it erases the character itself.
 */
tterase(c, atp)
int c;
struct tty *atp;
{
    register struct tty *tp;
    register int len;
    register int flags;
    int ctype;

    tp = atp;
    flags = tp->t_modes.t_iflags;

/* Was the character echoed? */

    if (flags & TI_DEFER_ECHO)
        return;
    ctype = (c & ~0177)? TB_NON_ASCII : class[c];
    if ((ctype & tp->t_modes.t_breaks) != 0)
    {
        if ((flags & TI_BRK_ECHO) == 0)
            return;
    }
    else
        if ((flags & TI_NONBRK_ECHO) == 0)
            return;

/* Apparently -- undo it */

    if ((flags & TI_CRT) == 0)
    {
        ttyoutput('\\', tp);
        ttyoutput(c, tp);
        return;
    }

    if (c & ~0177)
        len = 0;
    else switch(partab[c] & P_CTYPE)
    {
    case P_CTRL:
    case P_VTFF:
    case P_LF:
    case P_CR:
        len = 0;
        break;

    case P_PRINTING:
        if ((ctype == TB_UPPER) && (flags & TI_ONECASE))
            len = 2;
        else
            len = 1;
        break;

    case P_BS:
    case P_HT:
        ttreplay(tp);
        return;
    }

    if ((c == tp->t_modes.t_erase) || (c == tp->t_modes.t_kill))
        len++;
    while(len--)
    {
        ttyoutput('\b', tp);
        ttyoutput(' ', tp);
        ttyoutput('\b', tp);
    }
}

/* -------------------------- T T K I L L --------------------------- */
ttkill(atp)
struct tty *atp;
{
    register struct tty *tp;
    register col;

    tp = atp;
    if ((tp->t_modes.t_iflags & TI_CRT) == 0)
        ttyoutput('\n', tp);
    else
        ttyoutput('\n', tp);
}
/* -------------------------- T T R E P L A Y ----------------------- */
ttreplay()
{
}

/* -------------------------- T T W R I T E ------------------------- */
/*
 * Called from the device's write routine after it has
 * calculated the tty-structure given as argument.
 */
ttwrite(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int c;
	register int count;

	count = 0;
	tp = atp;
	if ((tp->t_state&TS_CARR_ON)==0)
		return;
	while ((c=cpass())>=0) {
		spl5();
		count++;
		while (tp->t_outq.c_cc > TTHIWAT) {
			ttstart(tp);
			tp->t_state =| TS_ASLEEP;
			sleep(&tp->t_outq, TTOPRI);
		}
		spl0();
		ttyoutput(c, tp);
	}
	ttstart(tp);
	dpadd(u.u_tio,count);   /* rand:greep - tty accounting */
}

/* -------------------------- T T Y O U T P U T --------------------- */
/*
 * put character on TTY output queue, adding delays,
 * expanding tabs, and handling the CR/NL bit.
 * It is called both from the top half for output, and from
 * interrupt level for echoing.
 * The arguments are the character and the tty structure.
 */
ttyoutput(ac, tp)
struct tty *tp;
{
	register int c;
	register struct tty *rtp;
	register char *colp;
	int ctype;

	rtp = tp;
	c = ac&0177;
	/*
	 * Turn tabs to spaces as required
	 */
        if (c=='\t' && rtp->t_modes.t_oflags&TO_XTABS) {
		do
			/* changed from ttyoutput call sfh */
			if( putc( ' ',&rtp->t_outq ) )
				return;
		while( ++rtp->t_pos.t_col&07 );
		return;
	}
	/*
	 * for upper-case-only terminals,
	 * generate escapes.
	 */
        if (rtp->t_modes.t_oflags&TO_ONECASE) {
		colp = "({)}!|^~'`";
		while(*colp++)
			if(c == *colp++) {
				ttyoutput('\\', rtp);
				c = colp[-2];
				break;
			}
		if ('a'<=c && c<='z')
			c =+ 'A' - 'a';
	}
	/*
	 * turn <nl> to <cr><lf> if desired.
	 */
        if (c=='\n' && rtp->t_modes.t_oflags&TO_CRMOD)
		ttyoutput('\r', rtp);
	if (putc(c, &rtp->t_outq))
		return;
	/*
	 * Calculate delays.
	 * The numbers here represent clock ticks
	 * and are not necessarily optimal for all terminals.
	 * The delays are indicated by characters above 0200,
	 * thus (unfortunately) restricting the transmission
	 * path to 7 bits.
	 */
	colp = &rtp->t_pos.t_col;
        ctype = partab[c];
        c = 0;

        switch (ctype & P_CTYPE) {

        /* ordinary */
        case P_PRINTING:
                (*colp)++;

        /* non-printing */
        case P_CTRL:
                break;

        /* backspace */
        case P_BS:
                if (*colp)
                        (*colp)--;
                break;

        /* newline */
        case P_LF:
                ctype = rtp->t_modes.t_oflags & TO_NL_DELAY;
                if (ctype == TO_NL_ANN_ARBOR) {
                    if (rtp->t_modes.t_speeds == ((B_9600<<L_SPEED)|B_9600))
                        putc('\020', &rtp->t_outq);
                } else if (ctype == TO_NL_37) { /* tty 37 */
                        if (*colp)
                                c = max((*colp>>4) + 3, 6);
                } else if (ctype == TO_NL_VT05) { /* vt05 */
                        c = 6;
                } else if (ctype == TO_NL_ECD) { /* ECD Translex */
                        c = 1;
                }
                *colp = 0;

                break;

        /* tab */
        case P_HT:
                ctype = rtp->t_modes.t_oflags & TO_TAB_DELAY;
                if(ctype == TO_TAB_37) { /* tty 37 */
                        c = 1 - (*colp | ~07);
                        if(c < 5)
                                c = 0;
                }
                *colp =| 07;
                (*colp)++;
                break;

        /* vertical motion */
        case P_VTFF:
                if(rtp->t_modes.t_oflags & TO_VT_DELAY) /* tty 37 */
                        c = 0177;
                break;

        /* carriage return */
        case P_CR:
                ctype = rtp->t_modes.t_oflags & TO_CR_DELAY;
                if (ctype == TO_CR_TN300) { /* tn 300 */
                        c = 5;
                } else
                if (ctype == TO_CR_TI700) { /* ti 700 */
                        c = 10;
                }
                *colp = 0;
        }
        if(c)
                putc(c|0200, &rtp->t_outq);
}

/* -------------------------- T T S T A R T ------------------------- */
/*
 * Start output on the typewriter. It is used from the top half
 * after some characters have been put on the output queue,
 * from the interrupt routine to transmit the next
 * character, and after a timeout has finished.
 * If the TS_SSTART bit is off for the tty the work is done here,
 * using the protocol of the single-line interfaces (KL, DL, DC);
 * otherwise the address word of the tty structure is
 * taken to be the name of the device-dependent startup routine.
 */
ttstart(atp)
struct tty *atp;
{
	register int *addr, c;
	register struct tty *tp;
	int sps;
        struct { int (*func)(); };
/*
 * structure of device registers for KL, DL, and DC
 * interfaces-- more particularly, those for which the
 * TS_SSTART bit is off and can be treated by general routines
 * (that is, not DH).
 */
        struct {
            int ttrcsr;
            int ttrbuf;
            int tttcsr;
            int tttbuf;
        };
        extern ttrstrt();

        tp = atp;
	sps = PS->integ;
	spl5();
	addr = tp->t_addr;
	if (tp->t_state&TS_SSTART) {
		(*addr.func)(tp);
		goto out;
	}
	if ((addr->tttcsr&DONE)==0 ||
			tp->t_state&(TS_TIMEOUT|TSO_XOFFHNG))  /* agn -XON/XOFF */
		goto out;
	if ((c=getc(&tp->t_outq)) >= 0) {
		if (c<=0177)
			addr->tttbuf = c | (partab[c]&0200);
		else {
			timeout(ttrstrt, tp, c&0177);
			tp->t_state =| TS_TIMEOUT;
		}
	}
	out:
		PS->integ = sps;
}

/* -------------------------- T T R S T R T ------------------------- */
/*
 * Restart typewriter output following a delay
 * timeout.
 * The name of the routine is passed to the timeout
 * subroutine and it is called during a clock interrupt.
 */
ttrstrt(atp)
{
	register struct tty *tp;

	tp = atp;
	tp->t_state =& ~TS_TIMEOUT;
	ttstart(tp);
}

/* -------------------------- T T Y C A P --------------------------- */
/*
 * Note: this routine was generated by tracing the behavior of ttread and
 * ttwrite under all possible conditions. Hence it is useful for any char
 * mode device which uses them.
 * Originally coded Sept 27 1978 Dan Franklin @ BBN
 */
ttycap(atp, v)
   struct tty *atp;
   int *v;
   {
   register struct tty *tp;

   tp = atp;
   if ((tp->t_state & TS_CARR_ON) == 0) /* Carrier gone? */
      {
      *v++ = -1;
      *v = -1;
      return;  /* That's all, folks. */
      }

   if (tp->t_delct == 0)
      *v = 0;
   else
/*
 * This is count of all available data.
 * (inqsize minus the T_DELIMs put in by ttyinput.)
 */
      *v = tp->t_inq.c_cc - tp->t_delct;
   v++;

   if (tp->t_outq.c_cc > TTHIWAT) /* Write will definitely block */
      *v = 0;
   else
/*
 * This is how much room is actually left before ttwrite blocks.
 * If tabs are being expanded, or padding put out, or LF mapped to
 * CR-LF, or chars echoed, etc., a write of this value will block.
 */
      *v = TTHIWAT - tp->t_outq.c_cc + 1;
   }