|
|
1.1 ! root 1: /*- ! 2: * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. ! 3: * Copyright (c) 1991 The Regents of the University of California. ! 4: * All rights reserved. ! 5: * ! 6: * Redistribution and use in source and binary forms, with or without ! 7: * modification, are permitted provided that the following conditions ! 8: * are met: ! 9: * 1. Redistributions of source code must retain the above copyright ! 10: * notice, this list of conditions and the following disclaimer. ! 11: * 2. Redistributions in binary form must reproduce the above copyright ! 12: * notice, this list of conditions and the following disclaimer in the ! 13: * documentation and/or other materials provided with the distribution. ! 14: * 3. All advertising materials mentioning features or use of this software ! 15: * must display the following acknowledgement: ! 16: * This product includes software developed by the University of ! 17: * California, Berkeley and its contributors. ! 18: * 4. Neither the name of the University nor the names of its contributors ! 19: * may be used to endorse or promote products derived from this software ! 20: * without specific prior written permission. ! 21: * ! 22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 32: * SUCH DAMAGE. ! 33: * ! 34: * @(#)tty.c 7.44 (Berkeley) 5/28/91 ! 35: */ ! 36: ! 37: #include "param.h" ! 38: #include "systm.h" ! 39: #include "ioctl.h" ! 40: #define TTYDEFCHARS ! 41: #include "tty.h" ! 42: #undef TTYDEFCHARS ! 43: #include "proc.h" ! 44: #include "file.h" ! 45: #include "conf.h" ! 46: #include "dkstat.h" ! 47: #include "uio.h" ! 48: #include "kernel.h" ! 49: #include "vnode.h" ! 50: #include "syslog.h" ! 51: ! 52: #include "vm/vm.h" ! 53: ! 54: static int proc_compare __P((struct proc *p1, struct proc *p2)); ! 55: ! 56: /* symbolic sleep message strings */ ! 57: char ttyin[] = "ttyin"; ! 58: char ttyout[] = "ttyout"; ! 59: char ttopen[] = "ttyopn"; ! 60: char ttclos[] = "ttycls"; ! 61: char ttybg[] = "ttybg"; ! 62: char ttybuf[] = "ttybuf"; ! 63: ! 64: /* ! 65: * Table giving parity for characters and indicating ! 66: * character classes to tty driver. The 8th bit ! 67: * indicates parity, the 7th bit indicates the character ! 68: * is an alphameric or underscore (for ALTWERASE), and the ! 69: * low 6 bits indicate delay type. If the low 6 bits are 0 ! 70: * then the character needs no special processing on output; ! 71: * classes other than 0 might be translated or (not currently) ! 72: * require delays. ! 73: */ ! 74: #define PARITY(c) (partab[c] & 0x80) ! 75: #define ISALPHA(c) (partab[(c)&TTY_CHARMASK] & 0x40) ! 76: #define CCLASSMASK 0x3f ! 77: #define CCLASS(c) (partab[c] & CCLASSMASK) ! 78: ! 79: #define E 0x00 /* even parity */ ! 80: #define O 0x80 /* odd parity */ ! 81: #define ALPHA 0x40 /* alpha or underscore */ ! 82: ! 83: #define NO ORDINARY ! 84: #define NA ORDINARY|ALPHA ! 85: #define CC CONTROL ! 86: #define BS BACKSPACE ! 87: #define NL NEWLINE ! 88: #define TB TAB ! 89: #define VT VTAB ! 90: #define CR RETURN ! 91: ! 92: char partab[] = { ! 93: E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ ! 94: O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ ! 95: O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ ! 96: E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ ! 97: O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ ! 98: E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ ! 99: E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ ! 100: O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ ! 101: O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ ! 102: E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ ! 103: E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ ! 104: O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ ! 105: E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ ! 106: O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ ! 107: O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ ! 108: E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ ! 109: /* ! 110: * "meta" chars; should be settable per charset. ! 111: * For now, treat all as normal characters. ! 112: */ ! 113: NA, NA, NA, NA, NA, NA, NA, NA, ! 114: NA, NA, NA, NA, NA, NA, NA, NA, ! 115: NA, NA, NA, NA, NA, NA, NA, NA, ! 116: NA, NA, NA, NA, NA, NA, NA, NA, ! 117: NA, NA, NA, NA, NA, NA, NA, NA, ! 118: NA, NA, NA, NA, NA, NA, NA, NA, ! 119: NA, NA, NA, NA, NA, NA, NA, NA, ! 120: NA, NA, NA, NA, NA, NA, NA, NA, ! 121: NA, NA, NA, NA, NA, NA, NA, NA, ! 122: NA, NA, NA, NA, NA, NA, NA, NA, ! 123: NA, NA, NA, NA, NA, NA, NA, NA, ! 124: NA, NA, NA, NA, NA, NA, NA, NA, ! 125: NA, NA, NA, NA, NA, NA, NA, NA, ! 126: NA, NA, NA, NA, NA, NA, NA, NA, ! 127: NA, NA, NA, NA, NA, NA, NA, NA, ! 128: NA, NA, NA, NA, NA, NA, NA, NA, ! 129: }; ! 130: #undef NO ! 131: #undef NA ! 132: #undef CC ! 133: #undef BS ! 134: #undef NL ! 135: #undef TB ! 136: #undef VT ! 137: #undef CR ! 138: ! 139: extern struct tty *constty; /* temporary virtual console */ ! 140: ! 141: /* ! 142: * Is 'c' a line delimiter ("break" character)? ! 143: */ ! 144: #define ttbreakc(c) ((c) == '\n' || ((c) == cc[VEOF] || \ ! 145: (c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE) ! 146: ! 147: ttychars(tp) ! 148: struct tty *tp; ! 149: { ! 150: ! 151: bcopy(ttydefchars, tp->t_cc, sizeof(ttydefchars)); ! 152: } ! 153: ! 154: /* ! 155: * Flush tty after output has drained. ! 156: */ ! 157: ttywflush(tp) ! 158: struct tty *tp; ! 159: { ! 160: int error; ! 161: ! 162: if ((error = ttywait(tp)) == 0) ! 163: ttyflush(tp, FREAD); ! 164: return (error); ! 165: } ! 166: ! 167: /* ! 168: * Wait for output to drain. ! 169: */ ! 170: ttywait(tp) ! 171: register struct tty *tp; ! 172: { ! 173: int error = 0, s = spltty(); ! 174: ! 175: while ((tp->t_outq.c_cc || tp->t_state&TS_BUSY) && ! 176: (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL) && ! 177: tp->t_oproc) { ! 178: (*tp->t_oproc)(tp); ! 179: tp->t_state |= TS_ASLEEP; ! 180: if (error = ttysleep(tp, (caddr_t)&tp->t_outq, ! 181: TTOPRI | PCATCH, ttyout, 0)) ! 182: break; ! 183: } ! 184: splx(s); ! 185: return (error); ! 186: } ! 187: ! 188: #define flushq(qq) { \ ! 189: register struct clist *q = qq; \ ! 190: if (q->c_cc) \ ! 191: ndflush(q, q->c_cc); \ ! 192: } ! 193: ! 194: /* ! 195: * Flush TTY read and/or write queues, ! 196: * notifying anyone waiting. ! 197: */ ! 198: ttyflush(tp, rw) ! 199: register struct tty *tp; ! 200: { ! 201: register s; ! 202: ! 203: s = spltty(); ! 204: if (rw & FREAD) { ! 205: flushq(&tp->t_canq); ! 206: flushq(&tp->t_rawq); ! 207: tp->t_rocount = 0; ! 208: tp->t_rocol = 0; ! 209: tp->t_state &= ~TS_LOCAL; ! 210: ttwakeup(tp); ! 211: } ! 212: if (rw & FWRITE) { ! 213: tp->t_state &= ~TS_TTSTOP; ! 214: (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw); ! 215: flushq(&tp->t_outq); ! 216: wakeup((caddr_t)&tp->t_outq); ! 217: if (tp->t_wsel) { ! 218: selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); ! 219: tp->t_wsel = 0; ! 220: tp->t_state &= ~TS_WCOLL; ! 221: } ! 222: } ! 223: splx(s); ! 224: } ! 225: ! 226: /* ! 227: * Send stop character on input overflow. ! 228: */ ! 229: ttyblock(tp) ! 230: register struct tty *tp; ! 231: { ! 232: register x; ! 233: ! 234: x = tp->t_rawq.c_cc + tp->t_canq.c_cc; ! 235: if (tp->t_rawq.c_cc > TTYHOG) { ! 236: ttyflush(tp, FREAD|FWRITE); ! 237: tp->t_state &= ~TS_TBLOCK; ! 238: } ! 239: /* ! 240: * Block further input iff: ! 241: * Current input > threshold AND input is available to user program ! 242: */ ! 243: if (x >= TTYHOG/2 && (tp->t_state & TS_TBLOCK) == 0 && ! 244: ((tp->t_lflag&ICANON) == 0) || (tp->t_canq.c_cc > 0) && ! 245: tp->t_cc[VSTOP] != _POSIX_VDISABLE) { ! 246: if (putc(tp->t_cc[VSTOP], &tp->t_outq) == 0) { ! 247: tp->t_state |= TS_TBLOCK; ! 248: ttstart(tp); ! 249: } ! 250: } ! 251: } ! 252: ! 253: ttstart(tp) ! 254: struct tty *tp; ! 255: { ! 256: ! 257: if (tp->t_oproc) /* kludge for pty */ ! 258: (*tp->t_oproc)(tp); ! 259: } ! 260: ! 261: ttrstrt(tp) /* XXX */ ! 262: struct tty *tp; ! 263: { ! 264: ! 265: #ifdef DIAGNOSTIC ! 266: if (tp == 0) ! 267: panic("ttrstrt"); ! 268: #endif ! 269: tp->t_state &= ~TS_TIMEOUT; ! 270: ttstart(tp); ! 271: } ! 272: ! 273: ! 274: /* ! 275: * Common code for ioctls on tty devices. ! 276: * Called after line-discipline-specific ioctl ! 277: * has been called to do discipline-specific functions ! 278: * and/or reject any of these ioctl commands. ! 279: */ ! 280: /*ARGSUSED*/ ! 281: ttioctl(tp, com, data, flag) ! 282: register struct tty *tp; ! 283: caddr_t data; ! 284: { ! 285: register struct proc *p = curproc; /* XXX */ ! 286: extern int nldisp; ! 287: int s, error; ! 288: ! 289: /* ! 290: * If the ioctl involves modification, ! 291: * hang if in the background. ! 292: */ ! 293: switch (com) { ! 294: ! 295: case TIOCSETD: ! 296: case TIOCFLUSH: ! 297: /*case TIOCSPGRP:*/ ! 298: case TIOCSTI: ! 299: case TIOCSWINSZ: ! 300: case TIOCSETA: ! 301: case TIOCSETAW: ! 302: case TIOCSETAF: ! 303: #ifdef COMPAT_43 ! 304: case TIOCSETP: ! 305: case TIOCSETN: ! 306: case TIOCSETC: ! 307: case TIOCSLTC: ! 308: case TIOCLBIS: ! 309: case TIOCLBIC: ! 310: case TIOCLSET: ! 311: case OTIOCSETD: ! 312: #endif ! 313: while (isbackground(curproc, tp) && ! 314: p->p_pgrp->pg_jobc && (p->p_flag&SPPWAIT) == 0 && ! 315: (p->p_sigignore & sigmask(SIGTTOU)) == 0 && ! 316: (p->p_sigmask & sigmask(SIGTTOU)) == 0) { ! 317: pgsignal(p->p_pgrp, SIGTTOU, 1); ! 318: if (error = ttysleep(tp, (caddr_t)&lbolt, ! 319: TTOPRI | PCATCH, ttybg, 0)) ! 320: return (error); ! 321: } ! 322: break; ! 323: } ! 324: ! 325: /* ! 326: * Process the ioctl. ! 327: */ ! 328: switch (com) { ! 329: ! 330: /* get discipline number */ ! 331: case TIOCGETD: ! 332: *(int *)data = tp->t_line; ! 333: break; ! 334: ! 335: /* set line discipline */ ! 336: case TIOCSETD: { ! 337: register int t = *(int *)data; ! 338: dev_t dev = tp->t_dev; ! 339: ! 340: if ((unsigned)t >= nldisp) ! 341: return (ENXIO); ! 342: if (t != tp->t_line) { ! 343: s = spltty(); ! 344: (*linesw[tp->t_line].l_close)(tp, flag); ! 345: error = (*linesw[t].l_open)(dev, tp); ! 346: if (error) { ! 347: (void)(*linesw[tp->t_line].l_open)(dev, tp); ! 348: splx(s); ! 349: return (error); ! 350: } ! 351: tp->t_line = t; ! 352: splx(s); ! 353: } ! 354: break; ! 355: } ! 356: ! 357: /* prevent more opens on channel */ ! 358: case TIOCEXCL: ! 359: tp->t_state |= TS_XCLUDE; ! 360: break; ! 361: ! 362: case TIOCNXCL: ! 363: tp->t_state &= ~TS_XCLUDE; ! 364: break; ! 365: ! 366: case TIOCHPCL: ! 367: tp->t_cflag |= HUPCL; ! 368: break; ! 369: ! 370: case TIOCFLUSH: { ! 371: register int flags = *(int *)data; ! 372: ! 373: if (flags == 0) ! 374: flags = FREAD|FWRITE; ! 375: else ! 376: flags &= FREAD|FWRITE; ! 377: ttyflush(tp, flags); ! 378: break; ! 379: } ! 380: ! 381: case FIOASYNC: ! 382: if (*(int *)data) ! 383: tp->t_state |= TS_ASYNC; ! 384: else ! 385: tp->t_state &= ~TS_ASYNC; ! 386: break; ! 387: ! 388: case FIONBIO: ! 389: break; /* XXX remove */ ! 390: ! 391: /* return number of characters immediately available */ ! 392: case FIONREAD: ! 393: *(off_t *)data = ttnread(tp); ! 394: break; ! 395: ! 396: case TIOCOUTQ: ! 397: *(int *)data = tp->t_outq.c_cc; ! 398: break; ! 399: ! 400: case TIOCSTOP: ! 401: s = spltty(); ! 402: if ((tp->t_state&TS_TTSTOP) == 0) { ! 403: tp->t_state |= TS_TTSTOP; ! 404: (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); ! 405: } ! 406: splx(s); ! 407: break; ! 408: ! 409: case TIOCSTART: ! 410: s = spltty(); ! 411: if ((tp->t_state&TS_TTSTOP) || (tp->t_lflag&FLUSHO)) { ! 412: tp->t_state &= ~TS_TTSTOP; ! 413: tp->t_lflag &= ~FLUSHO; ! 414: ttstart(tp); ! 415: } ! 416: splx(s); ! 417: break; ! 418: ! 419: /* ! 420: * Simulate typing of a character at the terminal. ! 421: */ ! 422: case TIOCSTI: ! 423: if (p->p_ucred->cr_uid && (flag & FREAD) == 0) ! 424: return (EPERM); ! 425: if (p->p_ucred->cr_uid && !isctty(p, tp)) ! 426: return (EACCES); ! 427: (*linesw[tp->t_line].l_rint)(*(char *)data, tp); ! 428: break; ! 429: ! 430: case TIOCGETA: { ! 431: struct termios *t = (struct termios *)data; ! 432: ! 433: bcopy(&tp->t_termios, t, sizeof(struct termios)); ! 434: break; ! 435: } ! 436: ! 437: case TIOCSETA: ! 438: case TIOCSETAW: ! 439: case TIOCSETAF: { ! 440: register struct termios *t = (struct termios *)data; ! 441: ! 442: s = spltty(); ! 443: if (com == TIOCSETAW || com == TIOCSETAF) { ! 444: if (error = ttywait(tp)) { ! 445: splx(s); ! 446: return (error); ! 447: } ! 448: if (com == TIOCSETAF) ! 449: ttyflush(tp, FREAD); ! 450: } ! 451: if ((t->c_cflag&CIGNORE) == 0) { ! 452: /* ! 453: * set device hardware ! 454: */ ! 455: if (tp->t_param && (error = (*tp->t_param)(tp, t))) { ! 456: splx(s); ! 457: return (error); ! 458: } else { ! 459: if ((tp->t_state&TS_CARR_ON) == 0 && ! 460: (tp->t_cflag&CLOCAL) && ! 461: (t->c_cflag&CLOCAL) == 0) { ! 462: tp->t_state &= ~TS_ISOPEN; ! 463: tp->t_state |= TS_WOPEN; ! 464: ttwakeup(tp); ! 465: } ! 466: tp->t_cflag = t->c_cflag; ! 467: tp->t_ispeed = t->c_ispeed; ! 468: tp->t_ospeed = t->c_ospeed; ! 469: } ! 470: ttsetwater(tp); ! 471: } ! 472: if (com != TIOCSETAF) { ! 473: if ((t->c_lflag&ICANON) != (tp->t_lflag&ICANON)) ! 474: if (t->c_lflag&ICANON) { ! 475: tp->t_lflag |= PENDIN; ! 476: ttwakeup(tp); ! 477: } ! 478: else { ! 479: struct clist tq; ! 480: ! 481: catq(&tp->t_rawq, &tp->t_canq); ! 482: tq = tp->t_rawq; ! 483: tp->t_rawq = tp->t_canq; ! 484: tp->t_canq = tq; ! 485: } ! 486: } ! 487: tp->t_iflag = t->c_iflag; ! 488: tp->t_oflag = t->c_oflag; ! 489: /* ! 490: * Make the EXTPROC bit read only. ! 491: */ ! 492: if (tp->t_lflag&EXTPROC) ! 493: t->c_lflag |= EXTPROC; ! 494: else ! 495: t->c_lflag &= ~EXTPROC; ! 496: tp->t_lflag = t->c_lflag; ! 497: bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); ! 498: splx(s); ! 499: break; ! 500: } ! 501: ! 502: /* ! 503: * Set controlling terminal. ! 504: * Session ctty vnode pointer set in vnode layer. ! 505: */ ! 506: case TIOCSCTTY: ! 507: if (!SESS_LEADER(p) || ! 508: (p->p_session->s_ttyvp || tp->t_session) && ! 509: (tp->t_session != p->p_session)) ! 510: return (EPERM); ! 511: tp->t_session = p->p_session; ! 512: tp->t_pgrp = p->p_pgrp; ! 513: p->p_session->s_ttyp = tp; ! 514: p->p_flag |= SCTTY; ! 515: break; ! 516: ! 517: /* ! 518: * Set terminal process group. ! 519: */ ! 520: case TIOCSPGRP: { ! 521: register struct pgrp *pgrp = pgfind(*(int *)data); ! 522: ! 523: if (!isctty(p, tp)) ! 524: return (ENOTTY); ! 525: else if (pgrp == NULL || pgrp->pg_session != p->p_session) ! 526: return (EPERM); ! 527: tp->t_pgrp = pgrp; ! 528: break; ! 529: } ! 530: ! 531: case TIOCGPGRP: ! 532: if (!isctty(p, tp)) ! 533: return (ENOTTY); ! 534: *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; ! 535: break; ! 536: ! 537: case TIOCSWINSZ: ! 538: if (bcmp((caddr_t)&tp->t_winsize, data, ! 539: sizeof (struct winsize))) { ! 540: tp->t_winsize = *(struct winsize *)data; ! 541: pgsignal(tp->t_pgrp, SIGWINCH, 1); ! 542: } ! 543: break; ! 544: ! 545: case TIOCGWINSZ: ! 546: *(struct winsize *)data = tp->t_winsize; ! 547: break; ! 548: ! 549: case TIOCCONS: ! 550: if (*(int *)data) { ! 551: if (constty && constty != tp && ! 552: (constty->t_state & (TS_CARR_ON|TS_ISOPEN)) == ! 553: (TS_CARR_ON|TS_ISOPEN)) ! 554: return (EBUSY); ! 555: #ifndef UCONSOLE ! 556: if (error = suser(p->p_ucred, &p->p_acflag)) ! 557: return (error); ! 558: #endif ! 559: constty = tp; ! 560: } else if (tp == constty) ! 561: constty = NULL; ! 562: break; ! 563: ! 564: case TIOCDRAIN: ! 565: if (error = ttywait(tp)) ! 566: return (error); ! 567: break; ! 568: ! 569: default: ! 570: #ifdef COMPAT_43 ! 571: return (ttcompat(tp, com, data, flag)); ! 572: #else ! 573: return (-1); ! 574: #endif ! 575: } ! 576: return (0); ! 577: } ! 578: ! 579: ttnread(tp) ! 580: struct tty *tp; ! 581: { ! 582: int nread = 0; ! 583: ! 584: if (tp->t_lflag & PENDIN) ! 585: ttypend(tp); ! 586: nread = tp->t_canq.c_cc; ! 587: if ((tp->t_lflag & ICANON) == 0) ! 588: nread += tp->t_rawq.c_cc; ! 589: return (nread); ! 590: } ! 591: ! 592: ttselect(dev, rw) ! 593: dev_t dev; ! 594: int rw; ! 595: { ! 596: register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)]; ! 597: int nread; ! 598: int s = spltty(); ! 599: ! 600: switch (rw) { ! 601: ! 602: case FREAD: ! 603: nread = ttnread(tp); ! 604: if (nread > 0 || ! 605: ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) ! 606: goto win; ! 607: if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait) ! 608: tp->t_state |= TS_RCOLL; ! 609: else ! 610: tp->t_rsel = curproc; ! 611: break; ! 612: ! 613: case FWRITE: ! 614: if (tp->t_outq.c_cc <= tp->t_lowat) ! 615: goto win; ! 616: if (tp->t_wsel && tp->t_wsel->p_wchan == (caddr_t)&selwait) ! 617: tp->t_state |= TS_WCOLL; ! 618: else ! 619: tp->t_wsel = curproc; ! 620: break; ! 621: } ! 622: splx(s); ! 623: return (0); ! 624: win: ! 625: splx(s); ! 626: return (1); ! 627: } ! 628: ! 629: /* ! 630: * Initial open of tty, or (re)entry to standard tty line discipline. ! 631: */ ! 632: ttyopen(dev, tp) ! 633: dev_t dev; ! 634: register struct tty *tp; ! 635: { ! 636: ! 637: tp->t_dev = dev; ! 638: ! 639: tp->t_state &= ~TS_WOPEN; ! 640: if ((tp->t_state & TS_ISOPEN) == 0) { ! 641: tp->t_state |= TS_ISOPEN; ! 642: bzero((caddr_t)&tp->t_winsize, sizeof(tp->t_winsize)); ! 643: } ! 644: return (0); ! 645: } ! 646: ! 647: /* ! 648: * "close" a line discipline ! 649: */ ! 650: ttylclose(tp, flag) ! 651: struct tty *tp; ! 652: int flag; ! 653: { ! 654: ! 655: if (flag&IO_NDELAY) ! 656: ttyflush(tp, FREAD|FWRITE); ! 657: else ! 658: ttywflush(tp); ! 659: } ! 660: ! 661: /* ! 662: * Handle close() on a tty line: flush and set to initial state, ! 663: * bumping generation number so that pending read/write calls ! 664: * can detect recycling of the tty. ! 665: */ ! 666: ttyclose(tp) ! 667: register struct tty *tp; ! 668: { ! 669: if (constty == tp) ! 670: constty = NULL; ! 671: ttyflush(tp, FREAD|FWRITE); ! 672: tp->t_session = NULL; ! 673: tp->t_pgrp = NULL; ! 674: tp->t_state = 0; ! 675: tp->t_gen++; ! 676: return (0); ! 677: } ! 678: ! 679: /* ! 680: * Handle modem control transition on a tty. ! 681: * Flag indicates new state of carrier. ! 682: * Returns 0 if the line should be turned off, otherwise 1. ! 683: */ ! 684: ttymodem(tp, flag) ! 685: register struct tty *tp; ! 686: { ! 687: ! 688: if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) { ! 689: /* ! 690: * MDMBUF: do flow control according to carrier flag ! 691: */ ! 692: if (flag) { ! 693: tp->t_state &= ~TS_TTSTOP; ! 694: ttstart(tp); ! 695: } else if ((tp->t_state&TS_TTSTOP) == 0) { ! 696: tp->t_state |= TS_TTSTOP; ! 697: (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); ! 698: } ! 699: } else if (flag == 0) { ! 700: /* ! 701: * Lost carrier. ! 702: */ ! 703: tp->t_state &= ~TS_CARR_ON; ! 704: if (tp->t_state&TS_ISOPEN && (tp->t_cflag&CLOCAL) == 0) { ! 705: if (tp->t_session && tp->t_session->s_leader) ! 706: psignal(tp->t_session->s_leader, SIGHUP); ! 707: ttyflush(tp, FREAD|FWRITE); ! 708: return (0); ! 709: } ! 710: } else { ! 711: /* ! 712: * Carrier now on. ! 713: */ ! 714: tp->t_state |= TS_CARR_ON; ! 715: ttwakeup(tp); ! 716: } ! 717: return (1); ! 718: } ! 719: ! 720: /* ! 721: * Default modem control routine (for other line disciplines). ! 722: * Return argument flag, to turn off device on carrier drop. ! 723: */ ! 724: nullmodem(tp, flag) ! 725: register struct tty *tp; ! 726: int flag; ! 727: { ! 728: ! 729: if (flag) ! 730: tp->t_state |= TS_CARR_ON; ! 731: else { ! 732: tp->t_state &= ~TS_CARR_ON; ! 733: if ((tp->t_cflag&CLOCAL) == 0) { ! 734: if (tp->t_session && tp->t_session->s_leader) ! 735: psignal(tp->t_session->s_leader, SIGHUP); ! 736: return (0); ! 737: } ! 738: } ! 739: return (1); ! 740: } ! 741: ! 742: /* ! 743: * reinput pending characters after state switch ! 744: * call at spltty(). ! 745: */ ! 746: ttypend(tp) ! 747: register struct tty *tp; ! 748: { ! 749: struct clist tq; ! 750: register c; ! 751: ! 752: tp->t_lflag &= ~PENDIN; ! 753: tp->t_state |= TS_TYPEN; ! 754: tq = tp->t_rawq; ! 755: tp->t_rawq.c_cc = 0; ! 756: tp->t_rawq.c_cf = tp->t_rawq.c_cl = 0; ! 757: while ((c = getc(&tq)) >= 0) ! 758: ttyinput(c, tp); ! 759: tp->t_state &= ~TS_TYPEN; ! 760: } ! 761: ! 762: /* ! 763: * Process input of a single character received on a tty. ! 764: */ ! 765: ttyinput(c, tp) ! 766: register c; ! 767: register struct tty *tp; ! 768: { ! 769: register int iflag = tp->t_iflag; ! 770: register int lflag = tp->t_lflag; ! 771: register u_char *cc = tp->t_cc; ! 772: int i, err; ! 773: ! 774: /* ! 775: * If input is pending take it first. ! 776: */ ! 777: if (lflag&PENDIN) ! 778: ttypend(tp); ! 779: /* ! 780: * Gather stats. ! 781: */ ! 782: tk_nin++; ! 783: if (lflag&ICANON) { ! 784: tk_cancc++; ! 785: tp->t_cancc++; ! 786: } else { ! 787: tk_rawcc++; ! 788: tp->t_rawcc++; ! 789: } ! 790: /* ! 791: * Handle exceptional conditions (break, parity, framing). ! 792: */ ! 793: if (err = (c&TTY_ERRORMASK)) { ! 794: c &= ~TTY_ERRORMASK; ! 795: if (err&TTY_FE && !c) { /* break */ ! 796: if (iflag&IGNBRK) ! 797: goto endcase; ! 798: else if (iflag&BRKINT && lflag&ISIG && ! 799: (cc[VINTR] != _POSIX_VDISABLE)) ! 800: c = cc[VINTR]; ! 801: else if (iflag&PARMRK) ! 802: goto parmrk; ! 803: } else if ((err&TTY_PE && iflag&INPCK) || err&TTY_FE) { ! 804: if (iflag&IGNPAR) ! 805: goto endcase; ! 806: else if (iflag&PARMRK) { ! 807: parmrk: ! 808: putc(0377|TTY_QUOTE, &tp->t_rawq); ! 809: putc(0|TTY_QUOTE, &tp->t_rawq); ! 810: putc(c|TTY_QUOTE, &tp->t_rawq); ! 811: goto endcase; ! 812: } else ! 813: c = 0; ! 814: } ! 815: } ! 816: /* ! 817: * In tandem mode, check high water mark. ! 818: */ ! 819: if (iflag&IXOFF) ! 820: ttyblock(tp); ! 821: if ((tp->t_state&TS_TYPEN) == 0 && (iflag&ISTRIP)) ! 822: c &= ~0x80; ! 823: if ((tp->t_lflag&EXTPROC) == 0) { ! 824: /* ! 825: * Check for literal nexting very first ! 826: */ ! 827: if (tp->t_state&TS_LNCH) { ! 828: c |= TTY_QUOTE; ! 829: tp->t_state &= ~TS_LNCH; ! 830: } ! 831: /* ! 832: * Scan for special characters. This code ! 833: * is really just a big case statement with ! 834: * non-constant cases. The bottom of the ! 835: * case statement is labeled ``endcase'', so goto ! 836: * it after a case match, or similar. ! 837: */ ! 838: ! 839: /* ! 840: * Control chars which aren't controlled ! 841: * by ICANON, ISIG, or IXON. ! 842: */ ! 843: if (lflag&IEXTEN) { ! 844: if (CCEQ(cc[VLNEXT], c)) { ! 845: if (lflag&ECHO) { ! 846: if (lflag&ECHOE) ! 847: ttyoutstr("^\b", tp); ! 848: else ! 849: ttyecho(c, tp); ! 850: } ! 851: tp->t_state |= TS_LNCH; ! 852: goto endcase; ! 853: } ! 854: if (CCEQ(cc[VDISCARD], c)) { ! 855: if (lflag&FLUSHO) ! 856: tp->t_lflag &= ~FLUSHO; ! 857: else { ! 858: ttyflush(tp, FWRITE); ! 859: ttyecho(c, tp); ! 860: if (tp->t_rawq.c_cc + tp->t_canq.c_cc) ! 861: ttyretype(tp); ! 862: tp->t_lflag |= FLUSHO; ! 863: } ! 864: goto startoutput; ! 865: } ! 866: } ! 867: /* ! 868: * Signals. ! 869: */ ! 870: if (lflag&ISIG) { ! 871: if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { ! 872: if ((lflag&NOFLSH) == 0) ! 873: ttyflush(tp, FREAD|FWRITE); ! 874: ttyecho(c, tp); ! 875: pgsignal(tp->t_pgrp, ! 876: CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); ! 877: goto endcase; ! 878: } ! 879: if (CCEQ(cc[VSUSP], c)) { ! 880: if ((lflag&NOFLSH) == 0) ! 881: ttyflush(tp, FREAD); ! 882: ttyecho(c, tp); ! 883: pgsignal(tp->t_pgrp, SIGTSTP, 1); ! 884: goto endcase; ! 885: } ! 886: } ! 887: /* ! 888: * Handle start/stop characters. ! 889: */ ! 890: if (iflag&IXON) { ! 891: if (CCEQ(cc[VSTOP], c)) { ! 892: if ((tp->t_state&TS_TTSTOP) == 0) { ! 893: tp->t_state |= TS_TTSTOP; ! 894: (*cdevsw[major(tp->t_dev)].d_stop)(tp, ! 895: 0); ! 896: return; ! 897: } ! 898: if (!CCEQ(cc[VSTART], c)) ! 899: return; ! 900: /* ! 901: * if VSTART == VSTOP then toggle ! 902: */ ! 903: goto endcase; ! 904: } ! 905: if (CCEQ(cc[VSTART], c)) ! 906: goto restartoutput; ! 907: } ! 908: /* ! 909: * IGNCR, ICRNL, & INLCR ! 910: */ ! 911: if (c == '\r') { ! 912: if (iflag&IGNCR) ! 913: goto endcase; ! 914: else if (iflag&ICRNL) ! 915: c = '\n'; ! 916: } else if (c == '\n' && iflag&INLCR) ! 917: c = '\r'; ! 918: } ! 919: if ((tp->t_lflag&EXTPROC) == 0 && lflag&ICANON) { ! 920: /* ! 921: * From here on down canonical mode character ! 922: * processing takes place. ! 923: */ ! 924: /* ! 925: * erase (^H / ^?) ! 926: */ ! 927: if (CCEQ(cc[VERASE], c)) { ! 928: if (tp->t_rawq.c_cc) ! 929: ttyrub(unputc(&tp->t_rawq), tp); ! 930: goto endcase; ! 931: } ! 932: /* ! 933: * kill (^U) ! 934: */ ! 935: if (CCEQ(cc[VKILL], c)) { ! 936: if (lflag&ECHOKE && tp->t_rawq.c_cc == tp->t_rocount && ! 937: (lflag&ECHOPRT) == 0) { ! 938: while (tp->t_rawq.c_cc) ! 939: ttyrub(unputc(&tp->t_rawq), tp); ! 940: } else { ! 941: ttyecho(c, tp); ! 942: if (lflag&ECHOK || lflag&ECHOKE) ! 943: ttyecho('\n', tp); ! 944: while (getc(&tp->t_rawq) > 0) ! 945: ; ! 946: tp->t_rocount = 0; ! 947: } ! 948: tp->t_state &= ~TS_LOCAL; ! 949: goto endcase; ! 950: } ! 951: /* ! 952: * word erase (^W) ! 953: */ ! 954: if (CCEQ(cc[VWERASE], c)) { ! 955: int ctype; ! 956: int alt = lflag&ALTWERASE; ! 957: ! 958: /* ! 959: * erase whitespace ! 960: */ ! 961: while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t') ! 962: ttyrub(c, tp); ! 963: if (c == -1) ! 964: goto endcase; ! 965: /* ! 966: * erase last char of word and remember the ! 967: * next chars type (for ALTWERASE) ! 968: */ ! 969: ttyrub(c, tp); ! 970: c = unputc(&tp->t_rawq); ! 971: if (c == -1) ! 972: goto endcase; ! 973: ctype = ISALPHA(c); ! 974: /* ! 975: * erase rest of word ! 976: */ ! 977: do { ! 978: ttyrub(c, tp); ! 979: c = unputc(&tp->t_rawq); ! 980: if (c == -1) ! 981: goto endcase; ! 982: } while (c != ' ' && c != '\t' && ! 983: (alt == 0 || ISALPHA(c) == ctype)); ! 984: (void) putc(c, &tp->t_rawq); ! 985: goto endcase; ! 986: } ! 987: /* ! 988: * reprint line (^R) ! 989: */ ! 990: if (CCEQ(cc[VREPRINT], c)) { ! 991: ttyretype(tp); ! 992: goto endcase; ! 993: } ! 994: /* ! 995: * ^T - kernel info and generate SIGINFO ! 996: */ ! 997: if (CCEQ(cc[VSTATUS], c)) { ! 998: pgsignal(tp->t_pgrp, SIGINFO, 1); ! 999: if ((lflag&NOKERNINFO) == 0) ! 1000: ttyinfo(tp); ! 1001: goto endcase; ! 1002: } ! 1003: } ! 1004: /* ! 1005: * Check for input buffer overflow ! 1006: */ ! 1007: if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG) { ! 1008: if (iflag&IMAXBEL) { ! 1009: if (tp->t_outq.c_cc < tp->t_hiwat) ! 1010: (void) ttyoutput(CTRL('g'), tp); ! 1011: } else ! 1012: ttyflush(tp, FREAD | FWRITE); ! 1013: goto endcase; ! 1014: } ! 1015: /* ! 1016: * Put data char in q for user and ! 1017: * wakeup on seeing a line delimiter. ! 1018: */ ! 1019: if (putc(c, &tp->t_rawq) >= 0) { ! 1020: if ((lflag&ICANON) == 0) { ! 1021: ttwakeup(tp); ! 1022: ttyecho(c, tp); ! 1023: goto endcase; ! 1024: } ! 1025: if (ttbreakc(c)) { ! 1026: tp->t_rocount = 0; ! 1027: catq(&tp->t_rawq, &tp->t_canq); ! 1028: ttwakeup(tp); ! 1029: } else if (tp->t_rocount++ == 0) ! 1030: tp->t_rocol = tp->t_col; ! 1031: if (tp->t_state&TS_ERASE) { ! 1032: /* ! 1033: * end of prterase \.../ ! 1034: */ ! 1035: tp->t_state &= ~TS_ERASE; ! 1036: (void) ttyoutput('/', tp); ! 1037: } ! 1038: i = tp->t_col; ! 1039: ttyecho(c, tp); ! 1040: if (CCEQ(cc[VEOF], c) && lflag&ECHO) { ! 1041: /* ! 1042: * Place the cursor over the '^' of the ^D. ! 1043: */ ! 1044: i = MIN(2, tp->t_col - i); ! 1045: while (i > 0) { ! 1046: (void) ttyoutput('\b', tp); ! 1047: i--; ! 1048: } ! 1049: } ! 1050: } ! 1051: endcase: ! 1052: /* ! 1053: * IXANY means allow any character to restart output. ! 1054: */ ! 1055: if ((tp->t_state&TS_TTSTOP) && (iflag&IXANY) == 0 && ! 1056: cc[VSTART] != cc[VSTOP]) ! 1057: return; ! 1058: restartoutput: ! 1059: tp->t_state &= ~TS_TTSTOP; ! 1060: tp->t_lflag &= ~FLUSHO; ! 1061: startoutput: ! 1062: ttstart(tp); ! 1063: } ! 1064: ! 1065: /* ! 1066: * Output a single character on a tty, doing output processing ! 1067: * as needed (expanding tabs, newline processing, etc.). ! 1068: * Returns < 0 if putc succeeds, otherwise returns char to resend. ! 1069: * Must be recursive. ! 1070: */ ! 1071: ttyoutput(c, tp) ! 1072: register c; ! 1073: register struct tty *tp; ! 1074: { ! 1075: register int col; ! 1076: register long oflag = tp->t_oflag; ! 1077: ! 1078: if ((oflag&OPOST) == 0) { ! 1079: if (tp->t_lflag&FLUSHO) ! 1080: return (-1); ! 1081: if (putc(c, &tp->t_outq)) ! 1082: return (c); ! 1083: tk_nout++; ! 1084: tp->t_outcc++; ! 1085: return (-1); ! 1086: } ! 1087: c &= TTY_CHARMASK; ! 1088: /* ! 1089: * Do tab expansion if OXTABS is set. ! 1090: * Special case if we have external processing, we don't ! 1091: * do the tab expansion because we'll probably get it ! 1092: * wrong. If tab expansion needs to be done, let it ! 1093: * happen externally. ! 1094: */ ! 1095: if (c == '\t' && oflag&OXTABS && (tp->t_lflag&EXTPROC) == 0) { ! 1096: register int s; ! 1097: ! 1098: c = 8 - (tp->t_col&7); ! 1099: if ((tp->t_lflag&FLUSHO) == 0) { ! 1100: s = spltty(); /* don't interrupt tabs */ ! 1101: c -= b_to_q(" ", c, &tp->t_outq); ! 1102: tk_nout += c; ! 1103: tp->t_outcc += c; ! 1104: splx(s); ! 1105: } ! 1106: tp->t_col += c; ! 1107: return (c ? -1 : '\t'); ! 1108: } ! 1109: if (c == CEOT && oflag&ONOEOT) ! 1110: return (-1); ! 1111: tk_nout++; ! 1112: tp->t_outcc++; ! 1113: /* ! 1114: * Newline translation: if ONLCR is set, ! 1115: * translate newline into "\r\n". ! 1116: */ ! 1117: if (c == '\n' && (tp->t_oflag&ONLCR) && ttyoutput('\r', tp) >= 0) ! 1118: return (c); ! 1119: if ((tp->t_lflag&FLUSHO) == 0 && putc(c, &tp->t_outq)) ! 1120: return (c); ! 1121: ! 1122: col = tp->t_col; ! 1123: switch (CCLASS(c)) { ! 1124: ! 1125: case ORDINARY: ! 1126: col++; ! 1127: ! 1128: case CONTROL: ! 1129: break; ! 1130: ! 1131: case BACKSPACE: ! 1132: if (col > 0) ! 1133: col--; ! 1134: break; ! 1135: ! 1136: case NEWLINE: ! 1137: col = 0; ! 1138: break; ! 1139: ! 1140: case TAB: ! 1141: col = (col + 8) &~ 0x7; ! 1142: break; ! 1143: ! 1144: case RETURN: ! 1145: col = 0; ! 1146: } ! 1147: tp->t_col = col; ! 1148: return (-1); ! 1149: } ! 1150: ! 1151: /* ! 1152: * Process a read call on a tty device. ! 1153: */ ! 1154: ttread(tp, uio, flag) ! 1155: register struct tty *tp; ! 1156: struct uio *uio; ! 1157: { ! 1158: register struct clist *qp; ! 1159: register int c; ! 1160: register long lflag; ! 1161: register u_char *cc = tp->t_cc; ! 1162: register struct proc *p = curproc; ! 1163: int s, first, error = 0; ! 1164: ! 1165: loop: ! 1166: lflag = tp->t_lflag; ! 1167: s = spltty(); ! 1168: /* ! 1169: * take pending input first ! 1170: */ ! 1171: if (lflag&PENDIN) ! 1172: ttypend(tp); ! 1173: splx(s); ! 1174: ! 1175: /* ! 1176: * Hang process if it's in the background. ! 1177: */ ! 1178: if (isbackground(p, tp)) { ! 1179: if ((p->p_sigignore & sigmask(SIGTTIN)) || ! 1180: (p->p_sigmask & sigmask(SIGTTIN)) || ! 1181: p->p_flag&SPPWAIT || p->p_pgrp->pg_jobc == 0) ! 1182: return (EIO); ! 1183: pgsignal(p->p_pgrp, SIGTTIN, 1); ! 1184: if (error = ttysleep(tp, (caddr_t)&lbolt, TTIPRI | PCATCH, ! 1185: ttybg, 0)) ! 1186: return (error); ! 1187: goto loop; ! 1188: } ! 1189: ! 1190: /* ! 1191: * If canonical, use the canonical queue, ! 1192: * else use the raw queue. ! 1193: * ! 1194: * (should get rid of clists...) ! 1195: */ ! 1196: qp = lflag&ICANON ? &tp->t_canq : &tp->t_rawq; ! 1197: ! 1198: /* ! 1199: * If there is no input, sleep on rawq ! 1200: * awaiting hardware receipt and notification. ! 1201: * If we have data, we don't need to check for carrier. ! 1202: */ ! 1203: s = spltty(); ! 1204: if (qp->c_cc <= 0) { ! 1205: int carrier; ! 1206: ! 1207: carrier = (tp->t_state&TS_CARR_ON) || (tp->t_cflag&CLOCAL); ! 1208: if (!carrier && tp->t_state&TS_ISOPEN) { ! 1209: splx(s); ! 1210: return (0); /* EOF */ ! 1211: } ! 1212: if (flag & IO_NDELAY) { ! 1213: splx(s); ! 1214: return (EWOULDBLOCK); ! 1215: } ! 1216: error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ! 1217: carrier ? ttyin : ttopen, 0); ! 1218: splx(s); ! 1219: if (error) ! 1220: return (error); ! 1221: goto loop; ! 1222: } ! 1223: splx(s); ! 1224: ! 1225: /* ! 1226: * Input present, check for input mapping and processing. ! 1227: */ ! 1228: first = 1; ! 1229: while ((c = getc(qp)) >= 0) { ! 1230: /* ! 1231: * delayed suspend (^Y) ! 1232: */ ! 1233: if (CCEQ(cc[VDSUSP], c) && lflag&ISIG) { ! 1234: pgsignal(tp->t_pgrp, SIGTSTP, 1); ! 1235: if (first) { ! 1236: if (error = ttysleep(tp, (caddr_t)&lbolt, ! 1237: TTIPRI | PCATCH, ttybg, 0)) ! 1238: break; ! 1239: goto loop; ! 1240: } ! 1241: break; ! 1242: } ! 1243: /* ! 1244: * Interpret EOF only in canonical mode. ! 1245: */ ! 1246: if (CCEQ(cc[VEOF], c) && lflag&ICANON) ! 1247: break; ! 1248: /* ! 1249: * Give user character. ! 1250: */ ! 1251: error = ureadc(c, uio); ! 1252: if (error) ! 1253: break; ! 1254: if (uio->uio_resid == 0) ! 1255: break; ! 1256: /* ! 1257: * In canonical mode check for a "break character" ! 1258: * marking the end of a "line of input". ! 1259: */ ! 1260: if (lflag&ICANON && ttbreakc(c)) ! 1261: break; ! 1262: first = 0; ! 1263: } ! 1264: /* ! 1265: * Look to unblock output now that (presumably) ! 1266: * the input queue has gone down. ! 1267: */ ! 1268: if (tp->t_state&TS_TBLOCK && tp->t_rawq.c_cc < TTYHOG/5) { ! 1269: if (cc[VSTART] != _POSIX_VDISABLE && ! 1270: putc(cc[VSTART], &tp->t_outq) == 0) { ! 1271: tp->t_state &= ~TS_TBLOCK; ! 1272: ttstart(tp); ! 1273: } ! 1274: } ! 1275: return (error); ! 1276: } ! 1277: ! 1278: /* ! 1279: * Check the output queue on tp for space for a kernel message ! 1280: * (from uprintf/tprintf). Allow some space over the normal ! 1281: * hiwater mark so we don't lose messages due to normal flow ! 1282: * control, but don't let the tty run amok. ! 1283: * Sleeps here are not interruptible, but we return prematurely ! 1284: * if new signals come in. ! 1285: */ ! 1286: ttycheckoutq(tp, wait) ! 1287: register struct tty *tp; ! 1288: int wait; ! 1289: { ! 1290: int hiwat, s, oldsig; ! 1291: extern int wakeup(); ! 1292: ! 1293: hiwat = tp->t_hiwat; ! 1294: s = spltty(); ! 1295: oldsig = curproc->p_sig; ! 1296: if (tp->t_outq.c_cc > hiwat + 200) ! 1297: while (tp->t_outq.c_cc > hiwat) { ! 1298: ttstart(tp); ! 1299: if (wait == 0 || curproc->p_sig != oldsig) { ! 1300: splx(s); ! 1301: return (0); ! 1302: } ! 1303: timeout(wakeup, (caddr_t)&tp->t_outq, hz); ! 1304: tp->t_state |= TS_ASLEEP; ! 1305: sleep((caddr_t)&tp->t_outq, PZERO - 1); ! 1306: } ! 1307: splx(s); ! 1308: return (1); ! 1309: } ! 1310: ! 1311: /* ! 1312: * Process a write call on a tty device. ! 1313: */ ! 1314: ttwrite(tp, uio, flag) ! 1315: register struct tty *tp; ! 1316: register struct uio *uio; ! 1317: { ! 1318: register char *cp; ! 1319: register int cc = 0, ce; ! 1320: register struct proc *p = curproc; ! 1321: int i, hiwat, cnt, error, s; ! 1322: char obuf[OBUFSIZ]; ! 1323: ! 1324: hiwat = tp->t_hiwat; ! 1325: cnt = uio->uio_resid; ! 1326: error = 0; ! 1327: loop: ! 1328: s = spltty(); ! 1329: if ((tp->t_state&TS_CARR_ON) == 0 && (tp->t_cflag&CLOCAL) == 0) { ! 1330: if (tp->t_state&TS_ISOPEN) { ! 1331: splx(s); ! 1332: return (EIO); ! 1333: } else if (flag & IO_NDELAY) { ! 1334: splx(s); ! 1335: error = EWOULDBLOCK; ! 1336: goto out; ! 1337: } else { ! 1338: /* ! 1339: * sleep awaiting carrier ! 1340: */ ! 1341: error = ttysleep(tp, (caddr_t)&tp->t_rawq, ! 1342: TTIPRI | PCATCH,ttopen, 0); ! 1343: splx(s); ! 1344: if (error) ! 1345: goto out; ! 1346: goto loop; ! 1347: } ! 1348: } ! 1349: splx(s); ! 1350: /* ! 1351: * Hang the process if it's in the background. ! 1352: */ ! 1353: if (isbackground(p, tp) && ! 1354: tp->t_lflag&TOSTOP && (p->p_flag&SPPWAIT) == 0 && ! 1355: (p->p_sigignore & sigmask(SIGTTOU)) == 0 && ! 1356: (p->p_sigmask & sigmask(SIGTTOU)) == 0 && ! 1357: p->p_pgrp->pg_jobc) { ! 1358: pgsignal(p->p_pgrp, SIGTTOU, 1); ! 1359: if (error = ttysleep(tp, (caddr_t)&lbolt, TTIPRI | PCATCH, ! 1360: ttybg, 0)) ! 1361: goto out; ! 1362: goto loop; ! 1363: } ! 1364: /* ! 1365: * Process the user's data in at most OBUFSIZ ! 1366: * chunks. Perform any output translation. ! 1367: * Keep track of high water mark, sleep on overflow ! 1368: * awaiting device aid in acquiring new space. ! 1369: */ ! 1370: while (uio->uio_resid > 0 || cc > 0) { ! 1371: if (tp->t_lflag&FLUSHO) { ! 1372: uio->uio_resid = 0; ! 1373: return (0); ! 1374: } ! 1375: if (tp->t_outq.c_cc > hiwat) ! 1376: goto ovhiwat; ! 1377: /* ! 1378: * Grab a hunk of data from the user, ! 1379: * unless we have some leftover from last time. ! 1380: */ ! 1381: if (cc == 0) { ! 1382: cc = min(uio->uio_resid, OBUFSIZ); ! 1383: cp = obuf; ! 1384: error = uiomove(cp, cc, uio); ! 1385: if (error) { ! 1386: cc = 0; ! 1387: break; ! 1388: } ! 1389: } ! 1390: /* ! 1391: * If nothing fancy need be done, grab those characters we ! 1392: * can handle without any of ttyoutput's processing and ! 1393: * just transfer them to the output q. For those chars ! 1394: * which require special processing (as indicated by the ! 1395: * bits in partab), call ttyoutput. After processing ! 1396: * a hunk of data, look for FLUSHO so ^O's will take effect ! 1397: * immediately. ! 1398: */ ! 1399: while (cc > 0) { ! 1400: if ((tp->t_oflag&OPOST) == 0) ! 1401: ce = cc; ! 1402: else { ! 1403: ce = cc - scanc((unsigned)cc, (u_char *)cp, ! 1404: (u_char *)partab, CCLASSMASK); ! 1405: /* ! 1406: * If ce is zero, then we're processing ! 1407: * a special character through ttyoutput. ! 1408: */ ! 1409: if (ce == 0) { ! 1410: tp->t_rocount = 0; ! 1411: if (ttyoutput(*cp, tp) >= 0) { ! 1412: /* no c-lists, wait a bit */ ! 1413: ttstart(tp); ! 1414: if (error = ttysleep(tp, ! 1415: (caddr_t)&lbolt, ! 1416: TTOPRI | PCATCH, ttybuf, 0)) ! 1417: break; ! 1418: goto loop; ! 1419: } ! 1420: cp++, cc--; ! 1421: if ((tp->t_lflag&FLUSHO) || ! 1422: tp->t_outq.c_cc > hiwat) ! 1423: goto ovhiwat; ! 1424: continue; ! 1425: } ! 1426: } ! 1427: /* ! 1428: * A bunch of normal characters have been found, ! 1429: * transfer them en masse to the output queue and ! 1430: * continue processing at the top of the loop. ! 1431: * If there are any further characters in this ! 1432: * <= OBUFSIZ chunk, the first should be a character ! 1433: * requiring special handling by ttyoutput. ! 1434: */ ! 1435: tp->t_rocount = 0; ! 1436: i = b_to_q(cp, ce, &tp->t_outq); ! 1437: ce -= i; ! 1438: tp->t_col += ce; ! 1439: cp += ce, cc -= ce, tk_nout += ce; ! 1440: tp->t_outcc += ce; ! 1441: if (i > 0) { ! 1442: /* out of c-lists, wait a bit */ ! 1443: ttstart(tp); ! 1444: if (error = ttysleep(tp, (caddr_t)&lbolt, ! 1445: TTOPRI | PCATCH, ttybuf, 0)) ! 1446: break; ! 1447: goto loop; ! 1448: } ! 1449: if (tp->t_lflag&FLUSHO || tp->t_outq.c_cc > hiwat) ! 1450: break; ! 1451: } ! 1452: ttstart(tp); ! 1453: } ! 1454: out: ! 1455: /* ! 1456: * If cc is nonzero, we leave the uio structure inconsistent, ! 1457: * as the offset and iov pointers have moved forward, ! 1458: * but it doesn't matter (the call will either return short ! 1459: * or restart with a new uio). ! 1460: */ ! 1461: uio->uio_resid += cc; ! 1462: return (error); ! 1463: ! 1464: ovhiwat: ! 1465: ttstart(tp); ! 1466: s = spltty(); ! 1467: /* ! 1468: * This can only occur if FLUSHO is set in t_lflag, ! 1469: * or if ttstart/oproc is synchronous (or very fast). ! 1470: */ ! 1471: if (tp->t_outq.c_cc <= hiwat) { ! 1472: splx(s); ! 1473: goto loop; ! 1474: } ! 1475: if (flag & IO_NDELAY) { ! 1476: splx(s); ! 1477: uio->uio_resid += cc; ! 1478: if (uio->uio_resid == cnt) ! 1479: return (EWOULDBLOCK); ! 1480: return (0); ! 1481: } ! 1482: tp->t_state |= TS_ASLEEP; ! 1483: error = ttysleep(tp, (caddr_t)&tp->t_outq, TTOPRI | PCATCH, ttyout, 0); ! 1484: splx(s); ! 1485: if (error) ! 1486: goto out; ! 1487: goto loop; ! 1488: } ! 1489: ! 1490: /* ! 1491: * Rubout one character from the rawq of tp ! 1492: * as cleanly as possible. ! 1493: */ ! 1494: ttyrub(c, tp) ! 1495: register c; ! 1496: register struct tty *tp; ! 1497: { ! 1498: register char *cp; ! 1499: register int savecol; ! 1500: int s; ! 1501: char *nextc(); ! 1502: ! 1503: if ((tp->t_lflag&ECHO) == 0 || (tp->t_lflag&EXTPROC)) ! 1504: return; ! 1505: tp->t_lflag &= ~FLUSHO; ! 1506: if (tp->t_lflag&ECHOE) { ! 1507: if (tp->t_rocount == 0) { ! 1508: /* ! 1509: * Screwed by ttwrite; retype ! 1510: */ ! 1511: ttyretype(tp); ! 1512: return; ! 1513: } ! 1514: if (c == ('\t'|TTY_QUOTE) || c == ('\n'|TTY_QUOTE)) ! 1515: ttyrubo(tp, 2); ! 1516: else switch (CCLASS(c &= TTY_CHARMASK)) { ! 1517: ! 1518: case ORDINARY: ! 1519: ttyrubo(tp, 1); ! 1520: break; ! 1521: ! 1522: case VTAB: ! 1523: case BACKSPACE: ! 1524: case CONTROL: ! 1525: case RETURN: ! 1526: case NEWLINE: ! 1527: if (tp->t_lflag&ECHOCTL) ! 1528: ttyrubo(tp, 2); ! 1529: break; ! 1530: ! 1531: case TAB: { ! 1532: int c; ! 1533: ! 1534: if (tp->t_rocount < tp->t_rawq.c_cc) { ! 1535: ttyretype(tp); ! 1536: return; ! 1537: } ! 1538: s = spltty(); ! 1539: savecol = tp->t_col; ! 1540: tp->t_state |= TS_CNTTB; ! 1541: tp->t_lflag |= FLUSHO; ! 1542: tp->t_col = tp->t_rocol; ! 1543: cp = tp->t_rawq.c_cf; ! 1544: if (cp) ! 1545: c = *cp; /* XXX FIX NEXTC */ ! 1546: for (; cp; cp = nextc(&tp->t_rawq, cp, &c)) ! 1547: ttyecho(c, tp); ! 1548: tp->t_lflag &= ~FLUSHO; ! 1549: tp->t_state &= ~TS_CNTTB; ! 1550: splx(s); ! 1551: /* ! 1552: * savecol will now be length of the tab ! 1553: */ ! 1554: savecol -= tp->t_col; ! 1555: tp->t_col += savecol; ! 1556: if (savecol > 8) ! 1557: savecol = 8; /* overflow screw */ ! 1558: while (--savecol >= 0) ! 1559: (void) ttyoutput('\b', tp); ! 1560: break; ! 1561: } ! 1562: ! 1563: default: ! 1564: /* XXX */ ! 1565: printf("ttyrub: would panic c = %d, val = %d\n", ! 1566: c, CCLASS(c)); ! 1567: /*panic("ttyrub");*/ ! 1568: } ! 1569: } else if (tp->t_lflag&ECHOPRT) { ! 1570: if ((tp->t_state&TS_ERASE) == 0) { ! 1571: (void) ttyoutput('\\', tp); ! 1572: tp->t_state |= TS_ERASE; ! 1573: } ! 1574: ttyecho(c, tp); ! 1575: } else ! 1576: ttyecho(tp->t_cc[VERASE], tp); ! 1577: tp->t_rocount--; ! 1578: } ! 1579: ! 1580: /* ! 1581: * Crt back over cnt chars perhaps ! 1582: * erasing them. ! 1583: */ ! 1584: ttyrubo(tp, cnt) ! 1585: register struct tty *tp; ! 1586: int cnt; ! 1587: { ! 1588: ! 1589: while (--cnt >= 0) ! 1590: ttyoutstr("\b \b", tp); ! 1591: } ! 1592: ! 1593: /* ! 1594: * Reprint the rawq line. ! 1595: * We assume c_cc has already been checked. ! 1596: */ ! 1597: ttyretype(tp) ! 1598: register struct tty *tp; ! 1599: { ! 1600: register char *cp; ! 1601: char *nextc(); ! 1602: int s, c; ! 1603: ! 1604: if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) ! 1605: ttyecho(tp->t_cc[VREPRINT], tp); ! 1606: (void) ttyoutput('\n', tp); ! 1607: s = spltty(); ! 1608: /*** XXX *** FIX *** NEXTC IS BROKEN - DOESN'T CHECK QUOTE ! 1609: BIT OF FIRST CHAR ****/ ! 1610: for (cp = tp->t_canq.c_cf, c=(cp?*cp:0); cp; cp = nextc(&tp->t_canq, cp, &c)) { ! 1611: ttyecho(c, tp); ! 1612: } ! 1613: for (cp = tp->t_rawq.c_cf, c=(cp?*cp:0); cp; cp = nextc(&tp->t_rawq, cp, &c)) { ! 1614: ttyecho(c, tp); ! 1615: } ! 1616: tp->t_state &= ~TS_ERASE; ! 1617: splx(s); ! 1618: tp->t_rocount = tp->t_rawq.c_cc; ! 1619: tp->t_rocol = 0; ! 1620: } ! 1621: ! 1622: /* ! 1623: * Echo a typed character to the terminal. ! 1624: */ ! 1625: ttyecho(c, tp) ! 1626: register c; ! 1627: register struct tty *tp; ! 1628: { ! 1629: if ((tp->t_state&TS_CNTTB) == 0) ! 1630: tp->t_lflag &= ~FLUSHO; ! 1631: if (((tp->t_lflag&ECHO) == 0 && ! 1632: ((tp->t_lflag&ECHONL) == 0 || c == '\n')) || (tp->t_lflag&EXTPROC)) ! 1633: return; ! 1634: if (tp->t_lflag&ECHOCTL) { ! 1635: if ((c&TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' || ! 1636: c == 0177) { ! 1637: (void) ttyoutput('^', tp); ! 1638: c &= TTY_CHARMASK; ! 1639: if (c == 0177) ! 1640: c = '?'; ! 1641: else ! 1642: c += 'A' - 1; ! 1643: } ! 1644: } ! 1645: (void) ttyoutput(c, tp); ! 1646: } ! 1647: ! 1648: /* ! 1649: * send string cp to tp ! 1650: */ ! 1651: ttyoutstr(cp, tp) ! 1652: register char *cp; ! 1653: register struct tty *tp; ! 1654: { ! 1655: register char c; ! 1656: ! 1657: while (c = *cp++) ! 1658: (void) ttyoutput(c, tp); ! 1659: } ! 1660: ! 1661: /* ! 1662: * Wake up any readers on a tty. ! 1663: */ ! 1664: ttwakeup(tp) ! 1665: register struct tty *tp; ! 1666: { ! 1667: ! 1668: if (tp->t_rsel) { ! 1669: selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL); ! 1670: tp->t_state &= ~TS_RCOLL; ! 1671: tp->t_rsel = 0; ! 1672: } ! 1673: if (tp->t_state & TS_ASYNC) ! 1674: pgsignal(tp->t_pgrp, SIGIO, 1); ! 1675: wakeup((caddr_t)&tp->t_rawq); ! 1676: } ! 1677: ! 1678: /* ! 1679: * Look up a code for a specified speed in a conversion table; ! 1680: * used by drivers to map software speed values to hardware parameters. ! 1681: */ ! 1682: ttspeedtab(speed, table) ! 1683: register struct speedtab *table; ! 1684: { ! 1685: ! 1686: for ( ; table->sp_speed != -1; table++) ! 1687: if (table->sp_speed == speed) ! 1688: return (table->sp_code); ! 1689: return (-1); ! 1690: } ! 1691: ! 1692: /* ! 1693: * set tty hi and low water marks ! 1694: * ! 1695: * Try to arrange the dynamics so there's about one second ! 1696: * from hi to low water. ! 1697: * ! 1698: */ ! 1699: ttsetwater(tp) ! 1700: struct tty *tp; ! 1701: { ! 1702: register cps = tp->t_ospeed / 10; ! 1703: register x; ! 1704: ! 1705: #define clamp(x, h, l) ((x)>h ? h : ((x)<l) ? l : (x)) ! 1706: tp->t_lowat = x = clamp(cps/2, TTMAXLOWAT, TTMINLOWAT); ! 1707: x += cps; ! 1708: x = clamp(x, TTMAXHIWAT, TTMINHIWAT); ! 1709: tp->t_hiwat = roundup(x, CBSIZE); ! 1710: #undef clamp ! 1711: } ! 1712: ! 1713: /* ! 1714: * Report on state of foreground process group. ! 1715: */ ! 1716: ttyinfo(tp) ! 1717: register struct tty *tp; ! 1718: { ! 1719: register struct proc *p, *pick; ! 1720: struct timeval utime, stime; ! 1721: int tmp; ! 1722: ! 1723: if (ttycheckoutq(tp,0) == 0) ! 1724: return; ! 1725: ! 1726: /* Print load average. */ ! 1727: tmp = (averunnable[0] * 100 + FSCALE / 2) >> FSHIFT; ! 1728: ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100); ! 1729: ! 1730: if (tp->t_session == NULL) ! 1731: ttyprintf(tp, "not a controlling terminal\n"); ! 1732: else if (tp->t_pgrp == NULL) ! 1733: ttyprintf(tp, "no foreground process group\n"); ! 1734: else if ((p = tp->t_pgrp->pg_mem) == NULL) ! 1735: ttyprintf(tp, "empty foreground process group\n"); ! 1736: else { ! 1737: /* Pick interesting process. */ ! 1738: for (pick = NULL; p != NULL; p = p->p_pgrpnxt) ! 1739: if (proc_compare(pick, p)) ! 1740: pick = p; ! 1741: ! 1742: ttyprintf(tp, " cmd: %s %d [%s] ", pick->p_comm, pick->p_pid, ! 1743: pick->p_stat == SRUN ? "running" : ! 1744: pick->p_wmesg ? pick->p_wmesg : "iowait"); ! 1745: ! 1746: /* ! 1747: * Lock out clock if process is running; get user/system ! 1748: * cpu time. ! 1749: */ ! 1750: if (curproc == pick) ! 1751: tmp = splclock(); ! 1752: utime = pick->p_utime; ! 1753: stime = pick->p_stime; ! 1754: if (curproc == pick) ! 1755: splx(tmp); ! 1756: ! 1757: /* Print user time. */ ! 1758: ttyprintf(tp, "%d.%02du ", ! 1759: utime.tv_sec, (utime.tv_usec + 5000) / 10000); ! 1760: ! 1761: /* Print system time. */ ! 1762: ttyprintf(tp, "%d.%02ds ", ! 1763: stime.tv_sec, (stime.tv_usec + 5000) / 10000); ! 1764: ! 1765: #define pgtok(a) (((a) * NBPG) / 1024) ! 1766: /* Print percentage cpu, resident set size. */ ! 1767: tmp = pick->p_pctcpu * 10000 + FSCALE / 2 >> FSHIFT; ! 1768: ttyprintf(tp, "%d%% %dk\n", ! 1769: tmp / 100, pgtok(pick->p_vmspace->vm_rssize)); ! 1770: } ! 1771: tp->t_rocount = 0; /* so pending input will be retyped if BS */ ! 1772: } ! 1773: ! 1774: /* ! 1775: * Returns 1 if p2 is "better" than p1 ! 1776: * ! 1777: * The algorithm for picking the "interesting" process is thus: ! 1778: * ! 1779: * 1) (Only foreground processes are eligable - implied) ! 1780: * 2) Runnable processes are favored over anything ! 1781: * else. The runner with the highest cpu ! 1782: * utilization is picked (p_cpu). Ties are ! 1783: * broken by picking the highest pid. ! 1784: * 3 Next, the sleeper with the shortest sleep ! 1785: * time is favored. With ties, we pick out ! 1786: * just "short-term" sleepers (SSINTR == 0). ! 1787: * Further ties are broken by picking the highest ! 1788: * pid. ! 1789: * ! 1790: */ ! 1791: #define isrun(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL)) ! 1792: #define TESTAB(a, b) ((a)<<1 | (b)) ! 1793: #define ONLYA 2 ! 1794: #define ONLYB 1 ! 1795: #define BOTH 3 ! 1796: ! 1797: static int ! 1798: proc_compare(p1, p2) ! 1799: register struct proc *p1, *p2; ! 1800: { ! 1801: ! 1802: if (p1 == NULL) ! 1803: return (1); ! 1804: /* ! 1805: * see if at least one of them is runnable ! 1806: */ ! 1807: switch (TESTAB(isrun(p1), isrun(p2))) { ! 1808: case ONLYA: ! 1809: return (0); ! 1810: case ONLYB: ! 1811: return (1); ! 1812: case BOTH: ! 1813: /* ! 1814: * tie - favor one with highest recent cpu utilization ! 1815: */ ! 1816: if (p2->p_cpu > p1->p_cpu) ! 1817: return (1); ! 1818: if (p1->p_cpu > p2->p_cpu) ! 1819: return (0); ! 1820: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ ! 1821: } ! 1822: /* ! 1823: * weed out zombies ! 1824: */ ! 1825: switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) { ! 1826: case ONLYA: ! 1827: return (1); ! 1828: case ONLYB: ! 1829: return (0); ! 1830: case BOTH: ! 1831: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ ! 1832: } ! 1833: /* ! 1834: * pick the one with the smallest sleep time ! 1835: */ ! 1836: if (p2->p_slptime > p1->p_slptime) ! 1837: return (0); ! 1838: if (p1->p_slptime > p2->p_slptime) ! 1839: return (1); ! 1840: /* ! 1841: * favor one sleeping in a non-interruptible sleep ! 1842: */ ! 1843: if (p1->p_flag&SSINTR && (p2->p_flag&SSINTR) == 0) ! 1844: return (1); ! 1845: if (p2->p_flag&SSINTR && (p1->p_flag&SSINTR) == 0) ! 1846: return (0); ! 1847: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ ! 1848: } ! 1849: ! 1850: /* ! 1851: * Output char to tty; console putchar style. ! 1852: */ ! 1853: tputchar(c, tp) ! 1854: int c; ! 1855: struct tty *tp; ! 1856: { ! 1857: register s = spltty(); ! 1858: ! 1859: if ((tp->t_state & (TS_CARR_ON|TS_ISOPEN)) == (TS_CARR_ON|TS_ISOPEN)) { ! 1860: if (c == '\n') ! 1861: (void) ttyoutput('\r', tp); ! 1862: (void) ttyoutput(c, tp); ! 1863: ttstart(tp); ! 1864: splx(s); ! 1865: return (0); ! 1866: } ! 1867: splx(s); ! 1868: return (-1); ! 1869: } ! 1870: ! 1871: /* ! 1872: * Sleep on chan, returning ERESTART if tty changed ! 1873: * while we napped and returning any errors (e.g. EINTR/ETIMEDOUT) ! 1874: * reported by tsleep. If the tty is revoked, restarting a pending ! 1875: * call will redo validation done at the start of the call. ! 1876: */ ! 1877: ttysleep(tp, chan, pri, wmesg, timo) ! 1878: struct tty *tp; ! 1879: caddr_t chan; ! 1880: int pri; ! 1881: char *wmesg; ! 1882: int timo; ! 1883: { ! 1884: int error; ! 1885: short gen = tp->t_gen; ! 1886: ! 1887: if (error = tsleep(chan, pri, wmesg, timo)) ! 1888: return (error); ! 1889: if (tp->t_gen != gen) ! 1890: return (ERESTART); ! 1891: return (0); ! 1892: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.