BBN-V6/newtty/tty.c
#
/*
* 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