home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / input.c < prev    next >
C/C++ Source or Header  |  1998-09-07  |  42KB  |  1,914 lines

  1. /*    INPUT:    Various input routines for vile
  2.  *        written by Daniel Lawrence    5/9/86
  3.  *        variously munged/massaged/relayered/slashed/burned
  4.  *            since then. -pgf
  5.  *
  6.  *    TTgetc()    raw 8-bit key from terminal driver.
  7.  *
  8.  *    sysmapped_c()    single "keystroke" -- may have SPEC bit, if it was
  9.  *            a sytem-mapped function key.  calls TTgetc().  these
  10.  *            system-mapped keys will never map to a multi-char
  11.  *            sequence.  the routine does have storage, to hold
  12.  *            keystrokes gathered "in error".
  13.  *
  14.  *    tgetc()        fresh, pushed back, or recorded output of result of
  15.  *            sysmapped_c() (i.e. dotcmd and the keyboard macros
  16.  *            are recordedand played back at this level).  this is
  17.  *            only called from mapgetc() in map.c
  18.  *
  19.  *    mapped_c()    (map.c) worker routine which will return a mapped
  20.  *            or non-mapped character from the mapping engine.
  21.  *            determines correct map, and uses its own pushback
  22.  *            buffers on top of calls to tgetc() (see mapgetc/
  23.  *            mapungetc).
  24.  *
  25.  *    mapped_keystroke() applies user-specified maps to user's input.
  26.  *            correct map used depending on mode (insert, command,
  27.  *            message-line).
  28.  *
  29.  *    keystroke()    returns pushback from mappings, the results of
  30.  *            previous calls to mapped_keystroke().
  31.  *
  32.  *    keystroke8()    as above, but masks off any "wideness", i.e. SPEC bits.
  33.  *
  34.  *    keystroke_raw() as above, but recording is forced even if
  35.  *            sysmapped_c() returns intrc. (old "tgetc(TRUE)")
  36.  *
  37.  *    kbd_seq()    the vile prefix keys (^X,^A,#) are checked for, and
  38.  *            appropriate key pairs are turned into CTLA|c, CTLX|c,
  39.  *            SPEC|c.
  40.  *
  41.  *
  42.  *    TTtypahead()       true if a key is avail from TTgetc().
  43.  *    sysmapped_c_avail() "  if a key is avail from sysmapped_c() or below.
  44.  *    tgetc_avail()     true if a key is avail from tgetc() or below.
  45.  *    keystroke_avail() true if a key is avail from keystroke() or below.
  46.  *
  47.  * $Header: /usr/build/vile/vile/RCS/input.c,v 1.188 1998/09/07 21:10:06 tom Exp $
  48.  *
  49.  */
  50.  
  51. #include    "estruct.h"
  52. #include    "edef.h"
  53. #include    "nefunc.h"
  54.  
  55. #define    DEFAULT_REG    -1
  56.  
  57. #define INFINITE_LOOP_COUNT 1200
  58.  
  59. typedef    struct    _kstack    {
  60.     struct    _kstack    *m_link;
  61.     int    m_save;        /* old value of 'kbdmode'        */
  62.     int    m_indx;        /* index identifying this macro        */
  63.     int    m_rept;        /* the number of times to execute the macro */
  64.     ITBUFF  *m_kbdm;        /* the macro-text to execute        */
  65.     ITBUFF  *m_dots;        /* workspace for "." command        */
  66. #ifdef GMDDOTMACRO
  67.     ITBUFF  *m_DOTS;        /* save-area for "." command        */
  68.     int    m_RPT0;        /* saves 'dotcmdcnt'            */
  69.     int    m_RPT1;        /* saves 'dotcmdrep'            */
  70. #endif
  71.     } KSTACK;
  72.  
  73. /*--------------------------------------------------------------------------*/
  74.  
  75. /*
  76.  * FIXME:
  77.  * Special hacks to convert null-terminated string to/from TBUFF, for interface
  78.  * with functions that still expect these.
  79.  */
  80. #define StrToBuff(buf) (buf)->tb_used = strlen(tb_values(buf))
  81. #define BuffToStr(buf) tb_values(buf)[tb_length(buf)] = EOS
  82.  
  83. /*--------------------------------------------------------------------------*/
  84. static    void    finish_kbm (void);
  85.  
  86. static    KSTACK *KbdStack;    /* keyboard/@-macros that are replaying */
  87. static    ITBUFF  *KbdMacro;    /* keyboard macro, recorded    */
  88. static    int    last_eolchar;    /* records last eolchar-match in 'kbd_string' */
  89.  
  90. /*--------------------------------------------------------------------------*/
  91.  
  92. /*
  93.  * Returns a pointer to the buffer that we use for saving text to replay with
  94.  * the "." command.
  95.  */
  96. static ITBUFF *
  97. TempDot(int init)
  98. {
  99.     static    ITBUFF  *tmpcmd;    /* dot commands, 'til we're sure */
  100.  
  101.     if (kbdmode == PLAY) {
  102.         if (init)
  103.             (void)itb_init(&(KbdStack->m_dots), abortc);
  104.         return KbdStack->m_dots;
  105.     }
  106.     if (init || (tmpcmd == 0))
  107.         (void)itb_init(&tmpcmd, abortc);
  108.     return tmpcmd;
  109. }
  110.  
  111. /*
  112.  * Dummy function to use when 'kbd_string()' does not handle automatic completion
  113.  */
  114. /*ARGSUSED*/
  115. int
  116. no_completion(int c GCC_UNUSED, char *buf GCC_UNUSED, unsigned *pos GCC_UNUSED)
  117. {
  118.     return FALSE;
  119. }
  120.  
  121. /*
  122.  * Complete names in a shell command using the filename-completion.  We'll have
  123.  * to scan back to the beginning of the appropriate name since that module
  124.  * expects only one name in a buffer.
  125.  *
  126.  * We only do shell-completion if filename-completion is configured.
  127.  */
  128. #if COMPLETE_FILES
  129. static    int    doing_shell;
  130. int
  131. shell_complete(
  132. int    c,
  133. char    *buf,
  134. unsigned *pos)
  135. {
  136.     int status;
  137.     unsigned len = *pos;
  138.     int base;
  139.     int first = 0;
  140.  
  141.     TRACE(("shell_complete %d:'%s'\n", *pos, buf))
  142.     if (isShellOrPipe(buf))
  143.         first++;
  144.  
  145.     for (base = len; base > first; ) {
  146.         base--;
  147.         if (isSpace(buf[base]) && (first || doing_shell)) {
  148.             base++;
  149.             break;
  150.         } else if (buf[base] == '$') {
  151.             break;
  152.         }
  153.     }
  154.     len -= base;
  155.     status = path_completion(c, buf+base, &len);
  156.     *pos = len + base;
  157.  
  158.     return status;
  159. }
  160. #endif
  161.  
  162. /*
  163.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  164.  * ABORT. The ABORT status is returned if the user bumps out of the question
  165.  * with an abortc. Used any time a confirmation is required.
  166.  */
  167.  
  168. int
  169. mlyesno(const char *prompt)
  170. {
  171.     char c;         /* input character */
  172.  
  173.     /* in case this is right after a shell escape */
  174.     if (!update(TRUE))
  175.         return (ABORT);
  176.  
  177.     for_ever {
  178.         mlforce("%s [y/n]? ",prompt);
  179.         c = (char)keystroke();    /* get the response */
  180.  
  181.         if (ABORTED(c))        /* Bail out! */
  182.             return(ABORT);
  183.  
  184.         if (c=='y' || c=='Y')
  185.             return(TRUE);
  186.  
  187.         if (c=='n' || c=='N')
  188.             return(FALSE);
  189.     }
  190. }
  191.  
  192. /*
  193.  * Ask a simple question in the message line. Return the single char response,
  194.  *  if it was one of the valid responses.
  195.  */
  196.  
  197. int
  198. mlquickask(const char *prompt, const char *respchars, int *cp)
  199. {
  200.     if (!update(TRUE))
  201.         return (ABORT);
  202.  
  203.     for_ever {
  204.         mlforce("%s ",prompt);
  205.         *cp = keystroke();    /* get the response */
  206.  
  207.         if (ABORTED(*cp))    /* Bail out! */
  208.             return(ABORT);
  209.  
  210.         if (strchr(respchars,*cp))
  211.             return TRUE;
  212.  
  213.         kbd_alarm();
  214.     }
  215. }
  216.  
  217. /*
  218.  * Prompt for a named-buffer (i.e., "register")
  219.  */
  220. int
  221. mlreply_reg(
  222. const char *prompt,
  223. char    *cbuf,        /* 2-char buffer for register+eol */
  224. int    *retp,        /* => the register-name */
  225. int    at_dft)        /* default-value (e.g., for "@@" command) */
  226. {
  227.     register int status;
  228.     register int c;
  229.  
  230.     if (clexec || isnamedcmd) {
  231.         if ((status = mlreply(prompt, cbuf, 2)) != TRUE)
  232.             return status;
  233.         c = cbuf[0];
  234.     } else {
  235.         c = keystroke();
  236.         if (ABORTED(c))
  237.             return ABORT;
  238.     }
  239.  
  240.     if (c == '@' && at_dft != -1) {
  241.         c = at_dft;
  242.     } else if (reg2index(c) < 0) {
  243.         mlwarn("[Invalid register name]");
  244.         return FALSE;
  245.     }
  246.  
  247.     *retp = c;
  248.     return TRUE;
  249. }
  250.  
  251. /*
  252.  * Prompt for a register-name and/or line-count (e.g., for the ":yank" and
  253.  * ":put" commands).  The register-name, if given, is first.
  254.  */
  255. int
  256. mlreply_reg_count(
  257. int    state,        /* negative=register, positive=count, zero=either */
  258. int    *retp,        /* returns the register-index or line-count */
  259. int    *next)        /* returns 0/1=register, 2=count */
  260. {
  261.     register int status;
  262.     char    prompt[80];
  263.     char    expect[80];
  264.     char    buffer[10];
  265.     UINT    length;
  266.  
  267.     *expect = EOS;
  268.     if (state <= 0)
  269.         (void)strcat(expect, " register");
  270.     if (state == 0)
  271.         (void)strcat(expect, " or");
  272.     if (state >= 0) {
  273.         (void)strcat(expect, " line-count");
  274.         length = sizeof(buffer);
  275.     } else
  276.         length = 2;
  277.  
  278.     (void)lsprintf(prompt, "Specify%s: ", expect);
  279.     *buffer = EOS;
  280.     status = kbd_string(prompt, buffer, length, ' ', 0, no_completion);
  281.  
  282.     if (status == TRUE) {
  283.         if (state <= 0
  284.          && isAlpha(buffer[0])
  285.          && buffer[1] == EOS
  286.          && (*retp = reg2index(*buffer)) >= 0) {
  287.             *next = isUpper(*buffer) ? 1 : 0;
  288.         } else if (state >= 0
  289.          && string_to_number(buffer, retp)
  290.          && *retp) {
  291.             *next = 2;
  292.         } else {
  293.             mlforce("[Expected%s]", expect);
  294.             kbd_alarm();
  295.             status = ABORT;
  296.         }
  297.     }
  298.     return status;
  299. }
  300.  
  301. /*
  302.  * Write a prompt into the message line, then read back a response. Keep
  303.  * track of the physical position of the cursor. If we are in a keyboard
  304.  * macro throw the prompt away, and return the remembered response. This
  305.  * lets macros run at full speed. The reply is always terminated by a carriage
  306.  * return. Handle erase, kill, and abort keys.
  307.  */
  308.  
  309. int
  310. mlreply(const char *prompt, char *buf, UINT bufn)
  311. {
  312.     return kbd_string(prompt, buf, bufn, '\n', KBD_NORMAL, no_completion);
  313. }
  314.  
  315. /* as above, but don't do anything to backslashes */
  316. int
  317. mlreply_no_bs(const char *prompt, char *buf, UINT bufn)
  318. {
  319.     int s;
  320. #if COMPLETE_FILES
  321.     doing_shell = TRUE;
  322.     init_filec(FILECOMPLETION_BufName);
  323. #endif
  324.     s = kbd_string(prompt, buf, bufn, '\n', KBD_EXPAND|KBD_SHPIPE, shell_complete);
  325. #if COMPLETE_FILES
  326.     doing_shell = FALSE;
  327. #endif
  328.     return s;
  329. }
  330.  
  331. /* as above, but neither expand nor do anything to backslashes */
  332. int
  333. mlreply_no_opts(const char *prompt, char *buf, UINT bufn)
  334. {
  335.     return kbd_string(prompt, buf, bufn, '\n', 0, no_completion);
  336. }
  337.  
  338.  
  339. /* the numbered buffer names increment each time they are referenced */
  340. void
  341. incr_dot_kregnum(void)
  342. {
  343.     if (dotcmdmode == PLAY) {
  344.         register int    c = itb_peek(dotcmd);
  345.         if (isDigit(c) && c < '9')
  346.             itb_stuff(dotcmd, ++c);
  347.     }
  348. }
  349.  
  350. /*
  351.  * Record a character for "." commands
  352.  */
  353. static void
  354. record_dot_char(int c)
  355. {
  356.     if (dotcmdmode == RECORD) {
  357.         ITBUFF    *tmp = TempDot(FALSE);
  358.         (void)itb_append(&tmp, c);
  359.     }
  360. }
  361.  
  362. /*
  363.  * Record a character for kbd-macros
  364.  */
  365. static void
  366. record_kbd_char(int c)
  367. {
  368.     if (dotcmdmode != PLAY && kbdmode == RECORD)
  369.         (void)itb_append(&KbdMacro, c);
  370. }
  371.  
  372. /* if we should preserve this input, do so */
  373. static void
  374. record_char(int c)
  375. {
  376.     record_dot_char(c);
  377.     record_kbd_char(c);
  378. }
  379.  
  380. /* get the next character of a replayed '.' or macro */
  381. int
  382. get_recorded_char(
  383. int eatit)  /* consume the character? */
  384. {
  385.     register int    c = -1;
  386.     register ITBUFF    *buffer;
  387.  
  388.     if (dotcmdmode == PLAY) {
  389.  
  390.         if (interrupted()) {
  391.             dotcmdmode = STOP;
  392.             return intrc;
  393.         } else {
  394.  
  395.             if (!itb_more(buffer = dotcmd)) {
  396.                 if (!eatit) {
  397.                     if (dotcmdrep > 1)
  398.                         return itb_get(buffer, 0);
  399.                 } else { /* at the end of last repetition?  */
  400.                     if (--dotcmdrep < 1) {
  401.                         dotcmdmode = STOP;
  402.                         (void)dotcmdbegin();
  403.                         /* immediately start recording
  404.                          * again, just in case.
  405.                          */
  406.                     } else {
  407.                         /* reset the macro to the
  408.                          * beginning for the next rep.
  409.                          */
  410.                         itb_first(buffer);
  411.                     }
  412.                 }
  413.             }
  414.  
  415.             /* if there is some left... */
  416.             if (itb_more(buffer)) {
  417.                 if (eatit)
  418.                     c = itb_next(buffer);
  419.                 else
  420.                     c = itb_peek(buffer);
  421.                 return c;
  422.             }
  423.         }
  424.     }
  425.  
  426.     if (kbdmode == PLAY) { /* if we are playing a keyboard macro back, */
  427.  
  428.         if (interrupted()) {
  429.             while (kbdmode == PLAY)
  430.                 finish_kbm();
  431.             return intrc;
  432.         } else {
  433.  
  434.             if (!itb_more(buffer = KbdStack->m_kbdm)) {
  435.                 if (--(KbdStack->m_rept) >= 1)
  436.                     itb_first(buffer);
  437.                 else
  438.                     finish_kbm();
  439.             }
  440.  
  441.             if (kbdmode == PLAY) {
  442.                 buffer = KbdStack->m_kbdm;
  443.                 if (eatit)
  444.                     record_dot_char(c = itb_next(buffer));
  445.                 else
  446.                     c = itb_peek(buffer);
  447.             }
  448.         }
  449.     }
  450.  
  451.     return c;
  452. }
  453.  
  454. void
  455. unkeystroke(int c)
  456. {
  457.     mapungetc(c|NOREMAP);
  458. }
  459.  
  460. int
  461. mapped_keystroke(void)
  462. {
  463.     return lastkey = mapped_c(DOMAP,NOQUOTED);
  464. }
  465.  
  466. int
  467. keystroke(void)
  468. {
  469.     return lastkey = mapped_c(NODOMAP,NOQUOTED);
  470. }
  471.  
  472. int
  473. keystroke8(void)
  474. {
  475.     int c;
  476.     for_ever {
  477.         c = mapped_c(NODOMAP,NOQUOTED);
  478.         if ((c & ~0xff) == 0)
  479.             return lastkey = c;
  480.         kbd_alarm();
  481.     }
  482. }
  483.  
  484. int
  485. keystroke_raw8(void)
  486. {
  487.     int c;
  488.     for_ever {
  489.         c = mapped_c(NODOMAP,QUOTED);
  490.         if ((c & ~0xff) == 0)
  491.             return lastkey = c;
  492.         kbd_alarm();
  493.     }
  494. }
  495.  
  496. static int
  497. mapped_keystroke_raw(void)
  498. {
  499.     return lastkey = mapped_c(DOMAP,QUOTED);
  500. }
  501.  
  502.  
  503. int
  504. keystroke_avail(void)
  505. {
  506.     return mapped_c_avail();
  507. }
  508.  
  509. static ITBUFF *tgetc_ungottenchars = NULL;
  510. static int tgetc_ungotcnt = 0;
  511.  
  512. void
  513. tungetc(int c)
  514. {
  515.     (void)itb_append(&tgetc_ungottenchars, c);
  516.     tgetc_ungotcnt++;
  517. }
  518.  
  519. int
  520. tgetc_avail(void)
  521. {
  522.     return tgetc_ungotcnt > 0 ||
  523.         get_recorded_char(FALSE) != -1 ||
  524.         sysmapped_c_avail();
  525. }
  526.  
  527. int
  528. tgetc(int quoted)
  529. {
  530.     register int c;    /* fetched character */
  531.  
  532.     if (tgetc_ungotcnt > 0) {
  533.         tgetc_ungotcnt--;
  534.         return itb_last(tgetc_ungottenchars);
  535.     }
  536.  
  537.     if ((c = get_recorded_char(TRUE)) == -1) {
  538.         /* fetch a character from the terminal driver */
  539.         not_interrupted();
  540.         if (setjmp(read_jmp_buf)) {
  541.             c = kcod2key(intrc);
  542.                 TRACE(("setjmp/getc:%c (%#x)\n", c, c))
  543. #if defined(linux) && defined(DISP_TERMCAP)
  544.             /*
  545.              * Linux bug (observed with kernels 1.2.13 & 2.0.0):
  546.              * when interrupted, the _next_ character that
  547.              * getchar() returns will be the same as the last
  548.              * character before the interrupt.  Eat it.
  549.              */
  550.             (void)ttgetc();
  551. #endif
  552.         } else {
  553.             (void) im_waiting(TRUE);
  554.             do { /* if it's sysV style signals,
  555.                  we want to try again, since this
  556.                  must not have been SIGINT, but
  557.                  was probably SIGWINCH */
  558.                 c = sysmapped_c();
  559.             } while (c == -1);
  560.         }
  561.         (void) im_waiting(FALSE);
  562.         if (quoted || ((UINT) c != kcod2key(intrc)))
  563.             record_char(c);
  564.     }
  565.  
  566.     /* and finally give the char back */
  567.     return c;
  568. }
  569.  
  570.  
  571. /*    KBD_SEQ:    Get a command sequence (multiple keystrokes) from
  572.         the keyboard.
  573.         Process all applicable prefix keys.
  574.         Set lastcmd for commands which want stuttering.
  575. */
  576. int
  577. kbd_seq(void)
  578. {
  579.     int c;        /* fetched keystroke */
  580.  
  581.     int prefix = 0;    /* accumulate prefix */
  582.  
  583.     c = mapped_keystroke();
  584.  
  585.     if (c == cntl_a) {
  586.         prefix = CTLA;
  587.         c = keystroke();
  588.     } else if (c == cntl_x) {
  589.         prefix = CTLX;
  590.         c = keystroke();
  591.     } else if (c == poundc) {
  592.         prefix = SPEC;
  593.         c = keystroke();
  594.     }
  595.  
  596.     c |= prefix;
  597.  
  598.     /* otherwise, just return it */
  599.     return (lastcmd = c);
  600. }
  601.  
  602. /*
  603.  * Get a command-key, suppressing the mapping
  604.  */
  605. int
  606. kbd_seq_nomap(void)
  607. {
  608.     unkeystroke(keystroke());
  609.     return kbd_seq();
  610. }
  611.  
  612. /* get a string consisting of inclchartype characters from the current
  613.     position.  if inclchartype is 0, return everything to eol */
  614. int
  615. screen_string (char *buf, int bufn, CHARTYPE inclchartype)
  616. {
  617.     register int i = 0;
  618.     MARK mk;
  619.  
  620.     mk = DOT;
  621.  
  622.     /* if from gototag(), grab from the beginning of the string */
  623.     if (b_val(curbp, MDTAGWORD)
  624.      && inclchartype == vl_ident
  625.      && DOT.o > 0
  626.      && istype(inclchartype, char_at(DOT))) {
  627.         while ( DOT.o > 0 ) {
  628.             DOT.o--;
  629.             if ( !istype(inclchartype, char_at(DOT)) ) {
  630.                 DOT.o++;
  631.                 break;
  632.             }
  633.         }
  634.     }
  635.     while ( i < (bufn-1) && !is_at_end_of_line(DOT)) {
  636.         buf[i] = char_at(DOT);
  637. #if OPT_WIDE_CTYPES
  638.         if (i == 0) {
  639.             if (inclchartype & vl_scrtch) {
  640.                 if (buf[0] != SCRTCH_LEFT[0])
  641.                     inclchartype &= ~vl_scrtch;
  642.             }
  643.             if (inclchartype & vl_shpipe) {
  644.                 if (buf[0] != SHPIPE_LEFT[0])
  645.                     inclchartype &= ~vl_shpipe;
  646.             }
  647.         }
  648.  
  649.         /* allow "[!command]" */
  650.         if ((inclchartype & vl_scrtch)
  651.          && (i == 1)
  652.          && (buf[1] == SHPIPE_LEFT[0])) {
  653.             /*EMPTY*/;
  654.         /* guard against things like "[Buffer List]" on VMS */
  655.         } else if ((inclchartype & vl_pathn)
  656.          && !ispath(buf[i])
  657.          && (inclchartype == vl_pathn)) {
  658.             break;
  659.         } else
  660. #endif
  661.         if (inclchartype && !istype(inclchartype, buf[i]))
  662.             break;
  663.         DOT.o++;
  664.         i++;
  665. #if OPT_WIDE_CTYPES
  666.         if (inclchartype & vl_scrtch) {
  667.             if ((i < bufn)
  668.              && (inclchartype & vl_pathn)
  669.              && ispath(char_at(DOT)))
  670.                 continue;
  671.             if (buf[i-1] == SCRTCH_RIGHT[0])
  672.                 break;
  673.         }
  674. #endif
  675.     }
  676.  
  677. #if OPT_WIDE_CTYPES
  678. #if OPT_VMS_PATH
  679.     if (inclchartype & vl_pathn) {
  680.         ;    /* override conflict with "[]" */
  681.     } else
  682. #endif
  683.     if (inclchartype & vl_scrtch) {
  684.         if (buf[i-1] != SCRTCH_RIGHT[0])
  685.             i = 0;
  686.     }
  687. #endif
  688.  
  689.     buf[i] = EOS;
  690.     DOT = mk;
  691.  
  692.     return buf[0] != EOS;
  693. }
  694.  
  695. /*
  696.  * Returns the character that ended the last call on 'kbd_string()'
  697.  */
  698. int
  699. end_string(void)
  700. {
  701.     return last_eolchar;
  702. }
  703.  
  704. void
  705. set_end_string(int c)
  706. {
  707.     last_eolchar = c;
  708. }
  709.  
  710. /*
  711.  * Returns an appropriate delimiter for /-commands, based on the end of the
  712.  * last reply.  That is, in a command such as
  713.  *
  714.  *    :s/first/last/
  715.  *
  716.  * we will get prompts for
  717.  *
  718.  *    :s/    /-delimiter saved in 'end_string()'
  719.  *    first/
  720.  *    last/
  721.  *
  722.  * If a newline is used at any stage, subsequent delimiters are forced to a
  723.  * newline.
  724.  */
  725. int
  726. kbd_delimiter(void)
  727. {
  728.     register int    c = '\n';
  729.  
  730.     if (isnamedcmd) {
  731.         register int    d = end_string();
  732.         if (isPunct(d))
  733.             c = d;
  734.     }
  735.     return c;
  736. }
  737.  
  738. /*
  739.  * Make sure that the buffer will have extra space when passing it to functions
  740.  * that don't know it's a TBUFF.
  741.  */
  742. static char *
  743. tbreserve(TBUFF **buf)
  744. {
  745.     char *result = tb_values(tb_alloc(buf, tb_length(*buf) + NSTRING));
  746.     BuffToStr(*buf);
  747.     return result;
  748. }
  749.  
  750. #define BACKSLASH '\\'
  751.  
  752. /* turn \X into X */
  753. static void
  754. remove_backslashes(TBUFF *buf)
  755. {
  756.     register char *cp = tb_values(buf);
  757.     register ALLOC_T s, d;
  758.  
  759.     for (s = d = 0; s < tb_length(buf); ) {
  760.         if (cp[s] == BACKSLASH)
  761.             s++;
  762.         cp[d++] = cp[s++];
  763.     }
  764.  
  765.     buf->tb_used = d;
  766. }
  767.  
  768. /* count backslashes so we can tell at any point whether we have the current
  769.  * position escaped by one.
  770.  */
  771. static UINT
  772. countBackSlashes(TBUFF * buf, UINT len)
  773. {
  774.     char *buffer = tb_values(buf);
  775.     register UINT    count;
  776.  
  777.     if (len && buffer[len-1] == BACKSLASH) {
  778.         count = 1;
  779.         while (count+1 <= len &&
  780.             buffer[len-1-count] == BACKSLASH)
  781.             count++;
  782.     } else {
  783.         count = 0;
  784.     }
  785.     return count;
  786. }
  787.  
  788. static void
  789. showChar(int c)
  790. {
  791.     if (disinp) {
  792.         int    save_expand = kbd_expand;
  793.         kbd_expand = 1;    /* show all controls */
  794.         kbd_putc(c);
  795.         kbd_expand = save_expand;
  796.     }
  797. }
  798.  
  799. static void
  800. show1Char(int c)
  801. {
  802.     if (disinp) {
  803.         showChar(c);
  804.         kbd_flush();
  805.     }
  806. }
  807.  
  808. /*
  809.  * Macro result is true if the buffer is a normal :-command with a leading "!",
  810.  * or is invoked from one of the places that supplies an implicit "!" for shell
  811.  * commands.
  812.  */
  813. #define editingShellCmd(buf,options) \
  814.      (((options & KBD_EXPCMD) && isShellOrPipe(buf)) \
  815.     || (options & KBD_SHPIPE))
  816.  
  817. /* expand a single character (only used on interactive input) */
  818. static int
  819. expandChar(
  820. TBUFF **buf,
  821. unsigned * position,
  822. int    c,
  823. UINT    options)
  824. {
  825.     register int    cpos = *position;
  826.     register char *    cp;
  827.     register BUFFER *bp;
  828.     char str[NFILEN];
  829.     char *buffer = tb_values(*buf);
  830.     int  shell = editingShellCmd(buffer,options);
  831.     int  expand = ((options & KBD_EXPAND) || shell);
  832.     int  exppat = (options & KBD_EXPPAT);
  833.  
  834.     /* Are we allowed to expand anything? */
  835.     if (!(expand || exppat || shell))
  836.          return FALSE;
  837.  
  838.     /* Is this a character that we know about? */
  839.     if (strchr(global_g_val_ptr(GVAL_EXPAND_CHARS),c) == 0)
  840.         return FALSE;
  841.  
  842.     switch (c)
  843.     {
  844.     case EXPC_THIS:
  845.     case EXPC_THAT:
  846.         if (!expand)
  847.             return FALSE;
  848.  
  849.         bp = (c == EXPC_THIS) ? curbp : find_alt();
  850.         if (bp == 0 || b_is_invisible(bp)) {
  851.             kbd_alarm();    /* complain a little */
  852.             return FALSE;    /* ...and let the user type it as-is */
  853.         }
  854.  
  855.         cp = bp->b_fname;
  856.         if (isInternalName(cp)) {
  857.             cp = bp->b_bname;
  858.         } else if (!global_g_val(GMDEXPAND_PATH)) {
  859.             cp = shorten_path(strcpy(str, cp), FALSE);
  860. #if OPT_MSDOS_PATH
  861.             /* always use backslashes if invoking external prog */
  862.             if (shell)
  863.                 cp = SL_TO_BSL(cp);
  864. #endif
  865.         }
  866.  
  867.         break;
  868.  
  869.     case EXPC_TOKEN:
  870.         if (!expand)
  871.             return FALSE;
  872.  
  873.         if (screen_string(str, sizeof(str), vl_pathn))
  874.             cp = str;
  875.         else
  876.             cp = NULL;
  877.  
  878.         break;
  879.  
  880.     case EXPC_RPAT:
  881.         if (!exppat)
  882.             return FALSE;
  883.  
  884.         if (rpat[0])
  885.             cp = rpat;
  886.         else
  887.             cp = NULL;
  888.  
  889.         break;
  890.  
  891.     case EXPC_SHELL:
  892.         if (!shell)
  893.             return FALSE;
  894.  
  895. #ifdef only_expand_first_bang
  896.         /* without this check, do as vi does -- expand '!'
  897.            to previous command anywhere it's typed */
  898.         if (cpos > (buffer[0] == '!'))
  899.             return FALSE;
  900. #endif
  901.         cp = tb_values(save_shell[!isShellOrPipe(buffer)]);
  902.         if (cp != NULL && isShellOrPipe(cp))
  903.             cp++;    /* skip the '!' */
  904.         break;
  905.  
  906.     default:
  907.         return FALSE;
  908.     }
  909.  
  910.     if (cp != NULL) {
  911.         while ((c = *cp++) != EOS) {
  912.             tb_insert(buf, cpos++, c);
  913.             showChar(c);
  914.         }
  915.         kbd_flush();
  916.     }
  917.     *position = cpos;
  918.     return TRUE;
  919. }
  920.  
  921. /*
  922.  * Returns true for the (presumably control-chars) that we use for line edits
  923.  */
  924. int
  925. is_edit_char(int c)
  926. {
  927.     return (isreturn(c)
  928.       ||    isbackspace(c)
  929.       ||    (c == wkillc)
  930.       ||    (c == killc));
  931. }
  932.  
  933. /*
  934.  * Erases the response from the screen for 'kbd_string()'
  935.  */
  936. void
  937. kbd_kill_response(TBUFF * buffer, unsigned * position, int c)
  938. {
  939.     char *buf = tb_values(buffer);
  940.     TBUFF    *tmp = 0;
  941.     int    cpos = *position;
  942.     UINT    mark = cpos;
  943.  
  944.     tmp = tb_copy(&tmp, buffer);
  945.     while (cpos > 0) {
  946.         cpos--;
  947.         kbd_erase();
  948.         if (c == wkillc) {
  949.             if (!isSpace(buf[cpos])) {
  950.                 if (cpos > 0 && isSpace(buf[cpos-1]))
  951.                     break;
  952.             }
  953.         }
  954.  
  955.         if (c != killc && c != wkillc)
  956.             break;
  957.     }
  958.     if (disinp)
  959.         kbd_flush();
  960.  
  961.     *position = cpos;
  962.     buffer->tb_used = cpos;
  963.     if (mark < tb_length(tmp)) {
  964.         tb_bappend(&buffer, tb_values(tmp)+mark, tb_length(tmp)-mark);
  965.     }
  966.     (void)tb_free(&tmp);
  967. }
  968.  
  969. /*
  970.  * Display the default response for 'kbd_string()', escaping backslashes if
  971.  * necessary.
  972.  */
  973. int
  974. kbd_show_response(
  975. TBUFF    **dst,        /* string with escapes */
  976. char    *src,        /* string w/o escapes */
  977. unsigned bufn,        /* # of chars we read from 'src[]' */
  978. int    eolchar,
  979. UINT    options)
  980. {
  981.     register unsigned k;
  982.  
  983.     /* add backslash escapes in front of volatile characters */
  984.     tb_init(dst, 0);
  985.  
  986.     for (k = 0; k < bufn; k++) {
  987.         register int c = src[k];
  988.  
  989.         if ((c == BACKSLASH) || (c == eolchar && eolchar != '\n')) {
  990.             if (options & KBD_QUOTES)
  991.                 tb_append(dst, BACKSLASH); /* add extra */
  992.         } else if (strchr(global_g_val_ptr(GVAL_EXPAND_CHARS),c) != 0) {
  993.             if (c == EXPC_RPAT && !(options & KBD_EXPPAT))
  994.                 /*EMPTY*/;
  995.             else if (c == EXPC_SHELL && !(options & KBD_SHPIPE))
  996.                 /*EMPTY*/;
  997.             else if ((options & KBD_QUOTES)
  998.              && (options & KBD_EXPAND))
  999.                 tb_append(dst, BACKSLASH); /* add extra */
  1000.         }
  1001.         tb_append(dst, c);
  1002.     }
  1003.  
  1004.     /* put out the default response, which is in the buffer */
  1005.     kbd_init();
  1006.     for (k = 0; k < tb_length(*dst); k++) {
  1007.         showChar(tb_values(*dst)[k]);
  1008.     }
  1009.     if (disinp)
  1010.         kbd_flush();
  1011.     return tb_length(*dst);
  1012. }
  1013.  
  1014. /* default function for 'edithistory()' */
  1015. int
  1016. /*ARGSUSED*/
  1017. eol_history(const char * buffer GCC_UNUSED, unsigned cpos GCC_UNUSED, int c, int eolchar)
  1018. {
  1019.     if (isPrint(eolchar) || eolchar == '\r') {
  1020.         if (c == eolchar || (eolchar == '\r' && c == '\n'))
  1021.             return TRUE;
  1022.     }
  1023.     return FALSE;
  1024. }
  1025.  
  1026. /*
  1027.  * Store a one-level push-back of the shell command text. This allows simple
  1028.  * prompt/substitution of shell commands, while keeping the "!" and text
  1029.  * separate for the command decoder.
  1030.  */
  1031. static    int    pushed_back;
  1032. static    int    pushback_flg;
  1033. static    const char * pushback_ptr;
  1034.  
  1035. void
  1036. kbd_pushback(TBUFF *buf, int skip)
  1037. {
  1038.     static    TBUFF    *PushBack;
  1039.     char *buffer = tb_values(buf);    /* FIXME */
  1040.  
  1041.     TRACE(("kbd_pushback(%s,%d)\n", tb_visible(buf), skip))
  1042.     if (macroize(&PushBack, buf, skip)) {
  1043.         pushed_back  = TRUE;
  1044.         pushback_flg = clexec;
  1045.         pushback_ptr = execstr;
  1046.         clexec       = TRUE;
  1047.         execstr      = tb_values(PushBack);
  1048.         buffer[skip] = EOS;
  1049.         buf->tb_used = skip;
  1050.     }
  1051. }
  1052.  
  1053. int
  1054. kbd_is_pushed_back(void)
  1055. {
  1056.     return clexec && pushed_back;
  1057. }
  1058.  
  1059. /*    A more generalized prompt/reply function allowing the caller
  1060.     to specify a terminator other than '\n'.  Both are accepted.
  1061.     Assumes the buffer already contains a valid (possibly
  1062.     null) string to use as the default response.
  1063. */
  1064. int
  1065. kbd_string(
  1066. const char *prompt,    /* put this out first */
  1067. char *extbuf,        /* the caller's (possibly full) buffer */
  1068. unsigned bufn,        /* the length of  " */
  1069. int eolchar,        /* char we can terminate on, in addition to '\n' */
  1070. UINT options,        /* KBD_EXPAND/KBD_QUOTES, etc. */
  1071. int (*complete)(DONE_ARGS)) /* handles completion */
  1072. {
  1073.     int code;
  1074.     static TBUFF *temp;
  1075.     tb_scopy(&temp, extbuf);
  1076.     code = kbd_reply(prompt, &temp, eol_history, eolchar, options, complete);
  1077.     if (bufn > tb_length(temp))
  1078.         bufn = tb_length(temp);
  1079.     if (bufn != 0)
  1080.         memcpy(extbuf, tb_values(temp), bufn);
  1081.     extbuf[bufn-1] = EOS;
  1082.     return code;
  1083. }
  1084.  
  1085. /*
  1086.  * We use the editc character to toggle between insert/command mode in the
  1087.  * minibuffer.  This is normally bound to ^G (the position command).
  1088.  */
  1089. static int
  1090. isMiniEdit(int c)
  1091. {
  1092.     if (c == editc)
  1093.         return TRUE;
  1094.     if (miniedit) {
  1095.         const CMDFUNC *cfp = kcod2fnc(c);
  1096.         if ((cfp != 0)
  1097.          && (cfp->c_flags & MOTION) != 0)
  1098.             return TRUE;
  1099.     }
  1100.     return FALSE;
  1101. }
  1102.  
  1103. /*
  1104.  * Shift the minibuffer left/right to keep the cursor visible, as well as the
  1105.  * prompt, if that's feasible.
  1106.  */
  1107. static void
  1108. shiftMiniBuffer(int offs)
  1109. {
  1110.     static const int slack = 5;
  1111.     int shift = w_val(wminip,WVAL_SIDEWAYS);
  1112.     int adjust;
  1113.     BUFFER *savebp;
  1114.     WINDOW *savewp;
  1115.     MARK savemk;
  1116.  
  1117.     beginDisplay();
  1118.     savebp = curbp;
  1119.     savewp = curwp;
  1120.     savemk = MK;
  1121.  
  1122.     curbp  = bminip;
  1123.     curwp  = wminip;
  1124.  
  1125.     adjust = offs - (shift + LastMsgCol - 1);
  1126.     if (adjust >= 0)
  1127.         mvrightwind(TRUE, 1+adjust);
  1128.     else if (shift > 0 && (slack + adjust) < 0)
  1129.         mvleftwind(TRUE, slack-adjust);
  1130.  
  1131.     curbp = savebp;
  1132.     curwp = savewp;
  1133.     MK = savemk;
  1134.  
  1135.     kbd_flush();
  1136.     endofDisplay();
  1137. }
  1138.  
  1139. static int
  1140. editMinibuffer(TBUFF **buf, unsigned *cpos, int c, int margin, int quoted)
  1141. {
  1142.     int edited = FALSE;
  1143.     const CMDFUNC *cfp = kcod2fnc(c);
  1144.     int savedexecmode = insertmode;
  1145.     BUFFER *savebp;
  1146.     WINDOW *savewp;
  1147.     MARK savemk;
  1148.  
  1149.     beginDisplay();
  1150.     savebp = curbp;
  1151.     savewp = curwp;
  1152.     curbp  = bminip;
  1153.     curwp  = wminip;
  1154.     savemk = MK;
  1155.  
  1156.     /* Use editc (normally ^G) to toggle insert/command mode */
  1157.     if (c == editc && !quoted) {
  1158.         miniedit = !miniedit;
  1159.     } else if (isspecial(c)
  1160.       ||  (miniedit && cfp != 0 && cfp->c_flags & MOTION)) {
  1161.  
  1162.         /* If we're allowed to honor SPEC bindings, then see if it's
  1163.          * bound to something, and execute it.
  1164.          */
  1165.         if (cfp) {
  1166.             int first = *cpos + margin;
  1167.             int old_clexec = clexec;
  1168.             int old_named  = isnamedcmd;
  1169.  
  1170.             /*
  1171.              * Reset flags that might cause a recursion into the
  1172.              * prompt/reply code.
  1173.              */
  1174.             clexec = 0;
  1175.             isnamedcmd = 0;
  1176.  
  1177.             /*
  1178.              * Set limits so we don't edit the prompt, and allow us
  1179.              * to move the cursor with the arrow keys just past the
  1180.              * end of line.
  1181.              */
  1182.             b_set_left_margin(bminip, margin);
  1183.             DOT.o = llength(DOT.l);
  1184.             linsert(1,' ');     /* pad the line so we can move */
  1185.  
  1186.             DOT.o = first;
  1187.             MK = DOT;
  1188.             curwp->w_line = DOT;
  1189.             (void)execute(cfp,FALSE,1);
  1190.             insertmode = savedexecmode;
  1191.             edited = TRUE;
  1192.             *cpos = DOT.o - margin;
  1193.  
  1194.             llength(DOT.l) -= 1;    /* strip the padding */
  1195.             b_set_left_margin(bminip, 0);
  1196.  
  1197.             clexec = old_clexec;
  1198.             isnamedcmd = old_named;
  1199.  
  1200.             /*
  1201.              * Cheat a little, since we may have used an alias for
  1202.              * '$', which can set the offset just past the end of
  1203.              * line.
  1204.              */
  1205.             if (DOT.o > llength(DOT.l))
  1206.                 DOT.o = llength(DOT.l);
  1207.  
  1208.             /* Do something reasonable if user tried to page up
  1209.              * in the minibuffer
  1210.              */
  1211.             if ((first == DOT.o)
  1212.              && (cfp->c_flags & MOTION))
  1213.                 kbd_alarm();
  1214.         } else
  1215.             kbd_alarm();
  1216.     /* FIXME: Below are some hacks for making it appear that we're
  1217.      * doing the right thing for certain non-motion commands...
  1218.      */
  1219.     } else if (miniedit && cfp == &f_insert) {
  1220.         miniedit = FALSE;
  1221.     } else if (miniedit && cfp == &f_insertbol) {
  1222.         edited = editMinibuffer(buf, cpos, fnc2kcod(&f_firstnonwhite),
  1223.                                 margin, quoted);
  1224.         miniedit = FALSE;
  1225.     } else if (miniedit && cfp == &f_appendeol) {
  1226.         edited = editMinibuffer(buf, cpos, fnc2kcod(&f_gotoeol),
  1227.                                 margin, quoted);
  1228.         miniedit = FALSE;
  1229.     } else if (miniedit && cfp == &f_append) {
  1230.         edited = editMinibuffer(buf, cpos, fnc2kcod(&f_forwchar_to_eol),
  1231.                                 margin, quoted);
  1232.         miniedit = FALSE;
  1233.     /* FIXME:  reject non-motion commands for now, since we haven't
  1234.      * resolved what to do with the minibuffer if someone inserts a
  1235.      * newline.
  1236.      */
  1237.     } else if (miniedit && cfp != 0) {
  1238.         kbd_alarm();
  1239.     } else {
  1240.         miniedit = FALSE;
  1241.         if (disinp) {
  1242.             show1Char(c);
  1243.             tb_init(buf, EOS);
  1244.             tb_bappend(buf, DOT.l->l_text + margin,
  1245.                    llength(DOT.l) - margin);
  1246.         }
  1247.         else {
  1248.             char tmp = c;
  1249.             tb_bappend(buf, &tmp, 1);
  1250.         }
  1251.         *cpos += 1;
  1252.         edited = TRUE;
  1253.     }
  1254.  
  1255.     shiftMiniBuffer(DOT.o);
  1256.  
  1257.     curbp = savebp;
  1258.     curwp = savewp;
  1259.     MK = savemk;
  1260.     kbd_flush();
  1261.     endofDisplay();
  1262.  
  1263.     return edited;
  1264. }
  1265.  
  1266. /*
  1267.  * Same as 'kbd_string()', except for adding the 'endfunc' parameter.
  1268.  *
  1269.  * Returns:
  1270.  *    ABORT - abort character given (usually ESC)
  1271.  *    SORTOFTRUE - backspace from empty-buffer
  1272.  *    TRUE - buffer is not empty
  1273.  *    FALSE - buffer is empty
  1274.  */
  1275. int
  1276. kbd_reply(
  1277. const char *prompt,        /* put this out first */
  1278. TBUFF **extbuf,            /* the caller's (possibly full) buffer */
  1279. int (*endfunc)(EOL_ARGS),    /* parsing with 'eolchar' delimiter */
  1280. int eolchar,            /* char we can terminate on, in addition to '\n' */
  1281. UINT options,            /* KBD_EXPAND/KBD_QUOTES */
  1282. int (*complete)(DONE_ARGS))    /* handles completion */
  1283. {
  1284.     int    c;
  1285.     int    done;
  1286.     unsigned cpos;        /* current character position in string */
  1287.     int    status;
  1288.     int    shell;
  1289.     int    margin;
  1290.     ALLOC_T    save_len;
  1291.  
  1292.     register int quotef;    /* are we quoting the next char? */
  1293.     register UINT backslashes; /* are we quoting the next expandable char? */
  1294.     UINT dontmap = (options & KBD_NOMAP);
  1295.     int firstch = TRUE;
  1296.     int lastch;
  1297.     unsigned newpos;
  1298.     TBUFF *buf = 0;
  1299.  
  1300.     miniedit = FALSE;
  1301.     last_eolchar = EOS;    /* ...in case we don't set it elsewhere */
  1302.     tb_unput(*extbuf);    /* FIXME: trim null */
  1303.  
  1304.     if (clexec) {
  1305.         execstr = token(execstr, tbreserve(extbuf), eolchar);
  1306.         StrToBuff(*extbuf); /* FIXME: token should use TBUFF */
  1307.         status = (tb_length(*extbuf) != 0);
  1308.         if (status) { /* i.e. we got some input */
  1309. #if !SMALLER
  1310.             /* FIXME: may have nulls */
  1311.             if ((options & KBD_LOWERC))
  1312.                 (void)mklower(tb_values(*extbuf));
  1313.             else if ((options & KBD_UPPERC))
  1314.                 (void)mkupper(tb_values(*extbuf));
  1315. #endif
  1316.             if (!(options & KBD_NOEVAL)) {
  1317.                 buf = tb_copy(&buf, *extbuf);
  1318.                 tb_append(&buf, EOS); /* FIXME: for tokval() */
  1319.                 (void)tb_scopy(extbuf,
  1320.                     tokval(tb_values(buf)));
  1321.                 tb_free(&buf);
  1322.                 tb_unput(*extbuf); /* trim the null */
  1323.             }
  1324.             if (complete != no_completion
  1325.              && !editingShellCmd(tb_values(*extbuf),options)) {
  1326.                 cpos =
  1327.                 newpos = tb_length(*extbuf);
  1328.                 if ((*complete)(NAMEC, tbreserve(extbuf), &newpos)) {
  1329.                     StrToBuff(*extbuf);
  1330.                 } else {
  1331.                     status = ABORT;
  1332.                     tb_put(extbuf, cpos, EOS);
  1333.                 }
  1334.             }
  1335.             /*
  1336.              * Splice for multi-part command
  1337.              */
  1338.             if ((last_eolchar = *execstr) != EOS)
  1339.                 last_eolchar = ' ';
  1340.         }
  1341.         if (pushed_back && (*execstr == EOS)) {
  1342.             pushed_back = FALSE;
  1343.             clexec  = pushback_flg;
  1344.             execstr = pushback_ptr;
  1345. #if    OPT_HISTORY
  1346.             if (!pushback_flg) {
  1347.                 hst_append(*extbuf, EOS);
  1348.             }
  1349. #endif
  1350.         }
  1351.         tb_append(extbuf, EOS);    /* FIXME */
  1352.         return status;
  1353.     }
  1354.  
  1355.     quotef = FALSE;
  1356.     reading_msg_line = TRUE;
  1357.  
  1358.     /* prompt the user for the input string */
  1359.     if (prompt != 0)
  1360.         mlprompt("%s", prompt);
  1361.  
  1362.     /*
  1363.      * Use the length of the prompt (plus whatever led up to it) as the
  1364.      * left-margin for editing.  We'll automatically scroll the edited
  1365.      * field left/right within the margins.
  1366.      */
  1367.     margin = llength(wminip->w_dot.l);
  1368.     cpos = kbd_show_response(&buf, tb_values(*extbuf), tb_length(*extbuf), eolchar, options);
  1369.     backslashes = 0; /* by definition, there is an even
  1370.                     number of backslashes */
  1371.     c = -1;            /* initialize 'lastch' */
  1372.  
  1373.     for_ever {
  1374.         int    EscOrQuo = (quotef ||
  1375.                 ((backslashes & 1) != 0));
  1376.  
  1377.         lastch = c;
  1378.  
  1379.         /*
  1380.          * Get a character from the user. If not quoted, treat escape
  1381.          * sequences as a single (16-bit) special character.
  1382.          */
  1383.         if (quotef)
  1384.             c = keystroke_raw8();
  1385.         else if (dontmap)
  1386.             /* this looks wrong, but isn't.  no mapping will happen
  1387.             anyway, since we're on the command line.  we want SPEC
  1388.             keys to be expanded to #c, but no further.  this does
  1389.             that */
  1390.             c = mapped_keystroke_raw();
  1391.         else
  1392.             c = keystroke();
  1393.  
  1394.         /* Ignore nulls if the calling function is not prepared to
  1395.          * process them.  We want to be able to search for nulls, but
  1396.          * must not use them in filenames, for example.  (We don't
  1397.          * support name-completion with embedded nulls, either).
  1398.          */
  1399.         if (c == 0 && !(options & KBD_0CHAR))
  1400.             continue;
  1401.  
  1402.         /* if we echoed ^V, erase it now */
  1403.         if (quotef) {
  1404.             firstch = FALSE;
  1405.             kbd_erase();
  1406.             kbd_flush();
  1407.         }
  1408.  
  1409.         /* If it is a <ret>, change it to a <NL> */
  1410.         if (c == '\r' && !quotef)
  1411.             c = '\n';
  1412.  
  1413.         /*
  1414.          * If they hit the line terminate (i.e., newline or unescaped
  1415.          * eolchar), wrap it up.
  1416.          *
  1417.          * Don't allow newlines in the string -- they cause real
  1418.          * problems, especially when searching for patterns
  1419.          * containing them -pgf
  1420.          */
  1421.         done = FALSE;
  1422.         if (c == '\n') {
  1423.             done = TRUE;
  1424.         } else if (!EscOrQuo
  1425.             && !is_edit_char(c)
  1426.             && !isMiniEdit(c)) {
  1427.             BuffToStr(buf);
  1428.             if ((*endfunc)(tb_values(buf),tb_length(buf),c,eolchar)) {
  1429.                 done = TRUE;
  1430.             }
  1431.         }
  1432.  
  1433.         if (complete != no_completion) {
  1434.             kbd_unquery();
  1435.             shell = editingShellCmd(tb_values(buf),options);
  1436.             if (done && (options & KBD_NULLOK) && cpos == 0)
  1437.                 /*EMPTY*/;
  1438.             else if ((done && !(options & KBD_MAYBEC))
  1439.              || (!EscOrQuo
  1440.               && !(shell && isPrint(c))
  1441.               && (c == TESTC || c == NAMEC))) {
  1442.                 if (shell && isreturn(c)) {
  1443.                     /*EMPTY*/;
  1444.                 } else if ((*complete)(c, tbreserve(&buf), &cpos)) {
  1445.                     done = TRUE;
  1446.                     StrToBuff(buf); /* FIXME */
  1447.                     if (c != NAMEC) /* cancel the unget */
  1448.                         (void)keystroke();
  1449.                 } else {
  1450.                     StrToBuff(buf); /* FIXME */
  1451.                     if (done) {    /* stay til matched! */
  1452.                         kbd_unquery();
  1453.                         (void)((*complete)(TESTC, tbreserve(&buf), &cpos));
  1454.                     }
  1455.                     firstch = FALSE;
  1456.                     continue;
  1457.                 }
  1458.             }
  1459.         }
  1460.  
  1461.         if (done) {
  1462.             last_eolchar = c;
  1463.             if (options & KBD_QUOTES)
  1464.                 remove_backslashes(buf); /* take out quoters */
  1465.  
  1466.             save_len = tb_length(buf);
  1467.             hst_append(buf, eolchar);
  1468.             (void)tb_copy(extbuf, buf);
  1469.             status = (tb_length(*extbuf) != 0);
  1470.  
  1471.             /* If this is a shell command, push-back the actual
  1472.              * text to separate the "!" from the command.  Note
  1473.              * that the history command tries to do this already,
  1474.              * but cannot for some special cases, e.g., the user
  1475.              * types
  1476.              *    :execute-named-command !ls -l
  1477.              * which is equivalent to
  1478.              *    :!ls -l
  1479.              */
  1480.             if (status == TRUE    /* ...we have some text */
  1481.              && (options & KBD_EXPCMD)
  1482. #if    OPT_HISTORY
  1483.              && (tb_length(buf) == save_len) /* history didn't split it */
  1484. #endif
  1485.              && isShellOrPipe(tb_values(buf))) {
  1486.                 kbd_pushback(*extbuf, 1);
  1487.             }
  1488.             break;
  1489.         }
  1490.  
  1491. #if    OPT_HISTORY
  1492.         if (!EscOrQuo
  1493.          && edithistory(&buf, &cpos, &c, options, endfunc, eolchar)) {
  1494.             backslashes = countBackSlashes(buf, cpos);
  1495.             firstch = TRUE;
  1496.             continue;
  1497.         } else
  1498. #endif
  1499.         /*
  1500.          * If editc and abortc are the same, don't abort unless the
  1501.          * user presses it twice in a row.
  1502.          */
  1503.         if ((c != editc || c == lastch)
  1504.           && ABORTED(c)
  1505.           && !quotef
  1506.           && !dontmap) {
  1507.             tb_init(&buf, abortc);
  1508.             status = esc_func(FALSE, 1);
  1509.             break;
  1510.         } else if ((isbackspace(c) ||
  1511.             c == wkillc ||
  1512.             c == killc) && !quotef) {
  1513.  
  1514.             if (prompt == 0 && c == killc)
  1515.                 cpos = 0;
  1516.  
  1517.             if (cpos == 0) {
  1518.                 if (prompt)
  1519.                     mlerase();
  1520.                 if (isbackspace(c)) {    /* splice calls */
  1521.                     unkeystroke(c);
  1522.                     status = SORTOFTRUE;
  1523.                 } else {
  1524.                     status = FALSE;
  1525.                 }
  1526.                 break;
  1527.             }
  1528.  
  1529.             kbd_kill_response(buf, &cpos, c);
  1530.  
  1531.             /*
  1532.              * If we backspaced to erase the buffer, it's ok to
  1533.              * return to the caller, who would be the :-line
  1534.              * parser, so we can do something reasonable with the
  1535.              * address specication.
  1536.              */
  1537.             if (cpos == 0
  1538.              && isbackspace(c)
  1539.              && (options & KBD_STATED)) {
  1540.                 status = SORTOFTRUE;
  1541.                 break;
  1542.             }
  1543.             backslashes = countBackSlashes(buf, cpos);
  1544.  
  1545.         } else if (firstch == TRUE
  1546.           && !quotef
  1547.           && !isMiniEdit(c)
  1548.           && !isspecial(c)) {
  1549.             /* clean the buffer on the first char typed */
  1550.             unkeystroke(c);
  1551.             kbd_kill_response(buf, &cpos, killc);
  1552.             backslashes = countBackSlashes(buf, cpos);
  1553.  
  1554.         } else if (c == quotec && !quotef) {
  1555.             quotef = TRUE;
  1556.             show1Char(c);
  1557.             continue;    /* keep firstch==TRUE */
  1558.  
  1559.         } else if (EscOrQuo || !expandChar(&buf, &cpos, c, options)) {
  1560.             if (c == BACKSLASH)
  1561.                 backslashes++;
  1562.             else
  1563.                 backslashes = 0;
  1564.             quotef = FALSE;
  1565.  
  1566. #if !SMALLER
  1567.             if (!isspecial(c)) {
  1568.                 if ((options & KBD_LOWERC)
  1569.                  && isUpper(c))
  1570.                     c = toLower(c);
  1571.                 else if ((options & KBD_UPPERC)
  1572.                  && isLower(c))
  1573.                     c = toUpper(c);
  1574.             }
  1575. #endif
  1576.             if (!editMinibuffer(&buf, &cpos, c, margin, EscOrQuo))
  1577.                 continue;    /* keep firstch==TRUE */
  1578.         }
  1579.         firstch = FALSE;
  1580.     }
  1581. #if OPT_POPUPCHOICE
  1582.     popdown_completions();
  1583. #endif
  1584.     miniedit = FALSE;
  1585.     reading_msg_line = FALSE;
  1586.     tb_free(&buf);
  1587.     shiftMiniBuffer(0);
  1588.  
  1589.     TRACE(("reply:%d:%d:%s\n", status,
  1590.         (int) tb_length(*extbuf),
  1591.         tb_visible(*extbuf)))
  1592.     tb_append(extbuf, EOS);    /* FIXME */
  1593.     return status;
  1594. }
  1595.  
  1596. /*
  1597.  * Make the "." replay the keyboard macro
  1598.  */
  1599. #ifdef GMDDOTMACRO
  1600. static void
  1601. dot_replays_macro(int macnum)
  1602. {
  1603.     extern    const    CMDFUNC    f_kbd_mac_exec;
  1604.     char    temp[NSTRING];
  1605.     ITBUFF    *tmp;
  1606.     int    c;
  1607.  
  1608.     if (macnum == DEFAULT_REG) {
  1609.         if ((c = fnc2kcod(&f_kbd_mac_exec)) == -1)
  1610.             return;
  1611.         (void)kcod2str(c, temp);
  1612.     } else {
  1613.         (void)lsprintf(temp, "@%c", index2reg(macnum));
  1614.     }
  1615.     dotcmdbegin();
  1616.     tmp = TempDot(FALSE);
  1617.     (void)itb_sappend(&tmp, temp);
  1618.     dotcmdfinish();
  1619.     dotcmdbegin();
  1620. }
  1621. #endif
  1622.  
  1623. /*
  1624.  * Begin recording the dot command macro.
  1625.  * Set up variables and return.
  1626.  * we use a temporary accumulator, in case this gets stopped prematurely
  1627.  */
  1628. int
  1629. dotcmdbegin(void)
  1630. {
  1631.     /* never record a playback */
  1632.     if (dotcmdmode != PLAY) {
  1633.         (void)TempDot(TRUE);
  1634.         dotcmdmode = RECORD;
  1635.         return TRUE;
  1636.     }
  1637.     return FALSE;
  1638. }
  1639.  
  1640. /*
  1641.  * Finish dot command, and copy it to the real holding area
  1642.  */
  1643. int
  1644. dotcmdfinish(void)
  1645. {
  1646.     if (dotcmdmode == RECORD) {
  1647.         ITBUFF    *tmp = TempDot(FALSE);
  1648.         if (itb_length(tmp) == 0    /* keep the old value */
  1649.          || itb_copy(&dotcmd, tmp) != 0) {
  1650.             itb_first(dotcmd);
  1651.             dotcmdmode = STOP;
  1652.             return TRUE;
  1653.         }
  1654.     }
  1655.     return FALSE;
  1656. }
  1657.  
  1658.  
  1659. /* stop recording a dot command,
  1660.     probably because the command is not re-doable */
  1661. void
  1662. dotcmdstop(void)
  1663. {
  1664.     if (dotcmdmode == RECORD)
  1665.         dotcmdmode = STOP;
  1666. }
  1667.  
  1668. /*
  1669.  * Execute the '.' command, by putting us in PLAY mode.
  1670.  * The command argument is the number of times to loop. Quit as soon as a
  1671.  * command gets an error. Return TRUE if all ok, else FALSE.
  1672.  */
  1673. int
  1674. dotcmdplay(int f, int n)
  1675. {
  1676.     if (!f)
  1677.         n = dotcmdarg ? dotcmdcnt:1;
  1678.     else if (n < 0)
  1679.         return TRUE;
  1680.  
  1681.     if (f)    /* we definitely have an argument */
  1682.         dotcmdarg = TRUE;
  1683.     /* else
  1684.         leave dotcmdarg alone; */
  1685.  
  1686.     if (dotcmdmode != STOP || itb_length(dotcmd) == 0) {
  1687.         dotcmdmode = STOP;
  1688.         dotcmdarg = FALSE;
  1689.         return FALSE;
  1690.     }
  1691.  
  1692.     if (n == 0) n = 1;
  1693.  
  1694.     dotcmdcnt = dotcmdrep = n;  /* remember how many times to execute */
  1695.     dotcmdmode = PLAY;    /* put us in play mode */
  1696.     itb_first(dotcmd);    /*    at the beginning */
  1697.  
  1698.     if (ukb != 0) /* save our kreg, if one was specified */
  1699.         dotcmdkreg = ukb;
  1700.     else /* use our old one, if it wasn't */
  1701.         ukb = dotcmdkreg;
  1702.  
  1703.     return TRUE;
  1704. }
  1705.  
  1706. /*
  1707.  * Test if we are replaying either '.' command, or keyboard macro.
  1708.  */
  1709. int
  1710. kbd_replaying(int match)
  1711. {
  1712.     if (dotcmdmode == PLAY) {
  1713.         /*
  1714.          * Force a false-return if we are in insert-mode and have
  1715.          * only one character to display.
  1716.          */
  1717.         if (match
  1718.          && insertmode == INSERT
  1719.          && b_val(curbp, MDSHOWMAT)
  1720.          && KbdStack == 0
  1721.          && (dotcmd->itb_last+1 >= dotcmd->itb_used)) {
  1722.             return FALSE;
  1723.         }
  1724.         return TRUE;
  1725.     }
  1726.     return (kbdmode == PLAY);
  1727. }
  1728.  
  1729. /*
  1730.  * Begin recording a keyboard macro.
  1731.  */
  1732. /* ARGSUSED */
  1733. int
  1734. kbd_mac_begin(int f GCC_UNUSED, int n GCC_UNUSED)
  1735. {
  1736.     if (kbdmode != STOP) {
  1737.         mlforce("[Macro already active]");
  1738.         return FALSE;
  1739.     }
  1740.     mlwrite("[Start macro]");
  1741.  
  1742.     kbdmode = RECORD;
  1743.     return (itb_init(&KbdMacro, abortc) != 0);
  1744. }
  1745.  
  1746. /*
  1747.  * End keyboard macro. Check for the same limit conditions as the above
  1748.  * routine. Set up the variables and return to the caller.
  1749.  */
  1750. /* ARGSUSED */
  1751. int
  1752. kbd_mac_end(int f GCC_UNUSED, int n GCC_UNUSED)
  1753. {
  1754.     if (kbdmode == STOP) {
  1755.         mlforce("[Macro not active]");
  1756.         return FALSE;
  1757.     }
  1758.     if (kbdmode == RECORD) {
  1759.         mlwrite("[End macro]");
  1760.         kbdmode = STOP;
  1761. #ifdef GMDDOTMACRO
  1762.         dot_replays_macro(DEFAULT_REG);
  1763. #endif
  1764.     }
  1765.     /* note that if kbd_mode == PLAY, we do nothing -- that makes
  1766.         the '^X-)' at the of the recorded buffer a no-op during
  1767.         playback */
  1768.     return TRUE;
  1769. }
  1770.  
  1771. /*
  1772.  * Execute a macro.
  1773.  * The command argument is the number of times to loop. Quit as soon as a
  1774.  * command gets an error. Return TRUE if all ok, else FALSE.
  1775.  */
  1776. /* ARGSUSED */
  1777. int
  1778. kbd_mac_exec(int f GCC_UNUSED, int n)
  1779. {
  1780.     if (kbdmode != STOP) {
  1781.         mlforce("[Can't execute macro while recording]");
  1782.         return FALSE;
  1783.     }
  1784.  
  1785.     if (n <= 0)
  1786.         return TRUE;
  1787.  
  1788.     return start_kbm(n, DEFAULT_REG, KbdMacro);
  1789. }
  1790.  
  1791. /* ARGSUSED */
  1792. int
  1793. kbd_mac_save(int f GCC_UNUSED, int n GCC_UNUSED)
  1794. {
  1795.     ksetup();
  1796.     itb_first(KbdMacro);
  1797.     while (itb_more(KbdMacro))
  1798.         if (!kinsert(itb_next(KbdMacro)))
  1799.             break;
  1800.     kdone();
  1801.     mlwrite("[Keyboard macro saved in register %c.]", index2reg(ukb));
  1802.     return TRUE;
  1803. }
  1804.  
  1805. /*
  1806.  * Test if the given macro has already been started.
  1807.  */
  1808. int
  1809. kbm_started(int macnum, int force)
  1810. {
  1811.     if (force || (kbdmode == PLAY)) {
  1812.         register KSTACK *sp;
  1813.         for (sp = KbdStack; sp != 0; sp = sp->m_link) {
  1814.             if (sp->m_indx == macnum) {
  1815.                 while (kbdmode == PLAY)
  1816.                     finish_kbm();
  1817.                 mlwarn("[Error: currently executing %s%c]",
  1818.                     macnum == DEFAULT_REG
  1819.                         ? "macro" : "register ",
  1820.                     index2reg(macnum));
  1821.                 return TRUE;
  1822.             }
  1823.         }
  1824.     }
  1825.     return FALSE;
  1826. }
  1827.  
  1828. /*
  1829.  * Start playback of the given keyboard command-string
  1830.  */
  1831. int
  1832. start_kbm(
  1833. int    n,            /* # of times to repeat */
  1834. int    macnum,            /* register to execute */
  1835. ITBUFF *ptr)            /* data to interpret */
  1836. {
  1837.     register KSTACK *sp = 0;
  1838.     ITBUFF  *tp = 0;
  1839.  
  1840.     if (interrupted())
  1841.         return FALSE;
  1842.  
  1843.     if (kbdmode == RECORD && KbdStack != 0)
  1844.         return TRUE;
  1845.  
  1846.     if (itb_length(ptr)
  1847.      && (sp = typealloc(KSTACK)) != 0
  1848.      && itb_copy(&tp, ptr) != 0) {
  1849.  
  1850.         /* make a copy of the macro in case recursion alters it */
  1851.         itb_first(tp);
  1852.  
  1853.         sp->m_save = kbdmode;
  1854.         sp->m_indx = macnum;
  1855.         sp->m_rept = n;
  1856.         sp->m_kbdm = tp;
  1857.         sp->m_link = KbdStack;
  1858.  
  1859.         KbdStack   = sp;
  1860.         kbdmode    = PLAY;     /* start us in play mode */
  1861.  
  1862.         /* save data for "." on the same stack */
  1863.         sp->m_dots = 0;
  1864.         if (dotcmdmode == PLAY) {
  1865. #ifdef GMDDOTMACRO
  1866.             sp->m_DOTS = dotcmd;
  1867.             sp->m_RPT0 = dotcmdcnt;
  1868.             sp->m_RPT1 = dotcmdrep;
  1869. #endif
  1870.             dotcmd     = 0;
  1871.             dotcmdmode = RECORD;
  1872.         }
  1873. #ifdef GMDDOTMACRO
  1874.           else {
  1875.             sp->m_DOTS = 0;
  1876.           }
  1877. #endif
  1878.         return (itb_init(&dotcmd, abortc) != 0
  1879.           &&    itb_init(&(sp->m_dots), abortc) != 0);
  1880.     }
  1881.     return FALSE;
  1882. }
  1883.  
  1884. /*
  1885.  * Finish a macro begun via 'start_kbm()'
  1886.  */
  1887. static void
  1888. finish_kbm(void)
  1889. {
  1890.     if (kbdmode == PLAY) {
  1891.         register KSTACK *sp = KbdStack;
  1892.  
  1893.         kbdmode = STOP;
  1894.         if (sp != 0) {
  1895.             kbdmode  = sp->m_save;
  1896.             KbdStack = sp->m_link;
  1897.  
  1898.             itb_free(&(sp->m_kbdm));
  1899.             itb_free(&(sp->m_dots));
  1900. #ifdef GMDDOTMACRO
  1901.             itb_free(&dotcmd);
  1902.             if (sp->m_DOTS != 0) {
  1903.                 dotcmd     = sp->m_DOTS;
  1904.                 dotcmdcnt  = sp->m_RPT0;
  1905.                 dotcmdrep  = sp->m_RPT1;
  1906.                 dotcmdmode = PLAY;
  1907.             }
  1908.             dot_replays_macro(sp->m_indx);
  1909. #endif
  1910.             free((char *)sp);
  1911.         }
  1912.     }
  1913. }
  1914.