home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / EMACSOS2.ZIP / INPUT.C < prev    next >
C/C++ Source or Header  |  1988-10-01  |  15KB  |  671 lines

  1. /*    INPUT:    Various input routines for MicroEMACS
  2.         written by Daniel Lawrence
  3.         5/9/86                        */
  4.  
  5. #include    <stdio.h>
  6. #include    "estruct.h"
  7. #include    "etype.h"
  8. #include    "edef.h"
  9.  
  10. /*
  11.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  12.  * ABORT. The ABORT status is returned if the user bumps out of the question
  13.  * with a ^G. Used any time a confirmation is required.
  14.  */
  15.  
  16. PASCAL NEAR mlyesno(prompt)
  17.  
  18. char *prompt;
  19.  
  20. {
  21.     char c;            /* input character */
  22.     char buf[NPAT];        /* prompt to user */
  23.  
  24.     for (;;) {
  25.         /* build and prompt the user */
  26.         strcpy(buf, prompt);
  27.         strcat(buf, " [y/n]? ");
  28.         mlwrite(buf);
  29.  
  30.         /* get the responce */
  31.         c = tgetc();
  32.  
  33.         if (c == ectoc(abortc))        /* Bail out! */
  34.             return(ABORT);
  35.  
  36.         if (c=='y' || c=='Y')
  37.             return(TRUE);
  38.  
  39.         if (c=='n' || c=='N')
  40.             return(FALSE);
  41.     }
  42. }
  43.  
  44. /*
  45.  * Write a prompt into the message line, then read back a response. Keep
  46.  * track of the physical position of the cursor. If we are in a keyboard
  47.  * macro throw the prompt away, and return the remembered response. This
  48.  * lets macros run at full speed. The reply is always terminated by a carriage
  49.  * return. Handle erase, kill, and abort keys.
  50.  */
  51.  
  52. PASCAL NEAR mlreply(prompt, buf, nbuf)
  53.     char *prompt;
  54.     char *buf;
  55. {
  56.     return(nextarg(prompt, buf, nbuf, ctoec((int) '\r')));
  57. }
  58.  
  59. PASCAL NEAR mlreplyt(prompt, buf, nbuf, eolchar)
  60.  
  61. char *prompt;
  62. char *buf;
  63. int eolchar;
  64.  
  65. {
  66.     return(nextarg(prompt, buf, nbuf, eolchar));
  67. }
  68.  
  69. /*    ectoc:    expanded character to character
  70.         collapse the CTRL and SPEC flags back into an ascii code   */
  71.  
  72. PASCAL NEAR ectoc(c)
  73.  
  74. int c;
  75.  
  76. {
  77.     if (c & CTRL)
  78.         c = c & ~(CTRL | 0x40);
  79.     if (c & SPEC)
  80.         c= c & 255;
  81.     return(c);
  82. }
  83.  
  84. /*    ctoec:    character to extended character
  85.         pull out the CTRL and SPEC prefixes (if possible)    */
  86.  
  87. PASCAL NEAR ctoec(c)
  88.  
  89. int c;
  90.  
  91. {
  92.         if (c>=0x00 && c<=0x1F)
  93.                 c = CTRL | (c+'@');
  94.         return(c);
  95. }
  96.  
  97. /* get a command name from the command line. Command completion means
  98.    that pressing a <SPACE> will attempt to complete an unfinished command
  99.    name if it is unique.
  100. */
  101.  
  102. int (PASCAL NEAR *PASCAL NEAR getname())()
  103.  
  104. {
  105.     register int cpos;    /* current column on screen output */
  106.     register int c;
  107.     register char *sp;    /* pointer to string for output */
  108.     register NBIND *ffp;    /* first ptr to entry in name binding table */
  109.     register NBIND *cffp;    /* current ptr to entry in name binding table */
  110.     register NBIND *lffp;    /* last ptr to entry in name binding table */
  111.     char buf[NSTRING];    /* buffer to hold tentative command name */
  112.  
  113.     /* starting at the beginning of the string buffer */
  114.     cpos = 0;
  115.  
  116.     /* if we are executing a command line get the next arg and match it */
  117.     if (clexec) {
  118.         if (macarg(buf) != TRUE)
  119.             return(FALSE);
  120.         return(fncmatch(&buf[0]));
  121.     }
  122.  
  123.     /* build a name string from the keyboard */
  124.     while (TRUE) {
  125.         c = tgetc();
  126.  
  127.         /* if we are at the end, just match it */
  128.         if (c == 0x0d) {
  129.             buf[cpos] = 0;
  130.  
  131.             /* and match it off */
  132.             return(fncmatch(&buf[0]));
  133.  
  134.         } else if (c == ectoc(abortc)) {    /* Bell, abort */
  135.             ctrlg(FALSE, 0);
  136.             TTflush();
  137.             return(NULL);
  138.  
  139.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  140.             if (cpos != 0) {
  141.                 TTputc('\b');
  142.                 TTputc(' ');
  143.                 TTputc('\b');
  144.                 --ttcol;
  145.                 --cpos;
  146.                 TTflush();
  147.             }
  148.  
  149.         } else if (c == 0x15) {    /* C-U, kill */
  150.             while (cpos != 0) {
  151.                 TTputc('\b');
  152.                 TTputc(' ');
  153.                 TTputc('\b');
  154.                 --cpos;
  155.                 --ttcol;
  156.             }
  157.  
  158.             TTflush();
  159.  
  160.         } else if (c == ' ') {
  161. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  162.     /* attempt a completion */
  163.     buf[cpos] = 0;        /* terminate it for us */
  164.     ffp = &names[0];    /* scan for matches */
  165.     while (ffp->n_func != NULL) {
  166.         if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
  167.             /* a possible match! More than one? */
  168.             if ((ffp + 1)->n_func == NULL ||
  169.                (strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
  170.                 /* no...we match, print it */
  171.                 sp = ffp->n_name + cpos;
  172.                 while (*sp)
  173.                     TTputc(*sp++);
  174.                 TTflush();
  175.                 return(ffp->n_func);
  176.             } else {
  177. /* << << << << << << << << << << << << << << << << << */
  178.     /* try for a partial match against the list */
  179.  
  180.     /* first scan down until we no longer match the current input */
  181.     lffp = (ffp + 1);
  182.     while ((lffp+1)->n_func != NULL) {
  183.         if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
  184.             break;
  185.         ++lffp;
  186.     }
  187.  
  188.     /* and now, attempt to partial complete the string, char at a time */
  189.     while (TRUE) {
  190.         /* add the next char in */
  191.         buf[cpos] = ffp->n_name[cpos];
  192.  
  193.         /* scan through the candidates */
  194.         cffp = ffp + 1;
  195.         while (cffp <= lffp) {
  196.             if (cffp->n_name[cpos] != buf[cpos])
  197.                 goto onward;
  198.             ++cffp;
  199.         }
  200.  
  201.         /* add the character */
  202.         TTputc(buf[cpos++]);
  203.     }
  204. /* << << << << << << << << << << << << << << << << << */
  205.             }
  206.         }
  207.         ++ffp;
  208.     }
  209.  
  210.     /* no match.....beep and onward */
  211.     TTbeep();
  212. onward:;
  213.     TTflush();
  214. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  215.         } else {
  216.             if (cpos < NSTRING-1 && c > ' ') {
  217.                 buf[cpos++] = c;
  218.                 TTputc(c);
  219.             }
  220.  
  221.             ++ttcol;
  222.             TTflush();
  223.         }
  224.     }
  225. }
  226.  
  227. #if    BCOMPL
  228. /*
  229.  * Read in the name of an buffer using TENEX style expansion and
  230.  * return the pointer to it.  If argument 'createf' is non-zero, a new
  231.  * buffer will be created, if requested.  'prompt' can be NULL, in
  232.  * which case neither a prompt or the default is printed in the
  233.  * minibuffer.  'def_name' may be a default buffer name or NULL.  If
  234.  * 'prompt' is present, it will be printed in the minibuffer and can
  235.  * be chosen by simply hitting RETURN.
  236.  *
  237.  * returns NULL, iff the user ABORTs or can't / isn't allowed to
  238.  * create a new buffer.
  239.  */
  240.  
  241. BUFFER *PASCAL NEAR tenexbuf(createf, prompt, def_bname)
  242.  
  243. int createf;
  244. char *prompt, *def_bname;
  245.  
  246. {
  247.     register int cpos;    /* current column on screen output */
  248.     register int c;
  249.     char bufname[NBUFN];    /* buffer to hold tentative buffer name */
  250.  
  251.     /* starting at the beginning of the string buffer */
  252.     cpos = 0;
  253.  
  254.     /* if we are executing a command line get the next arg and match it */
  255.     if (clexec) {
  256.         if (macarg(bufname) != TRUE)
  257.             return(NULL);
  258.         return(bfind(&bufname[0], createf, 0));
  259.     }
  260.  
  261.     if (prompt)
  262.         mlwrite ("%s[%s]: ", prompt,
  263.              def_bname ? def_bname : "no default");
  264.  
  265.     /* build a name string from the keyboard */
  266.     while (TRUE) {
  267.         c = tgetc();
  268.  
  269.         /* if we are at the end, just match it */
  270.         if (c == '\n'  ||  c == '\r') {
  271.             if (def_bname && cpos==0)
  272.                 return(bfind(def_bname, createf, 0));
  273.             else {
  274.                 bufname[cpos] = 0;
  275.                 return(bfind(&bufname[0], createf, 0));
  276.             }
  277.  
  278.         } else if (c == ectoc(abortc)) {    /* Bell, abort */
  279.             ctrlg(FALSE, 0);
  280.             TTflush();
  281.             return NULL;
  282.  
  283.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  284.             if (cpos != 0) {
  285.                 TTputc('\b');
  286.                 TTputc(' ');
  287.                 TTputc('\b');
  288.                 --ttcol;
  289.                 --cpos;
  290.                 TTflush();
  291.             }
  292.  
  293.         } else if (c == 0x15) {    /* C-U, kill */
  294.             while (cpos != 0) {
  295.                 TTputc('\b');
  296.                 TTputc(' ');
  297.                 TTputc('\b');
  298.                 --cpos;
  299.                 --ttcol;
  300.             }
  301.  
  302.             TTflush();
  303.  
  304.         } else if (c == ' ') {        /* attempt a completion */
  305.  
  306.             BUFFER *bp, *expbuf;
  307.             int exp_pos, i;
  308.             
  309.             bufname[cpos] = 0;    /* terminate it for us */
  310.             /*
  311.              * Search for the next common expansion.  expbuf points
  312.              * to the first matching buffer, exp_pos gets
  313.              * initialized to the full buffer name.  Each further
  314.              * matching buffer will shrink (via exp_pos) the most
  315.              * common name prefix.
  316.              */
  317.             for (expbuf=NULL, exp_pos=0, bp = bheadp;
  318.                  bp;
  319.                  bp = bp->b_bufp) {
  320.                 if ( strlen (bp->b_bname) > cpos+1  &&
  321.                      0==strncmp (bufname, bp->b_bname, cpos)) {
  322.                     /*
  323.                      * If you want a more
  324.                      * 'agressive' completion, you
  325.                      * could count the matches
  326.                      * right here after this
  327.                      * comment.  Then, if we found
  328.                      * exactly one matching
  329.                      * buffer, we could directly
  330.                      * switch to it (expbuf).
  331.                      */
  332.                     if ( ! expbuf ) {
  333.                         /* install first match */
  334.                         expbuf = bp;
  335.                         exp_pos = strlen
  336.                             (expbuf->b_bname) -1;
  337.                         continue;
  338.                     }
  339.                     /* another match; reduce prefix size */
  340.                     i=cpos;
  341.                     while (i<=exp_pos)
  342.                         if (bp->b_bname[i] !=
  343.                             expbuf->b_bname[i])
  344.                             break;
  345.                         else
  346.                             ++i;
  347.                     exp_pos = i-1;
  348.                 }
  349.             }
  350.  
  351.             if (!expbuf) {
  352.                 TTbeep();
  353.                 TTflush();
  354.                 continue;
  355.             }
  356.             
  357.             while (cpos <= exp_pos) {
  358.                 /* add the next char in */
  359.                 bufname[cpos] = expbuf->b_bname[cpos];
  360.                 TTputc(bufname[cpos++]);
  361.             }
  362.             TTflush();
  363.         } else {
  364.             if (cpos < NBUFN-1 && c > ' ') {
  365.                 bufname[cpos++] = c;
  366.                 TTputc(c);
  367.             }
  368.  
  369.             ++ttcol;
  370.             TTflush();
  371.         }
  372.     }
  373. }
  374. #endif
  375.  
  376. /*    tgetc:    Get a key from the terminal driver, resolve any keyboard
  377.         macro action                    */
  378.  
  379. int PASCAL NEAR tgetc()
  380.  
  381. {
  382.     int c;    /* fetched character */
  383.  
  384.     /* if we are playing a keyboard macro back, */
  385.     if (kbdmode == PLAY) {
  386.  
  387.         /* if there is some left... */
  388.         if (kbdptr < kbdend)
  389.             return((int)*kbdptr++);
  390.  
  391.         /* at the end of last repitition? */
  392.         if (--kbdrep < 1) {
  393.             kbdmode = STOP;
  394. #if    VISMAC == 0
  395.             /* force a screen update after all is done */
  396.             update(FALSE);
  397. #endif
  398.         } else {
  399.  
  400.             /* reset the macro to the begining for the next rep */
  401.             kbdptr = &kbdm[0];
  402.             return((int)*kbdptr++);
  403.         }
  404.     }
  405.  
  406.     /* fetch a character from the terminal driver */
  407.     c = TTgetc();
  408.  
  409.     /* record it for $lastkey */
  410.     lastkey = c;
  411.  
  412.     /* save it if we need to */
  413.     if (kbdmode == RECORD) {
  414.         *kbdptr++ = c;
  415.         kbdend = kbdptr;
  416.  
  417.         /* don't overrun the buffer */
  418.         if (kbdptr == &kbdm[NKBDM - 1]) {
  419.             kbdmode = STOP;
  420.             TTbeep();
  421.         }
  422.     }
  423.  
  424.     /* and finally give the char back */
  425.     return(c);
  426. }
  427.  
  428. /*    GET1KEY:    Get one keystroke. The only prefixs legal here
  429.             are the SPEC and CTRL prefixes.
  430.                                 */
  431.  
  432. PASCAL NEAR get1key()
  433.  
  434. {
  435.     int    c;
  436. #if    AMIGA
  437.     int    d;
  438. #endif
  439.  
  440.     /* get a keystroke */
  441.         c = tgetc();
  442.  
  443. #if    MSDOS | OS2 | ST520 | VMS
  444.     if (c == 0) {                /* Apply SPEC prefix    */
  445.             c = tgetc();
  446.  
  447.         /* a double null leadin means we need to process a mouse
  448.            button change.                    */
  449.         if (c == 0) {            /* apply the ALTD prefix */
  450.             /* grab the x/y position of the mouse */
  451.             xpos = tgetc();
  452.             ypos = tgetc();
  453.             c = tgetc();
  454.                 if (c>=0x00 && c<=0x1F)    /* control key? */
  455.                         c = CTRL | (c+'@');
  456.             return(ALTD | c);
  457.         }
  458.             if (c>=0x00 && c<=0x1F)        /* control key? */
  459.                     c = CTRL | (c+'@');
  460.         return(SPEC | c);
  461.     }
  462. #endif
  463.  
  464. #if    (USG | BSD) & VT100
  465.     if (c == 0x1b) {            /* Apply SPEC prefix    */
  466.             c = tgetc();
  467.             if (c>=0x00 && c<=0x1F)    /* control key? */
  468.                     c = CTRL | (c+'@');
  469.         if (c == '[') {
  470.             c = tgetc();
  471.                 if (c>=0x00 && c<=0x1F)    /* control key? */
  472.                         c = CTRL | (c+'@');
  473.             return(SPEC | c);
  474.         }
  475.         return(META | c);
  476.     }
  477. #endif
  478.  
  479. #if    AMIGA
  480.     /* apply SPEC prefix */
  481.     if ((unsigned)c == 155) {
  482.         c = tgetc();
  483.  
  484.         /* first try to see if it is a cursor key */
  485.         if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T')
  486.             return(SPEC | c);
  487.  
  488.         /* next, a 2 char sequence */
  489.         d = tgetc();
  490.         if (d == '~')
  491.             return(SPEC | c);
  492.  
  493.         /* decode a 3 char sequence */
  494.         c = d + 32;
  495.         /* if a shifted function key, eat the tilde */
  496.         if (d >= '0' && d <= '9')
  497.             d = tgetc();
  498.         return(SPEC | c);
  499.     }
  500. #endif
  501.  
  502. #if  WANGPC
  503.     if (c == 0x1F) {            /* Apply SPEC prefix    */
  504.             c = tgetc();
  505.         return(SPEC | c);
  506.     }
  507. #endif
  508.  
  509.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  510.                 c = CTRL | (c+'@');
  511.         return (c);
  512. }
  513.  
  514. /*    GETCMD:    Get a command from the keyboard. Process all applicable
  515.         prefix keys
  516.                             */
  517. PASCAL NEAR getcmd()
  518.  
  519. {
  520.     int c;        /* fetched keystroke */
  521.  
  522.     /* get initial character */
  523.     c = get1key();
  524.  
  525.     /* resolve META and CTLX prefixes */
  526.     if (getbind(c) == meta) {
  527.         c = get1key();
  528.         if (islower(c))        /* Force to upper */
  529.             c ^= DIFCASE;
  530.         c = META | c;
  531.     } else if (getbind(c) == cex) {
  532.         c = get1key();
  533.         if (islower(c))        /* Force to upper */
  534.             c ^= DIFCASE;
  535.         c = CTLX | c;
  536.     }
  537.  
  538.     /* return it */
  539.     return(c);
  540. }
  541.  
  542. /*    A more generalized prompt/reply function allowing the caller
  543.     to specify the proper terminator. If the terminator is not
  544.     a return ('\r'), return will echo as "<NL>"
  545.                             */
  546. PASCAL NEAR getstring(prompt, buf, nbuf, eolchar)
  547.  
  548. char *prompt;
  549. char *buf;
  550. int eolchar;
  551.  
  552. {
  553.     register int cpos;    /* current character position in string */
  554.     register int c;
  555.     register int quotef;    /* are we quoting the next char? */
  556.  
  557.     cpos = 0;
  558.     quotef = FALSE;
  559.  
  560.     /* prompt the user for the input string */
  561.     mlwrite(prompt);
  562.  
  563.     for (;;) {
  564.         /* get a character from the user */
  565.         tttermc = eolchar;    /* Tell terminal driver what
  566.                        our terminator is */
  567.         c = get1key();
  568.         tttermc = -1;    /* and now back to normal */
  569.  
  570.         /* if they hit the line terminate, wrap it up */
  571.         if (c == eolchar && quotef == FALSE) {
  572.             buf[cpos++] = 0;
  573.  
  574.             /* clear the message line */
  575.             mlwrite("");
  576.             TTflush();
  577.  
  578.             /* if we default the buffer, return FALSE */
  579.             if (buf[0] == 0)
  580.                 return(FALSE);
  581.  
  582.             return(TRUE);
  583.         }
  584.  
  585.         /* change from command form back to character form */
  586.         c = ectoc(c);
  587.  
  588.         if (c == ectoc(abortc) && quotef == FALSE) {
  589.             /* Abort the input? */
  590.             ctrlg(FALSE, 0);
  591.             TTflush();
  592.             return(ABORT);
  593.         } else if ((c==0x7F || c==0x08) && quotef==FALSE) {
  594.             /* rubout/erase */
  595.             if (cpos != 0) {
  596.                 outstring("\b \b");
  597.                 --ttcol;
  598.  
  599.                 if (buf[--cpos] < 0x20) {
  600.                     outstring("\b \b");
  601.                     --ttcol;
  602.                 }
  603.  
  604.                 if (buf[cpos] == '\r') {
  605.                     outstring("\b\b  \b\b");
  606.                     ttcol -= 2;
  607.                 }
  608.                 TTflush();
  609.             }
  610.  
  611.         } else if (c == 0x15 && quotef == FALSE) {
  612.             /* C-U, kill */
  613.             while (cpos != 0) {
  614.                 outstring("\b \b");
  615.                 --ttcol;
  616.  
  617.                 if (buf[--cpos] < 0x20) {
  618.                     outstring("\b \b");
  619.                     --ttcol;
  620.                 }
  621.             }
  622.             TTflush();
  623.  
  624.         } else if (c == quotec && quotef == FALSE) {
  625.             quotef = TRUE;
  626.         } else {
  627.             quotef = FALSE;
  628.             if (cpos < nbuf-1) {
  629.                 buf[cpos++] = c;
  630.  
  631.                 if ((c < ' ') && (c != '\r')) {
  632.                     outstring("^");
  633.                     ++ttcol;
  634.                     c ^= 0x40;
  635.                 }
  636.  
  637.                 if (c != '\r') {
  638.                     if (disinp)
  639.                         TTputc(c);
  640.                 } else {    /* put out <NL> for <ret> */
  641.                     outstring("<NL>");
  642.                     ttcol += 3;
  643.                 }
  644.                 ++ttcol;
  645.                 TTflush();
  646.             }
  647.         }
  648.     }
  649. }
  650.  
  651. PASCAL NEAR outstring(s) /* output a string of input characters */
  652.  
  653. char *s;    /* string to output */
  654.  
  655. {
  656.     if (disinp)
  657.         while (*s)
  658.             TTputc(*s++);
  659. }
  660.  
  661. PASCAL NEAR ostring(s)    /* output a string of output characters */
  662.  
  663. char *s;    /* string to output */
  664.  
  665. {
  666.     if (discmd)
  667.         while (*s)
  668.             TTputc(*s++);
  669. }
  670.  
  671.