# /* * general TTY subroutines */ #include "../defines.h" #include "../param.h" #ifdef AUSAML #include "../lnode.h" #endif AUSAML #include "../systm.h" #include "../file.h" #include "../user.h" #include "../tty.h" #include "../proc.h" #include "../inode.h" #include "../reg.h" #include "../conf.h" /* * 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. Mostly used for * upper-case only terminals. */ char maptab[] { 000,000,000,000,004,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, #ifdef SLOSHED 000,'|',000,000,000,000,000,'`', #endif #ifndef SLOSHED 000,'|',000,'#',000,000,000,'`', #endif '{','}',000,000,000,000,000,000, 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, #ifdef SLOSHED 000,000,000,000,000,000,000,000, #endif #ifndef SLOSHED '@',000,000,000,000,000,000,000, #endif 000,000,000,000,000,000,000,000, 000,000,000,000,000,000,000,000, #ifdef SLOSHED 000,000,000,000,'\\',000,'~',000, #endif #ifndef SLOSHED 000,000,000,000,000,000,'~',000, #endif 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, }; #ifndef ONCE #include "../cinit.h" #endif /* * structure of device registers for KL, DL, and DC * interfaces-- more particularly, those for which the * SSTART bit is off and can be treated by general routines * (that is, not DH). */ struct { int ttrcsr; int ttrbuf; int tttcsr; int tttbuf; }; /* * The routine implementing the gtty system call. * Just call lower level routine and pass back values. */ gtty() { int v[3]; register *up, *vp; vp = v; sgtty(vp); if (u.u_error) return; up = u.u_arg[0]; suword(up, *vp++); suword(++up, *vp++); suword(++up, *vp++); } /* * The routine implementing the stty system call. * Read in values and call lower level. */ stty() { register int *up; up = u.u_arg[0]; u.u_arg[0] = fuword(up); u.u_arg[1] = fuword(++up); u.u_arg[2] = fuword(++up); if(u.u_error) /* fix037 */ return; sgtty(0); } /* * Stuff common to stty and gtty. * Check legality and switch out to individual * device routine. * v is 0 for stty; the parameters are taken from u.u_arg[]. * c is non-zero for gtty and is the place in which the device * routines place their information. */ sgtty(v) int *v; { register struct file *fp; register struct inode *ip; if ((fp = getf(u.u_ar0[R0])) == NULL) return; ip = fp->f_inode; if ((ip->i_mode&IFMT) != IFCHR) { u.u_error = ENOTTY; return; } #ifdef TTY_SUSER if (u.u_uid != guid(ip) && ! suser()) return; #endif (*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v); } /* * Wait for output to drain, then flush input waiting. */ wflushtty(tp) register struct tty *tp; /* fix000 */ { spl5(); while (tp->t_outq.c_cc) { # ifdef TTY_MULTIPLEXED_PORTS ttstart( tp ); # endif TTY_MULTIPLEXED_PORTS tp->t_state =| ASLEEP; sleep(&tp->t_outq, TTOPRI); } flushtty(tp); spl0(); } /* * flush all TTY queues */ flushtty(tp) register struct tty *tp; /* fix000 */ { register int sps; #ifdef TTY_FLOW_CONTROL register *state = &tp->t_state; #endif while (getc(&tp->t_canq) >= 0); #ifndef TTY_FLOW_CONTROL while (getc(&tp->t_outq) >= 0); #endif #ifdef TTY_FLOW_CONTROL while ( getc(&tp->t_outq) >= 0 ) tp->t_count--; #endif wakeup(&tp->t_rawq); wakeup(&tp->t_outq); sps = PS->integ; spl5(); #ifdef TTY_FLOW_CONTROL if ( *state & ACKFLAG ) { *state =| FCINTFL; if ( *state & FCSLEEP ) wakeup( state ); *state =& ~(ACKFLAG|FCSLEEP); } #endif while (getc(&tp->t_rawq) >= 0); tp->t_delct = 0; PS->integ = sps; } /* * 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. */ canon(tp) register struct tty *tp; /* fix000 */ { register char *bp; char *bp1; #ifdef SLOSHED int slosh; register char *c; #endif #ifndef SLOSHED register int c; #endif spl5(); while (tp->t_delct==0) { if ((tp->t_state&CARR_ON)==0) return(0); sleep(&tp->t_rawq, TTIPRI); } spl0(); #ifndef SLOSHED loop: bp = &canonb[2]; #endif #ifdef SLOSHED bp = canonb; slosh = 0; #endif while ((c=getc(&tp->t_rawq)) >= 0) { if (c==0377) { tp->t_delct--; break; } #ifdef SLOSHED #ifndef TTY_TRUE_RAW if ((tp->t_flags&RAW)==0) { #endif if( slosh) { slosh = 0; if(maptab[c] && (tp->t_flags & LCASE) ) { c = maptab[c]; bp--; } else { if(c == tp->t_erase || c == tp->t_kill) bp--; } } else { if( c == tp->t_erase) { if(bp > canonb) bp--; continue; } if( c == tp->t_kill) c = 0377; else if( c == CEOT) continue; if( c == '\\') slosh = 1; } #ifndef TTY_TRUE_RAW } #endif *bp++ = c; if(bp >= canonb+TTYHOG) break; } c = canonb; bp1 = c; while( c < bp) { /**** 177777 below should read 377 but *c++ causes a 'movb' and ensuing sign-bit propagation screws it */ if( *c++ == 0177777) bp1 = c; } c = bp1; tp = &tp->t_canq; while(c < bp) { putc( *c, tp); *c++ = 0; /* so that passwords won't appear in core dumps */ } return(1); #endif #ifndef SLOSHED #ifndef TTY_TRUE_RAW if ((tp->t_flags&RAW)==0) { #endif if (bp[-1]!='\\') { if (c==tp->t_erase) { if (bp > &canonb[2]) bp--; continue; } if (c==tp->t_kill) goto loop; if (c==CEOT) continue; } else if (maptab[c] && (maptab[c]==c || (tp->t_flags&LCASE))) { if (bp[-2] != '\\') c = maptab[c]; bp--; } #ifndef TTY_TRUE_RAW } #endif *bp++ = c; if (bp>=canonb+TTYHOG) break; } bp1 = bp; bp = &canonb[2]; c = &tp->t_canq; while (bp<bp1) putc(*bp++, c); return(1); #endif } /* * 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(ac, tp) register struct tty *tp; /* fix000 */ { register c; register int t_flags; c = ac & 0177; #ifdef TTY_CONNECT if(tp->t_rtype) { /* * redirection of this tty port in progress */ # ifdef TTY_MULTIPLEXED_PORTS if ( tp->t_rtype == TMULTIPLEX ) { /** This line is being multiplexed **/ struct { int (*func)(); }; (*tp->t_redirect.func)( ac, tp ); return; } # endif TTY_MULTIPLEXED_PORTS if(c == tp->t_cchar && tp->t_rtype&TMASTER) { /* * master terminate received - stop slave&master */ tp = tp->t_redirect; tp->t_rtype = tp->t_cchar = 0; tp = tp->t_redirect; tp->t_rtype = tp->t_cchar = 0; tp->t_redirect = (tp->t_redirect)->t_redirect = 0; wakeup(&tp->t_rtype); return; } switch(tp->t_rtype) { case TMASTER+TCONLOG: case TCONLOG: tp = tp->t_redirect; if(tp->t_outq.c_cc < TTYHOG) { putc(c, &tp->t_outq); ttstart(tp); } return; #ifdef TTY_SPECIAL_POWERS case TMASTER+TCONSHARE: case TMASTER+TCONGRAB: tp = tp->t_redirect; break; case TCONGRAB: return; #endif TTY_SPECIAL_POWERS } } #endif TTY_CONNECT t_flags = tp->t_flags; if(c == '\r') { if (t_flags&CRMOD) c = '\n'; } #ifdef TTY_FLOW_CONTROL else if(c == ACK && (t_flags & (FLOWCNTRL|RAW)) == FLOWCNTRL) { if ( tp->t_state & FCSLEEP ) wakeup( &tp->t_state ); tp->t_state =& ~(ACKFLAG|FCINTFL|FCSLEEP); return; } #endif TTY_FLOW_CONTROL else if((c == CQUIT || c == CINTR) && (t_flags&RAW) == 0) { signal(tp, c==CINTR? SIGINT:SIGQIT); flushtty(tp); return; } if (tp->t_rawq.c_cc>=TTYHOG-1) { /* fix041 */ flushtty(tp); } else /* fix041 */ { #ifdef TTY_INVCASE if ( t_flags&INVCASE && c>='A' ) { if ( c <= 'Z' ) c =+ 'a'-'A'; else if ( c >= 'a' && c <= 'z' ) c =- 'a'-'A'; }else #endif TTY_INVCASE if (t_flags&LCASE && c>='A' && c<='Z') c =+ 'a'-'A'; #ifdef TTY_TRUE_RAW if ( t_flags & RAW ) { if ( tp->t_state & RAWSLEEP ) { tp->t_state =& ~RAWSLEEP; wakeup( &tp->t_rawq ); } if ( t_flags == RAW ) c = ac; putc(c, &tp->t_rawq); } else { putc(c, &tp->t_rawq); if(c == '\n' || c == 004) { wakeup(&tp->t_rawq); if (putc(0377, &tp->t_rawq)==0) tp->t_delct++; } } #else putc(c, &tp->t_rawq); if(t_flags&RAW || c=='\n' || c==004) { wakeup(&tp->t_rawq); if (putc(0377, &tp->t_rawq)==0) tp->t_delct++; } #endif TTY_TRUE_RAW } /* fix041 */ if(t_flags&ECHO) { ttyoutput(c, tp); if( c == '\b' && tp->t_erase == '\b') /* fix033 */ { /* fix033 */ ttyoutput(' ', tp); /* fix033 */ ttyoutput(c, tp); /* fix033 */ } /* fix033 */ ttstart(tp); } #ifdef TTY_SPECIAL_POWERS else if(tp->t_rtype == TCONVIEW || tp->t_rtype == TCONSHARE) { ttyoutput(c, tp->t_redirect); ttstart(tp->t_redirect); } #endif TTY_SPECIAL_POWERS } /* * put character on TTY output queue, adding delays, * expanding tabs, and handling the CR/NL bit. * It is called both from the top half for output, and from * interrupt level for echoing. * The arguments are the character and the tty structure. */ ttyoutput(c, tp) register char c; /* fix000 */ register struct tty *tp; /* fix000 */ { register union { char *colp; int flags; } i; /* fasterfix */ int ctype; #ifdef TTY_SPECIAL_POWERS if(tp->t_rtype) { switch(tp->t_rtype) { case TCONVIEW: case TCONSHARE: ttyoutput(c, tp->t_redirect); ttstart(tp->t_redirect); break; case TCONGRAB: ttyoutput(c, tp->t_redirect); ttstart(tp->t_redirect); return; } } #endif TTY_SPECIAL_POWERS i.flags = tp->t_flags; /* fasterfix */ #ifdef TTY_FLOW_CONTROL if ( i.flags & FLOWCNTRL && tp->t_state & ESCFL ) return; /* don't interrupt escape sequence */ #endif TTY_FLOW_CONTROL #ifdef TTY_TRUE_RAW if ( ( i.flags & RAW ) == 0 ) #endif c =& 0177; /* * Ignore EOT in normal mode to avoid hanging up * certain terminals. */ if (c==004 && (i.flags&RAW)==0) return; /* * Turn tabs to spaces as required */ if (c=='\t' && i.flags&XTABS) { c = 8 - (tp->t_col&07); /* fix022 */ do putc(' ', &tp->t_outq); while ( --c ); /* fix022 */ tp->t_col = (tp->t_col+8) & ~07; /* while (tp->t_col&07); /* fix022 */ return; } /* * for upper-case-only terminals, * generate escapes. */ if (i.flags&LCASE) { i.colp = "({)}!|^~'`"; while(*i.colp++) if(c == *i.colp++) { putc('\\', &tp->t_outq); tp->t_col++; c = i.colp[-2]; break; } if ('a'<=c && c<='z') c =+ 'A' - 'a'; i.flags = tp->t_flags; } /* * turn <nl> to <cr><lf> if desired. */ if (c=='\n' && i.flags&CRMOD) ttyoutput('\r', tp); if (putc(c, &tp->t_outq)) return; #ifdef TTY_TRUE_RAW if ( i.flags == RAW ) return; /* true RAW only */ #endif #ifdef TTY_FLOW_CONTROL tp->t_count++; #endif /* * Calculate delays. * The numbers here represent clock ticks * and are not necessarily optimal for all terminals. * The delays are indicated by characters above 0200, * thus (unfortunately) restricting the transmission * path to 7 bits. */ i.colp = &tp->t_col; ctype = partab[c]; c = 0; switch (ctype&077) { /* ordinary */ case 0: (*i.colp)++; /* non-printing */ case 1: break; /* backspace */ case 2: if (*i.colp) (*i.colp)--; break; /* newline */ case 3: ctype = (tp->t_flags >> 8) & 03; if(ctype == 1) { /* tty 37 */ if (*i.colp) c = max((*i.colp>>4) + 3, 6); } else if(ctype == 2) { /* vt05 */ c = 6; } *i.colp = 0; break; /* tab */ case 4: ctype = (tp->t_flags >> 10) & 03; if(ctype == 1) { /* tty 37 */ c = 1 - (*i.colp | ~07); if(c < 5) c = 0; } *i.colp =| 07; (*i.colp)++; break; /* vertical motion */ case 5: if(tp->t_flags & VTDELAY) /* tty 37 */ c = 0177; break; /* carriage return */ case 6: 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 ) { /* NCR260 */ /* need 1 tic delay for every 8 cols */ c = (*i.colp>>3); } *i.colp = 0; } if(c) putc(c|0200, &tp->t_outq); } /* * 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(tp) register struct tty *tp; { tp->t_state =& ~TIMEOUT; ttstart(tp); } /* * 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 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. * * Always assumed to be called at level five or greater * */ ttstart(tp) register struct tty *tp; /* fix000 */ { register int *addr, c; struct { int (*func)(); }; if ( tp->t_state & TIMEOUT ) /* fix008 */ return; addr = tp->t_addr; if (tp->t_state&SSTART) { (*addr.func)(tp); return; } if ((addr->tttcsr&DONE)==0) /* fix008 */ return; if ((c=getc(&tp->t_outq)) >= 0) { #ifdef TTY_TRUE_RAW if ( tp->t_flags == RAW ) addr->tttbuf = c; else #endif if (c<=0177) addr->tttbuf = c | (partab[c]&0200); else { timeout(ttrstrt, tp, c&0177); tp->t_state =| TIMEOUT; } } } /* * Called from device's read routine after it has * calculated the tty-structure given as argument. */ ttread(tp) register struct tty *tp; /* fix000 */ { #ifdef TTY_TRUE_RAW if ( tp->t_flags & RAW ) { do { spl5(); while ( tp->t_rawq.c_cc == 0 ) if ( tp->t_state & CARR_ON ) { tp->t_state =| RAWSLEEP; sleep( &tp->t_rawq , TTIPRI ); }else { spl0(); return; } spl0(); } while ( passc( getc( &tp->t_rawq ) ) >= 0 ); }else #endif #ifndef TTY_TRUE_RAW if ((tp->t_state&CARR_ON)==0) return; #endif if (tp->t_canq.c_cc || canon(tp)) while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0); } /* * Called from the device's write routine after it has * calculated the tty-structure given as argument. */ ttwrite(tp) register struct tty *tp; /* fix000 */ { register int *state, c; #ifdef TTY_FLOW_CONTROL extern int ttackfix(); #endif state = &tp->t_state; if ((*state&CARR_ON)==0) return; while ((c=cpass())>=0) { spl5(); #ifdef TTY_HISPEED while ( (tp->t_outq.c_cc>TTHIWAT) && ( ((tp->t_speeds&017)<=B1200) || (tp->t_outq.c_cc>TTHSHIWAT) ) ) { #endif #ifndef TTY_HISPEED while (tp->t_outq.c_cc > TTHIWAT) { #endif ttstart(tp); *state =| ASLEEP; sleep(&tp->t_outq, TTOPRI); } #ifdef TTY_FLOW_CONTROL if ( (tp->t_flags & FLOWCNTRL) && (tp->t_count >= FCBUFZ - 1) && ((*state & ESCFL) == 0) ) { while ( putc( ETX , &tp->t_outq ) ) delay( 2 ); while ( *state & ACKFLAG ) { if ( !tp->t_timep ) tp->t_timep = timeout( ttackfix, tp, FCTIMEOUT ); *state =| FCSLEEP; sleep( state , TTOPRI ); } untimeout( tp->t_timep, ttackfix, tp ); tp->t_timep = 0; *state =| ACKFLAG; tp->t_count =- FCBUFZ-1; } #endif spl0(); #ifdef TTY_FLOW_CONTROL if ( tp->t_flags & FLOWCNTRL ) if ( (*state & ESCFL) == 0 ) { if ( (c & 0177) == ESC ) { *state =| ESCFL; goto put; } } else { if ( *state & ESC2FL ) *state =& ~(ESCFL|ESC2FL); else switch ( c & 0177 ) { default: *state =& ~ESCFL; break; case VT: case HT: case RS: case US: *state =| ESC2FL; /* per DIABLO HYTERM */ } put: while ( putc( c, &tp->t_outq ) ) delay( 2 ); tp->t_count++; continue; } #endif ttyoutput(c, tp); } spl5(); ttstart(tp); spl0(); /* fix014 */ } /* * Common code for gtty and stty functions on typewriters. * If v is non-zero then gtty is being done and information is * passed back therein; * if it is zero stty is being done and the input information is in the * u_arg array. */ ttystty(tp, v) register int *v; /* fix000 */ register struct tty *tp; /* fix000 */ { if( v ) { *v++ = tp->t_speeds; v->lobyte = tp->t_erase; v->hibyte = tp->t_kill; v[1] = tp->t_flags; return(1); } wflushtty(tp); v = u.u_arg; #ifdef TTY_SUSER { register struct lnode *lp; if( (v->hibyte>tp->t_speeds.hibyte || v->lobyte>tp->t_speeds.lobyte) && (v->hibyte > TTY_MAXSPD || v->lobyte > TTY_MAXSPD) && (lp = u.u_procp->p_lnode) && (lp->l_flags & TTYOPTS) == 0) *v = tp->t_speeds; /* don't allow `bad' speeds */ } #endif TTY_SUSER tp->t_speeds = *v++; tp->t_erase = v->lobyte; tp->t_kill = v->hibyte; tp->t_flags = v[1]; return(0); } #ifdef TTY_FLOW_CONTROL ttackfix( tp ) register struct tty *tp; { tp->t_timep = 0; if ( tp->t_state & FCSLEEP ) wakeup( &tp->t_state ); tp->t_state =& ~(FCSLEEP|ACKFLAG); } #endif TTY_FLOW_CONTROL #ifdef TTY_CONNECT struct tty *ttyget(arg) int arg; { register struct tty *tp; register struct inode *ip; register struct file *fp; int dev; if((fp = getf(arg)) == NULL) { return 0; /* illegal fd passed */ } if((fp->f_flag&(FREAD|FWRITE)) != (FREAD|FWRITE)) { u.u_error = EACCES; /* no permission */ return 0; } ip = fp->f_inode; if((ip->i_mode&IFMT) != IFCHR) { u.u_error = ENOTTY; return 0; } dev = ip->i_addr[0]; if((tp = cdevsw[dev.d_major].d_tty) == 0) { u.u_error = ENOREDIRECT; /* not capable */ return 0; } return tp + dev.d_minor; } ttyconnect() { register struct tty *mtp, *stp; register int rtype; if(!suser()) return; rtype = u.u_arg[0]; /* master control character */ if((mtp = ttyget(u.u_ar0[R0])) == 0) return; if((stp = ttyget(u.u_ar0[R1])) == 0) return; spl5(); switch(rtype) { case 0: /* disconnect */ if(mtp != stp || mtp->t_rtype == 0) { u.u_error = EREDIRECT; } else { stp = mtp->t_redirect; if(stp->t_redirect != mtp) { u.u_error = EREDIRECT; } else { /* * wakeup master tty after * determining which it is */ if(mtp->t_rtype&TMASTER) wakeup(&mtp->t_rtype); else wakeup(&stp->t_rtype); mtp->t_rtype = stp->t_rtype = 0; mtp->t_cchar = stp->t_cchar = 0; mtp->t_redirect = stp->t_redirect = 0; } } break; case TCONLOG: #ifdef TTY_SPECIAL_POWERS case TCONVIEW: case TCONGRAB: case TCONSHARE: #endif TTY_SPECIAL_POWERS if(mtp == stp || mtp->t_rtype || stp->t_rtype ) { /* already redirected */ u.u_error = EREDIRECT; } else { mtp->t_rtype = TMASTER+rtype; stp->t_rtype = rtype; mtp->t_cchar = u.u_arg[1]; mtp->t_redirect = stp; stp->t_redirect = mtp; while(mtp->t_rtype) sleep(&mtp->t_rtype, PRIREDIRECT); } break; default: u.u_error = EINVAL; break; } spl0(); } #endif TTY_CONNECT