BBN-V6/newtty/tty.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/tty.h"
#include "../h/proc.h"

/* The entire tty driver is #ifdef'ed on NEWTTY. */

# ifdef NEWTTY

/* -------------------------- 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;
}

/* -------------------------- T T Y I N I T ------------------------- */
/*
 * ttyinit(tp) initializes a new tty structure.
 * It sets all function characters, sets TB_NL as the lone break class,
 * and clears all input and output flags except TI_CLR_MSB and the echo flags.
 * Speed is set to 300 baud for historical reasons; it's actually pretty
 * irrelevant as long as it isn't 0 or 134.5.
 */
ttyinit(atp)
struct tty *atp;
{
    register struct tty *tp;

    tp = atp;
    tp->t_modes.t_spcl[TC_ERASE] = CERASE;
    tp->t_modes.t_spcl[TC_KILL] = CKILL;
    tp->t_modes.t_spcl[SIGINT] = CINTR;
    tp->t_modes.t_spcl[SIGQIT] = CQUIT;
    tp->t_modes.t_spcl[TC_ESC] = CESC;
    tp->t_modes.t_spcl[TC_EOF] = CEOT;
    tp->t_modes.t_spcl[TC_REPLAY] = CREPLAY;

    tp->t_modes.t_breaks = TB_NL;
    tp->t_modes.t_oflags = TO_CLR_MSB;
    tp->t_modes.t_iflags = TI_CLR_MSB | TI_BRK_ECHO | TI_NONBRK_ECHO;

    tp->t_modes.t_pflags = 0;
    tp->t_modes.t_ispeed = B300;
    tp->t_modes.t_ospeed = B300;

    tp->t_modes.t_width = 0;
    tp->t_modes.t_len = 0;
    tp->t_modes.t_pagelen = 0;
    tp->t_modes.t_term = TY_UNKNOWN;

    tp->t_pos.t_col = 0;
    tp->t_pos.t_line = 0;
}


/* -------------------------- M O D T T Y ------------------------------- */
/*
 * The routine implementing the modtty system call.
 * first arg is type.
 * Second arg is pointer to user's buffer; third arg is length.
 */
modtty()
{
	struct modes v;     /* Actually modes, cursor, tty state, or sgtty structure */
        register char *up, *vp;
        register int ulen;
        int flag;
        int *wp;

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

        if (flag & M_SET)
        {
/* Read in existing structure in case user-supplied structure not long enough */
	    sgtty(&v, (flag & M_TYPE) | M_GET, &v);
            while (ulen-- > 0)
                *vp++ = fubyte(up++);
/* Maintain compatibility with older device routines (see stty() ) */
            wp = &v;
            u.u_arg[0] = *wp++;
            u.u_arg[1] = *wp++;
            u.u_arg[2] = *wp++;
            sgtty(0, flag, &v);
        }
        else
            sgtty(&v, flag, &v);

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

/* -------------------------- 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 & M_GET, gtty is being done and the information is
 * passed back in v;
 * if flag & M_SET, stty is being done and v contains the input information.
 *
 * The value returned is 0 if stty was done, nonzero otherwise.
 */
ttystty(tp, junk, flag, av)
struct tty *tp;
char *junk;
int flag;
char *av;
{
    register char *p;
    register char *v;
    register char *top;
    char *base;

    v = av;
    if (flag & M_OWAIT)
	ttwait(tp);
    if (flag & M_IFLUSH)
	iflushtty(tp);
    if (flag & M_OFLUSH)
	oflushtty(tp);

    switch(flag & M_TYPE)
    {
    case M_CURSOR:
	base = &(tp->t_pos);
	top = &(tp->t_pos) + 1;
	goto common;

    case M_MODES:
	base = &(tp->t_modes); /* Get base */
	top = &(tp->t_modes) + 1;

    common:
	if (flag & M_GET)
	    for (p = base; p < top;)
		*v++ = *p++;
	else if (flag & M_SET)
	    for (p = base; p < top;)
		*p++ = *v++;
	break;

    default:
	u.u_error = EINVAL;
	return(1);

    case M_SGTTY:
	if (flag & M_GET)
	    mod2g(tp, v);
	else if (flag & M_SET)
	    s2mod(v, tp);
	break;

    case 0:
	break;

    case M_STATE:
	if (flag & M_GET)
	{
	    v->m_state = tp->t_state;
	    v->m_rawq = tp->t_rawq.c_cc;
	    v->m_outq = tp->t_outq.c_cc;
            v->m_recsize = tp->t_canq.c_cc;
	    break;
	}
	if (flag & M_SET)
	{
	    u.u_error = EINVAL;
	    return(1);
	}
    }

    if (flag & M_SET)
	if (tp->t_modes.t_iflags & TI_LOCAL_ECHO)
	    tp->t_modes.t_iflags =& ~(TI_BRK_ECHO|TI_NONBRK_ECHO|TI_DEFER_ECHO);

    if (flag & M_ECHO)	/* Pre-echo, for deferred echo mode */
        ecanon(tp);

    if (flag & M_SET)
	return(0);
    else
	return(1);
}

/* -------------------------- S 2 M O D ----------------------------- */
/*
 * s2mod(stp, mtp) applies an old-style stty (using the table pointed to
 * by stp) to a new-style tty structure (pointed to by tp).
 * Warning: this code depends on the fact that certain TI_, TO_, and TP_
 * defines have values identical to certain old tty flag word values.
 */
s2mod(astp, atp)
struct sgttybuf *astp;
struct tty *atp;
{

    register struct sgttybuf *stp;
    register struct tty *tp;
    register int flags;

    stp = astp;
    tp = atp;

/* Some things haven't changed */

    tp->t_modes.t_ospeed = stp->s_ospeed;
    tp->t_modes.t_ispeed = stp->s_ispeed;
    tp->t_modes.t_spcl[TC_ERASE] = stp->s_erase;
    tp->t_modes.t_spcl[TC_KILL] = stp->s_kill;

    flags = stp->s_flags;

/* Physical properties */

    tp->t_modes.t_pflags =& ~ TP_HUPCL;
    tp->t_modes.t_pflags =| (flags & HUPCL);	/* HUPCL == TP_HUPCL */
/*
 * The parity bits have changed implementation.
 * This change, although it results in more complicated
 * compatibility code, simplifies driver code.
 */
    tp->t_modes.t_pflags =& ~ TP_PENABLE;
    if ( (flags & (EVENP|ODDP)) == EVENP || (flags & (EVENP|ODDP)) == ODDP)
    {
	tp->t_modes.t_pflags =& ~ TP_ODD;
	if (flags & ODDP)
	    tp->t_modes.t_pflags =| TP_ODD;
	tp->t_modes.t_pflags =| TP_PENABLE;
    }

/* Input properties */

    tp->t_modes.t_iflags =& ~(
			    TI_LOCAL_ECHO|
			    TI_CRMOD|
			    TI_ONECASE|

			    TI_BRK_ECHO|TI_NONBRK_ECHO|
			    TI_NOSPCL
			    );

    tp->t_modes.t_iflags =| flags & (
			    CRMOD|  /* == TI_CRMOD */
			    LCASE|  /* == TI_ONECASE */
			    RAW     /* == TI_NOSPCL */
			    );

    if (flags & ECHO)
	tp->t_modes.t_iflags =| (TI_BRK_ECHO|TI_NONBRK_ECHO);

/* Implement speed hack for TELNET server */

    if (tp->t_modes.t_ispeed == B134)
	tp->t_modes.t_iflags =| TI_LOCAL_ECHO;

/* Output properties */

    tp->t_modes.t_oflags =& ~(
			    TO_XTABS|
			    TO_NL_DELAY|
			    TO_TAB_DELAY|
			    TO_CR_DELAY|
			    TO_VT_DELAY|
			    TO_CRMOD|
			    TO_ONECASE|
			    TO_XONXOFF|
			    TO_LITERAL
			    );

    tp->t_modes.t_oflags =| flags & (
			    XTABS|	/* == TO_XTABS */
			    NLDELAY|	/* == TO_NL_DELAY */
			    TBDELAY|	/* == TO_TAB_DELAY */
			    CRDELAY|	/* == TO_CR_DELAY */
			    VTDELAY|	/* == TO_VT_DELAY */
			    CRMOD|	/* == TO_CRMOD */
			    LCASE	/* == TO_ONECASE */
			    );

/* "tab3" is a synonym for XON-XOFF processing */

    if ((flags & TBDELAY) == TBDELAY)	    /* XON-XOFF */
	tp->t_modes.t_oflags =| TO_XONXOFF;

/*
 * BIT8IO on means TO_CLR_MSB and TI_CLR_MSB off, TO_LITERAL on.
 * If BIT8IO is off, it may be in the act of being turned off,
 * which is ascertained by checking BIT8IO in t_flags. If so,
 * TO_CLR_MSB and TI_CLR_MSB are turned on. If not, TO_CLR_MSB is left
 * alone, in case udelay has been turned on via modtty.
 */
    if (flags & BIT8IO)
    {
	tp->t_modes.t_iflags =& ~ TI_CLR_MSB;
	tp->t_modes.t_oflags =& ~ TO_CLR_MSB;
	tp->t_modes.t_oflags =| TO_LITERAL;
    }
    else if (tp->t_flags & BIT8IO)
    {
	tp->t_modes.t_oflags =| TO_CLR_MSB;
	tp->t_modes.t_iflags =| TI_CLR_MSB;
	tp->t_modes.t_oflags =& ~ TO_LITERAL;
    }

    tp->t_modes.t_breaks = TB_LF;
    if (flags & RAW)
    {
	tp->t_modes.t_breaks = TB_ALL;
	tp->t_modes.t_oflags =& ~ TO_XONXOFF;
    }

    tp->t_flags = flags;

}

/* -------------------------- M O D 2 G ----------------------------- */
/*
 * mod2g(mtp, stp) translates a new-styl tty structure (as pointed to by mtp)
 * into the gtty structure pointed to by stp.
 * Warning: this code depends on the fact that certain TO_ TI_ and TP_ #defines
 * are defined to the same values as certain sgttybuf flag word #defines.
 * They are flagged with comments.
 */
mod2g(amtp, astp)
    struct tty *amtp;
    struct sgttybuf *astp;
{
    register struct tty *mtp;
    register struct sgttybuf *stp;
    register int flags;

    mtp = amtp;
    stp = astp;

/* Some things haven't changed */

    stp->s_ospeed = mtp->t_modes.t_ospeed;
    stp->s_ispeed = mtp->t_modes.t_ispeed;
    stp->s_erase = mtp->t_modes.t_spcl[TC_ERASE];
    stp->s_kill = mtp->t_modes.t_spcl[TC_KILL];

    flags = 0;	    /* Build up result */

/* Physical properties */

    flags =| (mtp->t_modes.t_pflags & TP_HUPCL);

    if (mtp->t_modes.t_pflags & TP_PENABLE)
	if ((mtp->t_modes.t_pflags & TP_PARITY) == TP_ODD)
	    flags =| ODDP;
	else
	    flags =| EVENP;
    else
	flags =| (EVENP|ODDP);

/* Other properties */

    flags =| (
	     mtp->t_modes.t_iflags & (
		TI_CRMOD|	/* == CRMOD */
		TI_ONECASE|	/* == LCASE */
		TI_BRK_ECHO|	/* == ECHO */
		TI_NOSPCL	/* == RAW */
	     )) | (
	     mtp->t_modes.t_oflags & (
		TO_XTABS|	/* == XTABS */
		TO_ONECASE|	/* == LCASE */
		TO_CRMOD|	/* == CRMOD */
		TO_TAB_DELAY|	/* == TBDELAY */
		TO_VT_DELAY|	/* == VTDELAY */
		TO_CR_DELAY|	/* == CRDELAY */
		TO_NL_DELAY	/* == NLDELAY */
	    ));

    if (mtp->t_modes.t_oflags & TO_XONXOFF)
	flags =| TBDELAY;	/* XONXOFF is tab3 */
    if ( (mtp->t_modes.t_oflags & (TO_CLR_MSB|TO_LITERAL)) == TO_LITERAL
      && (mtp->t_modes.t_iflags & TI_CLR_MSB) == 0 )
	flags =| BIT8IO;
    stp->s_flags = flags;
}

/* -------------------------- T T W A I T --------------------------- */
/*
 * Wait for output to drain.
 */
ttwait(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);
        }
        spl0();
}

/* -------------------------- I F L U S H T T Y --------------------- */
/*
 * Flush input queue.
 */
iflushtty(atp)
struct tty *atp;
{
	register struct tty *tp;
	register int sps;

	tp = atp;
	sps = PS->integ;
	spl5();
	while (getc(&tp->t_rawq) >= 0);
        while (getc(&tp->t_canq) >= 0);
	if (tp->t_state & TSI_XOFFHNG)
	{
	    ttyoutput(CXON, tp);
	    tp->t_state =& ~ TSI_XOFFHNG;
	    ttstart(tp);
	}
	lvpolite(tp);
	PS->integ = sps;
	wakeup(&tp->t_rawq);
}

/* -------------------------- O F L U S H T T Y --------------------- */
/*
 * Flush output queue.
 */
oflushtty(atp)
struct tty *atp;
{
	register struct tty *tp;

	tp = atp;
	while (getc(&tp->t_outq) != -1);
	wakeup(&tp->t_outq);
	if (tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE))
	{
	    tp->t_state =& ~ (TSO_XOFFHNG|TS_ENDPAGE);
	    ttstart(tp);
	}
}

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

        tp = atp;
        iflushtty(tp);
        oflushtty(tp);
}

/* -------------------------- W F L U S H T T Y --------------------- */
/*
 * Wait and flush.
 */
wflushtty(tp)
    struct tty * tp;
{
    ttwait(tp);
    flushtty(tp);
}

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

#ifndef SMALL
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
};

#endif

/* -------------------------- I S _ W A K E U P (macro) ------------- */
/*
 * This macro is used to determine whether a char in t_rawq is wakeup char.
 */
#ifndef SMALL
#define is_wakeup(c,tp) (c==T_EOFMARK?1:tp->t_modes.t_breaks & (c&~0177?TB_NON_ASCII:class[c]))
#endif

/*
 * This version is used when there is no break class table. It divides chars into
 * the classes TB_NON_ASCII, TB_NL, and not TB_NL.
 */
#ifdef SMALL
#define is_wakeup(c,tp) (c==T_EOFMARK?1:tp->t_modes.t_breaks&(c&~0177?TB_NON_ASCII:c=='\n'?TB_NL:~TB_NL))
#endif

/* -------------------------- 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.
 *
 * Read chars and copy to user until t_canq empty or passc returns negative.
 * Zero line for page-length control; clear ZOOM mode.
 */
ttread(atp)
struct tty *atp;
{
    register struct tty *tp;

    tp = atp;

    while (tp->t_canq.c_cc == 0)
    {
        if ((tp->t_state & TS_CARR_ON) == 0)
        {
            spl0();
            return;
        }
        spl6();
        if (tp->t_rawq.c_cc == 0 || ecanon(tp) == 0)
        {
            tp->t_state =| TSI_WAKEUP;
            sleep(&tp->t_rawq, TTIPRI);
        }
        else
            break;  /* Skip test on canq.c_cc because 
                     * ecanon can return nonzero with an empty canq
                     * (which means that the next char was EOT).
                     */
    }
    spl0();

/* Begin new page */

    tp->t_pos.t_line = 0;
    if (tp->t_state & TS_ENDPAGE)
    {
        tp->t_state =& ~ TS_ENDPAGE;
        ttstart(tp);
    }
    tp->t_state =& ~ TS_ZOOM;

/* Transfer chars */

    while (tp->t_canq.c_cc != 0)
        if (passc(getc(&tp->t_canq)) < 0)
            break;

    if (tp->t_canq.c_cc == 0) /* Ate break character */
        tp->t_state =& ~ TS_ALREADY_ECHOED;

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

    return;
}

/* -------------------------- E C A N O N --------------------------- */
/*
 * Transfer characters from t_rawq to t_canq, echoing if deferred echo is on.
 * The name is somewhat misleading, as canonicalization is now done in
 * the ttyinput routine. Called from ttread when characters are present,
 * and from ttystty upon an M_ECHO control order. Uses TS_ALREADY_ECHOED
 * to indicate whether the characters currently in the input queue,
 * up to the first break character, have been echoed yet.
 * If no break character is found, the t_canq is put back in the t_rawq and
 * 0 is returned. If a break character is found, returns nonzero.
 * Note that break characters are
 */
ecanon(atp)
    struct tty *atp;
{
    register struct tty *tp;
    register int c;
    register int echoflag;

    tp = atp;
    if (tp->t_modes.t_iflags & TI_DEFER_ECHO)
        echoflag = tp->t_state & TS_ALREADY_ECHOED;
    else
        echoflag = 1;

    for (;;)
    {
	c = getc(&tp->t_rawq);
	if (c == -1)	    /* Empty -- undo */
	{
	    tp->t_rawq.c_cc = tp->t_canq.c_cc;
	    tp->t_rawq.c_cf = tp->t_canq.c_cf;
	    tp->t_rawq.c_cl = tp->t_canq.c_cl;
	    tp->t_canq.c_cc = tp->t_canq.c_cf = tp->t_canq.c_cl = 0;
	    tp->t_state =| (TS_ALREADY_ECHOED|TS_ECHO);
	    return(0);
	}
	if (echoflag == 0)
	    echo(c, tp);
	if (is_wakeup(c, tp))
	{
	    if (c != T_EOFMARK)
		putc(c, &tp->t_canq);
	    tp->t_state =& ~ TS_ALREADY_ECHOED;
	    return(1);
	}
        else
            putc(c, &tp->t_canq);
    }
}
/* -------------------------- 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,
};

/* -------------------------- T T Y I N P U T ----------------------- */
/*
 * ttyinput(c, tp)
 * Puts c on input queue in tty structure pointed to by tp.
 * Called from interrupt side when a char received.
 * Handles X-ON X-OFF, special function chars, break char classes, etc.
 *
 * NOTES:
 *   1. t_modes.t_erase, etc. => t_modes.t_spcl[NCHARS]
 *	#define TC_ERASE, TC_KILL, TC_ESC, TC_REPLAY, TC_EOF
 *	Note that the INTR and QUIT chars go in positions SIGINT and SIGQIT resp
 *	Also need state TS_ESC for escape processing.
 *   2. TC_EOF, when typed, is turned into T_EOFMARK internally, which the user
 *	can't type.
 *   3. TI_NOSPCL turns all spcl chars on or off. Individual ones can be
 *	turned off by assigning 0377 (or some other char which is
 *	unlikely to be typed) to them, which is sufficient for most purposes.
 *   4. Now possible to put special functions on keys w. values > 0177.
 *   5. There is no longer a delimiter char or t_delct. Instead ttread, via 
 *      ecanon, decides when it has genuinely reached a break char. This is
 *      required by the definition of deferred echo.
 *   8. TC_ESC is now a never-break char.
 */
ttyinput(ac, atp)
    int ac;
    struct tty *atp;
{
    register int t_flags, c;
    register struct tty *tp;
    int esc;

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

    c =& 0377;
    if (t_flags & TI_CLR_MSB)
	c =& 0177;

    if (c == CXOFF && tp->t_modes.t_oflags & TO_XONXOFF)
    {
	tp->t_state =| TSO_XOFFHNG;
	return;
    }
/*
 * XON may either be from terminal, to continue, or from user,
 *  to go to next page. If the driver is XOFFHNG then the XON
 *  is probably from the terminal.
 * CZOOM is like CXON but causes later CPAUSEs to be ignored.
 */
    if (c == CXON || (c == CZOOM && tp->t_modes.t_pagelen != 0))
	if (tp->t_modes.t_oflags & TO_XONXOFF
	|| tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE))
	{
	    if (tp->t_state & TSO_XOFFHNG && c == CXON)
		tp->t_state =& ~TSO_XOFFHNG;
	    else
		tp->t_state =& ~TS_ENDPAGE;
	    if (c == CZOOM)
		tp->t_state =^ TS_ZOOM; /* Toggle */
	    ttstart(tp);
	    return;
	}

    if (c == '\r' && (t_flags & TI_CRMOD))
	c = '\n';

    esc = tp->t_state & TS_ESC;
    tp->t_state =& ~ TS_ESC;

    if (t_flags & TI_NOSPCL)
	goto NORMAL;
/*
 * See if special. Note pun of t_flags. Also note use of SIGINT and SIGQIT
 * as indices into t_spcl array as well as signal values.
 */
    for (t_flags = 0; t_flags < sizeof(tp->t_modes.t_spcl); t_flags++)
    {
	if ((tp->t_modes.t_spcl[t_flags] & 0377) == c && c != 0377)
	{
	    if (esc)
	    {
		if (t_flags != TC_ESC)
		    unputc(&tp->t_rawq);    /* Delete escape char from queue */
		goto NORMAL;
	    }
	    switch(t_flags)
	    {
	    case SIGINT:
	    case SIGQIT:
		signal(tp->t_pgrp, t_flags);
		oflushtty(tp);
		ttkill(tp, 1);
		iflushtty(tp);
		ttstart(tp);
		break;

	    case TC_KILL:
	    case TC_ERASE:

		kill_loop:
		    c = unputc(&tp->t_rawq);
		    if (is_wakeup(c, tp))   /* Can't edit past break char */
		    {
			putc(c, &tp->t_rawq);
			lvpolite(tp);
		    }
		    else if (c != -1)	/* Avoid end-of-queue */
			if (t_flags == TC_ERASE)
			    tterase(c, tp);
			else
			{
			    if (t_flags == TC_KILL)
			    {
				ttkill(tp, 1);
				t_flags++;  /* != TC_ERASE or TC_KILL */
			    }
			    goto kill_loop;
			}
		    if (tp->t_rawq.c_cc == 0)
			lvpolite(tp);
		break;

	    case TC_ESC:
		tp->t_state =| TS_ESC;
		goto NORMAL;

	    case TC_REPLAY:
		ttreplay(tp);
		break;

	    case TC_EOF:
		c = T_EOFMARK;
		goto NORMAL;
	    }
	    return;	/* That's all for specials */
	}
    }

NORMAL:
    t_flags = tp->t_modes.t_iflags;

    if (tp->t_rawq.c_cc >= TTYHOG)
    {

OVERFLOW:
        oflushtty(tp);          /* Prevent bells, etc. from filling up */
	ttyoutput('\7', tp);	/* Bell */
	ttstart(tp);
	tp->t_state =& ~ TS_ESC;
	return;
    }

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

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

	if (esc && !(c & ~0177) && maptab[c])
	{
	    c = maptab[c];
	    unputc(&tp->t_rawq);
	}
    }

    if (putc(c, &tp->t_rawq) != 0)
	goto OVERFLOW;

    if ( (t_flags & TI_DEFER_ECHO) == 0     /* If not deferred echo */
     || (tp->t_state & TSI_WAKEUP) != 0     /* or if it is, but read sleeping */
     || (tp->t_state & TS_ECHO) != 0        /* or special immediate echo */
       )
    {
        echo(c, tp);
        tp->t_state =| TS_ALREADY_ECHOED;
    }

    if ((tp->t_state & TS_ESC) == 0 && is_wakeup(c, tp))
    {
        ttwake(tp);
        tp->t_state =& ~ TS_ECHO;
        lvpolite(tp);
    }
    else
    {
        if (tp->t_modes.t_iflags & TI_POLITE)
            tp->t_state =| TSO_POLITE;
    }
}

/* -------------------------- L V P O L I T E ----------------------- */
/*
 * Leave polite state; if it was on, wakeup().
 */
lvpolite(atp)
    struct tty *atp;
{
    register struct tty *tp;

    tp = atp;
    if (tp->t_state & TSO_POLITE)
    {
	tp->t_state =& ~ TSO_POLITE;
	wakeup(&tp->t_outq.c_cf);
    }
}

/* -------------------------- T T W A K E --------------------------- */
/*
 * Called to indicate end of sequence of nonbreaks ended by break char.
 * If there is a sleeping ttread, wakes it up. In any
 * case it does await-awakening.
 */
ttwake(atp)
    struct tty * atp;
{
    register struct tty *tp;

    tp = atp;
    if (tp->t_state & TSI_WAKEUP)
    {
	tp->t_state =& ~TSI_WAKEUP;
	wakeup(&tp->t_rawq);
    }
    if (tp->t_itp != 0)
	awake(tp->t_itp, 0);
}

/* -------------------------- E C H O ------------------------------- */
/*
 * Echoes given character according to TI_BRK_ECHO, TI_NONBRK_ECHO flags.
 * Understands that T_EOFMARK is a break character and should be echoed
 * as t_spcl[TC_EOF].
 */
echo(c, atp)
    int c;
    struct tty *atp;
{
    register struct tty *tp;
    register int choice;

    tp = atp;
    if (is_wakeup(c, tp))
        choice = TI_BRK_ECHO;
    else
        choice = TI_NONBRK_ECHO;

    if (c == T_EOFMARK)
	c = tp->t_modes.t_spcl[TC_EOF];
    if ( (c == '\t' && (tp->t_modes.t_iflags & TI_TAB_ECHO))
         || (c == '\n' && (tp->t_modes.t_iflags & TI_CR_ECHO))
         || (choice & tp->t_modes.t_iflags) )
    {
        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? */

#ifndef SMALL
    if (flags & TI_DEFER_ECHO) /* If deferred echo, use wakeup flag */
	if ((tp->t_state & (TSI_WAKEUP|TS_ECHO)) == 0)
	    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;
#endif
#ifdef SMALL
    if ( (flags & (TI_BRK_ECHO|TI_NONBRK_ECHO)) == 0)
	return;
#endif

/* Apparently -- undo it */

    if ((flags & TI_CRT) == 0)
    {
	ttyoutput('\\', tp);
	ttyoutput(c, tp);
	ttstart(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:
	ttyoutput(' ', tp);
	len = 0;
	break;
    case P_HT:
	ttreplay(tp);
	return;
    }

    if ((c == tp->t_modes.t_spcl[TC_ERASE]) || (c == tp->t_modes.t_spcl[TC_KILL]))
	len++;
    while(len--)
    {
	ttyoutput('\b', tp);
	ttyoutput(' ', tp);
	ttyoutput('\b', tp);
    }
    ttstart(tp);
}

/* -------------------------- T T K I L L --------------------------- */
/*
 * Kill the line on the user's terminal. If flag is 1, indicate that
 * user's input has been erased.
 */
ttkill(atp, flag)
struct tty *atp;
int flag;
{
    register struct tty *tp;

    tp = atp;
    if (flag)
	echo(tp->t_modes.t_spcl[TC_KILL], tp);
    echo('\n', tp);
}


/* -------------------------- T T R E P L A Y ----------------------- */
/*
 * Replay queued-up input using peekc.
 */
ttreplay(atp)
struct tty *atp;
{
    register struct tty *tp;
    register int c;
    register int ps;
    struct clist peek;

    tp = atp;

    ttkill(tp, 0);

    peek.c_cf = tp->t_rawq.c_cf;
    peek.c_cl = tp->t_rawq.c_cl;
    peek.c_cc = tp->t_rawq.c_cc;

    if (peek.c_cc > 0)
    {
	tp->t_state =| (TSO_POLITE|TS_ECHO);
	while ((c = peekc(&peek)) != -1)
	{
	    echo(c, tp);

	/* Let pending interrupts in */

	    ps = PS->integ;
	    spl0();
	    PS->integ = ps;
	}
    }
}

/* -------------------------- 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) {
		spl6();
		count++;
		while (tp->t_outq.c_cc > TTHIWAT) {
			ttstart(tp);
			if (tp->t_outq.c_cc <= TTHIWAT) /* (For ptys) */
			    break;
			tp->t_state =| TS_ASLEEP;
			sleep(&tp->t_outq, TTOPRI);
		}
/* Allow ttyinput to echo and set polite state */
		spl0();
		spl6();
/* Sleep if in polite state */
		while (tp->t_state & TSO_POLITE)
		    sleep(&tp->t_outq.c_cf, 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 and page-length pauses,
 * expanding tabs, and handling the CR/NL bit.
 * ttyoutput is called both from ttwrite 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 outc;
	int ctype;

	rtp = tp;
	c = ac & 0377;
	if (rtp->t_modes.t_oflags & TO_CLR_MSB)
	    c =& 0177;
/*
 * 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';
	}
/*
 * Determine special actions, including tab expansion,
 * LF->CRLF conversion, delays, and page-length pauses.
 * c will be set to a delay in clock ticks, or CPAUSE
 * for a page-length pause.
 * The delays and CPAUSE are indicated by characters above 0200,
 * thus (unfortunately) restricting the transmission
 * path to 7 bits.
 * The delay is not necessarily optimal for all terminals.
 */
        outc = c;       /* Save for later */

        colp = &rtp->t_pos.t_col;
        ctype = partab[c];
        c = 0;

        switch (ctype & P_CTYPE) {

	case P_PRINTING:
		if (rtp->t_modes.t_oflags & TO_AUTONL &&
		    (rtp->t_modes.t_width & 0377) <= (*colp & 0377) &&
		    (rtp->t_modes.t_width & 0377) > 3)
		{
		    ttyoutput('\n', rtp);
		    ttyoutput('-', rtp);
		    ttyoutput('-', rtp);
		    ttyoutput('>', rtp);
		}
		(*colp)++;
	case P_CTRL:
                break;

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

	case P_LF:

	    /* turn <nl> to <cr><lf> if desired. */

		if (rtp->t_modes.t_oflags & TO_CRMOD)
		    ttyoutput('\r', rtp);

		++rtp->t_pos.t_line;

	    /* Pause at end of page if desired. */

		if (rtp->t_modes.t_oflags & TO_XONXOFF
		&& rtp->t_modes.t_pagelen != 0
		&& rtp->t_pos.t_line >= rtp->t_modes.t_pagelen)
		{
		    c = CPAUSE;
		    rtp->t_pos.t_line = 0;
		    *colp = 0;
		    break;
		}

	    /* Otherwise, calculate delay */

		ctype = rtp->t_modes.t_oflags & TO_NL_DELAY;
		if (ctype == TO_NL_ANN_ARBOR) {     /* Put out ctrl-P at 9600 */
		    if (rtp->t_modes.t_oflags & TO_LITERAL)
			c = 0;
		    else if (rtp->t_modes.t_ospeed == B9600) {
			putc(outc, &rtp->t_outq); /* Put out nl */
			outc = '\020';	/* Later put out padding char */
		    }
		} else if (ctype == TO_NL_37) {
			if (*colp)
				c = max((*colp>>4) + 3, 6);
			if (c > 0177)
				c = 0177;
		} else if (ctype == TO_NL_VT05) { /* vt05 */
			c = 6;
		} else if (ctype == TO_NL_ECD) { /* ECD Translex */
			c = 1;
		}
		*colp = 0;

		break;

	case P_HT:

            /* Turn tabs to spaces if required */

                if (rtp->t_modes.t_oflags & TO_XTABS)
                {
                    do
                        if (putc(' ', &rtp->t_outq))
                            return;
                    while (++rtp->t_pos.t_col & 07);
                    return;
                }

            /* Otherwise delay */

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

        case P_VTFF:

            /*
             * If page-length processing turned on,
             * and at least one line has been output, pause on FF
             */
                if (outc == '\014'
                && rtp->t_modes.t_oflags & TO_XONXOFF
                && rtp->t_modes.t_pagelen != 0
                && rtp->t_pos.t_line > 0)
                {
                    c = CPAUSE;
                    rtp->t_pos.t_line = 0;
                    break;
                }

            /* Otherwise delay */

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

        case P_CR:
                ctype = rtp->t_modes.t_oflags & TO_CR_DELAY;
                if (ctype == TO_CR_TN300)
                        c = 5;
                else if (ctype == TO_CR_TI700)
                        c = 10;
                *colp = 0;
	}

/*
 * If 8-bit output mode is on, delays cannot be used.
 */
	if (rtp->t_modes.t_oflags & TO_LITERAL)
	    c = 0;

/*
 * Page-length pauses happen before the char gets put out.
 * Ordinary delays happen after.
 */

	if (c == CPAUSE)
	{
	    putc(c, &rtp->t_outq);
	    c = 0;
	}

	if (putc(outc, &rtp->t_outq))
		return;

	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)(); };
	extern ttrstrt();
/*
 * 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;
	};

	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|TS_ENDPAGE))
	    goto out;
	if ((c=getc(&tp->t_outq)) >= 0)
	{
	    if (tp->t_modes.t_oflags & TO_LITERAL)
		addr->tttbuf = c;
	    else if (c <= 0177)
		addr->tttbuf = c | (partab[c]&0200);
	    else if (c == CPAUSE)
	    {
		if ((tp->t_state & TS_ZOOM) == 0)
		    tp->t_state =| TS_ENDPAGE;
	    }
	    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 --------------------------- */
/*
 * Return (by setting vector) the read and write capacity of the given
 * tty structure. For deferred echo, this may mean examining the input
 * queue to see what ttread would do with it.
 * 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.
 */
ttycap(atp, v)
   struct tty *atp;
   int *v;
   {
   register struct tty *tp;
   register int c;
   register int i;
   struct clist peek;

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

   if (tp->t_canq.c_cc != 0)  /* If there is already a record, return that */
      *v = tp->t_canq.c_cc;
   else if (tp->t_rawq.c_cc == 0) /* Otherwise, if nothing there, return that */
      *v = 0;
   else
/*
 * Calculate count of data that ttread would get if it were called under
 * the current set of break characters.
 */
   {
      spl6();
      peek.c_cc = tp->t_rawq.c_cc;
      peek.c_cf = tp->t_rawq.c_cf;
      peek.c_cl = tp->t_rawq.c_cl;
      i = 0;
      *v = 0;
      while ((c = peekc(&peek)) != -1 && *v == 0)
      {
	 i++;
	 if (is_wakeup(c, tp))
	    *v = i;
      }
      spl0();
   }
   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;
   }

/* ------------------------------------------------------------------ */
# endif NEWTTY