X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/101ba270204b16c97c3486b2872497e566d8fbe0..605d77121acc5c1841bb3dcd5b67ab59719edb1a:/usr/src/sys/kern/tty.c diff --git a/usr/src/sys/kern/tty.c b/usr/src/sys/kern/tty.c index 675990c525..08baeb428c 100644 --- a/usr/src/sys/kern/tty.c +++ b/usr/src/sys/kern/tty.c @@ -1,23 +1,77 @@ -/* tty.c 3.2 %H% */ +/* + * Copyright (c) 1982 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + * + * @(#)tty.c 6.22 (Berkeley) %G% + */ + +#include "../machine/reg.h" + +#include "param.h" +#include "systm.h" +#include "dir.h" +#include "user.h" +#include "ioctl.h" +#include "tty.h" +#include "proc.h" +#include "inode.h" +#include "file.h" +#include "conf.h" +#include "buf.h" +#include "dk.h" +#include "uio.h" +#include "kernel.h" /* - * general TTY subroutines + * Table giving parity for characters and indicating + * character classes to tty driver. In particular, + * if the low 6 bits are 0, then the character needs + * no special processing on output. */ -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/tty.h" -#include "../h/proc.h" -#include "../h/mx.h" -#include "../h/inode.h" -#include "../h/file.h" -#include "../h/reg.h" -#include "../h/conf.h" -#include "../h/buf.h" - -char partab[]; +char partab[] = { + 0001,0201,0201,0001,0201,0001,0001,0201, + 0202,0004,0003,0201,0005,0206,0201,0001, + 0201,0001,0001,0201,0001,0201,0201,0001, + 0001,0201,0201,0001,0201,0001,0001,0201, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0201, + + /* + * 7 bit ascii ends with the last character above, + * but we contine through all 256 codes for the sake + * of the tty output routines which use special vax + * instructions which need a 256 character trt table. + */ + + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007, + 0007,0007,0007,0007,0007,0007,0007,0007 +}; /* * Input mapping table-- if an entry is non-zero, when the @@ -25,7 +79,6 @@ char partab[]; * sequence is replaced by the table value. Mostly used for * upper-case only terminals. */ - char maptab[] ={ 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, @@ -45,540 +98,889 @@ char maptab[] ={ 'X','Y','Z',000,000,000,000,000, }; +short tthiwat[16] = + { 100,100,100,100,100,100,100,200,200,400,400,400,650,650,1300,2000 }; +short ttlowat[16] = + { 30, 30, 30, 30, 30, 30, 30, 50, 50,120,120,120,125,125,125,125 }; -/* - * shorthand - */ -#define q1 tp->t_rawq -#define q2 tp->t_canq -#define q3 tp->t_outq -#define q4 tp->t_un.t_ctlq +struct ttychars ttydefaults = { + CERASE, CKILL, CINTR, CQUIT, CSTART, CSTOP, CEOF, + CBRK, CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE,CLNEXT +}; -#define OBUFSIZ 100 +ttychars(tp) + struct tty *tp; +{ + + tp->t_chars = ttydefaults; +} /* - * routine called on first teletype open. - * establishes a process group for distribution - * of quits and interrupts from the tty. + * Wait for output to drain, then flush input waiting. */ -ttyopen(dev, tp) -dev_t dev; -register struct tty *tp; +ttywflush(tp) + register struct tty *tp; { - register struct proc *pp; - pp = u.u_procp; - tp->t_dev = dev; - if(pp->p_pgrp == 0) { - u.u_ttyp = tp; - u.u_ttyd = dev; - if (tp->t_pgrp==0) - tp->t_pgrp = pp->p_pid; - pp->p_pgrp = tp->t_pgrp; - } - tp->t_state &= ~WOPEN; - tp->t_state |= ISOPEN; + ttywait(tp); + ttyflush(tp, FREAD); } - -/* - * set default control characters. - */ -ttychars(tp) -register struct tty *tp; +ttywait(tp) + register struct tty *tp; { - tun.t_intrc = CINTR; - tun.t_quitc = CQUIT; - tun.t_startc = CSTART; - tun.t_stopc = CSTOP; - tun.t_eofc = CEOT; - tun.t_brkc = CBRK; - tp->t_erase = CERASE; - tp->t_kill = CKILL; + register int s = spltty(); + + while ((tp->t_outq.c_cc || tp->t_state&TS_BUSY) && + tp->t_state&TS_CARR_ON) { + (*tp->t_oproc)(tp); + tp->t_state |= TS_ASLEEP; + sleep((caddr_t)&tp->t_outq, TTOPRI); + } + splx(s); } /* - * clean tp on last close + * Flush all TTY queues */ -ttyclose(tp) -register struct tty *tp; +ttyflush(tp, rw) + register struct tty *tp; { + register s; - tp->t_pgrp = 0; - wflushtty(tp); - tp->t_state = 0; + s = spltty(); + if (rw & FREAD) { + while (getc(&tp->t_canq) >= 0) + ; + wakeup((caddr_t)&tp->t_rawq); + } + if (rw & FWRITE) { + wakeup((caddr_t)&tp->t_outq); + tp->t_state &= ~TS_TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw); + while (getc(&tp->t_outq) >= 0) + ; + } + if (rw & FREAD) { + while (getc(&tp->t_rawq) >= 0) + ; + tp->t_rocount = 0; + tp->t_rocol = 0; + tp->t_state &= ~TS_LOCAL; + } + splx(s); } /* - * stty/gtty writearound + * Send stop character on input overflow. */ -stty() +ttyblock(tp) + register struct tty *tp; { - u.u_arg[2] = u.u_arg[1]; - u.u_arg[1] = TIOCSETP; - ioctl(); -} + register x; -gtty() -{ - u.u_arg[2] = u.u_arg[1]; - u.u_arg[1] = TIOCGETP; - ioctl(); + x = tp->t_rawq.c_cc + tp->t_canq.c_cc; + if (tp->t_rawq.c_cc > TTYHOG) { + ttyflush(tp, FREAD|FWRITE); + tp->t_state &= ~TS_TBLOCK; + } + /* + * Block further input iff: + * Current input > threshold AND input is available to user program + */ + if (x >= TTYHOG/2 && + ((tp->t_flags & (RAW|CBREAK)) || (tp->t_canq.c_cc > 0))) { + if (putc(tp->t_stopc, &tp->t_outq)==0) { + tp->t_state |= TS_TBLOCK; + ttstart(tp); + } + } } /* - * Do nothing specific version of line - * discipline specific ioctl command. + * 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. */ -nullioctl(tp, cmd, addr) -register struct tty *tp; -caddr_t addr; +ttrstrt(tp) + register struct tty *tp; { - return (cmd); + if (tp == 0) + panic("ttrstrt"); + tp->t_state &= ~TS_TIMEOUT; + ttstart(tp); } /* - * ioctl system call - * Check legality, execute common code, and switch out to individual - * device routine. + * 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. */ -ioctl() +ttstart(tp) + register struct tty *tp; { - register struct file *fp; - register struct inode *ip; - register struct a { - int fdes; - int cmd; - caddr_t cmarg; - } *uap; - register dev_t dev; - register fmt; - - uap = (struct a *)u.u_ap; - if ((fp = getf(uap->fdes)) == NULL) - return; - if (uap->cmd==FIOCLEX) { - u.u_pofile[uap->fdes] |= EXCLOSE; - return; - } - if (uap->cmd==FIONCLEX) { - u.u_pofile[uap->fdes] &= ~EXCLOSE; - return; - } - ip = fp->f_inode; - fmt = ip->i_mode & IFMT; - if (fmt != IFCHR && fmt != IFMPC) { - u.u_error = ENOTTY; - return; - } - dev = ip->i_un.i_rdev; - u.u_r.r_val1 = 0; - (*cdevsw[major(dev)].d_ioctl)(dev, uap->cmd, uap->cmarg, fp->f_flag); + register s; + + s = spltty(); + if ((tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) == 0 && + tp->t_oproc) /* kludge for pty */ + (*tp->t_oproc)(tp); + splx(s); } /* - * Common code for several tty ioctl commands + * Common code for tty ioctls. */ -ttioccomm(com, tp, addr, dev) -register struct tty *tp; -caddr_t addr; +/*ARGSUSED*/ +ttioctl(tp, com, data, flag) + register struct tty *tp; + caddr_t data; { - unsigned t; - struct ttiocb iocb; + int dev = tp->t_dev; extern int nldisp; - register s; - - switch(com) { + int s; + register int newflags; /* - * get discipline number + * If the ioctl involves modification, + * hang if in the background. */ - case TIOCGETD: - t = tp->t_line; - if (copyout((caddr_t)&t, addr, sizeof(t))) - u.u_error = EFAULT; + switch (com) { + + case TIOCSETD: + case TIOCSETP: + case TIOCSETN: + case TIOCFLUSH: + case TIOCSETC: + case TIOCSLTC: + case TIOCSPGRP: + case TIOCLBIS: + case TIOCLBIC: + case TIOCLSET: + case TIOCSTI: + case TIOCSWINSZ: + while (tp->t_line == NTTYDISC && + u.u_procp->p_pgrp != tp->t_pgrp && tp == u.u_ttyp && + (u.u_procp->p_flag&SVFORK) == 0 && + !(u.u_procp->p_sigignore & sigmask(SIGTTOU)) && + !(u.u_procp->p_sigmask & sigmask(SIGTTOU))) { + gsignal(u.u_procp->p_pgrp, SIGTTOU); + sleep((caddr_t)&lbolt, TTOPRI); + } break; + } /* - * set line discipline + * Process the ioctl. */ - case TIOCSETD: - if (copyin(addr, (caddr_t)&t, sizeof(t))) { - u.u_error = EFAULT; - break; - } - if (t >= nldisp) { - u.u_error = ENXIO; - break; + switch (com) { + + /* get discipline number */ + case TIOCGETD: + *(int *)data = tp->t_line; + break; + + /* set line discipline */ + case TIOCSETD: { + register int t = *(int *)data; + int error = 0; + + if ((unsigned) t >= nldisp) + return (ENXIO); + s = spltty(); + (*linesw[tp->t_line].l_close)(tp); + error = (*linesw[t].l_open)(dev, tp); + if (error) { + (void) (*linesw[tp->t_line].l_open)(dev, tp); + splx(s); + return (error); } - s = spl5(); - if (tp->t_line) - (*linesw[tp->t_line].l_close)(tp); - if (t) - (*linesw[t].l_open)(dev, tp, addr); - if (u.u_error==0) - tp->t_line = t; + tp->t_line = t; splx(s); break; + } - /* - * prevent more opens on channel - */ + /* prevent more opens on channel */ case TIOCEXCL: - tp->t_state |= XCLUDE; + tp->t_state |= TS_XCLUDE; break; case TIOCNXCL: - tp->t_state &= ~XCLUDE; + tp->t_state &= ~TS_XCLUDE; + break; + + /* hang up line on last close */ + case TIOCHPCL: + tp->t_state |= TS_HUPCLS; + break; + + case TIOCFLUSH: { + register int flags = *(int *)data; + + if (flags == 0) + flags = FREAD|FWRITE; + else + flags &= FREAD|FWRITE; + ttyflush(tp, flags); + break; + } + + /* return number of characters immediately available */ + case FIONREAD: + *(off_t *)data = ttnread(tp); + break; + + case TIOCOUTQ: + *(int *)data = tp->t_outq.c_cc; + break; + + case TIOCSTOP: + s = spltty(); + if ((tp->t_state&TS_TTSTOP) == 0) { + tp->t_state |= TS_TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); + } + splx(s); + break; + + case TIOCSTART: + s = spltty(); + if ((tp->t_state&TS_TTSTOP) || (tp->t_flags&FLUSHO)) { + tp->t_state &= ~TS_TTSTOP; + tp->t_flags &= ~FLUSHO; + ttstart(tp); + } + splx(s); break; /* - * Set new parameters + * Simulate typing of a character at the terminal. */ + case TIOCSTI: + if (u.u_uid && (flag & FREAD) == 0) + return (EPERM); + if (u.u_uid && u.u_ttyp != tp) + return (EACCES); + (*linesw[tp->t_line].l_rint)(*(char *)data, tp); + break; + case TIOCSETP: - wflushtty(tp); - case TIOCSETN: - if (copyin(addr, (caddr_t)&iocb, sizeof(iocb))) { - u.u_error = EFAULT; - return(1); + case TIOCSETN: { + register struct sgttyb *sg = (struct sgttyb *)data; + + tp->t_erase = sg->sg_erase; + tp->t_kill = sg->sg_kill; + tp->t_ispeed = sg->sg_ispeed; + tp->t_ospeed = sg->sg_ospeed; + newflags = (tp->t_flags&0xffff0000) | (sg->sg_flags&0xffff); + s = spltty(); + if (tp->t_flags&RAW || newflags&RAW || com == TIOCSETP) { + ttywait(tp); + ttyflush(tp, FREAD); + } else if ((tp->t_flags&CBREAK) != (newflags&CBREAK)) { + if (newflags&CBREAK) { + struct clist tq; + + catq(&tp->t_rawq, &tp->t_canq); + tq = tp->t_rawq; + tp->t_rawq = tp->t_canq; + tp->t_canq = tq; + } else { + tp->t_flags |= PENDIN; + newflags |= PENDIN; + ttwakeup(tp); + } } - (void) spl5(); - while (canon(tp)>=0) - ; - if ((tp->t_state&SPEEDS)==0) { - tp->t_ispeed = iocb.ioc_ispeed; - tp->t_ospeed = iocb.ioc_ospeed; + tp->t_flags = newflags; + if (tp->t_flags&RAW) { + tp->t_state &= ~TS_TTSTOP; + ttstart(tp); } - tp->t_erase = iocb.ioc_erase; - tp->t_kill = iocb.ioc_kill; - tp->t_flags = iocb.ioc_flags; - (void) spl0(); + splx(s); break; + } - /* - * send current parameters to user - */ - case TIOCGETP: - iocb.ioc_ispeed = tp->t_ispeed; - iocb.ioc_ospeed = tp->t_ospeed; - iocb.ioc_erase = tp->t_erase; - iocb.ioc_kill = tp->t_kill; - iocb.ioc_flags = tp->t_flags; - if (copyout((caddr_t)&iocb, addr, sizeof(iocb))) - u.u_error = EFAULT; + /* send current parameters to user */ + case TIOCGETP: { + register struct sgttyb *sg = (struct sgttyb *)data; + + sg->sg_ispeed = tp->t_ispeed; + sg->sg_ospeed = tp->t_ospeed; + sg->sg_erase = tp->t_erase; + sg->sg_kill = tp->t_kill; + sg->sg_flags = tp->t_flags; break; + } - /* - * Hang up line on last close - */ + case FIONBIO: + if (*(int *)data) + tp->t_state |= TS_NBIO; + else + tp->t_state &= ~TS_NBIO; + break; - case TIOCHPCL: - tp->t_state |= HUPCLS; + case FIOASYNC: + if (*(int *)data) + tp->t_state |= TS_ASYNC; + else + tp->t_state &= ~TS_ASYNC; break; - case TIOCFLUSH: - flushtty(tp); + case TIOCGETC: + bcopy((caddr_t)&tp->t_intrc, data, sizeof (struct tchars)); + break; + + case TIOCSETC: + bcopy(data, (caddr_t)&tp->t_intrc, sizeof (struct tchars)); + break; + + /* set/get local special characters */ + case TIOCSLTC: + bcopy(data, (caddr_t)&tp->t_suspc, sizeof (struct ltchars)); + break; + + case TIOCGLTC: + bcopy((caddr_t)&tp->t_suspc, data, sizeof (struct ltchars)); break; /* - * ioctl entries to line discipline + * Modify local mode word. */ - case DIOCSETP: - case DIOCGETP: - if ((*linesw[tp->t_line].l_ioctl)(com, tp, addr)) - u.u_error = ENOTTY; + case TIOCLBIS: + tp->t_flags |= *(int *)data << 16; + break; + + case TIOCLBIC: + tp->t_flags &= ~(*(int *)data << 16); + break; + + case TIOCLSET: + tp->t_flags &= 0xffff; + tp->t_flags |= *(int *)data << 16; + break; + + case TIOCLGET: + *(int *)data = ((unsigned) tp->t_flags) >> 16; break; /* - * set and fetch special characters + * Allow SPGRP only if tty is open for reading. + * Quick check: if we can find a process in the new pgrp, + * this user must own that process. + * SHOULD VERIFY THAT PGRP IS IN USE AND IS THIS USER'S. */ - case TIOCSETC: - if (copyin(addr, (caddr_t)&tun, sizeof(struct tc))) - u.u_error = EFAULT; + case TIOCSPGRP: { + struct proc *p; + int pgrp = *(int *)data; + + if (u.u_uid && (flag & FREAD) == 0) + return (EPERM); + p = pfind(pgrp); + if (p && p->p_pgrp == pgrp && + p->p_uid != u.u_uid && u.u_uid && !inferior(p)) + return (EPERM); + tp->t_pgrp = pgrp; break; + } - case TIOCGETC: - if (copyout((caddr_t)&tun, addr, sizeof(struct tc))) - u.u_error = EFAULT; + case TIOCGPGRP: + *(int *)data = tp->t_pgrp; + break; + + case TIOCSWINSZ: + if (bcmp((caddr_t)&tp->t_winsize, data, + sizeof (struct winsize))) { + tp->t_winsize = *(struct winsize *)data; + gsignal(tp->t_pgrp, SIGWINCH); + } + break; + + case TIOCGWINSZ: + *(struct winsize *)data = tp->t_winsize; break; default: - return(0); + return (-1); } - return(1); + return (0); +} + +ttnread(tp) + struct tty *tp; +{ + int nread = 0; + + if (tp->t_flags & PENDIN) + ttypend(tp); + nread = tp->t_canq.c_cc; + if (tp->t_flags & (RAW|CBREAK)) + nread += tp->t_rawq.c_cc; + return (nread); +} + +ttselect(dev, rw) + dev_t dev; + int rw; +{ + register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)]; + int nread; + int s = spltty(); + + switch (rw) { + + case FREAD: + nread = ttnread(tp); + if ((nread > 0) || ((tp->t_state & TS_CARR_ON) == 0)) + goto win; + if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait) + tp->t_state |= TS_RCOLL; + else + tp->t_rsel = u.u_procp; + break; + + case FWRITE: + if (tp->t_outq.c_cc <= TTLOWAT(tp)) + goto win; + if (tp->t_wsel && tp->t_wsel->p_wchan == (caddr_t)&selwait) + tp->t_state |= TS_WCOLL; + else + tp->t_wsel = u.u_procp; + break; + } + splx(s); + return (0); +win: + splx(s); + return (1); } /* - * Wait for output to drain, then flush input waiting. + * Initial open of tty, or (re)entry to line discipline. + * Establish a process group for distribution of + * quits and interrupts from the tty. */ -wflushtty(tp) -register struct tty *tp; +ttyopen(dev, tp) + dev_t dev; + register struct tty *tp; { + register struct proc *pp; - (void) spl5(); - while (tp->t_outq.c_cc && tp->t_state&CARR_ON) { - (*tp->t_oproc)(tp); - tp->t_state |= ASLEEP; - sleep((caddr_t)&tp->t_outq, TTOPRI); + pp = u.u_procp; + tp->t_dev = dev; + if (pp->p_pgrp == 0) { + u.u_ttyp = tp; + u.u_ttyd = dev; + if (tp->t_pgrp == 0) + tp->t_pgrp = pp->p_pid; + pp->p_pgrp = tp->t_pgrp; } - flushtty(tp); - (void) spl0(); + tp->t_state &= ~TS_WOPEN; + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_ISOPEN; + bzero((caddr_t)&tp->t_winsize, sizeof(tp->t_winsize)); + if (tp->t_line != NTTYDISC) + ttywflush(tp); + } + return (0); } /* - * flush all TTY queues + * "close" a line discipline */ -flushtty(tp) -register struct tty *tp; +ttylclose(tp) + register struct tty *tp; { - register s; - s = spl6(); - while (getc(&tp->t_canq) >= 0) - ; - wakeup((caddr_t)&tp->t_rawq); - wakeup((caddr_t)&tp->t_outq); - tp->t_state &= ~TTSTOP; - (*cdevsw[major(tp->t_dev)].d_stop)(tp); - while (getc(&tp->t_outq) >= 0) - ; - while (getc(&tp->t_rawq) >= 0) - ; - tp->t_delct = 0; - splx(s); + ttywflush(tp); + tp->t_line = 0; } +/* + * clean tp on last close + */ +ttyclose(tp) + register struct tty *tp; +{ + ttyflush(tp, FREAD|FWRITE); + tp->t_pgrp = 0; + tp->t_state = 0; +} /* - * transfer raw input list to canonical list, - * doing erase-kill processing and handling escapes. - * It waits until a full line has been typed in cooked mode, - * or until any character has been typed in raw mode. + * Handle modem control transition on a tty. + * Flag indicates new state of carrier. + * Returns 0 if the line should be turned off, otherwise 1. */ -canon(tp) -register struct tty *tp; +ttymodem(tp, flag) + register struct tty *tp; { - register char *bp; - char *bp1; - register int c; - int mc; - int s; - if ((tp->t_flags&(RAW|CBREAK))==0 && tp->t_delct==0 - || (tp->t_flags&(RAW|CBREAK))!=0 && tp->t_rawq.c_cc==0) { - return(-1); - } - s = spl0(); -loop: - bp = &canonb[2]; - while ((c=getc(&tp->t_rawq)) >= 0) { - if ((tp->t_flags&(RAW|CBREAK))==0) { - if (c==0377) { - tp->t_delct--; - break; - } - if (bp[-1]!='\\') { - if (c==tp->t_erase) { - if (bp > &canonb[2]) - bp--; - continue; - } - if (c==tp->t_kill) - goto loop; - if (c==tun.t_eofc) - continue; - } else { - mc = maptab[c]; - if (c==tp->t_erase || c==tp->t_kill) - mc = c; - if (mc && (mc==c || (tp->t_flags&LCASE))) { - if (bp[-2] != '\\') - c = mc; - bp--; - } - } - } - *bp++ = c; - if (bp>=canonb+CANBSIZ) - break; - } - bp1 = &canonb[2]; - (void) b_to_q(bp1, bp-bp1, &tp->t_canq); - - if (tp->t_state&TBLOCK && tp->t_rawq.c_cc < TTYHOG/5) { - if (putc(tun.t_startc, &tp->t_outq)==0) { - tp->t_state &= ~TBLOCK; + if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_flags & MDMBUF)) { + /* + * MDMBUF: do flow control according to carrier flag + */ + if (flag) { + tp->t_state &= ~TS_TTSTOP; ttstart(tp); + } else if ((tp->t_state&TS_TTSTOP) == 0) { + tp->t_state |= TS_TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); } - tp->t_char = 0; + } else if (flag == 0) { + /* + * Lost carrier. + */ + tp->t_state &= ~TS_CARR_ON; + if (tp->t_state & TS_ISOPEN) { + if ((tp->t_flags & NOHANG) == 0) { + gsignal(tp->t_pgrp, SIGHUP); + gsignal(tp->t_pgrp, SIGCONT); + ttyflush(tp, FREAD|FWRITE); + return (0); + } + } + } else { + /* + * Carrier now on. + */ + tp->t_state |= TS_CARR_ON; + wakeup((caddr_t)&tp->t_rawq); } - - splx(s); - return(0); + return (1); } - /* - * block transfer input handler. + * reinput pending characters after state switch + * call at spltty(). */ -ttyrend(tp, pb, pe) -register struct tty *tp; -register char *pb, *pe; +ttypend(tp) + register struct tty *tp; { - int tandem; - - tandem = tp->t_flags&TANDEM; - if (tp->t_flags&RAW) { - (void) b_to_q(pb, pe-pb, &tp->t_rawq); - if (tp->t_chan) - (void) sdata(tp->t_chan); else - wakeup((caddr_t)&tp->t_rawq); - } else { - tp->t_flags &= ~TANDEM; - while (pb < pe) - ttyinput(*pb++, tp); - tp->t_flags |= tandem; - } - if (tandem) - ttyblock(tp); + struct clist tq; + register c; + + tp->t_flags &= ~PENDIN; + tp->t_state |= TS_TYPEN; + tq = tp->t_rawq; + tp->t_rawq.c_cc = 0; + tp->t_rawq.c_cf = tp->t_rawq.c_cl = 0; + while ((c = getc(&tq)) >= 0) + ttyinput(c, tp); + tp->t_state &= ~TS_TYPEN; } /* - * Place a character on raw TTY input queue, putting in delimiters - * and waking up top half as needed. - * Also echo if required. - * The arguments are the character and the appropriate - * tty structure. + * Place a character on raw TTY input queue, + * putting in delimiters and waking up top + * half as needed. Also echo if required. + * The arguments are the character and the + * appropriate tty structure. */ ttyinput(c, tp) -register c; -register struct tty *tp; + register c; + register struct tty *tp; { - register int t_flags; - register struct chan *cp; + register int t_flags = tp->t_flags; + int i; - tk_nin += 1; + /* + * If input is pending take it first. + */ + if (t_flags&PENDIN) + ttypend(tp); + tk_nin++; c &= 0377; - t_flags = tp->t_flags; + + /* + * In tandem mode, check high water mark. + */ if (t_flags&TANDEM) ttyblock(tp); - if ((t_flags&RAW)==0) { + + if (t_flags&RAW) { + /* + * Raw mode, just put character + * in input q w/o interpretation. + */ + if (tp->t_rawq.c_cc > TTYHOG) + ttyflush(tp, FREAD|FWRITE); + else { + if (putc(c, &tp->t_rawq) >= 0) + ttwakeup(tp); + ttyecho(c, tp); + } + goto endcase; + } + + /* + * Ignore any high bit added during + * previous ttyinput processing. + */ + if ((tp->t_state&TS_TYPEN) == 0 && (t_flags&PASS8) == 0) c &= 0177; - if (tp->t_state&TTSTOP) { - if (c==tun.t_startc) { - tp->t_state &= ~TTSTOP; - ttstart(tp); - return; - } - if (c==tun.t_stopc) - return; - tp->t_state &= ~TTSTOP; - ttstart(tp); - } else { - if (c==tun.t_stopc) { - tp->t_state |= TTSTOP; - (*cdevsw[major(tp->t_dev)].d_stop)(tp); - return; + /* + * Check for literal nexting very first + */ + if (tp->t_state&TS_LNCH) { + c |= 0200; + tp->t_state &= ~TS_LNCH; + } + + /* + * Scan for special characters. This code + * is really just a big case statement with + * non-constant cases. The bottom of the + * case statement is labeled ``endcase'', so goto + * it after a case match, or similar. + */ + if (tp->t_line == NTTYDISC) { + if (c == tp->t_lnextc) { + if (t_flags&ECHO) + ttyout("^\b", tp); + tp->t_state |= TS_LNCH; + goto endcase; + } + if (c == tp->t_flushc) { + if (t_flags&FLUSHO) + tp->t_flags &= ~FLUSHO; + else { + ttyflush(tp, FWRITE); + ttyecho(c, tp); + if (tp->t_rawq.c_cc + tp->t_canq.c_cc) + ttyretype(tp); + tp->t_flags |= FLUSHO; } - if (c==tun.t_startc) - return; + goto startoutput; } - if (c==tun.t_quitc || c==tun.t_intrc) { - flushtty(tp); - c = (c==tun.t_intrc) ? SIGINT:SIGQUIT; - if (tp->t_chan) - scontrol(tp->t_chan, M_SIG, c); - else - signal(tp->t_pgrp, c); + if (c == tp->t_suspc) { + if ((t_flags&NOFLSH) == 0) + ttyflush(tp, FREAD); + ttyecho(c, tp); + gsignal(tp->t_pgrp, SIGTSTP); + goto endcase; + } + } + + /* + * Handle start/stop characters. + */ + if (c == tp->t_stopc) { + if ((tp->t_state&TS_TTSTOP) == 0) { + tp->t_state |= TS_TTSTOP; + (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); return; } - if (c=='\r' && t_flags&CRMOD) - c = '\n'; + if (c != tp->t_startc) + return; + goto endcase; } - if (tp->t_rawq.c_cc>TTYHOG) { - flushtty(tp); - return; + if (c == tp->t_startc) + goto restartoutput; + + /* + * Look for interrupt/quit chars. + */ + if (c == tp->t_intrc || c == tp->t_quitc) { + if ((t_flags&NOFLSH) == 0) + ttyflush(tp, FREAD|FWRITE); + ttyecho(c, tp); + gsignal(tp->t_pgrp, c == tp->t_intrc ? SIGINT : SIGQUIT); + goto endcase; } - if (t_flags&LCASE && c>='A' && c<='Z') - c += 'a'-'A'; - (void) putc(c, &tp->t_rawq); - if (t_flags&(RAW|CBREAK)||(c=='\n'||c==tun.t_eofc||c==tun.t_brkc)) { - if ((t_flags&(RAW|CBREAK))==0 && putc(0377, &tp->t_rawq)==0) - tp->t_delct++; - if ((cp=tp->t_chan)!=NULL) - (void) sdata(cp); else - wakeup((caddr_t)&tp->t_rawq); - } - if (t_flags&ECHO) { - ttyoutput(c, tp); - if (c==tp->t_kill && (t_flags&(RAW|CBREAK))==0) - ttyoutput('\n', tp); - ttstart(tp); + + if (tp->t_flags & LCASE && c <= 0177) { + if (tp->t_state&TS_BKSL) { + ttyrub(unputc(&tp->t_rawq), tp); + if (maptab[c]) + c = maptab[c]; + c |= 0200; + tp->t_state &= ~(TS_BKSL|TS_QUOT); + } else if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + else if (c == '\\') + tp->t_state |= TS_BKSL; } -} + /* + * Cbreak mode, don't process line editing + * characters; check high water mark for wakeup. + */ + if (t_flags&CBREAK) { + if (tp->t_rawq.c_cc > TTYHOG) { + if (tp->t_outq.c_cc < TTHIWAT(tp) && + tp->t_line == NTTYDISC) + (void) ttyoutput(CTRL(g), tp); + } else if (putc(c, &tp->t_rawq) >= 0) { + ttwakeup(tp); + ttyecho(c, tp); + } + goto endcase; + } -/* - * Send stop character on input overflow. - */ -ttyblock(tp) -register struct tty *tp; -{ - register x; - x = q1.c_cc + q2.c_cc; - if (q1.c_cc > TTYHOG) { - flushtty(tp); - tp->t_state &= ~TBLOCK; - } - if (x >= TTYHOG/2) { - if (putc(tun.t_stopc, &tp->t_outq)==0) { - tp->t_state |= TBLOCK; - tp->t_char++; - ttstart(tp); + /* + * From here on down cooked mode character + * processing takes place. + */ + if ((tp->t_state&TS_QUOT) && + (c == tp->t_erase || c == tp->t_kill)) { + ttyrub(unputc(&tp->t_rawq), tp); + c |= 0200; + } + if (c == tp->t_erase) { + if (tp->t_rawq.c_cc) + ttyrub(unputc(&tp->t_rawq), tp); + goto endcase; + } + if (c == tp->t_kill) { + if (t_flags&CRTKIL && + tp->t_rawq.c_cc == tp->t_rocount) { + while (tp->t_rawq.c_cc) + ttyrub(unputc(&tp->t_rawq), tp); + } else { + ttyecho(c, tp); + ttyecho('\n', tp); + while (getc(&tp->t_rawq) > 0) + ; + tp->t_rocount = 0; + } + tp->t_state &= ~TS_LOCAL; + goto endcase; + } + + /* + * New line discipline, + * check word erase/reprint line. + */ + if (tp->t_line == NTTYDISC) { + if (c == tp->t_werasc) { + if (tp->t_rawq.c_cc == 0) + goto endcase; + do { + c = unputc(&tp->t_rawq); + if (c != ' ' && c != '\t') + goto erasenb; + ttyrub(c, tp); + } while (tp->t_rawq.c_cc); + goto endcase; + erasenb: + do { + ttyrub(c, tp); + if (tp->t_rawq.c_cc == 0) + goto endcase; + c = unputc(&tp->t_rawq); + } while (c != ' ' && c != '\t'); + (void) putc(c, &tp->t_rawq); + goto endcase; + } + if (c == tp->t_rprntc) { + ttyretype(tp); + goto endcase; } } + + /* + * Check for input buffer overflow + */ + if (tp->t_rawq.c_cc+tp->t_canq.c_cc >= TTYHOG) { + if (tp->t_line == NTTYDISC) + (void) ttyoutput(CTRL(g), tp); + goto endcase; + } + + /* + * Put data char in q for user and + * wakeup on seeing a line delimiter. + */ + if (putc(c, &tp->t_rawq) >= 0) { + if (ttbreakc(c, tp)) { + tp->t_rocount = 0; + catq(&tp->t_rawq, &tp->t_canq); + ttwakeup(tp); + } else if (tp->t_rocount++ == 0) + tp->t_rocol = tp->t_col; + tp->t_state &= ~TS_QUOT; + if (c == '\\') + tp->t_state |= TS_QUOT; + if (tp->t_state&TS_ERASE) { + tp->t_state &= ~TS_ERASE; + (void) ttyoutput('/', tp); + } + i = tp->t_col; + ttyecho(c, tp); + if (c == tp->t_eofc && t_flags&ECHO) { + i = MIN(2, tp->t_col - i); + while (i > 0) { + (void) ttyoutput('\b', tp); + i--; + } + } + } +endcase: + /* + * If DEC-style start/stop is enabled don't restart + * output until seeing the start character. + */ + if (t_flags&DECCTQ && tp->t_state&TS_TTSTOP && + tp->t_startc != tp->t_stopc) + return; +restartoutput: + tp->t_state &= ~TS_TTSTOP; + tp->t_flags &= ~FLUSHO; +startoutput: + ttstart(tp); } /* - * put character on TTY output queue, adding delays, + * 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. + * This is called both from the top half for output, + * and from interrupt level for echoing. * The arguments are the character and the tty structure. + * Returns < 0 if putc succeeds, otherwise returns char to resend + * Must be recursive. */ ttyoutput(c, tp) -register c; -register struct tty *tp; + register c; + register struct tty *tp; { register char *colp; register ctype; - /* - * Ignore EOT in normal mode to avoid hanging up - * certain terminals. - * In raw mode dump the char unchanged. - */ - if ((tp->t_flags&RAW)==0) { - c &= 0177; - if ((tp->t_flags&CBREAK)==0 && c==CEOT) - return; - } else { + if (tp->t_flags & (RAW|LITOUT)) { + if (tp->t_flags&FLUSHO) + return (-1); + if (putc(c, &tp->t_outq)) + return (c); tk_nout++; - (void) putc(c, &tp->t_outq); - return; + return (-1); } + /* + * Ignore EOT in normal mode to avoid + * hanging up certain terminals. + */ + c &= 0177; + if (c == CEOT && (tp->t_flags&CBREAK) == 0) + return (-1); /* * Turn tabs to spaces as required */ - if (c=='\t' && (tp->t_flags&TBDELAY)==XTABS) { - c = 8 - (tp->t_col & 7); - (void) b_to_q(" ", c, &tp->t_outq); + if (c == '\t' && (tp->t_flags&TBDELAY) == XTABS) { + register int s; + + c = 8 - (tp->t_col&7); + if ((tp->t_flags&FLUSHO) == 0) { + s = spltty(); /* don't interrupt tabs */ + c -= b_to_q(" ", c, &tp->t_outq); + tk_nout += c; + splx(s); + } tp->t_col += c; - tk_nout += c; - return; + return (c ? -1 : '\t'); } tk_nout++; /* @@ -587,21 +989,30 @@ register struct tty *tp; */ if (tp->t_flags&LCASE) { colp = "({)}!|^~'`"; - while(*colp++) - if(c == *colp++) { - ttyoutput('\\', tp); + while (*colp++) + if (c == *colp++) { + if (ttyoutput('\\', tp) >= 0) + return (c); c = colp[-2]; break; } - if ('a'<=c && c<='z') + if ('A' <= c && c <= 'Z') { + if (ttyoutput('\\', tp) >= 0) + return (c); + } else if ('a' <= c && c <= 'z') c += 'A' - 'a'; } + /* * turn to if desired. */ - if (c=='\n' && tp->t_flags&CRMOD) - ttyoutput('\r', tp); - (void) putc(c, &tp->t_outq); + if (c == '\n' && tp->t_flags&CRMOD) + if (ttyoutput('\r', tp) >= 0) + return (c); + if (c == '~' && tp->t_flags&TILDE) + c = '`'; + if ((tp->t_flags&FLUSHO) == 0 && putc(c, &tp->t_outq)) + return (c); /* * Calculate delays. * The numbers here represent clock ticks @@ -609,215 +1020,624 @@ register struct tty *tp; * The delays are indicated by characters above 0200. * In raw mode there are no delays and the * transmission path is 8 bits wide. + * + * SHOULD JUST ALLOW USER TO SPECIFY DELAYS */ colp = &tp->t_col; ctype = partab[c]; c = 0; switch (ctype&077) { - /* ordinary */ - case 0: + case ORDINARY: (*colp)++; - /* non-printing */ - case 1: + case CONTROL: break; - /* backspace */ - case 2: + case BACKSPACE: if (*colp) (*colp)--; break; - /* newline */ - case 3: + /* + * This macro is close enough to the correct thing; + * it should be replaced by real user settable delays + * in any event... + */ +#define mstohz(ms) (((ms) * hz) >> 10) + case NEWLINE: ctype = (tp->t_flags >> 8) & 03; - if(ctype == 1) { /* tty 37 */ - if (*colp) - c = max(((unsigned)*colp>>4) + 3, (unsigned)6); - } else - if(ctype == 2) { /* vt05 */ - c = 6; - } + if (ctype == 1) { /* tty 37 */ + if (*colp > 0) + c = max((((unsigned)*colp) >> 4) + 3, + (unsigned)6); + } else if (ctype == 2) /* vt05 */ + c = mstohz(100); *colp = 0; break; - /* tab */ - case 4: + case TAB: ctype = (tp->t_flags >> 10) & 03; - if(ctype == 1) { /* tty 37 */ + if (ctype == 1) { /* tty 37 */ c = 1 - (*colp | ~07); - if(c < 5) + if (c < 5) c = 0; } *colp |= 07; (*colp)++; break; - /* vertical motion */ - case 5: - if(tp->t_flags & VTDELAY) /* tty 37 */ + case VTAB: + if (tp->t_flags&VTDELAY) /* tty 37 */ c = 0177; break; - /* carriage return */ - case 6: + case RETURN: ctype = (tp->t_flags >> 12) & 03; - if(ctype == 1) { /* tn 300 */ - c = 5; - } else if(ctype == 2) { /* ti 700 */ - c = 10; - } else if(ctype == 3) { /* concept 100 */ + if (ctype == 1) /* tn 300 */ + c = mstohz(83); + else if (ctype == 2) /* ti 700 */ + c = mstohz(166); + else if (ctype == 3) { /* concept 100 */ int i; - for (i= *colp; i<9; i++) - (void) putc(0177, &tp->t_outq); + + if ((i = *colp) >= 0) + for (; i < 9; i++) + (void) putc(0177, &tp->t_outq); } *colp = 0; } - if(c) + if (c && (tp->t_flags&FLUSHO) == 0) (void) putc(c|0200, &tp->t_outq); + return (-1); } +#undef mstohz /* - * 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. + * Called from device's read routine after it has + * calculated the tty-structure given as argument. */ -ttrstrt(tp) -register struct tty *tp; +ttread(tp, uio) + register struct tty *tp; + struct uio *uio; { + register struct clist *qp; + register c, t_flags; + int s, first, error = 0; - tp->t_state &= ~TIMEOUT; - ttstart(tp); -} +loop: + /* + * Take any pending input first. + */ + s = spltty(); + if (tp->t_flags&PENDIN) + ttypend(tp); + splx(s); -/* - * 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. - */ -ttstart(tp) -register struct tty *tp; -{ - register s; + if ((tp->t_state&TS_CARR_ON)==0) + return (EIO); - s = spl5(); - if((tp->t_state&(TIMEOUT|TTSTOP|BUSY)) == 0) - (*tp->t_oproc)(tp); + /* + * Hang process if it's in the background. + */ + if (tp == u.u_ttyp && u.u_procp->p_pgrp != tp->t_pgrp) { + if ((u.u_procp->p_sigignore & sigmask(SIGTTIN)) || + (u.u_procp->p_sigmask & sigmask(SIGTTIN)) || + u.u_procp->p_flag&SVFORK) + return (EIO); + gsignal(u.u_procp->p_pgrp, SIGTTIN); + sleep((caddr_t)&lbolt, TTIPRI); + goto loop; + } + t_flags = tp->t_flags; + + /* + * In raw mode take characters directly from the + * raw queue w/o processing. Interlock against + * device interrupts when interrogating rawq. + */ + if (t_flags&RAW) { + s = spltty(); + if (tp->t_rawq.c_cc <= 0) { + if ((tp->t_state&TS_CARR_ON) == 0 || + (tp->t_state&TS_NBIO)) { + splx(s); + return (EWOULDBLOCK); + } + sleep((caddr_t)&tp->t_rawq, TTIPRI); + splx(s); + goto loop; + } + splx(s); + while (!error && tp->t_rawq.c_cc && uio->uio_resid) + error = ureadc(getc(&tp->t_rawq), uio); + goto checktandem; + } + + /* + * In cbreak mode use the rawq, otherwise + * take characters from the canonicalized q. + */ + qp = t_flags&CBREAK ? &tp->t_rawq : &tp->t_canq; + + /* + * No input, sleep on rawq awaiting hardware + * receipt and notification. + */ + s = spltty(); + if (qp->c_cc <= 0) { + if ((tp->t_state&TS_CARR_ON) == 0 || + (tp->t_state&TS_NBIO)) { + splx(s); + return (EWOULDBLOCK); + } + sleep((caddr_t)&tp->t_rawq, TTIPRI); + splx(s); + goto loop; + } splx(s); + + /* + * Input present, perform input mapping + * and processing (we're not in raw mode). + */ + first = 1; + while ((c = getc(qp)) >= 0) { + if (t_flags&CRMOD && c == '\r') + c = '\n'; + /* + * Check for delayed suspend character. + */ + if (tp->t_line == NTTYDISC && c == tp->t_dsuspc) { + gsignal(tp->t_pgrp, SIGTSTP); + if (first) { + sleep((caddr_t)&lbolt, TTIPRI); + goto loop; + } + break; + } + /* + * Interpret EOF only in cooked mode. + */ + if (c == tp->t_eofc && (t_flags&CBREAK) == 0) + break; + /* + * Give user character. + */ + error = ureadc(t_flags&PASS8 ? c : c & 0177, uio); + if (error) + break; + if (uio->uio_resid == 0) + break; + /* + * In cooked mode check for a "break character" + * marking the end of a "line of input". + */ + if ((t_flags&CBREAK) == 0 && ttbreakc(c, tp)) + break; + first = 0; + } + +checktandem: + /* + * Look to unblock output now that (presumably) + * the input queue has gone down. + */ + if (tp->t_state&TS_TBLOCK && tp->t_rawq.c_cc < TTYHOG/5) + if (putc(tp->t_startc, &tp->t_outq) == 0) { + tp->t_state &= ~TS_TBLOCK; + ttstart(tp); + } + return (error); } /* - * Called from device's read routine after it has - * calculated the tty-structure given as argument. + * Check the output queue on tp for space for a kernel message + * (from uprintf/tprintf). Allow some space over the normal + * hiwater mark so we don't lose messages due to normal flow + * control, but don't let the tty run amok. */ -ttread(tp) -register struct tty *tp; +ttycheckoutq(tp, wait) + register struct tty *tp; + int wait; { -register s; - - if ((tp->t_state&CARR_ON)==0) - return(-1); - s = spl5(); - if (tp->t_canq.c_cc==0) - while (canon(tp)<0) - if (tp->t_chan==NULL) { - sleep((caddr_t)&tp->t_rawq, TTIPRI); - } else { - splx(s); - return(0); - } + int hiwat, s; + + hiwat = TTHIWAT(tp); + s = spltty(); + if (tp->t_outq.c_cc > hiwat + 200) + while (tp->t_outq.c_cc > hiwat) { + ttstart(tp); + if (wait == 0) { + splx(s); + return (0); + } + tp->t_state |= TS_ASLEEP; + sleep((caddr_t)&tp->t_outq, TTOPRI); + } splx(s); - while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0) - ; - return(tp->t_rawq.c_cc+tp->t_canq.c_cc); + return (1); } /* * Called from the device's write routine after it has * calculated the tty-structure given as argument. */ -caddr_t -ttwrite(tp) -register struct tty *tp; +ttwrite(tp, uio) + register struct tty *tp; + register struct uio *uio; { - /* - * THE POSITIONING OF CP, CC, AND CE ARE CRITICAL - * AND MUST NOT BE CHANGED WITHOUT PATCHING - * THE 'ASM' INLINES BELOW. WATCH OUT. - */ register char *cp; - register int cc, ce; - register i; + register int cc, ce, c; + int i, hiwat, cnt, error, s; char obuf[OBUFSIZ]; - if ((tp->t_state&CARR_ON)==0) - return(NULL); - while (u.u_count) { - cc = MIN(u.u_count, OBUFSIZ); + hiwat = TTHIWAT(tp); + cnt = uio->uio_resid; + error = 0; +loop: + if ((tp->t_state&TS_CARR_ON) == 0) + return (EIO); + /* + * Hang the process if it's in the background. + */ + if (u.u_procp->p_pgrp != tp->t_pgrp && tp == u.u_ttyp && + (tp->t_flags&TOSTOP) && (u.u_procp->p_flag&SVFORK)==0 && + !(u.u_procp->p_sigignore & sigmask(SIGTTOU)) && + !(u.u_procp->p_sigmask & sigmask(SIGTTOU))) { + gsignal(u.u_procp->p_pgrp, SIGTTOU); + sleep((caddr_t)&lbolt, TTIPRI); + goto loop; + } + + /* + * Process the user's data in at most OBUFSIZ + * chunks. Perform lower case simulation and + * similar hacks. Keep track of high water + * mark, sleep on overflow awaiting device aid + * in acquiring new space. + */ + while (uio->uio_resid > 0) { + /* + * Grab a hunk of data from the user. + */ + cc = uio->uio_iov->iov_len; + if (cc == 0) { + uio->uio_iovcnt--; + uio->uio_iov++; + if (uio->uio_iovcnt <= 0) + panic("ttwrite"); + continue; + } + if (cc > OBUFSIZ) + cc = OBUFSIZ; cp = obuf; - iomove(cp, (unsigned)cc, B_WRITE); - if (u.u_error) + error = uiomove(cp, cc, UIO_WRITE, uio); + if (error) break; - (void) spl5(); - while (tp->t_outq.c_cc > TTHIWAT) { - ttstart(tp); - tp->t_state |= ASLEEP; - if (tp->t_chan) { - u.u_base -= cc; - u.u_offset -= cc; - u.u_count += cc; - (void) spl0(); - return((caddr_t)&tp->t_outq); + if (tp->t_outq.c_cc > hiwat) + goto ovhiwat; + if (tp->t_flags&FLUSHO) + continue; + /* + * If we're mapping lower case or kludging tildes, + * then we've got to look at each character, so + * just feed the stuff to ttyoutput... + */ + if (tp->t_flags & (LCASE|TILDE)) { + while (cc > 0) { + c = *cp++; + tp->t_rocount = 0; + while ((c = ttyoutput(c, tp)) >= 0) { + /* out of clists, wait a bit */ + ttstart(tp); + sleep((caddr_t)&lbolt, TTOPRI); + tp->t_rocount = 0; + if (cc != 0) { + uio->uio_iov->iov_base -= cc; + uio->uio_iov->iov_len += cc; + uio->uio_resid += cc; + uio->uio_offset -= cc; + } + goto loop; + } + --cc; + if (tp->t_outq.c_cc > hiwat) + goto ovhiwat; } - sleep((caddr_t)&tp->t_outq, TTOPRI); - } - (void) spl0(); - if (tp->t_flags&LCASE) { - while (cc--) - ttyoutput(*cp++,tp); continue; } - while (cc) { - if (tp->t_flags&RAW) - ce=cc; + /* + * If nothing fancy need be done, grab those characters we + * can handle without any of ttyoutput's processing and + * just transfer them to the output q. For those chars + * which require special processing (as indicated by the + * bits in partab), call ttyoutput. After processing + * a hunk of data, look for FLUSHO so ^O's will take effect + * immediately. + */ + while (cc > 0) { + if (tp->t_flags & (RAW|LITOUT)) + ce = cc; else { -#ifdef VAX - asm(" scanc r9,(r10),_partab,$077"); - asm(" subl3 r0,r9,r8"); -#else - ce=0; - while(((partab[*(cp+ce)]&077)==0)&&(cet_rocount = 0; + if (ttyoutput(*cp, tp) >= 0) { + /* no c-lists, wait a bit */ + ttstart(tp); + sleep((caddr_t)&lbolt, TTOPRI); + if (cc != 0) { + uio->uio_iov->iov_base -= cc; + uio->uio_iov->iov_len += cc; + uio->uio_resid += cc; + uio->uio_offset -= cc; + } + goto loop; + } + cp++, cc--; + if (tp->t_flags&FLUSHO || + tp->t_outq.c_cc > hiwat) + goto ovhiwat; + continue; } } - i=b_to_q(cp,ce,&tp->t_outq); - ce-=i; - tk_nout+=ce; - tp->t_col+=ce; - cp+=ce; - cc-=ce; - if (i == 0) - continue; -check: - if (tp->t_outq.c_cc > TTHIWAT) { - (void) spl5(); - while (tp->t_outq.c_cc > TTHIWAT) { - ttstart(tp); - tp->t_state |= ASLEEP; - sleep((caddr_t)&tp->t_outq, TTOPRI); - } - (void) spl0(); + /* + * A bunch of normal characters have been found, + * transfer them en masse to the output queue and + * continue processing at the top of the loop. + * If there are any further characters in this + * <= OBUFSIZ chunk, the first should be a character + * requiring special handling by ttyoutput. + */ + tp->t_rocount = 0; + i = b_to_q(cp, ce, &tp->t_outq); + ce -= i; + tp->t_col += ce; + cp += ce, cc -= ce, tk_nout += ce; + if (i > 0) { + /* out of c-lists, wait a bit */ + ttstart(tp); + sleep((caddr_t)&lbolt, TTOPRI); + uio->uio_iov->iov_base -= cc; + uio->uio_iov->iov_len += cc; + uio->uio_resid += cc; + uio->uio_offset -= cc; + goto loop; } + if (tp->t_flags&FLUSHO || tp->t_outq.c_cc > hiwat) + goto ovhiwat; } } ttstart(tp); - return(NULL); + return (error); + +ovhiwat: + s = spltty(); + if (cc != 0) { + uio->uio_iov->iov_base -= cc; + uio->uio_iov->iov_len += cc; + uio->uio_resid += cc; + uio->uio_offset -= cc; + } + /* + * This can only occur if FLUSHO + * is also set in t_flags. + */ + if (tp->t_outq.c_cc <= hiwat) { + splx(s); + goto loop; + } + ttstart(tp); + if (tp->t_state&TS_NBIO) { + splx(s); + if (uio->uio_resid == cnt) + return (EWOULDBLOCK); + return (0); + } + tp->t_state |= TS_ASLEEP; + sleep((caddr_t)&tp->t_outq, TTOPRI); + splx(s); + goto loop; +} + +/* + * Rubout one character from the rawq of tp + * as cleanly as possible. + */ +ttyrub(c, tp) + register c; + register struct tty *tp; +{ + register char *cp; + register int savecol; + int s; + char *nextc(); + + if ((tp->t_flags&ECHO) == 0) + return; + tp->t_flags &= ~FLUSHO; + c &= 0377; + if (tp->t_flags&CRTBS) { + if (tp->t_rocount == 0) { + /* + * Screwed by ttwrite; retype + */ + ttyretype(tp); + return; + } + if (c == ('\t'|0200) || c == ('\n'|0200)) + ttyrubo(tp, 2); + else switch (partab[c&=0177]&0177) { + + case ORDINARY: + if (tp->t_flags&LCASE && c >= 'A' && c <= 'Z') + ttyrubo(tp, 2); + else + ttyrubo(tp, 1); + break; + + case VTAB: + case BACKSPACE: + case CONTROL: + case RETURN: + if (tp->t_flags&CTLECH) + ttyrubo(tp, 2); + break; + + case TAB: + if (tp->t_rocount < tp->t_rawq.c_cc) { + ttyretype(tp); + return; + } + s = spltty(); + savecol = tp->t_col; + tp->t_state |= TS_CNTTB; + tp->t_flags |= FLUSHO; + tp->t_col = tp->t_rocol; + cp = tp->t_rawq.c_cf; + for (; cp; cp = nextc(&tp->t_rawq, cp)) + ttyecho(*cp, tp); + tp->t_flags &= ~FLUSHO; + tp->t_state &= ~TS_CNTTB; + splx(s); + /* + * savecol will now be length of the tab + */ + savecol -= tp->t_col; + tp->t_col += savecol; + if (savecol > 8) + savecol = 8; /* overflow screw */ + while (--savecol >= 0) + (void) ttyoutput('\b', tp); + break; + + default: + panic("ttyrub"); + } + } else if (tp->t_flags&PRTERA) { + if ((tp->t_state&TS_ERASE) == 0) { + (void) ttyoutput('\\', tp); + tp->t_state |= TS_ERASE; + } + ttyecho(c, tp); + } else + ttyecho(tp->t_erase, tp); + tp->t_rocount--; +} + +/* + * Crt back over cnt chars perhaps + * erasing them. + */ +ttyrubo(tp, cnt) + register struct tty *tp; + int cnt; +{ + register char *rubostring = tp->t_flags&CRTERA ? "\b \b" : "\b"; + + while (--cnt >= 0) + ttyout(rubostring, tp); +} + +/* + * Reprint the rawq line. + * We assume c_cc has already been checked. + */ +ttyretype(tp) + register struct tty *tp; +{ + register char *cp; + char *nextc(); + int s; + + if (tp->t_rprntc != 0377) + ttyecho(tp->t_rprntc, tp); + (void) ttyoutput('\n', tp); + s = spltty(); + for (cp = tp->t_canq.c_cf; cp; cp = nextc(&tp->t_canq, cp)) + ttyecho(*cp, tp); + for (cp = tp->t_rawq.c_cf; cp; cp = nextc(&tp->t_rawq, cp)) + ttyecho(*cp, tp); + tp->t_state &= ~TS_ERASE; + splx(s); + tp->t_rocount = tp->t_rawq.c_cc; + tp->t_rocol = 0; +} + +/* + * Echo a typed character to the terminal + */ +ttyecho(c, tp) + register c; + register struct tty *tp; +{ + + if ((tp->t_state&TS_CNTTB) == 0) + tp->t_flags &= ~FLUSHO; + if ((tp->t_flags&ECHO) == 0) + return; + c &= 0377; + if (tp->t_flags&RAW) { + (void) ttyoutput(c, tp); + return; + } + if (c == '\r' && tp->t_flags&CRMOD) + c = '\n'; + if (tp->t_flags&CTLECH) { + if ((c&0177) <= 037 && c!='\t' && c!='\n' || (c&0177)==0177) { + (void) ttyoutput('^', tp); + c &= 0177; + if (c == 0177) + c = '?'; + else if (tp->t_flags&LCASE) + c += 'a' - 1; + else + c += 'A' - 1; + } + } + (void) ttyoutput(c&0177, tp); +} + +/* + * Is c a break char for tp? + */ +ttbreakc(c, tp) + register c; + register struct tty *tp; +{ + return (c == '\n' || c == tp->t_eofc || c == tp->t_brkc || + c == '\r' && (tp->t_flags&CRMOD)); +} + +/* + * send string cp to tp + */ +ttyout(cp, tp) + register char *cp; + register struct tty *tp; +{ + register char c; + + while (c = *cp++) + (void) ttyoutput(c, tp); +} + +ttwakeup(tp) + struct tty *tp; +{ + + if (tp->t_rsel) { + selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL); + tp->t_state &= ~TS_RCOLL; + tp->t_rsel = 0; + } + if (tp->t_state & TS_ASYNC) + gsignal(tp->t_pgrp, SIGIO); + wakeup((caddr_t)&tp->t_rawq); }