home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume27 / trn-3.3 / part09 / term.c < prev   
C/C++ Source or Header  |  1993-11-27  |  28KB  |  1,302 lines

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