home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / trn / part08 / term.c < prev   
Encoding:
C/C++ Source or Header  |  1991-12-02  |  25.4 KB  |  1,229 lines

  1. /* $Id: term.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
  2.  *
  3.  * $Log: term.c,v $
  4.  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  5.  * Patchlevel 2 changes
  6.  *
  7.  * Revision 4.4  1991/09/09  20:27:37  sob
  8.  * release 4.4
  9.  *
  10.  *
  11.  * 
  12.  */
  13. /* This software is Copyright 1991 by Stan Barber. 
  14.  *
  15.  * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  16.  * use this software as long as: there is no monetary profit gained
  17.  * specifically from the use or reproduction of this software, it is not
  18.  * sold, rented, traded or otherwise marketed, and this copyright notice is
  19.  * included prominently in any copy made. 
  20.  *
  21.  * The author make no claims as to the fitness or correctness of this software
  22.  * for any use whatsoever, and it is provided as is. Any use of this software
  23.  * is at the user's own risk. 
  24.  */
  25.  
  26. #include "EXTERN.h"
  27. #include "common.h"
  28. #include "util.h"
  29. #include "final.h"
  30. #include "help.h"
  31. #include "cheat.h"
  32. #include "intrp.h"
  33. #include "INTERN.h"
  34. #include "term.h"
  35.  
  36. char ERASECH;        /* rubout character */
  37. char KILLCH;        /* line delete character */
  38. char tcarea[TCSIZE];    /* area for "compiled" termcap strings */
  39.  
  40. #ifdef USETHREADS
  41. int upcost;
  42. #endif
  43.  
  44. /* guarantee capability pointer != Nullch */
  45. /* (I believe terminfo will ignore the &tmpaddr argument.) */
  46.  
  47. #define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr)
  48.  
  49. #ifdef PUSHBACK
  50. struct keymap {
  51.     char km_type[128];
  52.     union km_union {
  53.     struct keymap *km_km;
  54.     char *km_str;
  55.     } km_ptr[128];
  56. };
  57.  
  58. #define KM_NOTHIN 0
  59. #define KM_STRING 1
  60. #define KM_KEYMAP 2
  61. #define KM_BOGUS 3
  62.  
  63. #define KM_TMASK 3
  64. #define KM_GSHIFT 4
  65. #define KM_GMASK 7
  66.  
  67. typedef struct keymap KEYMAP;
  68.  
  69. KEYMAP *topmap INIT(Null(KEYMAP*));
  70.  
  71. void mac_init();
  72. KEYMAP *newkeymap();
  73. void show_keymap();
  74. void pushstring();
  75. #endif
  76.  
  77. void line_col_calcs();
  78.  
  79. /* terminal initialization */
  80.  
  81. void
  82. term_init()
  83. {
  84.     savetty();                /* remember current tty state */
  85.  
  86. #ifdef TERMIO
  87.     outspeed = _tty.c_cflag & CBAUD;    /* for tputs() */
  88.     ERASECH = _tty.c_cc[VERASE];    /* for finish_command() */
  89.     KILLCH = _tty.c_cc[VKILL];        /* for finish_command() */
  90.     if (GT = ((_tty.c_oflag & TABDLY) != TAB3))
  91.     /* we have tabs, so that's OK */;
  92.     else
  93.     _tty.c_oflag &= ~TAB3;    /* turn off kernel tabbing -- done in rn */
  94. #else /* !TERMIO */
  95. # ifdef TERMIOS
  96.     outspeed = cfgetospeed(&_tty);    /* for tputs() (output) */
  97.     ERASECH = _tty.c_cc[VERASE];    /* for finish_command() */
  98.     KILLCH = _tty.c_cc[VKILL];        /* for finish_command() */
  99. /*    _tty.c_oflag &= ~OXTABS;        /* turn off kernel tabbing-done in rn */
  100. # else /* !TERMIOS */
  101.     outspeed = _tty.sg_ospeed;        /* for tputs() */
  102.     ERASECH = _tty.sg_erase;        /* for finish_command() */
  103.     KILLCH = _tty.sg_kill;        /* for finish_command() */
  104.     if (GT = ((_tty.sg_flags & XTABS) != XTABS))
  105.     /* we have tabs, so that's OK */;
  106.     else
  107.     _tty.sg_flags &= ~XTABS;
  108. # endif /* TERMIOS */
  109. #endif /* TERMIO */
  110.  
  111.     /* The following could be a table but I can't be sure that there isn't */
  112.     /* some degree of sparsity out there in the world. */
  113.  
  114.     switch (outspeed) {            /* 1 second of padding */
  115. #ifdef BEXTA
  116.         case BEXTA:  just_a_sec = 1920; break;
  117. #else
  118. #ifdef B19200
  119.         case B19200: just_a_sec = 1920; break;
  120. #endif
  121. #endif
  122.         case B9600:  just_a_sec =  960; break;
  123.         case B4800:  just_a_sec =  480; break;
  124.         case B2400:  just_a_sec =  240; break;
  125.         case B1800:  just_a_sec =  180; break;
  126.         case B1200:  just_a_sec =  120; break;
  127.         case B600:   just_a_sec =   60; break;
  128.     case B300:   just_a_sec =   30; break;
  129.     /* do I really have to type the rest of this??? */
  130.         case B200:   just_a_sec =   20; break;
  131.         case B150:   just_a_sec =   15; break;
  132.         case B134:   just_a_sec =   13; break;
  133.         case B110:   just_a_sec =   11; break;
  134.         case B75:    just_a_sec =    8; break;
  135.         case B50:    just_a_sec =    5; break;
  136.         default:     just_a_sec =  960; break;
  137.                     /* if we are running detached I */
  138.     }                    /*  don't want to know about it! */
  139. }
  140.  
  141. /* set terminal characteristics */
  142.  
  143. void
  144. term_set(tcbuf)
  145. char *tcbuf;        /* temp area for "uncompiled" termcap entry */
  146. {
  147.     char *tmpaddr;            /* must not be register */
  148.     register char *tmpstr;
  149.     char *tgetstr();
  150.     char *s;
  151.     int status;
  152. #ifdef TIOCGWINSZ
  153.     struct winsize winsize;
  154. #endif
  155.  
  156. #ifdef PENDING
  157. #if ! defined (FIONREAD) && ! defined (RDCHK)
  158.     /* do no delay reads on something that always gets closed on exit */
  159.  
  160.     devtty = open("/dev/tty",0);
  161.     if (devtty < 0) {
  162.     printf(cantopen,"/dev/tty") FLUSH;
  163.     finalize(1);
  164.     }
  165.     fcntl(devtty,F_SETFL,O_NDELAY);
  166. #endif
  167. #endif
  168.     
  169.     /* get all that good termcap stuff */
  170.  
  171. #ifdef HAVETERMLIB
  172.     status = tgetent(tcbuf,getenv("TERM"));    /* get termcap entry */
  173.     if (status < 1) {
  174. #ifdef VERBOSE
  175.     printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH;
  176. #else
  177.     fputs("Termcap botch\n",stdout) FLUSH;
  178. #endif
  179.     finalize(1);
  180.     }
  181.     tmpaddr = tcarea;            /* set up strange tgetstr pointer */
  182.     s = Tgetstr("pc");            /* get pad character */
  183.     PC = *s;                /* get it where tputs wants it */
  184.     if (!tgetflag("bs")) {        /* is backspace not used? */
  185.     BC = Tgetstr("bc");        /* find out what is */
  186.     if (BC == nullstr)         /* terminfo grok's 'bs' but not 'bc' */
  187.         BC = Tgetstr("le");
  188.     } else
  189.     BC = "\b";            /* make a backspace handy */
  190.     UP = Tgetstr("up");            /* move up a line */
  191.     if (!*UP)                /* no UP string? */
  192.     marking = 0;            /* disable any marking */
  193.     if (muck_up_clear)            /* this is for weird HPs */
  194.     CL = "\n\n\n\n";
  195.     else
  196.     CL = Tgetstr("cl");        /* get clear string */
  197.     CE = Tgetstr("ce");            /* clear to end of line string */
  198.     TI = Tgetstr("ti");            /* initialize display */
  199.     TE = Tgetstr("te");            /* reset display */
  200. #if defined(CLEAREOL) || defined(USETHREADS)
  201.     HO = Tgetstr("ho");            /* home cursor if no CM */
  202.     CM = Tgetstr("cm");            /* cursor motion */
  203.     if (*CM || *HO)
  204.     can_home = TRUE;
  205. #endif
  206. #ifdef CLEAREOL
  207.     CD = Tgetstr("cd");            /* clear to end of display */
  208.     if (!*CE || !*CD || !can_home)    /* can we CE, CD, and home? */
  209.     can_home_clear = FALSE;        /*  no, so disable use of clear eol */
  210.     if (!*CE) CE = CD;
  211. #endif /* CLEAREOL */
  212. #ifdef USETHREADS
  213.     upcost = strlen(UP);
  214. #endif
  215.     SO = Tgetstr("so");            /* begin standout */
  216.     SE = Tgetstr("se");            /* end standout */
  217.     if ((SG = tgetnum("sg"))<0)
  218.     SG = 0;                /* blanks left by SG, SE */
  219.     US = Tgetstr("us");            /* start underline */
  220.     UE = Tgetstr("ue");            /* end underline */
  221.     if ((UG = tgetnum("ug"))<0)
  222.     UG = 0;                /* blanks left by US, UE */
  223.     if (*US)
  224.     UC = nullstr;            /* UC must not be NULL */
  225.     else
  226.     UC = Tgetstr("uc");        /* underline a character */
  227.     if (!*US && !*UC) {            /* no underline mode? */
  228.     US = SO;            /* substitute standout mode */
  229.     UE = SE;
  230.     UG = SG;
  231.     }
  232.     LINES = tgetnum("li");        /* lines per page */
  233.     COLS = tgetnum("co");        /* columns on page */
  234.  
  235. #ifdef TIOCGWINSZ
  236.     { struct winsize ws;
  237.     if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) {
  238.         LINES = ws.ws_row;
  239.         COLS = ws.ws_col;
  240.     }
  241.     }
  242. #endif
  243.     
  244.     AM = tgetflag("am");        /* terminal wraps automatically? */
  245.     XN = tgetflag("xn");        /* then eats next newline? */
  246.     VB = Tgetstr("vb");
  247.     if (!*VB)
  248.     VB = "\007";
  249.     CR = Tgetstr("cr");
  250.     if (!*CR) {
  251.     if (tgetflag("nc") && *UP) {
  252.         CR = safemalloc((MEM_SIZE)strlen(UP)+2);
  253.         sprintf(CR,"%s\r",UP);
  254.     }
  255.     else
  256.         CR = "\r";
  257.     }
  258. #ifdef TIOCGWINSZ
  259.     if (ioctl(1, TIOCGWINSZ, &winsize)>=0) {
  260.         if (winsize.ws_row>0) LINES=winsize.ws_row;
  261.         if (winsize.ws_col>0) COLS=winsize.ws_col;
  262.     }
  263. #endif
  264. #else
  265.     ??????                /* Roll your own... */
  266. #endif
  267.     termlib_init();
  268.     line_col_calcs();
  269.     noecho();                /* turn off echo */
  270.     crmode();                /* enter cbreak mode */
  271.  
  272. #ifdef PUSHBACK
  273.     mac_init(tcbuf);
  274. #endif
  275. }
  276.  
  277. #ifdef PUSHBACK
  278. void
  279. mac_init(tcbuf)
  280. char *tcbuf;
  281. {
  282.     char tmpbuf[1024];
  283.  
  284.     tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r");
  285.     if (tmpfp != Nullfp) {
  286.     while (fgets(tcbuf,1024,tmpfp) != Nullch) {
  287.         mac_line(tcbuf,tmpbuf,(sizeof tmpbuf));
  288.     }
  289.     fclose(tmpfp);
  290.     }
  291. }
  292.  
  293. void
  294. mac_line(line,tmpbuf,tbsize)
  295. char *line;
  296. char *tmpbuf;
  297. int tbsize;
  298. {
  299.     register char *s, *m;
  300.     register KEYMAP *curmap;
  301.     register int ch;
  302.     register int garbage = 0;
  303.     static char override[] = "\nkeymap overrides string\n";
  304.  
  305.     if (topmap == Null(KEYMAP*))
  306.     topmap = newkeymap();
  307.     if (*line == '#' || *line == '\n')
  308.     return;
  309.     if (line[ch = strlen(line)-1] == '\n')
  310.     line[ch] = '\0';
  311.     m = dointerp(tmpbuf,tbsize,line," \t");
  312.     if (!*m)
  313.     return;
  314.     while (*m == ' ' || *m == '\t') m++;
  315.     for (s=tmpbuf,curmap=topmap; *s; s++) {
  316.     ch = *s & 0177;
  317.     if (s[1] == '+' && isdigit(s[2])) {
  318.         s += 2;
  319.         garbage = (*s & KM_GMASK) << KM_GSHIFT;
  320.     }
  321.     else
  322.         garbage = 0;
  323.     if (s[1]) {
  324.         if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) {
  325.         fputs(override,stdout) FLUSH;
  326.         free(curmap->km_ptr[ch].km_str);
  327.         curmap->km_ptr[ch].km_str = Nullch;
  328.         }
  329.         curmap->km_type[ch] = KM_KEYMAP + garbage;
  330.         if (curmap->km_ptr[ch].km_km == Null(KEYMAP*))
  331.         curmap->km_ptr[ch].km_km = newkeymap();
  332.         curmap = curmap->km_ptr[ch].km_km;
  333.     }
  334.     else {
  335.         if ((curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP)
  336.         fputs(override,stdout) FLUSH;
  337.         else {
  338.         curmap->km_type[ch] = KM_STRING + garbage;
  339.         curmap->km_ptr[ch].km_str = savestr(m);
  340.         }
  341.     }
  342.     }
  343. }
  344.  
  345. KEYMAP*
  346. newkeymap()
  347. {
  348.     register int i;
  349.     register KEYMAP *map;
  350.  
  351. #ifndef lint
  352.     map = (KEYMAP*)safemalloc(sizeof(KEYMAP));
  353. #else
  354.     map = Null(KEYMAP*);
  355. #endif /* lint */
  356.     for (i=127; i>=0; --i) {
  357.     map->km_ptr[i].km_km = Null(KEYMAP*);
  358.     map->km_type[i] = KM_NOTHIN;
  359.     }
  360.     return map;
  361. }
  362.  
  363. void
  364. show_macros()
  365. {
  366.     char prebuf[64];
  367.  
  368.     if (topmap != Null(KEYMAP*)) {
  369.     print_lines("Macros:\n",STANDOUT);
  370.     *prebuf = '\0';
  371.     show_keymap(topmap,prebuf);
  372.     }
  373.     else {
  374.     print_lines("No macros defined.\n", NOMARKING);
  375.     }
  376. }
  377.  
  378. void
  379. show_keymap(curmap,prefix)
  380. register KEYMAP *curmap;
  381. char *prefix;
  382. {
  383.     register int i;
  384.     register char *next = prefix + strlen(prefix);
  385.     register int kt;
  386.  
  387.     for (i=0; i<128; i++) {
  388.     if (kt = curmap->km_type[i]) {
  389.         if (i < ' ')
  390.         sprintf(next,"^%c",i+64);
  391.         else if (i == ' ')
  392.         strcpy(next,"\\040");
  393.         else if (i == 127)
  394.         strcpy(next,"^?");
  395.         else
  396.         sprintf(next,"%c",i);
  397.         if ((kt >> KM_GSHIFT) & KM_GMASK) {
  398.         sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK);
  399.         strcat(next,cmd_buf);
  400.         }
  401.         switch (kt & KM_TMASK) {
  402.         case KM_NOTHIN:
  403.         sprintf(cmd_buf,"%s    %c\n",prefix,i);
  404.         print_lines(cmd_buf,NOMARKING);
  405.         break;
  406.         case KM_KEYMAP:
  407.         show_keymap(curmap->km_ptr[(char)i].km_km, prefix);
  408.         break;
  409.         case KM_STRING:
  410.         sprintf(cmd_buf,"%s    %s\n",prefix,curmap->km_ptr[i].km_str);
  411.         print_lines(cmd_buf,NOMARKING);
  412.         break;
  413.         case KM_BOGUS:
  414.         sprintf(cmd_buf,"%s    BOGUS\n",prefix);
  415.         print_lines(cmd_buf,STANDOUT);
  416.         break;
  417.         }
  418.     }
  419.     }
  420. }
  421.  
  422. #endif
  423.  
  424. /* routine to pass to tputs */
  425.  
  426. char
  427. putchr(ch)
  428. register char_int ch;
  429. {
  430.     putchar(ch);
  431. #ifdef lint
  432.     ch = Null(char);
  433.     ch = ch;
  434. #endif
  435.     return((char) 0);
  436. }
  437.  
  438. /* input the 2nd and succeeding characters of a multi-character command */
  439. /* returns TRUE if command finished, FALSE if they rubbed out first character */
  440.  
  441. bool
  442. finish_command(donewline)
  443. int donewline;
  444. {
  445.     register char *s;
  446.     register bool quoteone = FALSE;
  447.  
  448.     s = buf;
  449.     if (s[1] != FINISHCMD)        /* someone faking up a command? */
  450.     return TRUE;
  451.     do {
  452.       top:
  453.     if (*(unsigned char *)s < ' ') {
  454.         putchar('^');
  455.         putchar(*s | 64);
  456.     }
  457.     else if (*s == '\177') {
  458.         putchar('^');
  459.         putchar('?');
  460.     }
  461.     else
  462.         putchar(*s);        /* echo previous character */
  463.     s++;
  464. re_read:
  465.     fflush(stdout);
  466.     getcmd(s);
  467.     if (quoteone) {
  468.         quoteone = FALSE;
  469.         continue;
  470.     }
  471.     if (errno || *s == '\f') {
  472.         *s = Ctl('r');        /* force rewrite on CONT */
  473.     }
  474.     if (*s == '\033') {        /* substitution desired? */
  475. #ifdef ESCSUBS
  476.         char tmpbuf[4], *cpybuf;
  477.  
  478.         tmpbuf[0] = '%';
  479.         read_tty(&tmpbuf[1],1);
  480. #ifdef RAWONLY
  481.         tmpbuf[1] &= 0177;
  482. #endif
  483.         tmpbuf[2] = '\0';
  484.         if (tmpbuf[1] == 'h') {
  485.         (void) help_subs();
  486.         *s = '\0';
  487.         reprint();
  488.         goto re_read;
  489.         }
  490.         else if (tmpbuf[1] == '\033') {
  491.         *s = '\0';
  492.         cpybuf = savestr(buf);
  493.         interp(buf, (sizeof buf), cpybuf);
  494.         free(cpybuf);
  495.         s = buf + strlen(buf);
  496.         reprint();
  497.         goto re_read;
  498.         }
  499.         else {
  500.         interp(s,(sizeof buf) - (s-buf),tmpbuf);
  501.         fputs(s,stdout);
  502.         s += strlen(s);
  503.         }
  504.         goto re_read;
  505. #else
  506.         notincl("^[");
  507.         *s = '\0';
  508.         reprint();
  509.         goto re_read;
  510. #endif
  511.     }
  512.     else if (*s == ERASECH) {    /* they want to rubout a char? */
  513.         rubout();
  514.         s--;            /* discount the char rubbed out */
  515.         if (*(unsigned char *)s < ' ' || *s == '\177')
  516.         rubout();
  517.         if (s == buf) {        /* entire string gone? */
  518.         fflush(stdout);        /* return to single char command mode */
  519.         return FALSE;
  520.         }
  521.         else
  522.         goto re_read;
  523.     }
  524.     else if (*s == KILLCH) {    /* wipe out the whole line? */
  525.         while (s-- != buf) {    /* emulate that many ERASEs */
  526.         rubout();
  527.         if (*(unsigned char *)s < ' ' || *s == '\177')
  528.             rubout();
  529.         }
  530.         fflush(stdout);
  531.         return FALSE;        /* return to single char mode */
  532.     }
  533. #ifdef WORDERASE
  534.     else if (*s == Ctl('w')) {    /* wipe out one word? */
  535.         *s-- = ' ';
  536.         while (!isspace(*s) || isspace(s[1])) {
  537.         rubout();
  538.         if (s-- == buf) {
  539.             fflush(stdout);
  540.             return FALSE;    /* return to single char mode */
  541.         }
  542.         if (*(unsigned char *)s < ' ' || *s == '\177')
  543.             rubout();
  544.         }
  545.         s++;
  546.         goto re_read;
  547.     }
  548. #endif
  549.     else if (*s == Ctl('r')) {
  550.         *s = '\0';
  551.         reprint();
  552.         goto re_read;
  553.     }
  554.     else if (*s == Ctl('v')) {
  555.         putchar('^');
  556.         backspace();
  557.         fflush(stdout);
  558.         getcmd(s);
  559.         goto top;
  560.     }
  561.     else if (*s == '\\') {
  562.         quoteone = TRUE;
  563.     }
  564. #ifdef cray
  565.     } while (*s != '\r');        /* till a newline (not echoed) */
  566. #else
  567.     } while (*s != '\n');        /* till a newline (not echoed) */
  568. #endif
  569.     *s = '\0';                /* terminate the string nicely */
  570.     if (donewline)
  571.     putchar('\n') FLUSH;
  572.     return TRUE;            /* say we succeeded */
  573. }
  574.  
  575. /* discard any characters typed ahead */
  576.  
  577. void
  578. eat_typeahead()
  579. {
  580. #ifdef PUSHBACK
  581.     if (!typeahead && nextin==nextout)    /* cancel only keyboard stuff */
  582. #else
  583.     if (!typeahead)
  584. #endif
  585.     {
  586. #ifdef PENDING
  587.     while (input_pending())
  588.         read_tty(buf,sizeof(buf));
  589. #else /* this is probably v7 */
  590. #if defined(TERMIO) || defined(TERMIOS)
  591.     ioctl(_tty_ch,TCSETAW,&_tty);
  592. #else
  593.     ioctl(_tty_ch,TIOCSETP,&_tty);
  594. #endif
  595. #endif
  596.     }
  597. }
  598.  
  599. void
  600. settle_down()
  601. {
  602.     dingaling();
  603.     fflush(stdout);
  604. /*    sleep(1); */
  605. #ifdef PUSHBACK
  606.     nextout = nextin;            /* empty circlebuf */
  607. #endif
  608.     eat_typeahead();
  609. }
  610.  
  611. #ifdef PUSHBACK
  612. /* read a character from the terminal, with multi-character pushback */
  613.  
  614. int
  615. read_tty(addr,size)
  616. char *addr;
  617. int size;
  618. {
  619.     if (nextout != nextin) {
  620.     *addr = circlebuf[nextout++];
  621.     nextout %= PUSHSIZE;
  622.     return 1;
  623.     }
  624.     else {
  625.     size = read(0,addr,size);
  626. #ifdef RAWONLY
  627.     *addr &= 0177;
  628. #endif
  629.     return size;
  630.     }
  631. }
  632.  
  633. #ifdef PENDING
  634. #if ! defined (FIONREAD) && ! defined (RDCHK)
  635. int
  636. circfill()
  637. {
  638.     register int Howmany;
  639.  
  640.     errno = 0;
  641.     Howmany = read(devtty,circlebuf+nextin,1);
  642.  
  643.     if (Howmany < 0 && (errno == EAGAIN || errno == EINTR))
  644.     Howmany = 0;
  645.     if (Howmany) {
  646.     nextin += Howmany;
  647.     nextin %= PUSHSIZE;
  648.     }
  649.     return Howmany;
  650. }
  651. #endif /* PENDING */
  652. #endif /* FIONREAD */
  653.  
  654. void
  655. pushchar(c)
  656. char_int c;
  657. {
  658.     nextout--;
  659.     if (nextout < 0)
  660.     nextout = PUSHSIZE - 1;
  661.     if (nextout == nextin) {
  662.     fputs("\npushback buffer overflow\n",stdout) FLUSH;
  663.     sig_catcher(0);
  664.     }
  665.     circlebuf[nextout] = c;
  666. }
  667.  
  668. #else /* PUSHBACK */
  669. #ifndef read_tty
  670. /* read a character from the terminal, with hacks for O_NDELAY reads */
  671.  
  672. int
  673. read_tty(addr,size)
  674. char *addr;
  675. int size;
  676. {
  677.     if (is_input) {
  678.     *addr = pending_ch;
  679.     is_input = FALSE;
  680.     return 1;
  681.     }
  682.     else {
  683.     size = read(0,addr,size);
  684. #ifdef RAWONLY
  685.     *addr &= 0177;
  686. #endif
  687.     return size;
  688.     }
  689. }
  690. #endif /* read_tty */
  691. #endif /* PUSHBACK */
  692.  
  693. /* print an underlined string, one way or another */
  694.  
  695. void
  696. underprint(s)
  697. register char *s;
  698. {
  699.     assert(UC);
  700.     if (*UC) {        /* char by char underline? */
  701.     while (*s) {
  702.         if (*(unsigned char *)s < ' ') {
  703.         putchar('^');
  704.         backspace();/* back up over it */
  705.         underchar();/* and do the underline */
  706.         putchar(*s+64);
  707.         backspace();/* back up over it */
  708.         underchar();/* and do the underline */
  709.         }
  710.         else {
  711.         putchar(*s);
  712.         backspace();/* back up over it */
  713.         underchar();/* and do the underline */
  714.         }
  715.         s++;
  716.     }
  717.     }
  718.     else {        /* start and stop underline */
  719.     underline();    /* start underlining */
  720.     while (*s) {
  721.         if (*(unsigned char *)s < ' ') {
  722.         putchar('^');
  723.         putchar(*s+64);
  724.         }
  725.         else
  726.         putchar(*s);
  727.         s++;
  728.     }
  729.     un_underline();    /* stop underlining */
  730.     }
  731. }
  732.  
  733. /* keep screen from flashing strangely on magic cookie terminals */
  734.  
  735. #ifdef NOFIREWORKS
  736. void
  737. no_sofire()
  738. {
  739.     if (*UP && *SE) {        /* should we disable fireworks? */
  740.     putchar('\n');
  741.     un_standout();
  742.     up_line();
  743.     carriage_return();
  744.     }
  745. }
  746.  
  747. void
  748. no_ulfire()
  749. {
  750.     if (*UP && *US) {        /* should we disable fireworks? */
  751.     putchar('\n');
  752.     un_underline();
  753.     up_line();
  754.     carriage_return();
  755.     }
  756. }
  757. #endif
  758.  
  759. /* get a character into a buffer */
  760.  
  761. void
  762. getcmd(whatbuf)
  763. register char *whatbuf;
  764. {
  765. #ifdef PUSHBACK
  766.     register KEYMAP *curmap;
  767.     register int i;
  768.     bool no_macros; 
  769.     int times = 0;            /* loop detector */
  770.     char scrchar;
  771.  
  772. tryagain:
  773.     curmap = topmap;
  774.     no_macros = (whatbuf != buf && nextin == nextout); 
  775. #endif
  776.     for (;;) {
  777.     int_count = 0;
  778.     errno = 0;
  779.     if (read_tty(whatbuf,1) < 0){
  780.         if (!errno)
  781.             errno = EINTR;
  782.         if (errno == EINTR)
  783.         return;
  784.         perror(readerr);
  785.         sig_catcher(0);
  786.     }
  787. #ifdef PUSHBACK
  788.     if (*whatbuf & 0200 || no_macros) {
  789.         *whatbuf &= 0177;
  790.         goto got_canonical;
  791.     }
  792.     if (curmap == Null(KEYMAP*))
  793.         goto got_canonical;
  794.     for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){
  795.         read_tty(&scrchar,1);
  796.     }
  797.     switch (curmap->km_type[*whatbuf] & KM_TMASK) {
  798.     case KM_NOTHIN:            /* no entry? */
  799.         if (curmap == topmap)    /* unmapped canonical */
  800.         goto got_canonical;
  801.         settle_down();
  802.         goto tryagain;
  803.     case KM_KEYMAP:            /* another keymap? */
  804.         curmap = curmap->km_ptr[*whatbuf].km_km;
  805.         assert(curmap != Null(KEYMAP*));
  806.         break;
  807.     case KM_STRING:            /* a string? */
  808.         pushstring(curmap->km_ptr[*whatbuf].km_str);
  809.         if (++times > 20) {        /* loop? */
  810.         fputs("\nmacro loop?\n",stdout);
  811.         settle_down();
  812.         }
  813.         no_macros = FALSE;
  814.         goto tryagain;
  815.     }
  816. #else
  817. #ifdef RAWONLY
  818.     *whatbuf &= 0177;
  819. #endif
  820.     break;
  821. #endif
  822.     }
  823.  
  824. got_canonical:
  825. #if !defined(TERMIO) && !defined(TERMIOS)
  826.     if (*whatbuf == '\r')
  827.     *whatbuf = '\n';
  828. #endif
  829.     if (whatbuf == buf)
  830.     whatbuf[1] = FINISHCMD;        /* tell finish_command to work */
  831. }
  832.  
  833. #ifdef PUSHBACK
  834. void
  835. pushstring(str)
  836. char *str;
  837. {
  838.     register int i;
  839.     char tmpbuf[PUSHSIZE];
  840.     register char *s = tmpbuf;
  841.  
  842.     assert(str != Nullch);
  843.     interp(s,PUSHSIZE,str);
  844.     for (i = strlen(s)-1; i >= 0; --i) {
  845.     s[i] ^= 0200; 
  846.     pushchar(s[i]);
  847.     }
  848. }
  849. #endif
  850.  
  851. int
  852. get_anything()
  853. {
  854.     char tmpbuf[2];
  855.  
  856. reask_anything:
  857.     unflush_output();            /* disable any ^O in effect */
  858.     standout();
  859. #ifdef VERBOSE
  860.     IF(verbose)
  861.     fputs("[Type space to continue] ",stdout);
  862.     ELSE
  863. #endif
  864. #ifdef TERSE
  865.     fputs("[MORE] ",stdout);
  866. #endif
  867.     un_standout();
  868.     fflush(stdout);
  869.     eat_typeahead();
  870.     if (int_count) {
  871.     return -1;
  872.     }
  873.     collect_subjects();            /* loads subject cache until */
  874.                     /* input is pending */
  875.     getcmd(tmpbuf);
  876.     if (errno || *tmpbuf == '\f') {
  877.     putchar('\n') FLUSH;        /* if return from stop signal */
  878.     goto reask_anything;        /* give them a prompt again */
  879.     }
  880.     if (*tmpbuf == 'h') {
  881. #ifdef VERBOSE
  882.     IF(verbose)
  883.         fputs("\nType q to quit or space to continue.\n",stdout) FLUSH;
  884.     ELSE
  885. #endif
  886. #ifdef TERSE
  887.         fputs("\nq to quit, space to continue.\n",stdout) FLUSH;
  888. #endif
  889.     goto reask_anything;
  890.     }
  891.     else if (*tmpbuf != ' ' && *tmpbuf != '\n') {
  892.     carriage_return();
  893.     erase_eol();    /* erase the prompt */
  894.     carriage_return();
  895.     return *tmpbuf == 'q' ? -1 : *tmpbuf;
  896.     }
  897.     if (*tmpbuf == '\n') {
  898.     page_line = LINES - 1;
  899.     carriage_return();
  900.     erase_eol();
  901.     carriage_return();
  902.     }
  903.     else {
  904.     page_line = 1;
  905.     if (erase_screen)        /* -e? */
  906.         clear();            /* clear screen */
  907.     else {
  908.         carriage_return();
  909.         erase_eol();        /* erase the prompt */
  910.         carriage_return();
  911.     }
  912.     }
  913.     return 0;
  914. }
  915.  
  916. #ifdef USETHREADS
  917. int
  918. pause_getcmd()
  919. {
  920.     unflush_output();            /* disable any ^O in effect */
  921.     standout();
  922. #ifdef VERBOSE
  923.     IF(verbose)
  924.     fputs("[Type space or a command] ",stdout);
  925.     ELSE
  926. #endif
  927. #ifdef TERSE
  928.     fputs("[CMD] ",stdout);
  929. #endif
  930.     un_standout();
  931.     fflush(stdout);
  932.     eat_typeahead();
  933.     if (int_count) {
  934.     return -1;
  935.     }
  936.     getcmd(buf);
  937.     if (errno || *buf == '\f') {
  938.     return 0;            /* if return from stop signal */
  939.     }
  940.     else if (*buf != ' ') {
  941.     carriage_return();
  942.     erase_eol();    /* erase the prompt */
  943.     carriage_return();
  944.     return *buf;
  945.     }
  946.     return 0;
  947. }
  948. #endif
  949.  
  950. void
  951. in_char(prompt, newmode)
  952. char *prompt;
  953. char_int newmode;
  954. {
  955.     char oldmode = mode;
  956.  
  957. reask_in_char:
  958.     unflush_output();            /* disable any ^O in effect */
  959.     fputs(prompt,stdout);
  960.     fflush(stdout);
  961.     eat_typeahead();
  962.     mode = newmode;
  963.     getcmd(buf);
  964.     if (errno || *buf == '\f') {
  965.     putchar('\n') FLUSH;        /* if return from stop signal */
  966.     goto reask_in_char;        /* give them a prompt again */
  967.     }
  968.     mode = oldmode;
  969. }
  970.  
  971. int
  972. print_lines(what_to_print,hilite)
  973. char *what_to_print;
  974. int hilite;
  975. {
  976.     register char *s;
  977.     register int i;
  978.  
  979.     if (page_line < 0)            /* they do not want to see this? */
  980.     return -1;
  981.     for (s=what_to_print; *s; ) {
  982.     if (page_line >= LINES || int_count) {
  983.         if (i = -1, int_count || (i = get_anything())) {
  984.         page_line = -1;        /* disable further print_lines */
  985.         return i;
  986.         }
  987.     }
  988.     page_line++;
  989.     if (hilite == STANDOUT) {
  990. #ifdef NOFIREWORKS
  991.         if (erase_screen)
  992.         no_sofire();
  993. #endif
  994.         standout();
  995.     }
  996.     else if (hilite == UNDERLINE) {
  997. #ifdef NOFIREWORKS
  998.         if (erase_screen)
  999.         no_ulfire();
  1000. #endif
  1001.         underline();
  1002.     }
  1003.     for (i=0; i<COLS; i++) {
  1004.         if (!*s)
  1005.         break;
  1006.         if (*(unsigned char *)s >= ' ')
  1007.         putchar(*s);
  1008.         else if (*s == '\t') {
  1009.         putchar(*s);
  1010.         i = ((i+8) & ~7) - 1; 
  1011.         }
  1012.         else if (*s == '\n') {
  1013.         i = 32000;
  1014.         }
  1015.         else {
  1016.         i++;
  1017.         putchar('^');
  1018.         putchar(*s + 64);
  1019.         }
  1020.         s++;
  1021.     }
  1022.     if (i) {
  1023.         if (hilite == STANDOUT)
  1024.         un_standout();
  1025.         else if (hilite == UNDERLINE)
  1026.         un_underline();
  1027.         if (AM && i == COLS)
  1028.         fflush(stdout);
  1029.         else
  1030.         putchar('\n') FLUSH;
  1031.     }
  1032.     }
  1033.     return 0;
  1034. }
  1035.  
  1036. void
  1037. page_init()
  1038. {
  1039.     page_line = 1;
  1040.     if (erase_screen)
  1041.     clear();
  1042.     else
  1043.     putchar('\n') FLUSH;
  1044. }
  1045.  
  1046. void
  1047. pad(num)
  1048. int num;
  1049. {
  1050.     register int i;
  1051.  
  1052.     for (i = num; i; --i)
  1053.     putchar(PC);
  1054.     fflush(stdout);
  1055. }
  1056.  
  1057. /* echo the command just typed */
  1058.  
  1059. #ifdef VERIFY
  1060. void
  1061. printcmd()
  1062. {
  1063.     if (verify && buf[1] == FINISHCMD) {
  1064.     if (*(unsigned char *)buf < ' ') {
  1065.         putchar('^');
  1066.         putchar(*buf | 64);
  1067.         backspace();
  1068.         backspace();
  1069.     }
  1070.     else {
  1071.         putchar(*buf);
  1072.         backspace();
  1073.     }
  1074.     fflush(stdout);
  1075.     }
  1076. }
  1077. #endif
  1078.  
  1079. void
  1080. rubout()
  1081. {
  1082.     backspace();            /* do the old backspace, */
  1083.     putchar(' ');            /*   space, */
  1084.     backspace();            /*     backspace trick */
  1085. }
  1086.  
  1087. void
  1088. reprint()
  1089. {
  1090.     register char *s;
  1091.  
  1092.     fputs("^R\n",stdout) FLUSH;
  1093.     for (s = buf; *s; s++) {
  1094.     if (*(unsigned char *)s < ' ') {
  1095.         putchar('^');
  1096.         putchar(*s | 64);
  1097.     }
  1098.     else
  1099.         putchar(*s);
  1100.     }
  1101. }
  1102.  
  1103. #if defined(CLEAREOL) || defined(USETHREADS)
  1104. void
  1105. home_cursor()
  1106. {
  1107.     char *tgoto();
  1108.  
  1109.     if (!*HO) {            /* no home sequence? */
  1110.     if (!*CM) {        /* no cursor motion either? */
  1111.         fputs ("\n\n\n", stdout);
  1112.         return;        /* forget it. */
  1113.     }
  1114.     tputs (tgoto (CM, 0, 0), 1, putchr);    /* go to home via CM */
  1115.     return;
  1116.     }
  1117.     else {            /* we have home sequence */
  1118.     tputs (HO, 1, putchr);    /* home via HO */
  1119.     }
  1120. }
  1121. #endif
  1122.  
  1123. #ifdef USETHREADS
  1124. void
  1125. goto_line(from,to)    /* assumes caller is already at beginning of line */
  1126. int from,to;
  1127. {
  1128.     char *tgoto(), *str;
  1129.     int cmcost;
  1130.  
  1131.     if (from == to) {
  1132.     return;
  1133.     }
  1134.     if (*CM && !muck_up_clear) {
  1135.     cmcost = strlen(str = tgoto(CM,0,to));
  1136.     } else {
  1137.     cmcost = 9999;
  1138.     }
  1139.     if (to > from) {
  1140.       go_down:
  1141.     if (to - from <= cmcost) {
  1142.         while(from++ < to) {
  1143.         putchar('\n');
  1144.         }
  1145.         return;
  1146.     }
  1147.     } else if(*UP) {
  1148.     if ((from - to) * upcost <= cmcost) {
  1149.         while(from-- > to) {
  1150.         tputs(UP,1,putchr);
  1151.         }
  1152.         return;
  1153.     }
  1154.     } else if (cmcost == 9999) {
  1155.     home_cursor();
  1156.     from = 0;
  1157.     goto go_down;
  1158.     }
  1159.     tputs(str,1,putchr);
  1160. }
  1161. #endif
  1162.  
  1163. void
  1164. line_col_calcs()
  1165. {
  1166.      if (LINES > 0) {            /* is this a crt? */
  1167.       if ((!initlines) || (!initlines_specified))
  1168.            /* no -i or unreasonable value for initlines */
  1169.            if (outspeed >= B9600)     /* whole page at >= 9600 baud */
  1170.             initlines = LINES;
  1171.            else if (outspeed >= B4800)/* 16 lines at 4800 */
  1172.             initlines = 16;
  1173.            else            /* otherwise just header */
  1174.             initlines = 8;
  1175.      }
  1176.      else {                /* not a crt */
  1177.       LINES = 30000;        /* so don't page */
  1178.       CL = "\n\n";            /* put a couple of lines between */
  1179.       if ((!initlines) || (!initlines_specified))
  1180.            /* make initlines reasonable */
  1181.            initlines = 8;
  1182.      }
  1183.      if (COLS <= 0)
  1184.       COLS = 80;
  1185. }
  1186.  
  1187.  
  1188. #ifdef SIGWINCH
  1189. SIGRET
  1190. winch_catcher()
  1191. {
  1192.      /* Reset signal in case of System V dain bramage */
  1193.      sigset(SIGWINCH, winch_catcher);
  1194.  
  1195.      /* Come here if window size change signal received */
  1196. #ifdef TIOCGWINSZ
  1197.      { struct winsize ws;
  1198.        if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) {
  1199.      LINES = ws.ws_row;
  1200.      COLS = ws.ws_col;
  1201.      line_col_calcs();
  1202.        }
  1203.      }
  1204. #else
  1205.      /* Well, if SIGWINCH is defined, but TIOCGWINSZ isn't, there's    */
  1206.      /* almost certainly something wrong.  Figure it out for yourself, */
  1207.      /* because I don't know now to deal :-)                           */
  1208. #endif
  1209. }
  1210. #endif
  1211. void
  1212. termlib_init()
  1213. {
  1214. #ifdef USETITE
  1215.     if (TI && *TI)
  1216.         tputs (TI,1,putchr);
  1217. #endif
  1218.     return;
  1219. }
  1220. void
  1221. termlib_reset()
  1222. {
  1223. #ifdef USETITE
  1224.     if (TE && *TE)
  1225.         tputs (TE,1,putchr);
  1226. #endif
  1227.     return;
  1228. }
  1229.