home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / command.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  30KB  |  1,608 lines

  1. /*
  2.  * Copyright (C) 1984-2000  Mark Nudelman
  3.  *
  4.  * You may distribute under the terms of either the GNU General Public
  5.  * License or the Less License, as specified in the README file.
  6.  *
  7.  * For more information about less, or for information on how to 
  8.  * contact the author, see the README file.
  9.  */
  10.  
  11.  
  12. /*
  13.  * User-level command processor.
  14.  */
  15.  
  16. #include "less.h"
  17. #if MSDOS_COMPILER==WIN32C
  18. #include <windows.h>
  19. #endif
  20. #include "position.h"
  21. #include "option.h"
  22. #include "cmd.h"
  23.  
  24. extern int erase_char, kill_char;
  25. extern int sigs;
  26. extern int quit_at_eof;
  27. extern int quit_if_one_screen;
  28. extern int squished;
  29. extern int hit_eof;
  30. extern int sc_width;
  31. extern int sc_height;
  32. extern int swindow;
  33. extern int jump_sline;
  34. extern int quitting;
  35. extern int wscroll;
  36. extern int top_scroll;
  37. extern int ignore_eoi;
  38. extern int secure;
  39. extern int hshift;
  40. extern int show_attn;
  41. extern char *every_first_cmd;
  42. extern char *curr_altfilename;
  43. extern char version[];
  44. extern struct scrpos initial_scrpos;
  45. extern IFILE curr_ifile;
  46. extern void constant *ml_search;
  47. extern void constant *ml_examine;
  48. #if SHELL_ESCAPE || PIPEC
  49. extern void constant *ml_shell;
  50. #endif
  51. #if EDITOR
  52. extern char *editor;
  53. extern char *editproto;
  54. #endif
  55. extern int screen_trashed;    /* The screen has been overwritten */
  56. extern int shift_count;
  57.  
  58. static char ungot[UNGOT_SIZE];
  59. static char *ungotp = NULL;
  60. #if SHELL_ESCAPE
  61. static char *shellcmd = NULL;    /* For holding last shell command for "!!" */
  62. #endif
  63. static int mca;            /* The multicharacter command (action) */
  64. static int search_type;        /* The previous type of search */
  65. static int number;        /* The number typed by the user */
  66. static char optchar;
  67. static int optflag;
  68. static int optgetname;
  69. static POSITION bottompos;
  70. #if PIPEC
  71. static char pipec;
  72. #endif
  73.  
  74. static void multi_search();
  75.  
  76. /*
  77.  * Move the cursor to lower left before executing a command.
  78.  * This looks nicer if the command takes a long time before
  79.  * updating the screen.
  80.  */
  81.     static void
  82. cmd_exec()
  83. {
  84.     clear_attn();
  85.     lower_left();
  86.     flush();
  87. }
  88.  
  89. /*
  90.  * Set up the display to start a new multi-character command.
  91.  */
  92.     static void
  93. start_mca(action, prompt, mlist, cmdflags)
  94.     int action;
  95.     char *prompt;
  96.     void *mlist;
  97.     int cmdflags;
  98. {
  99.     mca = action;
  100.     clear_cmd();
  101.     cmd_putstr(prompt);
  102.     set_mlist(mlist, cmdflags);
  103. }
  104.  
  105.     public int
  106. in_mca()
  107. {
  108.     return (mca != 0 && mca != A_PREFIX);
  109. }
  110.  
  111. /*
  112.  * Set up the display to start a new search command.
  113.  */
  114.     static void
  115. mca_search()
  116. {
  117.     if (search_type & SRCH_FORW)
  118.         mca = A_F_SEARCH;
  119.     else
  120.         mca = A_B_SEARCH;
  121.  
  122.     clear_cmd();
  123.  
  124.     if (search_type & SRCH_NO_MATCH)
  125.         cmd_putstr("Non-match ");
  126.     if (search_type & SRCH_FIRST_FILE)
  127.         cmd_putstr("First-file ");
  128.     if (search_type & SRCH_PAST_EOF)
  129.         cmd_putstr("EOF-ignore ");
  130.     if (search_type & SRCH_NO_MOVE)
  131.         cmd_putstr("Keep-pos ");
  132.     if (search_type & SRCH_NO_REGEX)
  133.         cmd_putstr("Regex-off ");
  134.  
  135.     if (search_type & SRCH_FORW)
  136.         cmd_putstr("/");
  137.     else
  138.         cmd_putstr("?");
  139.     set_mlist(ml_search, 0);
  140. }
  141.  
  142. /*
  143.  * Set up the display to start a new toggle-option command.
  144.  */
  145.     static void
  146. mca_opt_toggle()
  147. {
  148.     int no_prompt;
  149.     int flag;
  150.     char *dash;
  151.     
  152.     no_prompt = (optflag & OPT_NO_PROMPT);
  153.     flag = (optflag & ~OPT_NO_PROMPT);
  154.     dash = (flag == OPT_NO_TOGGLE) ? "_" : "-";
  155.  
  156.     mca = A_OPT_TOGGLE;
  157.     clear_cmd();
  158.     cmd_putstr(dash);
  159.     if (optgetname)
  160.         cmd_putstr(dash);
  161.     if (no_prompt)
  162.         cmd_putstr("(P)");
  163.     switch (flag)
  164.     {
  165.     case OPT_UNSET:
  166.         cmd_putstr("+");
  167.         break;
  168.     case OPT_SET:
  169.         cmd_putstr("!");
  170.         break;
  171.     }
  172.     set_mlist(NULL, 0);
  173. }
  174.  
  175. /*
  176.  * Execute a multicharacter command.
  177.  */
  178.     static void
  179. exec_mca()
  180. {
  181.     register char *cbuf;
  182.  
  183.     cmd_exec();
  184.     cbuf = get_cmdbuf();
  185.  
  186.     switch (mca)
  187.     {
  188.     case A_F_SEARCH:
  189.     case A_B_SEARCH:
  190.         multi_search(cbuf, number);
  191.         break;
  192.     case A_FIRSTCMD:
  193.         /*
  194.          * Skip leading spaces or + signs in the string.
  195.          */
  196.         while (*cbuf == '+' || *cbuf == ' ')
  197.             cbuf++;
  198.         if (every_first_cmd != NULL)
  199.             free(every_first_cmd);
  200.         if (*cbuf == '\0')
  201.             every_first_cmd = NULL;
  202.         else
  203.             every_first_cmd = save(cbuf);
  204.         break;
  205.     case A_OPT_TOGGLE:
  206.         toggle_option(optchar, cbuf, optflag);
  207.         optchar = '\0';
  208.         break;
  209.     case A_F_BRACKET:
  210.         match_brac(cbuf[0], cbuf[1], 1, number);
  211.         break;
  212.     case A_B_BRACKET:
  213.         match_brac(cbuf[1], cbuf[0], 0, number);
  214.         break;
  215. #if EXAMINE
  216.     case A_EXAMINE:
  217.         if (secure)
  218.             break;
  219.         edit_list(cbuf);
  220.         /* If tag structure is loaded then clean it up. */
  221.         cleantags();
  222.         break;
  223. #endif
  224. #if SHELL_ESCAPE
  225.     case A_SHELL:
  226.         /*
  227.          * !! just uses whatever is in shellcmd.
  228.          * Otherwise, copy cmdbuf to shellcmd,
  229.          * expanding any special characters ("%" or "#").
  230.          */
  231.         if (*cbuf != '!')
  232.         {
  233.             if (shellcmd != NULL)
  234.                 free(shellcmd);
  235.             shellcmd = fexpand(cbuf);
  236.         }
  237.  
  238.         if (secure)
  239.             break;
  240.         if (shellcmd == NULL)
  241.             lsystem("", "!done");
  242.         else
  243.             lsystem(shellcmd, "!done");
  244.         break;
  245. #endif
  246. #if PIPEC
  247.     case A_PIPE:
  248.         if (secure)
  249.             break;
  250.         (void) pipe_mark(pipec, cbuf);
  251.         error("|done", NULL_PARG);
  252.         break;
  253. #endif
  254.     }
  255. }
  256.  
  257. /*
  258.  * Add a character to a multi-character command.
  259.  */
  260.     static int
  261. mca_char(c)
  262.     int c;
  263. {
  264.     char *p;
  265.     int flag;
  266.     char buf[3];
  267.     PARG parg;
  268.  
  269.     switch (mca)
  270.     {
  271.     case 0:
  272.         /*
  273.          * Not in a multicharacter command.
  274.          */
  275.         return (NO_MCA);
  276.  
  277.     case A_PREFIX:
  278.         /*
  279.          * In the prefix of a command.
  280.          * This not considered a multichar command
  281.          * (even tho it uses cmdbuf, etc.).
  282.          * It is handled in the commands() switch.
  283.          */
  284.         return (NO_MCA);
  285.  
  286.     case A_DIGIT:
  287.         /*
  288.          * Entering digits of a number.
  289.          * Terminated by a non-digit.
  290.          */
  291.         if ((c < '0' || c > '9') && 
  292.           editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID)
  293.         {
  294.             /*
  295.              * Not part of the number.
  296.              * Treat as a normal command character.
  297.              */
  298.             number = cmd_int();
  299.             mca = 0;
  300.             cmd_accept();
  301.             return (NO_MCA);
  302.         }
  303.         break;
  304.  
  305.     case A_OPT_TOGGLE:
  306.         /*
  307.          * Special case for the TOGGLE_OPTION command.
  308.          * If the option letter which was entered is a
  309.          * single-char option, execute the command immediately,
  310.          * so user doesn't have to hit RETURN.
  311.          * If the first char is + or -, this indicates
  312.          * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
  313.          * "--" begins inputting a long option name.
  314.          */
  315.         if (optchar == '\0' && len_cmdbuf() == 0)
  316.         {
  317.             flag = (optflag & ~OPT_NO_PROMPT);
  318.             if (flag == OPT_NO_TOGGLE)
  319.             {
  320.                 switch (c)
  321.                 {
  322.                 case '_':
  323.                     /* "__" = long option name. */
  324.                     optgetname = TRUE;
  325.                     mca_opt_toggle();
  326.                     return (MCA_MORE);
  327.                 }
  328.             } else
  329.             {
  330.                 switch (c)
  331.                 {
  332.                 case '+':
  333.                     /* "-+" = UNSET. */
  334.                     optflag = (flag == OPT_UNSET) ?
  335.                         OPT_TOGGLE : OPT_UNSET;
  336.                     mca_opt_toggle();
  337.                     return (MCA_MORE);
  338.                 case '!':
  339.                     /* "-!" = SET */
  340.                     optflag = (flag == OPT_SET) ?
  341.                         OPT_TOGGLE : OPT_SET;
  342.                     mca_opt_toggle();
  343.                     return (MCA_MORE);
  344.                 case CONTROL('P'):
  345.                     optflag ^= OPT_NO_PROMPT;
  346.                     mca_opt_toggle();
  347.                     return (MCA_MORE);
  348.                 case '-':
  349.                     /* "--" = long option name. */
  350.                     optgetname = TRUE;
  351.                     mca_opt_toggle();
  352.                     return (MCA_MORE);
  353.                 }
  354.             }
  355.         }
  356.         if (optgetname)
  357.         {
  358.             /*
  359.              * We're getting a long option name.
  360.              * See if we've matched an option name yet.
  361.              * If so, display the complete name and stop 
  362.              * accepting chars until user hits RETURN.
  363.              */
  364.             struct option *o;
  365.             char *oname;
  366.             int lc;
  367.  
  368.             if (c == '\n' || c == '\r')
  369.             {
  370.                 /*
  371.                  * When the user hits RETURN, make sure
  372.                  * we've matched an option name, then
  373.                  * pretend he just entered the equivalent
  374.                  * option letter.
  375.                  */
  376.                 if (optchar == '\0')
  377.                 {
  378.                     parg.p_string = get_cmdbuf();
  379.                     error("There is no --%s option", &parg);
  380.                     return (MCA_DONE);
  381.                 }
  382.                 optgetname = FALSE;
  383.                 cmd_reset();
  384.                 c = optchar;
  385.             } else
  386.             {
  387.                 if (optchar != '\0')
  388.                 {
  389.                     /*
  390.                      * Already have a match for the name.
  391.                      * Don't accept anything but erase/kill.
  392.                      */
  393.                     if (c == erase_char || c == kill_char)
  394.                         return (MCA_DONE);
  395.                     return (MCA_MORE);
  396.                 }
  397.                 /*
  398.                  * Add char to cmd buffer and try to match
  399.                  * the option name.
  400.                  */
  401.                 if (cmd_char(c) == CC_QUIT)
  402.                     return (MCA_DONE);
  403.                 p = get_cmdbuf();
  404.                 lc = islower(p[0]);
  405.                 o = findopt_name(&p, &oname, NULL);
  406.                 if (o != NULL)
  407.                 {
  408.                     /*
  409.                      * Got a match.
  410.                      * Remember the option letter and
  411.                      * display the full option name.
  412.                      */
  413.                     optchar = o->oletter;
  414.                     if (!lc && islower(optchar))
  415.                         optchar = toupper(optchar);
  416.                     cmd_reset();
  417.                     mca_opt_toggle();
  418.                     for (p = oname;  *p != '\0';  p++)
  419.                     {
  420.                         c = *p;
  421.                         if (!lc && islower(c))
  422.                             c = toupper(c);
  423.                         if (cmd_char(c) != CC_OK)
  424.                             return (MCA_DONE);
  425.                     }
  426.                 }
  427.                 return (MCA_MORE);
  428.             }
  429.         } else
  430.         {
  431.             if (c == erase_char || c == kill_char)
  432.                 break;
  433.             if (optchar != '\0')
  434.                 /* We already have the option letter. */
  435.                 break;
  436.         }
  437.  
  438.         optchar = c;
  439.         if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE ||
  440.             single_char_option(c))
  441.         {
  442.             toggle_option(c, "", optflag);
  443.             return (MCA_DONE);
  444.         }
  445.         /*
  446.          * Display a prompt appropriate for the option letter.
  447.          */
  448.         if ((p = opt_prompt(c)) == NULL)
  449.         {
  450.             buf[0] = '-';
  451.             buf[1] = c;
  452.             buf[2] = '\0';
  453.             p = buf;
  454.         }
  455.         start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0);
  456.         return (MCA_MORE);
  457.  
  458.     case A_F_SEARCH:
  459.     case A_B_SEARCH:
  460.         /*
  461.          * Special case for search commands.
  462.          * Certain characters as the first char of 
  463.          * the pattern have special meaning:
  464.          *    !  Toggle the NO_MATCH flag
  465.          *    *  Toggle the PAST_EOF flag
  466.          *    @  Toggle the FIRST_FILE flag
  467.          */
  468.         if (len_cmdbuf() > 0)
  469.             /*
  470.              * Only works for the first char of the pattern.
  471.              */
  472.             break;
  473.  
  474.         flag = 0;
  475.         switch (c)
  476.         {
  477.         case CONTROL('E'): /* ignore END of file */
  478.         case '*':
  479.             flag = SRCH_PAST_EOF;
  480.             break;
  481.         case CONTROL('F'): /* FIRST file */
  482.         case '@':
  483.             flag = SRCH_FIRST_FILE;
  484.             break;
  485.         case CONTROL('K'): /* KEEP position */
  486.             flag = SRCH_NO_MOVE;
  487.             break;
  488.         case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */
  489.             flag = SRCH_NO_REGEX;
  490.             break;
  491.         case CONTROL('N'): /* NOT match */
  492.         case '!':
  493.             flag = SRCH_NO_MATCH;
  494.             break;
  495.         }
  496.         if (flag != 0)
  497.         {
  498.             search_type ^= flag;
  499.             mca_search();
  500.             return (MCA_MORE);
  501.         }
  502.         break;
  503.     }
  504.  
  505.     /*
  506.      * Any other multicharacter command
  507.      * is terminated by a newline.
  508.      */
  509.     if (c == '\n' || c == '\r')
  510.     {
  511.         /*
  512.          * Execute the command.
  513.          */
  514.         exec_mca();
  515.         return (MCA_DONE);
  516.     }
  517.  
  518.     /*
  519.      * Append the char to the command buffer.
  520.      */
  521.     if (cmd_char(c) == CC_QUIT)
  522.         /*
  523.          * Abort the multi-char command.
  524.          */
  525.         return (MCA_DONE);
  526.  
  527.     if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
  528.     {
  529.         /*
  530.          * Special case for the bracket-matching commands.
  531.          * Execute the command after getting exactly two
  532.          * characters from the user.
  533.          */
  534.         exec_mca();
  535.         return (MCA_DONE);
  536.     }
  537.  
  538.     /*
  539.      * Need another character.
  540.      */
  541.     return (MCA_MORE);
  542. }
  543.  
  544. /*
  545.  * Make sure the screen is displayed.
  546.  */
  547.     static void
  548. make_display()
  549. {
  550.     /*
  551.      * If nothing is displayed yet, display starting from initial_scrpos.
  552.      */
  553.     if (empty_screen())
  554.     {
  555.         if (initial_scrpos.pos == NULL_POSITION)
  556.             /*
  557.              * {{ Maybe this should be:
  558.              *    jump_loc(ch_zero(), jump_sline);
  559.              *    but this behavior seems rather unexpected 
  560.              *    on the first screen. }}
  561.              */
  562.             jump_loc(ch_zero(), 1);
  563.         else
  564.             jump_loc(initial_scrpos.pos, initial_scrpos.ln);
  565.     } else if (screen_trashed)
  566.     {
  567.         int save_top_scroll;
  568.         save_top_scroll = top_scroll;
  569.         top_scroll = 1;
  570.         repaint();
  571.         top_scroll = save_top_scroll;
  572.     }
  573. }
  574.  
  575. /*
  576.  * Display the appropriate prompt.
  577.  */
  578.     static void
  579. prompt()
  580. {
  581.     register char *p;
  582.  
  583.     if (ungotp != NULL && ungotp > ungot)
  584.     {
  585.         /*
  586.          * No prompt necessary if commands are from 
  587.          * ungotten chars rather than from the user.
  588.          */
  589.         return;
  590.     }
  591.  
  592.     /*
  593.      * Make sure the screen is displayed.
  594.      */
  595.     make_display();
  596.     bottompos = position(BOTTOM_PLUS_ONE);
  597.  
  598.     /*
  599.      * If the -E flag is set and we've hit EOF on the last file, quit.
  600.      */
  601.     if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) &&
  602.         hit_eof && !(ch_getflags() & CH_HELPFILE) && 
  603.         next_ifile(curr_ifile) == NULL_IFILE)
  604.         quit(QUIT_OK);
  605.     quit_if_one_screen = FALSE;
  606. #if 0 /* This doesn't work well because some "te"s clear the screen. */
  607.     /*
  608.      * If the -e flag is set and we've hit EOF on the last file,
  609.      * and the file is squished (shorter than the screen), quit.
  610.      */
  611.     if (quit_at_eof && squished &&
  612.         next_ifile(curr_ifile) == NULL_IFILE)
  613.         quit(QUIT_OK);
  614. #endif
  615.  
  616. #if MSDOS_COMPILER==WIN32C
  617.     /* 
  618.      * In Win32, display the file name in the window title.
  619.      */
  620.     if (!(ch_getflags() & CH_HELPFILE))
  621.         SetConsoleTitle(pr_expand("Less?f - %f.", 0));
  622. #endif
  623.     /*
  624.      * Select the proper prompt and display it.
  625.      */
  626.     clear_cmd();
  627.     p = pr_string();
  628.     if (p == NULL)
  629.         putchr(':');
  630.     else
  631.     {
  632.         so_enter();
  633.         putstr(p);
  634.         so_exit();
  635.     }
  636. }
  637.  
  638. /*
  639.  * Display the less version message.
  640.  */
  641.     public void
  642. dispversion()
  643. {
  644.     PARG parg;
  645.  
  646.     parg.p_string = version;
  647.     error("less %s", &parg);
  648. }
  649.  
  650. /*
  651.  * Get command character.
  652.  * The character normally comes from the keyboard,
  653.  * but may come from ungotten characters
  654.  * (characters previously given to ungetcc or ungetsc).
  655.  */
  656.     public int
  657. getcc()
  658. {
  659.     if (ungotp == NULL)
  660.         /*
  661.          * Normal case: no ungotten chars, so get one from the user.
  662.          */
  663.         return (getchr());
  664.  
  665.     if (ungotp > ungot)
  666.         /*
  667.          * Return the next ungotten char.
  668.          */
  669.         return (*--ungotp);
  670.  
  671.     /*
  672.      * We have just run out of ungotten chars.
  673.      */
  674.     ungotp = NULL;
  675.     if (len_cmdbuf() == 0 || !empty_screen())
  676.         return (getchr());
  677.     /*
  678.      * Command is incomplete, so try to complete it.
  679.      */
  680.     switch (mca)
  681.     {
  682.     case A_DIGIT:
  683.         /*
  684.          * We have a number but no command.  Treat as #g.
  685.          */
  686.         return ('g');
  687.  
  688.     case A_F_SEARCH:
  689.     case A_B_SEARCH:
  690.         /*
  691.          * We have "/string" but no newline.  Add the \n.
  692.          */
  693.         return ('\n'); 
  694.  
  695.     default:
  696.         /*
  697.          * Some other incomplete command.  Let user complete it.
  698.          */
  699.         return (getchr());
  700.     }
  701. }
  702.  
  703. /*
  704.  * "Unget" a command character.
  705.  * The next getcc() will return this character.
  706.  */
  707.     public void
  708. ungetcc(c)
  709.     int c;
  710. {
  711.     if (ungotp == NULL)
  712.         ungotp = ungot;
  713.     if (ungotp >= ungot + sizeof(ungot))
  714.     {
  715.         error("ungetcc overflow", NULL_PARG);
  716.         quit(QUIT_ERROR);
  717.     }
  718.     *ungotp++ = c;
  719. }
  720.  
  721. /*
  722.  * Unget a whole string of command characters.
  723.  * The next sequence of getcc()'s will return this string.
  724.  */
  725.     public void
  726. ungetsc(s)
  727.     char *s;
  728. {
  729.     register char *p;
  730.  
  731.     for (p = s + strlen(s) - 1;  p >= s;  p--)
  732.         ungetcc(*p);
  733. }
  734.  
  735. /*
  736.  * Search for a pattern, possibly in multiple files.
  737.  * If SRCH_FIRST_FILE is set, begin searching at the first file.
  738.  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
  739.  */
  740.     static void
  741. multi_search(pattern, n)
  742.     char *pattern;
  743.     int n;
  744. {
  745.     register int nomore;
  746.     IFILE save_ifile;
  747.     int changed_file;
  748.  
  749.     changed_file = 0;
  750.     save_ifile = save_curr_ifile();
  751.  
  752.     if (search_type & SRCH_FIRST_FILE)
  753.     {
  754.         /*
  755.          * Start at the first (or last) file 
  756.          * in the command line list.
  757.          */
  758.         if (search_type & SRCH_FORW)
  759.             nomore = edit_first();
  760.         else
  761.             nomore = edit_last();
  762.         if (nomore)
  763.         {
  764.             unsave_ifile(save_ifile);
  765.             return;
  766.         }
  767.         changed_file = 1;
  768.         search_type &= ~SRCH_FIRST_FILE;
  769.     }
  770.  
  771.     for (;;)
  772.     {
  773.         n = search(search_type, pattern, n);
  774.         /*
  775.          * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared
  776.          * after being used once.  This allows "n" to work after
  777.          * using a /@@ search.
  778.          */
  779.         search_type &= ~SRCH_NO_MOVE;
  780.         if (n == 0)
  781.         {
  782.             /*
  783.              * Found it.
  784.              */
  785.             unsave_ifile(save_ifile);
  786.             return;
  787.         }
  788.  
  789.         if (n < 0)
  790.             /*
  791.              * Some kind of error in the search.
  792.              * Error message has been printed by search().
  793.              */
  794.             break;
  795.  
  796.         if ((search_type & SRCH_PAST_EOF) == 0)
  797.             /*
  798.              * We didn't find a match, but we're
  799.              * supposed to search only one file.
  800.              */
  801.             break;
  802.         /*
  803.          * Move on to the next file.
  804.          */
  805.         if (search_type & SRCH_FORW)
  806.             nomore = edit_next(1);
  807.         else
  808.             nomore = edit_prev(1);
  809.         if (nomore)
  810.             break;
  811.         changed_file = 1;
  812.     }
  813.  
  814.     /*
  815.      * Didn't find it.
  816.      * Print an error message if we haven't already.
  817.      */
  818.     if (n > 0)
  819.         error("Pattern not found", NULL_PARG);
  820.  
  821.     if (changed_file)
  822.     {
  823.         /*
  824.          * Restore the file we were originally viewing.
  825.          */
  826.         reedit_ifile(save_ifile);
  827.     }
  828. }
  829.  
  830. /*
  831.  * Main command processor.
  832.  * Accept and execute commands until a quit command.
  833.  */
  834.     public void
  835. commands()
  836. {
  837.     register int c;
  838.     register int action;
  839.     register char *cbuf;
  840.     int newaction;
  841.     int save_search_type;
  842.     char *extra;
  843.     char tbuf[2];
  844.     PARG parg;
  845.     IFILE old_ifile;
  846.     IFILE new_ifile;
  847.     char *tagfile;
  848.  
  849.     search_type = SRCH_FORW;
  850.     wscroll = (sc_height + 1) / 2;
  851.     newaction = A_NOACTION;
  852.  
  853.     for (;;)
  854.     {
  855.         mca = 0;
  856.         cmd_accept();
  857.         number = 0;
  858.         optchar = '\0';
  859.  
  860.         /*
  861.          * See if any signals need processing.
  862.          */
  863.         if (sigs)
  864.         {
  865.             psignals();
  866.             if (quitting)
  867.                 quit(QUIT_SAVED_STATUS);
  868.         }
  869.  
  870.         /*
  871.          * See if window size changed, for systems that don't
  872.          * generate SIGWINCH.
  873.          */
  874.         check_winch();
  875.  
  876.         /*
  877.          * Display prompt and accept a character.
  878.          */
  879.         cmd_reset();
  880.         prompt();
  881.         if (sigs)
  882.             continue;
  883.         if (newaction == A_NOACTION)
  884.             c = getcc();
  885.  
  886.     again:
  887.         if (sigs)
  888.             continue;
  889.  
  890.         if (newaction != A_NOACTION)
  891.         {
  892.             action = newaction;
  893.             newaction = A_NOACTION;
  894.         } else
  895.         {
  896.             /*
  897.              * If we are in a multicharacter command, call mca_char.
  898.              * Otherwise we call fcmd_decode to determine the
  899.              * action to be performed.
  900.              */
  901.             if (mca)
  902.                 switch (mca_char(c))
  903.                 {
  904.                 case MCA_MORE:
  905.                     /*
  906.                      * Need another character.
  907.                      */
  908.                     c = getcc();
  909.                     goto again;
  910.                 case MCA_DONE:
  911.                     /*
  912.                      * Command has been handled by mca_char.
  913.                      * Start clean with a prompt.
  914.                      */
  915.                     continue;
  916.                 case NO_MCA:
  917.                     /*
  918.                      * Not a multi-char command
  919.                      * (at least, not anymore).
  920.                      */
  921.                     break;
  922.                 }
  923.  
  924.             /*
  925.              * Decode the command character and decide what to do.
  926.              */
  927.             if (mca)
  928.             {
  929.                 /*
  930.                  * We're in a multichar command.
  931.                  * Add the character to the command buffer
  932.                  * and display it on the screen.
  933.                  * If the user backspaces past the start 
  934.                  * of the line, abort the command.
  935.                  */
  936.                 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
  937.                     continue;
  938.                 cbuf = get_cmdbuf();
  939.             } else
  940.             {
  941.                 /*
  942.                  * Don't use cmd_char if we're starting fresh
  943.                  * at the beginning of a command, because we
  944.                  * don't want to echo the command until we know
  945.                  * it is a multichar command.  We also don't
  946.                  * want erase_char/kill_char to be treated
  947.                  * as line editing characters.
  948.                  */
  949.                 tbuf[0] = c;
  950.                 tbuf[1] = '\0';
  951.                 cbuf = tbuf;
  952.             }
  953.             extra = NULL;
  954.             action = fcmd_decode(cbuf, &extra);
  955.             /*
  956.              * If an "extra" string was returned,
  957.              * process it as a string of command characters.
  958.              */
  959.             if (extra != NULL)
  960.                 ungetsc(extra);
  961.         }
  962.         /*
  963.          * Clear the cmdbuf string.
  964.          * (But not if we're in the prefix of a command,
  965.          * because the partial command string is kept there.)
  966.          */
  967.         if (action != A_PREFIX)
  968.             cmd_reset();
  969.  
  970.         switch (action)
  971.         {
  972.         case A_DIGIT:
  973.             /*
  974.              * First digit of a number.
  975.              */
  976.             start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE);
  977.             goto again;
  978.  
  979.         case A_F_WINDOW:
  980.             /*
  981.              * Forward one window (and set the window size).
  982.              */
  983.             if (number > 0)
  984.                 swindow = number;
  985.             /* FALLTHRU */
  986.         case A_F_SCREEN:
  987.             /*
  988.              * Forward one screen.
  989.              */
  990.             if (number <= 0)
  991.                 number = get_swindow();
  992.             cmd_exec();
  993.             if (show_attn)
  994.                 set_attnpos(bottompos);
  995.             forward(number, 0, 1);
  996.             break;
  997.  
  998.         case A_B_WINDOW:
  999.             /*
  1000.              * Backward one window (and set the window size).
  1001.              */
  1002.             if (number > 0)
  1003.                 swindow = number;
  1004.             /* FALLTHRU */
  1005.         case A_B_SCREEN:
  1006.             /*
  1007.              * Backward one screen.
  1008.              */
  1009.             if (number <= 0)
  1010.                 number = get_swindow();
  1011.             cmd_exec();
  1012.             backward(number, 0, 1);
  1013.             break;
  1014.  
  1015.         case A_F_LINE:
  1016.             /*
  1017.              * Forward N (default 1) line.
  1018.              */
  1019.             if (number <= 0)
  1020.                 number = 1;
  1021.             cmd_exec();
  1022.             if (show_attn == OPT_ONPLUS && number > 1)
  1023.                 set_attnpos(bottompos);
  1024.             forward(number, 0, 0);
  1025.             break;
  1026.  
  1027.         case A_B_LINE:
  1028.             /*
  1029.              * Backward N (default 1) line.
  1030.              */
  1031.             if (number <= 0)
  1032.                 number = 1;
  1033.             cmd_exec();
  1034.             backward(number, 0, 0);
  1035.             break;
  1036.  
  1037.         case A_FF_LINE:
  1038.             /*
  1039.              * Force forward N (default 1) line.
  1040.              */
  1041.             if (number <= 0)
  1042.                 number = 1;
  1043.             cmd_exec();
  1044.             if (show_attn == OPT_ONPLUS && number > 1)
  1045.                 set_attnpos(bottompos);
  1046.             forward(number, 1, 0);
  1047.             break;
  1048.  
  1049.         case A_BF_LINE:
  1050.             /*
  1051.              * Force backward N (default 1) line.
  1052.              */
  1053.             if (number <= 0)
  1054.                 number = 1;
  1055.             cmd_exec();
  1056.             backward(number, 1, 0);
  1057.             break;
  1058.         
  1059.         case A_FF_SCREEN:
  1060.             /*
  1061.              * Force forward one screen.
  1062.              */
  1063.             if (number <= 0)
  1064.                 number = get_swindow();
  1065.             cmd_exec();
  1066.             if (show_attn == OPT_ONPLUS)
  1067.                 set_attnpos(bottompos);
  1068.             forward(number, 1, 0);
  1069.             break;
  1070.  
  1071.         case A_F_FOREVER:
  1072.             /*
  1073.              * Forward forever, ignoring EOF.
  1074.              */
  1075.             if (ch_getflags() & CH_HELPFILE)
  1076.                 break;
  1077.             cmd_exec();
  1078.             jump_forw();
  1079.             ignore_eoi = 1;
  1080.             hit_eof = 0;
  1081.             while (!sigs)
  1082.                 forward(1, 0, 0);
  1083.             ignore_eoi = 0;
  1084.             /*
  1085.              * This gets us back in "F mode" after processing 
  1086.              * a non-abort signal (e.g. window-change).  
  1087.              */
  1088.             if (sigs && !ABORT_SIGS())
  1089.                 newaction = A_F_FOREVER;
  1090.             break;
  1091.  
  1092.         case A_F_SCROLL:
  1093.             /*
  1094.              * Forward N lines 
  1095.              * (default same as last 'd' or 'u' command).
  1096.              */
  1097.             if (number > 0)
  1098.                 wscroll = number;
  1099.             cmd_exec();
  1100.             if (show_attn == OPT_ONPLUS)
  1101.                 set_attnpos(bottompos);
  1102.             forward(wscroll, 0, 0);
  1103.             break;
  1104.  
  1105.         case A_B_SCROLL:
  1106.             /*
  1107.              * Forward N lines 
  1108.              * (default same as last 'd' or 'u' command).
  1109.              */
  1110.             if (number > 0)
  1111.                 wscroll = number;
  1112.             cmd_exec();
  1113.             backward(wscroll, 0, 0);
  1114.             break;
  1115.  
  1116.         case A_FREPAINT:
  1117.             /*
  1118.              * Flush buffers, then repaint screen.
  1119.              * Don't flush the buffers on a pipe!
  1120.              */
  1121.             if (ch_getflags() & CH_CANSEEK)
  1122.             {
  1123.                 ch_flush();
  1124.                 clr_linenum();
  1125. #if HILITE_SEARCH
  1126.                 clr_hilite();
  1127. #endif
  1128.             }
  1129.             /* FALLTHRU */
  1130.         case A_REPAINT:
  1131.             /*
  1132.              * Repaint screen.
  1133.              */
  1134.             cmd_exec();
  1135.             repaint();
  1136.             break;
  1137.  
  1138.         case A_GOLINE:
  1139.             /*
  1140.              * Go to line N, default beginning of file.
  1141.              */
  1142.             if (number <= 0)
  1143.                 number = 1;
  1144.             cmd_exec();
  1145.             jump_back(number);
  1146.             break;
  1147.  
  1148.         case A_PERCENT:
  1149.             /*
  1150.              * Go to a specified percentage into the file.
  1151.              */
  1152.             if (number < 0)
  1153.                 number = 0;
  1154.             if (number > 100)
  1155.                 number = 100;
  1156.             cmd_exec();
  1157.             jump_percent(number);
  1158.             break;
  1159.  
  1160.         case A_GOEND:
  1161.             /*
  1162.              * Go to line N, default end of file.
  1163.              */
  1164.             cmd_exec();
  1165.             if (number <= 0)
  1166.                 jump_forw();
  1167.             else
  1168.                 jump_back(number);
  1169.             break;
  1170.  
  1171.         case A_GOPOS:
  1172.             /*
  1173.              * Go to a specified byte position in the file.
  1174.              */
  1175.             cmd_exec();
  1176.             if (number < 0)
  1177.                 number = 0;
  1178.             jump_line_loc((POSITION)number, jump_sline);
  1179.             break;
  1180.  
  1181.         case A_STAT:
  1182.             /*
  1183.              * Print file name, etc.
  1184.              */
  1185.             if (ch_getflags() & CH_HELPFILE)
  1186.                 break;
  1187.             cmd_exec();
  1188.             parg.p_string = eq_message();
  1189.             error("%s", &parg);
  1190.             break;
  1191.  
  1192.         case A_VERSION:
  1193.             /*
  1194.              * Print version number, without the "@(#)".
  1195.              */
  1196.             cmd_exec();
  1197.             dispversion();
  1198.             break;
  1199.  
  1200.         case A_QUIT:
  1201.             /*
  1202.              * Exit.
  1203.              */
  1204.             if (curr_ifile != NULL_IFILE && 
  1205.                 ch_getflags() & CH_HELPFILE)
  1206.             {
  1207.                 /*
  1208.                  * Quit while viewing the help file
  1209.                  * just means return to viewing the
  1210.                  * previous file.
  1211.                  */
  1212.                 if (edit_prev(1) == 0)
  1213.                     break;
  1214.             }
  1215.             if (extra != NULL)
  1216.                 quit(*extra);
  1217.             quit(QUIT_OK);
  1218.             break;
  1219.  
  1220. /*
  1221.  * Define abbreviation for a commonly used sequence below.
  1222.  */
  1223. #define    DO_SEARCH()    if (number <= 0) number = 1;    \
  1224.             mca_search();            \
  1225.             cmd_exec();            \
  1226.             multi_search((char *)NULL, number);
  1227.  
  1228.  
  1229.         case A_F_SEARCH:
  1230.             /*
  1231.              * Search forward for a pattern.
  1232.              * Get the first char of the pattern.
  1233.              */
  1234.             search_type = SRCH_FORW;
  1235.             if (number <= 0)
  1236.                 number = 1;
  1237.             mca_search();
  1238.             c = getcc();
  1239.             goto again;
  1240.  
  1241.         case A_B_SEARCH:
  1242.             /*
  1243.              * Search backward for a pattern.
  1244.              * Get the first char of the pattern.
  1245.              */
  1246.             search_type = SRCH_BACK;
  1247.             if (number <= 0)
  1248.                 number = 1;
  1249.             mca_search();
  1250.             c = getcc();
  1251.             goto again;
  1252.  
  1253.         case A_AGAIN_SEARCH:
  1254.             /*
  1255.              * Repeat previous search.
  1256.              */
  1257.             DO_SEARCH();
  1258.             break;
  1259.         
  1260.         case A_T_AGAIN_SEARCH:
  1261.             /*
  1262.              * Repeat previous search, multiple files.
  1263.              */
  1264.             search_type |= SRCH_PAST_EOF;
  1265.             DO_SEARCH();
  1266.             break;
  1267.  
  1268.         case A_REVERSE_SEARCH:
  1269.             /*
  1270.              * Repeat previous search, in reverse direction.
  1271.              */
  1272.             save_search_type = search_type;
  1273.             search_type = SRCH_REVERSE(search_type);
  1274.             DO_SEARCH();
  1275.             search_type = save_search_type;
  1276.             break;
  1277.  
  1278.         case A_T_REVERSE_SEARCH:
  1279.             /* 
  1280.              * Repeat previous search, 
  1281.              * multiple files in reverse direction.
  1282.              */
  1283.             save_search_type = search_type;
  1284.             search_type = SRCH_REVERSE(search_type);
  1285.             search_type |= SRCH_PAST_EOF;
  1286.             DO_SEARCH();
  1287.             search_type = save_search_type;
  1288.             break;
  1289.  
  1290.         case A_UNDO_SEARCH:
  1291.             undo_search();
  1292.             break;
  1293.  
  1294.         case A_HELP:
  1295.             /*
  1296.              * Help.
  1297.              */
  1298.             if (ch_getflags() & CH_HELPFILE)
  1299.                 break;
  1300.             cmd_exec();
  1301.             (void) edit(FAKE_HELPFILE);
  1302.             break;
  1303.  
  1304.         case A_EXAMINE:
  1305. #if EXAMINE
  1306.             /*
  1307.              * Edit a new file.  Get the filename.
  1308.              */
  1309.             if (secure)
  1310.             {
  1311.                 error("Command not available", NULL_PARG);
  1312.                 break;
  1313.             }
  1314.             start_mca(A_EXAMINE, "Examine: ", ml_examine, 0);
  1315.             c = getcc();
  1316.             goto again;
  1317. #else
  1318.             error("Command not available", NULL_PARG);
  1319.             break;
  1320. #endif
  1321.             
  1322.         case A_VISUAL:
  1323.             /*
  1324.              * Invoke an editor on the input file.
  1325.              */
  1326. #if EDITOR
  1327.             if (secure)
  1328.             {
  1329.                 error("Command not available", NULL_PARG);
  1330.                 break;
  1331.             }
  1332.             if (ch_getflags() & CH_HELPFILE)
  1333.                 break;
  1334.             if (strcmp(get_filename(curr_ifile), "-") == 0)
  1335.             {
  1336.                 error("Cannot edit standard input", NULL_PARG);
  1337.                 break;
  1338.             }
  1339.             if (curr_altfilename != NULL)
  1340.             {
  1341.                 error("Cannot edit file processed with LESSOPEN", 
  1342.                     NULL_PARG);
  1343.                 break;
  1344.             }
  1345.             start_mca(A_SHELL, "!", ml_shell, 0);
  1346.             /*
  1347.              * Expand the editor prototype string
  1348.              * and pass it to the system to execute.
  1349.              * (Make sure the screen is displayed so the
  1350.              * expansion of "+%lm" works.)
  1351.              */
  1352.             make_display();
  1353.             cmd_exec();
  1354.             lsystem(pr_expand(editproto, 0), (char*)NULL);
  1355.             break;
  1356. #else
  1357.             error("Command not available", NULL_PARG);
  1358.             break;
  1359. #endif
  1360.  
  1361.         case A_NEXT_FILE:
  1362.             /*
  1363.              * Examine next file.
  1364.              */
  1365.             if (ntags())
  1366.             {
  1367.                 error("No next file", NULL_PARG);
  1368.                 break;
  1369.             }
  1370.             if (number <= 0)
  1371.                 number = 1;
  1372.             if (edit_next(number))
  1373.             {
  1374.                 if (quit_at_eof && hit_eof && 
  1375.                     !(ch_getflags() & CH_HELPFILE))
  1376.                     quit(QUIT_OK);
  1377.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1378.                 error("No %snext file", &parg);
  1379.             }
  1380.             break;
  1381.  
  1382.         case A_PREV_FILE:
  1383.             /*
  1384.              * Examine previous file.
  1385.              */
  1386.             if (ntags())
  1387.             {
  1388.                 error("No previous file", NULL_PARG);
  1389.                 break;
  1390.             }
  1391.             if (number <= 0)
  1392.                 number = 1;
  1393.             if (edit_prev(number))
  1394.             {
  1395.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1396.                 error("No %sprevious file", &parg);
  1397.             }
  1398.             break;
  1399.  
  1400.         case A_NEXT_TAG:
  1401.             if (number <= 0)
  1402.                 number = 1;
  1403.             tagfile = nexttag(number);
  1404.             if (tagfile == NULL)
  1405.             {
  1406.                 error("No next tag", NULL_PARG);
  1407.                 break;
  1408.             }
  1409.             if (edit(tagfile) == 0)
  1410.             {
  1411.                 POSITION pos = tagsearch();
  1412.                 if (pos != NULL_POSITION)
  1413.                     jump_loc(pos, jump_sline);
  1414.             }
  1415.             break;
  1416.  
  1417.         case A_PREV_TAG:
  1418.             if (number <= 0)
  1419.                 number = 1;
  1420.             tagfile = prevtag(number);
  1421.             if (tagfile == NULL)
  1422.             {
  1423.                 error("No previous tag", NULL_PARG);
  1424.                 break;
  1425.             }
  1426.             if (edit(tagfile) == 0)
  1427.             {
  1428.                 POSITION pos = tagsearch();
  1429.                 if (pos != NULL_POSITION)
  1430.                     jump_loc(pos, jump_sline);
  1431.             }
  1432.             break;
  1433.  
  1434.         case A_INDEX_FILE:
  1435.             /*
  1436.              * Examine a particular file.
  1437.              */
  1438.             if (number <= 0)
  1439.                 number = 1;
  1440.             if (edit_index(number))
  1441.                 error("No such file", NULL_PARG);
  1442.             break;
  1443.  
  1444.         case A_REMOVE_FILE:
  1445.             if (ch_getflags() & CH_HELPFILE)
  1446.                 break;
  1447.             old_ifile = curr_ifile;
  1448.             new_ifile = getoff_ifile(curr_ifile);
  1449.             if (new_ifile == NULL_IFILE)
  1450.             {
  1451.                 bell();
  1452.                 break;
  1453.             }
  1454.             if (edit_ifile(new_ifile) != 0)
  1455.             {
  1456.                 reedit_ifile(old_ifile);
  1457.                 break;
  1458.             }
  1459.             del_ifile(old_ifile);
  1460.             break;
  1461.  
  1462.         case A_OPT_TOGGLE:
  1463.             optflag = OPT_TOGGLE;
  1464.             optgetname = FALSE;
  1465.             mca_opt_toggle();
  1466.             c = getcc();
  1467.             goto again;
  1468.  
  1469.         case A_DISP_OPTION:
  1470.             /*
  1471.              * Report a flag setting.
  1472.              */
  1473.             optflag = OPT_NO_TOGGLE;
  1474.             optgetname = FALSE;
  1475.             mca_opt_toggle();
  1476.             c = getcc();
  1477.             goto again;
  1478.  
  1479.         case A_FIRSTCMD:
  1480.             /*
  1481.              * Set an initial command for new files.
  1482.              */
  1483.             start_mca(A_FIRSTCMD, "+", (void*)NULL, 0);
  1484.             c = getcc();
  1485.             goto again;
  1486.  
  1487.         case A_SHELL:
  1488.             /*
  1489.              * Shell escape.
  1490.              */
  1491. #if SHELL_ESCAPE
  1492.             if (secure)
  1493.             {
  1494.                 error("Command not available", NULL_PARG);
  1495.                 break;
  1496.             }
  1497.             start_mca(A_SHELL, "!", ml_shell, 0);
  1498.             c = getcc();
  1499.             goto again;
  1500. #else
  1501.             error("Command not available", NULL_PARG);
  1502.             break;
  1503. #endif
  1504.  
  1505.         case A_SETMARK:
  1506.             /*
  1507.              * Set a mark.
  1508.              */
  1509.             if (ch_getflags() & CH_HELPFILE)
  1510.                 break;
  1511.             start_mca(A_SETMARK, "mark: ", (void*)NULL, 0);
  1512.             c = getcc();
  1513.             if (c == erase_char || c == kill_char ||
  1514.                 c == '\n' || c == '\r')
  1515.                 break;
  1516.             setmark(c);
  1517.             break;
  1518.  
  1519.         case A_GOMARK:
  1520.             /*
  1521.              * Go to a mark.
  1522.              */
  1523.             start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0);
  1524.             c = getcc();
  1525.             if (c == erase_char || c == kill_char || 
  1526.                 c == '\n' || c == '\r')
  1527.                 break;
  1528.             gomark(c);
  1529.             break;
  1530.  
  1531.         case A_PIPE:
  1532. #if PIPEC
  1533.             if (secure)
  1534.             {
  1535.                 error("Command not available", NULL_PARG);
  1536.                 break;
  1537.             }
  1538.             start_mca(A_PIPE, "|mark: ", (void*)NULL, 0);
  1539.             c = getcc();
  1540.             if (c == erase_char || c == kill_char)
  1541.                 break;
  1542.             if (c == '\n' || c == '\r')
  1543.                 c = '.';
  1544.             if (badmark(c))
  1545.                 break;
  1546.             pipec = c;
  1547.             start_mca(A_PIPE, "!", ml_shell, 0);
  1548.             c = getcc();
  1549.             goto again;
  1550. #else
  1551.             error("Command not available", NULL_PARG);
  1552.             break;
  1553. #endif
  1554.  
  1555.         case A_B_BRACKET:
  1556.         case A_F_BRACKET:
  1557.             start_mca(action, "Brackets: ", (void*)NULL, 0);
  1558.             c = getcc();
  1559.             goto again;
  1560.  
  1561.         case A_LSHIFT:
  1562.             if (number > 0)
  1563.                 shift_count = number;
  1564.             else
  1565.                 number = (shift_count > 0) ?
  1566.                     shift_count : sc_width / 2;
  1567.             if (number > hshift)
  1568.                 number = hshift;
  1569.             hshift -= number;
  1570.             screen_trashed = 1;
  1571.             break;
  1572.  
  1573.         case A_RSHIFT:
  1574.             if (number > 0)
  1575.                 shift_count = number;
  1576.             else
  1577.                 number = (shift_count > 0) ?
  1578.                     shift_count : sc_width / 2;
  1579.             hshift += number;
  1580.             screen_trashed = 1;
  1581.             break;
  1582.  
  1583.         case A_PREFIX:
  1584.             /*
  1585.              * The command is incomplete (more chars are needed).
  1586.              * Display the current char, so the user knows
  1587.              * what's going on, and get another character.
  1588.              */
  1589.             if (mca != A_PREFIX)
  1590.             {
  1591.                 cmd_reset();
  1592.                 start_mca(A_PREFIX, " ", (void*)NULL,
  1593.                     CF_QUIT_ON_ERASE);
  1594.                 (void) cmd_char(c);
  1595.             }
  1596.             c = getcc();
  1597.             goto again;
  1598.  
  1599.         case A_NOACTION:
  1600.             break;
  1601.  
  1602.         default:
  1603.             bell();
  1604.             break;
  1605.         }
  1606.     }
  1607. }
  1608.