home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 10: Diskmags / nf_archive_10.iso / MAGS / PURE_B / PBMAG22A.MSA / MINT095S.ZIP / SRC / TTY.C < prev    next >
C/C++ Source or Header  |  1987-04-22  |  18KB  |  771 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith. All rights reserved.
  3. */
  4.  
  5. /*
  6.  * read/write routines for TTY devices
  7.  */
  8.  
  9. #include "mint.h"
  10.  
  11. static void _erase P_((FILEPTR *, int));
  12. static int escseq P_((struct tty *, int));
  13.  
  14. /* setting a special character to this value disables it */
  15. #define UNDEF 0
  16.  
  17.  
  18. /* default terminal characteristics */
  19.  
  20. struct tty default_tty = {
  21.     0,            /* process group */
  22.     0,            /* state */
  23.     0,            /* use_cnt */
  24.     0,            /* reserved short */
  25.     {
  26.     13, 13,            /* input speed == output speed == 9600 baud */
  27.     CTRL('H'),        /* erase */
  28.     CTRL('U'),        /* kill */
  29.     T_ECHO|T_CRMOD|T_TOSTOP|T_XKEY, /* flags */
  30.     },
  31.     {
  32.     CTRL('C'),        /* interrupt */
  33.     CTRL('\\'),        /* quit */
  34.     CTRL('Q'),        /* start */
  35.     CTRL('S'),        /* stop */
  36.     CTRL('D'),        /* EOF */
  37.     '\r'            /* alternate end of line */
  38.     },
  39.     {
  40.     CTRL('Z'),        /* suspend */
  41.     CTRL('Y'),        /* suspend after read */
  42.     CTRL('R'),        /* reprint */
  43.     UNDEF,            /* flush output */
  44.     UNDEF,            /* erase word */
  45.     UNDEF            /* quote next char */
  46.     },
  47.     {
  48.     0, 0, 0, 0        /* window size is unknown */
  49.     },
  50.     0,            /* no process is selecting us for reading */
  51.     0,            /* or for writing */
  52.     0            /* use default XKEY map */
  53. };
  54.  
  55. #define _put(f, c) (tty_putchar((f), (c), RAW))
  56.  
  57. static void
  58. _erase(f, c)
  59.     FILEPTR *f;
  60.     int c;
  61. {
  62.     _put(f, '\010');
  63.     _put(f, ' ');
  64.     _put(f, '\010');
  65. /* watch out for control characters -- they're printed as e.g. "^C" */
  66.     if (c >= 0 && c < ' ') {
  67.         _put(f, '\010'); _put(f, ' '); _put(f, '\010');
  68.     }
  69. }
  70.  
  71. #define put(f, c)   { if (mode & T_ECHO) _put(f, c); }
  72. #define erase(f, c) { if (mode & T_ECHO) _erase(f, c); }
  73.  
  74. long
  75. tty_read(f, buf, nbytes)
  76.     FILEPTR *f;
  77.     void *buf;
  78.     long nbytes;
  79. {
  80.     long r;
  81.     long bytes_read = 0;
  82.     unsigned char ch, *ptr;
  83.     int rdmode, mode;
  84.     struct tty *tty;
  85.  
  86.     tty = (struct tty *)f->devinfo;
  87.     assert(tty != 0);
  88.  
  89.     if (f->flags & O_HEAD) {    /* pty server side? */
  90.         rdmode = RAW;        /* yes -- always raw mode */
  91.         mode = T_RAW;
  92.     }
  93.     else if (curproc->domain == DOM_MINT) {    /* MiNT domain process? */
  94.         mode = tty->sg.sg_flags;
  95.         rdmode = COOKED|NOECHO;
  96.         if ( mode & (T_RAW | T_CBREAK) ) {
  97.             rdmode = (mode & T_RAW) ? RAW : COOKED;
  98.         }
  99.         if (mode & T_XKEY)
  100.             rdmode |= ESCSEQ;
  101.     }
  102.     else {
  103.         rdmode = COOKED|NOECHO;
  104.         mode = T_TOS | T_ECHO;
  105.     }
  106.  
  107.     ptr = buf;
  108.  
  109.     while (bytes_read < nbytes) {
  110.         r = tty_getchar(f, rdmode);
  111.         if (r < 0) {
  112.             DEBUG("tty_read: tty_getchar returned %ld", r);
  113.             return (bytes_read) ? bytes_read : r;
  114.         }
  115.         else if (r == MiNTEOF)
  116.             return bytes_read;
  117.         ch = r & 0xff;
  118.  
  119.         if ( (mode & T_CRMOD) && (ch == '\r') )
  120.             ch = '\n';
  121.  
  122. /* 1 character reads in TOS mode are always raw */
  123.         if (nbytes == 1 && (mode & T_TOS)) {
  124.             put(f, ch);
  125.             *ptr = ch;
  126.             return 1;
  127.         }
  128.  
  129. /* T_CBREAK mode doesn't do erase or kill processing */
  130. /* also note that setting a special character to UNDEF disables it */
  131.  
  132.         if (rdmode & COOKED && !(mode & T_CBREAK) && ch != UNDEF) {
  133.             if ((char)ch == tty->sg.sg_erase) {  /* backspace */
  134.                 if (bytes_read > 0) {
  135.                     --ptr;
  136.                     erase(f, *ptr);
  137.                     bytes_read--;
  138.                 }
  139.                 continue;
  140.             }
  141.             else if (ch == CTRL('X')) {
  142.                 while (bytes_read > 0) {
  143.                     --ptr;
  144.                     erase(f, *ptr);
  145.                     bytes_read--;
  146.                 }
  147.                 continue;
  148.             }
  149.             else if ((char)ch ==tty->ltc.t_rprntc || 
  150.                  (char)ch == tty->sg.sg_kill) {
  151.                 if (mode & T_TOS)
  152.                     put(f, '#');
  153.                 put(f, '\r');
  154.                 put(f, '\n');
  155.                 ptr = buf;
  156.                 if ((char)ch == tty->sg.sg_kill) {
  157.                     bytes_read = 0;
  158.                 }
  159.                 else {
  160.                     for (r = 0; r < bytes_read; r++, ptr++)
  161.                         put(f, *ptr);
  162.                 }
  163.                 continue;
  164.             }
  165.             else if ((char)ch == tty->tc.t_eofc && !(mode & T_TOS))
  166.                 return bytes_read;
  167.         }
  168.  
  169. /* both T_CBREAK and T_COOKED modes have to do signals, though */
  170.         if ((rdmode & COOKED) && ch != UNDEF) {
  171.             if ((char)ch == tty->tc.t_intrc
  172.                  || (char)ch == tty->tc.t_quitc
  173.                  || (char)ch == tty->ltc.t_dsuspc
  174.                  || (char)ch == tty->ltc.t_suspc
  175.                 ) {
  176. /* the device driver raised the appropriate signal; if we get here, the
  177.    signal was caught by the user (or ignored). flush buffers and continue
  178.  */
  179.                 if (!(tty->sg.sg_flags & T_NOFLSH)) {
  180.                     DEBUG("tty_read: flushing input");
  181.                     bytes_read = 0;
  182.                     ptr = buf;
  183.                 }
  184.                 continue;
  185.             }
  186.             else if (ch == '\n' || (char)ch == tty->tc.t_brkc) {
  187.                 put(f, '\r');
  188.                 if (!(mode & T_TOS)) {
  189.                     *ptr++ = ch;
  190.                     put(f, '\n');
  191.                     bytes_read++;
  192.                 }
  193.                 return bytes_read;
  194.             }
  195.  
  196.         }
  197.  
  198. /* do the following for both RAW and COOKED mode */
  199.         *ptr++ = ch;
  200.         if (ch < ' ') {        /* ch is unsigned */
  201.             put(f, '^'); put(f, ch+'@');
  202.         }
  203.         else
  204.             put(f, ch);
  205.         bytes_read++;
  206.  
  207. /* for RAW mode, if there are no more characters then break */
  208.         if ( (mode & (T_RAW|T_CBREAK)) &&
  209.             !((rdmode & ESCSEQ) && (tty->state & TS_ESC))) {
  210.             r = 1;
  211.             (void)(*f->dev->ioctl)(f, FIONREAD, &r);
  212.             if (r <= 0) break;
  213.         }    
  214.     }
  215.  
  216.     return bytes_read;
  217. }
  218.  
  219. long
  220. tty_write(f, buf, nbytes)
  221.     FILEPTR *f;
  222.     const void *buf;
  223.     long nbytes;
  224. {
  225.     unsigned const char *ptr;
  226.     long c;
  227.     long bytes_written;
  228.     int mode, rwmode;
  229.     struct tty *tty;
  230.     int use_putchar = 0;
  231.     static long cr_char = '\r';
  232. #define LBUFSIZ 128
  233.     long lbuf[LBUFSIZ];
  234.  
  235.     tty = (struct tty *)f->devinfo;
  236.     assert(tty != 0);
  237.  
  238.     ptr = buf;
  239.     if (f->flags & O_HEAD) {
  240.         use_putchar = 1;
  241.         mode = T_RAW;
  242.     }
  243.     else if (curproc->domain == DOM_TOS)
  244. /* for TOS programs, 1 byte writes are always in raw mode */
  245.         mode = (nbytes == 1) ? T_RAW : T_TOS;
  246.     else
  247.         mode = tty->sg.sg_flags;
  248.  
  249.     rwmode = (mode & T_RAW) ? RAW : COOKED;
  250.  
  251.     bytes_written = 0;
  252.  
  253. /*
  254.  * "mode" can now be reduced to just T_CRMODE or not
  255.  */
  256.     if ((curproc->domain == DOM_MINT) && (mode & T_CRMOD) &&
  257.         !(mode & T_RAW))
  258.         mode = T_CRMOD;
  259.     else
  260.         mode = 0;
  261.  
  262. /*
  263.  * we always write at least 1 byte with tty_putchar, since that takes
  264.  * care of job control and terminal states. After that, we may be able
  265.  * to use (*f->dev->write) directly.
  266.  */
  267.  
  268.  
  269.     if (nbytes == 0) return bytes_written;
  270.     c = *ptr++;
  271.  
  272.     if (c == '\n' && mode) {    /* remember, "mode" now means CRMOD */
  273.         tty_putchar(f, cr_char, rwmode);
  274.     }
  275.     tty_putchar(f, c, rwmode);
  276.     nbytes--;
  277.     bytes_written++;
  278.  
  279.     if (use_putchar) {
  280.         while (nbytes-- > 0) {
  281.             c = *ptr++;
  282.             if (c == '\n' && mode)
  283.                 tty_putchar(f, cr_char, rwmode);
  284.             tty_putchar(f, c, rwmode);
  285.             bytes_written++;
  286.         }
  287.     } else {
  288. /* write in big chunks if possible; but never more than 1 line
  289.  * (so that ^S/^Q can happen reasonably quickly for the user)
  290.  */
  291.         long bytes_to_write = 0;
  292.         long *s = lbuf;
  293.  
  294.         while (nbytes-- > 0) {
  295.             c = *ptr++;
  296.             if (c == '\n') {
  297.                 if (bytes_to_write) {
  298.                     (*f->dev->write)(f, (char *)lbuf,
  299.                         bytes_to_write);
  300.                     bytes_to_write = 0;
  301.                     s = lbuf;
  302.                 }
  303.                 if (mode)    /* i.e. T_CRMODE */
  304.                     tty_putchar(f, cr_char, rwmode);
  305.                 tty_putchar(f, (long)c, rwmode);
  306.                 bytes_written++;
  307.             } else {
  308.                 *s++ = c;
  309.                 bytes_written++;
  310.                 bytes_to_write += 4;
  311.                 if (bytes_to_write >= LBUFSIZ*4) {
  312.                     (*f->dev->write)(f, (char *)lbuf,
  313.                              bytes_to_write);
  314.                     bytes_to_write = 0;
  315.                     s = lbuf;
  316.                 }
  317.             }
  318.         }
  319.         if (bytes_to_write) {
  320.             (*f->dev->write)(f, (char *)lbuf, bytes_to_write);
  321.         }
  322.     }
  323.  
  324.     return bytes_written;
  325. }
  326.  
  327. /* some notable scan codes */
  328. #define K_INSERT    0x52
  329. #define K_HOME        0x47
  330. #define K_UNDO        0x61
  331. #define K_HELP        0x62
  332. #define CURS_UP        0x48
  333. #define CURS_DN        0x50
  334. #define CURS_RT        0x4d
  335. #define CURS_LF        0x4b
  336. #define F_1        0x3b
  337. #define F_10        0x44
  338. #define F_11        0x54
  339. #define F_20        0x5d
  340. #define ALT_1        0x78
  341. #define ALT_0        0x81
  342.  
  343. /* Default function key table:
  344.  * entries:    0-9 are F1-F10
  345.  *            10-19 are F11-F20
  346.  *        20-23 are cursor up, down, right, and left
  347.  *        24-27 are help, undo, insert, and home
  348.  *        28-31 are shift+cursor up, down, right, and left
  349.  */
  350.  
  351. static char vt52xkey[256] = {
  352. '\033', 'P', 0, 0, 0, 0, 0, 0,
  353. '\033', 'Q', 0, 0, 0, 0, 0, 0,
  354. '\033', 'R', 0, 0, 0, 0, 0, 0,
  355. '\033', 'S', 0, 0, 0, 0, 0, 0,
  356. '\033', 'T', 0, 0, 0, 0, 0, 0,
  357. '\033', 'U', 0, 0, 0, 0, 0, 0,
  358. '\033', 'V', 0, 0, 0, 0, 0, 0,
  359. '\033', 'W', 0, 0, 0, 0, 0, 0,
  360. '\033', 'X', 0, 0, 0, 0, 0, 0,
  361. '\033', 'Y', 0, 0, 0, 0, 0, 0,
  362. '\033', 'p', 0, 0, 0, 0, 0, 0,
  363. '\033', 'q', 0, 0, 0, 0, 0, 0,
  364. '\033', 'r', 0, 0, 0, 0, 0, 0,
  365. '\033', 's', 0, 0, 0, 0, 0, 0,
  366. '\033', 't', 0, 0, 0, 0, 0, 0,
  367. '\033', 'u', 0, 0, 0, 0, 0, 0,
  368. '\033', 'v', 0, 0, 0, 0, 0, 0,
  369. '\033', 'w', 0, 0, 0, 0, 0, 0,
  370. '\033', 'x', 0, 0, 0, 0, 0, 0,
  371. '\033', 'y', 0, 0, 0, 0, 0, 0,
  372. '\033', 'A', 0, 0, 0, 0, 0, 0,
  373. '\033', 'B', 0, 0, 0, 0, 0, 0,
  374. '\033', 'C', 0, 0, 0, 0, 0, 0,
  375. '\033', 'D', 0, 0, 0, 0, 0, 0,
  376. '\033', 'H', 0, 0, 0, 0, 0, 0,
  377. '\033', 'K', 0, 0, 0, 0, 0, 0,
  378. '\033', 'I', 0, 0, 0, 0, 0, 0,
  379. '\033', 'E', 0, 0, 0, 0, 0, 0,
  380. '\033', 'a', 0, 0, 0, 0, 0, 0,
  381. '\033', 'b', 0, 0, 0, 0, 0, 0,
  382. '\033', 'c', 0, 0, 0, 0, 0, 0,
  383. '\033', 'd', 0, 0, 0, 0, 0, 0,
  384. };
  385.  
  386. static char unxbaud P_((long));
  387.  
  388. /* convert a number describing the baud rate into a Unix
  389.  * style baud rate number. Returns the Unix baud rate,
  390.  * or 16 (EXTA) if the rate is unknown
  391.  */
  392.  
  393. #define EXTA 16
  394.  
  395. static long ubaud[EXTA] = {
  396. 0L, 50L, 75L, 110L, 134L, 150L, 200L, 300L,
  397. 600L, 1200L, 1800L, 2400L, 4800L, 9600L, 19200L, 38400L
  398. };
  399.  
  400. static char
  401. unxbaud(baud)
  402.     long baud;
  403. {
  404.     int i;
  405.     for (i = 1; i < EXTA; i++) {
  406.         if (ubaud[i] == baud)
  407.             break;
  408.     }
  409.     return i;
  410. }
  411.  
  412. #define tosbaud(c) ( ((c) < 0 || (c) >= EXTA) ? -1L : ubaud[c] )
  413.  
  414. long
  415. tty_ioctl(f, mode, arg)
  416.     FILEPTR *f;
  417.     int mode;
  418.     void *arg;
  419. {
  420.     struct sgttyb *sg;
  421.     struct tchars *tc;
  422.     struct ltchars *ltc;
  423.     struct tty *tty;
  424.     struct winsize *sz;
  425.     struct xkey *xk;
  426.     char *xktab;
  427.     int i;
  428.     long baud;
  429.     short flags;
  430.  
  431.     if (!is_terminal(f)) {
  432.         DEBUG("tty_ioctl(mode %x): file is not a tty", mode);
  433.         return EINVFN;
  434.     }
  435.     tty = (struct tty *)f->devinfo;
  436.     assert(tty != 0);
  437.  
  438.     switch(mode) {
  439.     case TIOCGETP:
  440.         sg = (struct sgttyb *)arg;
  441.     /* get input and output baud rates from the terminal device */
  442.         baud = -1L;
  443.         (*f->dev->ioctl)(f, TIOCIBAUD, &baud);
  444.         tty->sg.sg_ispeed = unxbaud(baud);
  445.         baud = -1L;
  446.         (*f->dev->ioctl)(f, TIOCOBAUD, &baud);
  447.         tty->sg.sg_ospeed = unxbaud(baud);
  448.     /* get terminal flags */
  449.         flags = 0;
  450.         if ((*f->dev->ioctl)(f, TIOCGFLAGS, &flags) == 0) {
  451.             tty->sg.sg_flags &= ~TF_FLAGS;
  452.             tty->sg.sg_flags |= (flags & TF_FLAGS);
  453.         }
  454.         *sg = tty->sg;
  455.         return 0;
  456.     case TIOCSETP:
  457.         sg = (struct sgttyb *)arg;
  458.         tty->sg = *sg;
  459.     /* set baud rates */
  460.         baud = tosbaud(sg->sg_ispeed);
  461.         (*f->dev->ioctl)(f, TIOCIBAUD, &baud);
  462.         baud = tosbaud(sg->sg_ospeed);
  463.         (*f->dev->ioctl)(f, TIOCOBAUD, &baud);
  464.     /* set parity, etc. */
  465.         flags = TF_8BIT;
  466.         if (sg->sg_flags & (T_EVENP|T_ODDP)) {
  467.             flags = TF_7BIT;
  468.         }
  469.         flags |= (sg->sg_flags & TF_FLAGS);
  470.         (*f->dev->ioctl)(f, TIOCSFLAGS, &flags);
  471.         return 0;
  472.     case TIOCGETC:
  473.         tc = (struct tchars *)arg;
  474.         *tc = tty->tc;
  475.         return 0;
  476.     case TIOCSETC:
  477.         tc = (struct tchars *)arg;
  478.         tty->tc = *tc;
  479.         return 0;
  480.     case TIOCGLTC:
  481.         ltc = (struct ltchars *)arg;
  482.         *ltc = tty->ltc;
  483.         return 0;
  484.     case TIOCSLTC:
  485.         ltc = (struct ltchars *)arg;
  486.         tty->ltc = *ltc;
  487.         return 0;
  488.     case TIOCGWINSZ:
  489.         sz = (struct winsize *)arg;
  490.         *sz = tty->wsiz;
  491.         return 0;
  492.     case TIOCSWINSZ:
  493.         sz = (struct winsize *)arg;
  494.         tty->wsiz = *sz;
  495.         return 0;
  496.     case TIOCGPGRP:
  497.         *((long *)arg) = tty->pgrp;
  498.         return 0;
  499.     case TIOCSPGRP:
  500.         tty->pgrp = (*((long *)arg) & 0x00007fffL);
  501.         return 0;
  502.     case TIOCSTART:
  503.         tty->state &= ~TS_HOLD;
  504.         return 0;
  505.     case TIOCSTOP:
  506.         tty->state |= TS_HOLD;
  507.         return 0;
  508.     case TIOCGXKEY:
  509.         xk = (struct xkey *)arg;
  510.         i = xk->xk_num;
  511.         if (i < 0 || i > 31) return ERANGE;
  512.         xktab = tty->xkey;
  513.         if (!xktab) xktab = vt52xkey;
  514.         xktab += i*8;
  515.         for (i = 0; i < 8; i++)
  516.             xk->xk_def[i] = *xktab++;
  517.         return 0;
  518.     case TIOCSXKEY:
  519.         xk = (struct xkey *)arg;
  520.         xktab = tty->xkey;
  521.         if (!xktab) {
  522.             xktab = kmalloc((long)256);
  523.             if (!xktab) return ENSMEM;
  524.             for (i = 0; i < 256; i++)
  525.                 xktab[i] = vt52xkey[i];
  526.             tty->xkey = xktab;
  527.         }
  528.         i = xk->xk_num;
  529.         if (i < 0 || i > 31) return ERANGE;
  530.         xktab += i*8;
  531.         for (i = 0; i < 7; i++)
  532.             xktab[i] = xk->xk_def[i];
  533.         xktab[7] = 0;
  534.         return 0;
  535.     default:
  536.         DEBUG("tty_ioctl: bad function call");
  537.         return EINVFN;
  538.     }
  539. }
  540.  
  541. /*
  542.  * function for translating extended characters (e.g. cursor keys, or
  543.  * ALT+key sequences) into either escape sequences or meta characters.
  544.  * for escape sequences, we return the the first character of the
  545.  * sequence (normally ESC) and set the tty's state so that subsequent
  546.  * calls to tty_getchar will pick up the remaining characters.
  547.  * Note that escape sequences are limited to 7 characters at most.
  548.  */
  549.  
  550. static int
  551. escseq(tty, scan)
  552.     struct tty *tty;
  553.     int scan;
  554. {
  555.     char *tab;
  556.     int i;
  557.  
  558.     switch(scan) {
  559.     case CURS_UP: i = 20; break;
  560.     case CURS_DN: i = 21; break;
  561.     case CURS_RT: i = 22; break;
  562.     case CURS_LF: i = 23; break;
  563.     case K_HELP:  i = 24; break;
  564.     case K_UNDO:  i = 25; break;
  565.     case K_INSERT:i = 26; break;
  566.     case K_HOME:  i = 27; break;
  567.     case CURS_UP+0x100: i = 28; break;
  568.     case CURS_DN+0x100: i = 29; break;
  569.     case CURS_RT+0x100: i = 30; break;
  570.     case CURS_LF+0x100: i = 31; break;
  571.     default:
  572.         if (scan >= F_1 && scan <= F_10) {
  573.             i = scan - F_1;
  574.         } else if (scan >= F_11 && scan <= F_20) {
  575.             i = 10 + scan - F_11;
  576.         } else
  577.             i = -1;
  578.     }
  579.  
  580.     if (i >= 0) {        /* an extended escape sequence */
  581.         tab = tty->xkey;
  582.         if (!tab) tab = vt52xkey;
  583.         i *= 8;
  584.         scan = tab[i++];
  585.         if (scan) {
  586.             if (tab[i] == 0) i = 0;
  587.             tty->state = (tty->state & ~TS_ESC) | i;
  588.         }
  589.         return scan;
  590.     }
  591.  
  592.     if (scan >= ALT_1 && scan <= ALT_0) {
  593.         scan -= (ALT_1-1);
  594.         if (scan == 10) scan = 0;
  595.         return (scan + '0') | 0x80;
  596.     }
  597.  
  598.     tab = *( ((char **)Keytbl((void *)-1L, (void *)-1L, (void *)-1L)) + 2 );    /* gratuitous (void *) for Lattice */
  599.     scan = tab[scan];
  600.     if (scan >= 'A' && scan <= 'Z') return scan | 0x80;
  601.     return 0;
  602. }
  603.  
  604. long
  605. tty_getchar(f, mode)
  606.     FILEPTR *f;
  607.     int mode;
  608. {
  609.     struct tty *tty = (struct tty *)f->devinfo;
  610.     char c, *tab;
  611.     long r, ret;
  612.     int scan;
  613.     int master = f->flags & O_HEAD;
  614.  
  615.     assert(tty);
  616.  
  617. /* pty masters never worry about job control and always read in raw mode */
  618.     if (master) {
  619.         ret = (*f->dev->read)(f, (char *)&r, 4L);
  620.         return (ret != 4L) ? MiNTEOF : r;
  621.     }
  622.  
  623. /* job control check */
  624.     if (tty->pgrp != curproc->pgrp && tty->pgrp > 0) {
  625.         TRACE("job control: tty pgrp is %d proc pgrp is %d",
  626.             tty->pgrp, curproc->pgrp);
  627.         killgroup(curproc->pgrp, SIGTTIN);
  628.     }
  629.  
  630.     if (mode & COOKED)
  631.         tty->state |= TS_COOKED;
  632.     else
  633.         tty->state &= ~TS_COOKED;
  634.  
  635.     c = UNDEF+1;    /* set to UNDEF when we successfully read a character */
  636.  
  637. /* we may be in the middle of an escape sequence */
  638.     if (scan = (tty->state & TS_ESC)) {
  639.         if (mode & ESCSEQ) {
  640.             tab = tty->xkey ? tty->xkey : vt52xkey;
  641.             r = (unsigned char) tab[scan++];
  642.             if (r) {
  643.                 c = UNDEF;
  644.                 if (tab[scan] == 0) scan = 0;
  645.             }
  646.             else
  647.                 scan = 0;
  648.             tty->state = (tty->state & ~TS_ESC) | scan;
  649.         }
  650.         else
  651.             tty->state &= ~TS_ESC;
  652.     }
  653.  
  654.     while (c != UNDEF) {
  655.         ret = (*f->dev->read)(f, (char *)&r, 4L);
  656.         if (ret != 4L) {
  657.             DEBUG("EOF on tty device");
  658.             return MiNTEOF;
  659.         }
  660.         c = r & 0x00ff;
  661.         scan = (r & 0x00ff0000) >> 16;
  662.         if ( (c == 0) && (mode & ESCSEQ) && scan) {
  663.             c = UNDEF;
  664.     /* translate cursor keys, etc. into escape sequences or
  665.      * META characters
  666.      */
  667.             r = escseq(tty, scan);
  668.         } else if ((mode & ESCSEQ) && ((scan == CURS_UP && c == '8') ||
  669.                (scan == CURS_DN && c == '2') ||
  670.                (scan == CURS_RT && c == '6') ||
  671.                (scan == CURS_LF && c == '4'))) {
  672.             c = UNDEF;
  673.             r = escseq(tty, scan+0x100);
  674.         } else if (mode & COOKED) {
  675.             if (c == UNDEF)
  676.                 ;    /* do nothing */
  677.             else if (c == tty->ltc.t_dsuspc)
  678.                 killgroup(curproc->pgrp, SIGTSTP);
  679.             else if (c == tty->tc.t_intrc)
  680.                 killgroup(curproc->pgrp, SIGINT);
  681.             else if (c == tty->tc.t_stopc)
  682.                 tty->state |= TS_HOLD;
  683.             else if (c == tty->tc.t_stopc)
  684.                 tty->state &= ~TS_HOLD;
  685.             else
  686.                 c = UNDEF;
  687.         }
  688.         else
  689.                 c = UNDEF;
  690.     }
  691.  
  692.     if (mode & ECHO)
  693.         tty_putchar(f, r, mode);
  694.  
  695.     return r;
  696. }
  697.  
  698. /*
  699.  * tty_putchar: returns number of bytes successfully written
  700.  */
  701.  
  702. long
  703. tty_putchar(f, data, mode)
  704.     FILEPTR *f;
  705.     long data;
  706.     int mode;
  707. {
  708.     struct tty *tty;
  709.     int master;        /* file is pty master side */
  710.     char ch;
  711.  
  712.     tty = (struct tty *)f->devinfo;
  713.  
  714.     master = f->flags & O_HEAD;
  715.  
  716. /* pty masters don't need to worry about job control */
  717.     if (master) {
  718.         ch = data & 0xff;
  719.  
  720.         if ( (tty->state & TS_COOKED) && ch != UNDEF) {
  721. /* see if we're putting control characters into the buffer */
  722.             if (ch == tty->tc.t_intrc) {
  723.                 tty->state &= ~TS_HOLD;
  724.                 killgroup(tty->pgrp, SIGINT);
  725.                 return 4L;
  726.             }
  727.             else if (ch == tty->tc.t_quitc) {
  728.                 tty->state &= ~TS_HOLD;
  729.                 killgroup(tty->pgrp, SIGQUIT);
  730.                 return 4L;
  731.             }
  732.             else if (ch == tty->ltc.t_suspc) {
  733.                 tty->state &= ~TS_HOLD;
  734.                 killgroup(tty->pgrp, SIGTSTP);
  735.                 return 4L;
  736.             }
  737.             else if (ch == tty->tc.t_stopc) {
  738.                 tty->state |= TS_HOLD;
  739.                 return 4L;
  740.             }
  741.             else if (ch == tty->tc.t_startc) {
  742.                 tty->state &= ~TS_HOLD;
  743.                 return 4L;
  744.             }
  745.             else if (tty->state & TS_HOLD) {
  746.                 return 0;
  747.             }
  748.         }
  749.         goto do_putchar;
  750.     }
  751. /* job control checks */
  752. /* AKP: added T_TOSTOP; don't stop BG output if T_TOSTOP is clear */
  753.     if (tty->pgrp != curproc->pgrp && tty->pgrp > 0 
  754.         && (tty->sg.sg_flags & T_TOSTOP)) {
  755.         TRACE("job control: tty pgrp is %d proc pgrp is %d",
  756.             tty->pgrp, curproc->pgrp);
  757.         killgroup(curproc->pgrp, SIGTTOU);
  758.     }
  759.  
  760.     if (mode & COOKED) {
  761.         tty->state |= TS_COOKED;
  762.         while (tty->state & TS_HOLD)
  763.             nap(60);    /* sleep for 60 milliseconds */
  764.     }
  765.     else
  766.         tty->state &= ~TS_COOKED;
  767.  
  768. do_putchar:
  769.     return (*f->dev->write)(f, (char *)&data, 4L);
  770. }
  771.