home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / micrognu / part02 / echo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-01-26  |  10.4 KB  |  514 lines

  1. /*
  2.  *        Echo line reading and writing.
  3.  *
  4.  * Common routines for reading
  5.  * and writing characters in the echo line area
  6.  * of the display screen. Used by the entire
  7.  * known universe.
  8.  */
  9. #include    "def.h"
  10. #ifdef    VARARGS
  11. #  include    <varargs.h>
  12.     static veread();
  13. #endif
  14. static eformat();
  15.  
  16. int    epresf    = FALSE;        /* Stuff in echo line flag.    */
  17. /*
  18.  * Erase the echo line.
  19.  */
  20. eerase() {
  21.     ttcolor(CTEXT);
  22.     ttmove(nrow-1, 0);
  23.     tteeol();
  24.     ttflush();
  25.     epresf = FALSE;
  26. }
  27.  
  28. /*
  29.  * Ask "yes" or "no" question.
  30.  * Return ABORT if the user answers the question
  31.  * with the abort ("^G") character. Return FALSE
  32.  * for "no" and TRUE for "yes". No formatting
  33.  * services are available. No newline required.
  34.  */
  35. eyorn(sp) char *sp; {
  36.     register KEY    s;
  37.  
  38.     if (kbdmop == NULL) ewprintf("%s? (y or n) ", sp);
  39.     for (;;) {
  40.         s = getkey(0);
  41.         if (s == 'y' || s == 'Y') return (TRUE);
  42.         if (s == 'n' || s == 'N') return (FALSE);
  43.         if (s == (KCTRL|'G') || s == (KCTLX|KCTRL|'G')
  44.         ||  s == (KMETA|KCTRL|'G')) {
  45.             (VOID) ctrlg(FALSE, 1, KRANDOM);
  46.             return ABORT;
  47.         }
  48.         if (kbdmop == NULL)
  49.             ewprintf("Please answer y or n.  %s? (y or n) ", sp);
  50.     }
  51. }
  52.  
  53. /*
  54.  * Like eyorn, but for more important question. User must type either all of
  55.  * "yes" or "no", and the trainling newline.
  56.  */
  57. eyesno(sp) char *sp; {
  58.     register int    s;
  59.     char        buf[64];
  60.  
  61.     s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
  62.     for (;;) {
  63.         if (s == ABORT) return ABORT;
  64.         if (s != FALSE) {
  65.             if ((buf[0] == 'y' || buf[0] == 'Y')
  66.             &&  (buf[1] == 'e' || buf[1] == 'E')
  67.             &&  (buf[2] == 's' || buf[2] == 'S')) return TRUE;
  68.             if ((buf[0] == 'n' || buf[0] == 'N')
  69.             &&  (buf[1] == 'o' || buf[0] == 'O')) return FALSE;
  70.         }
  71.         s = ereply("Please answer yes or no.  %s? (yes or no) ",
  72.                buf, sizeof(buf), sp);
  73.     }
  74. }
  75. /*
  76.  * Write out a prompt, and read back a
  77.  * reply. The prompt is now written out with full "ewprintf"
  78.  * formatting, although the arguments are in a rather strange
  79.  * place. This is always a new message, there is no auto
  80.  * completion, and the return is echoed as such.
  81.  */
  82. #ifdef    VARARGS
  83. ereply(va_alist)
  84. va_dcl
  85. {
  86.     register int i;
  87.     va_list pvar;
  88.     register char *fp, *buf;
  89.     register int nbuf;
  90.     
  91.     va_start(pvar);
  92.     fp = va_arg(pvar, char *);
  93.     buf = va_arg(pvar, char *);
  94.     nbuf = va_arg(pvar, int);
  95.     i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar);
  96.     va_end(pvar);
  97.     return i;
  98. }
  99. #else
  100. /* VARARGS3 */
  101. ereply(fp, buf, nbuf, arg) char *fp, *buf; int nbuf; long arg; {
  102.     return (eread(fp, buf, nbuf, EFNEW|EFCR, (char *)&arg));
  103. }
  104. #endif
  105.  
  106. /*
  107.  * This is the general "read input from the
  108.  * echo line" routine. The basic idea is that the prompt
  109.  * string "prompt" is written to the echo line, and a one
  110.  * line reply is read back into the supplied "buf" (with
  111.  * maximum length "len"). The "flag" contains EFNEW (a
  112.  * new prompt), an EFFUNC (autocomplete), or EFCR (echo
  113.  * the carriage return as CR).
  114.  */
  115. /* VARARGS4 */
  116. #ifdef    VARARGS
  117. eread(va_alist)
  118. va_dcl
  119. {
  120.     va_list pvar;
  121.     char *fp, *buf;
  122.     int nbuf, flag, i;
  123.     va_start(pvar);
  124.     fp   = va_arg(pvar, char *);
  125.     buf  = va_arg(pvar, char *);
  126.     nbuf = va_arg(pvar, int);
  127.     flag = va_arg(pvar, int);
  128.     i = veread(fp, buf, nbuf, flag, &pvar);
  129.     va_end(pvar);
  130.     return i;
  131. }
  132. #endif
  133.  
  134. #ifdef    VARARGS
  135. static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; {
  136. #else
  137. eread(fp, buf, nbuf, flag, ap) char *fp; char *buf; char *ap; {
  138. #endif
  139.     register int    cpos;
  140.     register int    i;
  141.     register KEY    c;
  142.  
  143.     cpos = 0;
  144.     if (kbdmop != NULL) {            /* In a macro.        */
  145.         while ((c = *kbdmop++) != '\0')
  146.             buf[cpos++] = (char) c;
  147.         buf[cpos] = '\0';
  148.         goto done;
  149.     }
  150.     if ((flag&EFNEW)!=0 || ttrow!=nrow-1) {
  151.         ttcolor(CTEXT);
  152.         ttmove(nrow-1, 0);
  153.         epresf = TRUE;
  154.     } else
  155.         eputc(' ');
  156.     eformat(fp, ap);
  157.     tteeol();
  158.     ttflush();
  159.     for (;;) {
  160.         c = getkey(KQUOTE|KNOMAC);
  161.         if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
  162.             cpos += complete(flag, c, buf, cpos);
  163.             continue;
  164.         }
  165.         switch (c) {
  166.         case 0x0D:            /* Return, done.    */
  167.             if ((flag&EFFUNC) != 0) {
  168.                 if ((i = complete(flag, c, buf, cpos)) == 0)
  169.                     continue;
  170.                 if (i > 0) cpos += i;
  171.             }
  172.             buf[cpos] = '\0';
  173.             if ((flag&EFCR) != 0) {
  174.                 ttputc(0x0D);
  175.                 ttflush();
  176.             }
  177.             if (kbdmip != NULL) {
  178.                 if (kbdmip+cpos+1 > &kbdm[NKBDM-3]) {
  179.                     ewprintf("Keyboard macro overflow");
  180.                     ttflush();
  181.                     return FALSE;
  182.                 }
  183.                 for (i = 0; i <= cpos; ++i)
  184.                     *kbdmip++ = (KEY) buf[i];
  185.             }
  186.             goto done;
  187.  
  188.         case CCHR('G'):            /* Bell, abort.        */
  189.             eputc(CCHR('G'));
  190.             (VOID) ctrlg(FALSE, 0, KRANDOM);
  191.             ttflush();
  192.             return (ABORT);
  193.  
  194.         case 0x7F:            /* Rubout, erase.    */
  195.             if (cpos != 0) {
  196.                 ttputc('\b');
  197.                 ttputc(' ');
  198.                 ttputc('\b');
  199.                 --ttcol;
  200.                 if (ISCTRL(buf[--cpos]) != FALSE) {
  201.                     ttputc('\b');
  202.                     ttputc(' ');
  203.                     ttputc('\b');
  204.                     --ttcol;
  205.                 }
  206.                 ttflush();
  207.             }
  208.             break;
  209.  
  210.         case CCHR('X'):            /* C-X            */
  211.         case CCHR('U'):            /* C-U, kill line.    */
  212.             while (cpos != 0) {
  213.                 ttputc('\b');
  214.                 ttputc(' ');
  215.                 ttputc('\b');
  216.                 --ttcol;
  217.                 if (ISCTRL(buf[--cpos]) != FALSE) {
  218.                     ttputc('\b');
  219.                     ttputc(' ');
  220.                     ttputc('\b');
  221.                     --ttcol;
  222.                 }
  223.             }
  224.             ttflush();
  225.             break;
  226.  
  227.         case CCHR('Q'):            /* C-Q, quote next    */
  228.             c = getkey(KQUOTE|KNOMAC) ;
  229.         default:            /* All the rest.    */
  230.             if (cpos < nbuf-1) {
  231.                 buf[cpos++] = (char) c;
  232.                 eputc((char) c);
  233.                 ttflush();
  234.             }
  235.         }
  236.     }
  237. done:
  238.     if (buf[0] == '\0')
  239.         return (FALSE);
  240.     return (TRUE);
  241. }
  242.  
  243. /*
  244.  * do completion on a list of objects.
  245.  */
  246. complete(flags, c, buf, cpos) register char *buf; register int cpos; {
  247.     register LIST    *lh, *lh2;
  248. #ifndef    MANX
  249.     register    /* too many registers mess Manx up */
  250. #endif
  251.     int        i, nxtra;
  252.     int        nhits, bxtra;
  253.     int        wflag = FALSE;
  254.     int        msglen, nshown;
  255.     char        *msg;
  256.  
  257.     if ((flags&EFFUNC) != 0) lh = &(symbol[0]->s_list);
  258.     else if ((flags&EFBUF) != 0) lh = &(bheadp->b_list);
  259.     else panic("broken complete call: flags");
  260.  
  261.     if (c == ' ') wflag = TRUE;
  262.     else if (c != '\t' && c != 0x0D) panic("broken complete call: c");
  263.  
  264.     nhits = 0;
  265.     nxtra = HUGE;
  266.  
  267.     while (lh != NULL) {
  268.         for (i=0; i<cpos; ++i) {
  269.             if (buf[i] != lh->l_name[i])
  270.                 break;
  271.         }
  272.         if (i == cpos) {
  273.             if (nhits == 0)
  274.                 lh2 = lh;
  275.             ++nhits;
  276.             if (lh->l_name[i] == '\0') nxtra = -1;
  277.             else {
  278.                 bxtra = getxtra(lh, lh2, cpos, wflag);
  279.                 if (bxtra < nxtra) nxtra = bxtra;
  280.                 lh2 = lh;
  281.             }
  282.         }
  283.         lh = lh->l_next;
  284.     }
  285.     if (nhits == 0)
  286.         msg = " [No match]";
  287.     else if (nhits > 1 && nxtra == 0)
  288.         msg = " [Ambiguous]";
  289.     else {        /* Got a match, do it to it */
  290.         /*
  291.          * Being lazy - ought to check length, but all things
  292.          * autocompleted have known types/lengths.
  293.          */ 
  294.         if (nxtra < 0 && nhits > 1 && c == ' ') nxtra = 1;
  295.         for (i = 0; i < nxtra; ++i) {
  296.             buf[cpos] = lh2->l_name[cpos];
  297.             eputc(buf[cpos++]);
  298.         }
  299.         ttflush();
  300.         if (nxtra < 0 && c != 0x0D) return 0;
  301.         return nxtra;
  302.     }
  303.         /* Set up backspaces, etc., being mindful of echo line limit */
  304.     msglen = strlen(msg);
  305.     nshown = (ttcol + msglen + 2 > ncol) ? 
  306.             ncol - ttcol - 2 : msglen;
  307.     eputs(msg);
  308.     ttcol -= (i = nshown);        /* update ttcol!        */
  309.     while (i--)            /* move back before msg        */
  310.         ttputc('\b');
  311.     ttflush();            /* display to user        */
  312.     i = nshown;
  313.     while (i--)            /* blank out    on next flush    */
  314.         eputc(' ');
  315.     ttcol -= (i = nshown);        /* update ttcol on BS's        */
  316.     while (i--)
  317.         ttputc('\b');        /* update ttcol again!        */
  318.     return 0;
  319.  
  320. }
  321.  
  322. /*
  323.  * The "lp1" and "lp2" point to list structures. The
  324.  * "cpos" is a horizontal position in the name.
  325.  * Return the longest block of characters that can be
  326.  * autocompleted at this point. Sometimes the two
  327.  * symbols are the same, but this is normal.
  328.   */
  329. getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag; {
  330.     register int    i;
  331.  
  332.     i = cpos;
  333.     for (;;) {
  334.         if (lp1->l_name[i] != lp2->l_name[i]) break;
  335.         if (lp1->l_name[i] == '\0') break;
  336.         ++i;
  337.         if (wflag && !ISWORD(lp1->l_name[i-1])) break;
  338.     }
  339.     return (i - cpos);
  340. }
  341.  
  342. /*
  343.  * Special "printf" for the echo line.
  344.  * Each call to "ewprintf" starts a new line in the
  345.  * echo area, and ends with an erase to end of the
  346.  * echo line. The formatting is done by a call
  347.  * to the standard formatting routine.
  348.  */
  349. #ifdef    VARARGS
  350. ewprintf(va_alist)
  351. va_dcl
  352. {
  353.     va_list pvar;
  354.     register char *fp;
  355.  
  356.     va_start(pvar);
  357.     fp = va_arg(pvar, char *);
  358. #else
  359. /* VARARGS1 */
  360. ewprintf(fp, arg) char *fp; {
  361. #endif
  362.     ttcolor(CTEXT);
  363.     ttmove(nrow-1, 0);
  364. #ifdef    VARARGS
  365.     eformat(fp, &pvar);
  366.     va_end(pvar);
  367. #else
  368.     eformat(fp, (char *)&arg);
  369. #endif
  370.     tteeol();
  371.     ttflush();
  372.     epresf = TRUE;
  373. }
  374.  
  375. /*
  376.  * Printf style formatting. This is
  377.  * called by both "ewprintf" and "ereply" to provide
  378.  * formatting services to their clients. The move to the
  379.  * start of the echo line, and the erase to the end of
  380.  * the echo line, is done by the caller.
  381.  * Note: %c works, and prints the "name" of the key. However
  382.  * the key must be cast to an int to avoid tripping over
  383.  * various oddities in C argument passing.
  384.  */
  385. static eformat(fp, ap) register char *fp;
  386. #ifdef VARARGS
  387. register va_list *ap;
  388. #else
  389. register char *ap;
  390. #endif
  391. {
  392.     register int    c;
  393.     char        kname[NKNAME];
  394.  
  395.     while ((c = *fp++) != '\0') {
  396.         if (c != '%')
  397.             eputc(c);
  398.         else {
  399.             c = *fp++;
  400.             switch (c) {
  401.             case 'c':
  402. #ifdef    VARARGS
  403.                 keyname(kname, va_arg(*ap, int));
  404. #else
  405.                 /*NOSTRICT*/
  406.                 keyname(kname, *(int *)ap);
  407.                 ap += sizeof(int);
  408. #endif
  409.                 eputs(kname);
  410.                 break;
  411.  
  412.             case 'd':
  413. #ifdef    VARARGS
  414.                 eputi(va_arg(*ap, int), 10);
  415. #else
  416.                 /*NOSTRICT*/
  417.                 eputi(*(int *)ap, 10);
  418.                 ap += sizeof(int);
  419. #endif
  420.                 break;
  421.  
  422.             case 'o':
  423. #ifdef    VARARGS
  424.                 eputi(va_arg(*ap, int), 8);
  425. #else
  426.                 /*NOSTRICT*/
  427.                 eputi(*(int *)ap,  8);
  428.                 ap += sizeof(int);
  429. #endif
  430.                 break;
  431.  
  432.             case 's':
  433. #ifdef    VARARGS
  434.                 eputs(va_arg(*ap, char *));
  435. #else
  436.                 /*NOSTRICT*/
  437.                 eputs(*(char **)ap);
  438.                 ap += sizeof(char *);
  439. #endif
  440.                 break;
  441.             case 'l':/* explicit longword */
  442.                 c = *fp++;
  443.                 switch(c) {
  444.                 case 'd':
  445. #ifdef    VARARGS
  446.                     eputl((long)va_arg(*ap, long), 10L);
  447. #else
  448.                     /*NOSTRICT*/
  449.                     eputl(*(long *)ap, 10L);
  450.                     ap += sizeof(long);
  451. #endif
  452.                     break;
  453.                 default:
  454.                     eputc(c);
  455.                     break;
  456.                 }
  457.                 break;
  458.  
  459.             default:
  460.                 eputc(c);
  461.             }
  462.         }
  463.     }
  464. }
  465.  
  466. /*
  467.  * Put integer, in radix "r".
  468.  */
  469. static eputi(i, r) register int i; register int r; {
  470.     register int    q;
  471.  
  472.     if ((q=i/r) != 0)
  473.         eputi(q, r);
  474.     eputc(i%r+'0');
  475. }
  476.  
  477. /*
  478.  * Put long, in radix "r".
  479.  */
  480. static eputl(l, r) register long l; register long r; {
  481.     register long    q;
  482.  
  483.     if ((q=l/r) != 0)
  484.         eputl(q, r);
  485.     eputc((int)(l%r)+'0');
  486. }
  487.  
  488. /*
  489.  * Put string.
  490.  */
  491. eputs(s) register char *s; {
  492.     register int    c;
  493.  
  494.     while ((c = *s++) != '\0')
  495.         eputc(c);
  496. }
  497.  
  498. /*
  499.  * Put character. Watch for
  500.  * control characters, and for the line
  501.  * getting too long.
  502.  */
  503. static eputc(c) register char c; {
  504.     if (ttcol+2 < ncol) {
  505.         if (ISCTRL(c) != FALSE) {
  506.             eputc('^');
  507.             c ^= 0x40;
  508.         }
  509.         ttputc(c);
  510.         ++ttcol;
  511.     }
  512. }
  513.  
  514.