home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume23 / trn / part07 / term.c < prev   
Encoding:
C/C++ Source or Header  |  1991-08-22  |  25.4 KB  |  1,223 lines

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