home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / less / command.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-31  |  21.5 KB  |  1,177 lines

  1. /*
  2.  * User-level command processor.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #include "option.h"
  8. #include "cmd.h"
  9.  
  10. #define    NO_MCA        0
  11. #define    MCA_DONE    1
  12. #define    MCA_MORE    2
  13.  
  14. extern int erase_char, kill_char;
  15. extern int ispipe;
  16. extern int sigs;
  17. extern int quit_at_eof;
  18. extern int hit_eof;
  19. extern int sc_width;
  20. extern int sc_height;
  21. extern int swindow;
  22. extern int jump_sline;
  23. extern int quitting;
  24. extern int scroll;
  25. extern int nohelp;
  26. extern int ignore_eoi;
  27. extern char *every_first_cmd;
  28. extern char version[];
  29. extern struct scrpos initial_scrpos;
  30. extern IFILE curr_ifile;
  31. #if EDITOR
  32. extern char *editor;
  33. extern char *editproto;
  34. #endif
  35. extern int screen_trashed;    /* The screen has been overwritten */
  36.  
  37. static char ungot[100];
  38. static char *ungotp = NULL;
  39. #if SHELL_ESCAPE
  40. static char *shellcmd = NULL;    /* For holding last shell command for "!!" */
  41. #endif
  42. static int mca;            /* The multicharacter command (action) */
  43. static int search_type;        /* The previous type of search */
  44. static int number;        /* The number typed by the user */
  45. static char optchar;
  46. static int optflag;
  47. #if PIPEC
  48. static char pipec;
  49. #endif
  50.  
  51. static void multi_search();
  52.  
  53. /*
  54.  * Move the cursor to lower left before executing a command.
  55.  * This looks nicer if the command takes a long time before
  56.  * updating the screen.
  57.  */
  58.     static void
  59. cmd_exec()
  60. {
  61.     lower_left();
  62.     flush();
  63. }
  64.  
  65. /*
  66.  * Set up the display to start a new multi-character command.
  67.  */
  68.     static void
  69. start_mca(action, prompt)
  70.     int action;
  71.     char *prompt;
  72. {
  73.     mca = action;
  74.     lower_left();
  75.     clear_eol();
  76.     cmd_putstr(prompt);
  77. }
  78.  
  79. /*
  80.  * Set up the display to start a new search command.
  81.  */
  82.     static void
  83. search_mca()
  84. {
  85.     switch (SRCH_DIR(search_type))
  86.     {
  87.     case SRCH_FORW:
  88.         mca = A_F_SEARCH;
  89.         break;
  90.     case SRCH_BACK:
  91.         mca = A_B_SEARCH;
  92.         break;
  93.     }
  94.  
  95.     lower_left();
  96.     clear_eol();
  97.  
  98.     if (search_type & SRCH_FIRST_FILE)
  99.         cmd_putstr("@");
  100.     else
  101.         cmd_putstr(" ");
  102.  
  103.     if (search_type & SRCH_PAST_EOF)
  104.         cmd_putstr("*");
  105.     else
  106.         cmd_putstr(" ");
  107.  
  108.     cmd_putstr(" ");
  109.  
  110.     if (search_type & SRCH_NOMATCH)
  111.         cmd_putstr("!");
  112.     else
  113.         cmd_putstr(" ");
  114.  
  115.     switch (SRCH_DIR(search_type))
  116.     {
  117.     case SRCH_FORW:
  118.         cmd_putstr("/");
  119.         break;
  120.     case SRCH_BACK:
  121.         cmd_putstr("?");
  122.         break;
  123.     }
  124. }
  125.  
  126. /*
  127.  * Execute a multicharacter command.
  128.  */
  129.     static void
  130. exec_mca()
  131. {
  132.     register char *cbuf;
  133.     register char *s;
  134.  
  135.     cmd_exec();
  136.     cbuf = get_cmdbuf();
  137.  
  138.     switch (mca)
  139.     {
  140.     case A_F_SEARCH:
  141.     case A_B_SEARCH:
  142.         multi_search(cbuf, number);
  143.         break;
  144.     case A_FIRSTCMD:
  145.         /*
  146.          * Skip leading spaces or + signs in the string.
  147.          */
  148.         while (*cbuf == '+' || *cbuf == ' ')
  149.             cbuf++;
  150.         if (every_first_cmd != NULL)
  151.             free(every_first_cmd);
  152.         if (*cbuf == '\0')
  153.             every_first_cmd = NULL;
  154.         else
  155.             every_first_cmd = save(cbuf);
  156.         break;
  157.     case A_OPT_TOGGLE:
  158.         toggle_option(optchar, cbuf, optflag);
  159.         optchar = '\0';
  160.         break;
  161.     case A_F_BRACKET:
  162.         match_brac(cbuf[0], cbuf[1], 1, number);
  163.         break;
  164.     case A_B_BRACKET:
  165.         match_brac(cbuf[1], cbuf[0], 0, number);
  166.         break;
  167.     case A_EXAMINE:
  168.         /*
  169.          * Ignore leading spaces and glob the filename.
  170.          */
  171.         cbuf = skipsp(cbuf);
  172.         s = glob(cbuf);
  173.         if (s != NULL)
  174.         {
  175.             edit_list(s);
  176.             free(s);
  177.         } else
  178.             edit_list(cbuf);
  179.         break;
  180. #if SHELL_ESCAPE
  181.     case A_SHELL:
  182.         /*
  183.          * !! just uses whatever is in shellcmd.
  184.          * Otherwise, copy cmdbuf to shellcmd,
  185.          * expanding any special characters ("%" or "#").
  186.          */
  187.         if (*cbuf != '!')
  188.         {
  189.             if (shellcmd != NULL)
  190.                 free(shellcmd);
  191.             shellcmd = fexpand(cbuf);
  192.             if (shellcmd == NULL)
  193.                 break;
  194.         }
  195.  
  196.         if (shellcmd == NULL)
  197.             lsystem("");
  198.         else
  199.             lsystem(shellcmd);
  200.         error("!done", NULL_PARG);
  201.         break;
  202. #endif
  203. #if PIPEC
  204.     case A_PIPE:
  205.         (void) pipe_mark(pipec, cbuf);
  206.         error("|done", NULL_PARG);
  207.         break;
  208. #endif
  209.     }
  210. }
  211.  
  212. /*
  213.  * Add a character to a multi-character command.
  214.  */
  215.     static int
  216. mca_char(c)
  217.     int c;
  218. {
  219.     char *p;
  220.     int flag;
  221.     char buf[3];
  222.  
  223.     switch (mca)
  224.     {
  225.     case 0:
  226.         /*
  227.          * Not in a multicharacter command.
  228.          */
  229.         return (NO_MCA);
  230.  
  231.     case A_PREFIX:
  232.         /*
  233.          * In the prefix of a command.
  234.          * This not considered a multichar command
  235.          * (even tho it uses cmdbuf, etc.).
  236.          * It is handled in the commands() switch.
  237.          */
  238.         return (NO_MCA);
  239.  
  240.     case A_DIGIT:
  241.         /*
  242.          * Entering digits of a number.
  243.          * Terminated by a non-digit.
  244.          */
  245.         if ((c < '0' || c > '9') &&
  246.             c != erase_char && c != kill_char)
  247.         {
  248.             /*
  249.              * Not part of the number.
  250.              * Treat as a normal command character.
  251.              */
  252.             number = cmd_int();
  253.             mca = 0;
  254.             return (NO_MCA);
  255.         }
  256.         break;
  257.  
  258.     case A_OPT_TOGGLE:
  259.         /*
  260.          * Special case for the TOGGLE_OPTION command.
  261.          * If the option letter which was entered is a
  262.          * single-char option, execute the command immediately,
  263.          * so user doesn't have to hit RETURN.
  264.          * If the first char is + or -, this indicates
  265.          * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE.
  266.          */
  267.         if (c == erase_char || c == kill_char)
  268.             break;
  269.         if (optchar != '\0' && optchar != '+' && optchar != '-')
  270.             /*
  271.              * We already have the option letter.
  272.              */
  273.             break;
  274.         switch (c)
  275.         {
  276.         case '+':
  277.             optflag = OPT_UNSET;
  278.             break;
  279.         case '-':
  280.             optflag = OPT_SET;
  281.             break;
  282.         default:
  283.             optchar = c;
  284.             if (optflag != OPT_TOGGLE || single_char_option(c))
  285.             {
  286.                 toggle_option(c, "", optflag);
  287.                 return (MCA_DONE);
  288.             }
  289.             break;
  290.         }
  291.         if (optchar == '+' || optchar == '-')
  292.         {
  293.             optchar = c;
  294.             break;
  295.         }
  296.         /*
  297.          * Display a prompt appropriate for the option letter.
  298.          */
  299.         if ((p = opt_prompt(c)) == NULL)
  300.         {
  301.             buf[0] = '-';
  302.             buf[1] = c;
  303.             buf[2] = '\0';
  304.             p = buf;
  305.         }
  306.         start_mca(A_OPT_TOGGLE, p);
  307.         return (MCA_MORE);
  308.  
  309.     case A_F_SEARCH:
  310.     case A_B_SEARCH:
  311.         /*
  312.          * Special case for search commands.
  313.          * Certain characters as the first char of
  314.          * the pattern have special meaning:
  315.          *    !  Toggle the NOMATCH flag
  316.          *    *  Toggle the PAST_EOF flag
  317.          *    @  Toggle the FIRST_FILE flag
  318.          */
  319.         if (len_cmdbuf() > 0)
  320.             /*
  321.              * Only works for the first char of the pattern.
  322.              */
  323.             break;
  324.  
  325.         flag = 0;
  326.         switch (c)
  327.         {
  328.         case '!':
  329.             flag = SRCH_NOMATCH;
  330.             break;
  331.         case '@':
  332.             flag = SRCH_FIRST_FILE;
  333.             break;
  334.         case '*':
  335.             flag = SRCH_PAST_EOF;
  336.             break;
  337.         }
  338.         if (flag != 0)
  339.         {
  340.             search_type ^= flag;
  341.             search_mca();
  342.             return (MCA_MORE);
  343.         }
  344.         break;
  345.     }
  346.  
  347.     /*
  348.      * Any other multicharacter command
  349.      * is terminated by a newline.
  350.      */
  351.     if (c == '\n' || c == '\r')
  352.     {
  353.         /*
  354.          * Execute the command.
  355.          */
  356.         exec_mca();
  357.         return (MCA_DONE);
  358.     }
  359.     /*
  360.      * Append the char to the command buffer.
  361.      */
  362.     if (cmd_char(c))
  363.         /*
  364.          * Abort the multi-char command.
  365.          */
  366.         return (MCA_DONE);
  367.  
  368.     if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2)
  369.     {
  370.         /*
  371.          * Special case for the bracket-matching commands.
  372.          * Execute the command after getting exactly two
  373.          * characters from the user.
  374.          */
  375.         exec_mca();
  376.         return (MCA_DONE);
  377.     }
  378.  
  379.     /*
  380.      * Need another character.
  381.      */
  382.     return (MCA_MORE);
  383. }
  384.  
  385. /*
  386.  * Display the appropriate prompt.
  387.  */
  388.     static void
  389. prompt()
  390. {
  391.     register char *p;
  392.  
  393.     if (ungotp != NULL && ungotp > ungot)
  394.     {
  395.         /*
  396.          * No prompt necessary if commands are from
  397.          * ungotten chars rather than from the user.
  398.          */
  399.         return;
  400.     }
  401.  
  402.     /*
  403.      * If nothing is displayed yet, display starting from initial_scrpos.
  404.      */
  405.     if (empty_screen())
  406.     {
  407.         if (initial_scrpos.pos == NULL_POSITION)
  408.             /*
  409.              * {{ Maybe this should be:
  410.              *    jump_loc(ch_zero(), jump_sline);
  411.              *    but this behavior seems rather unexpected
  412.              *    on the first screen. }}
  413.              */
  414.             jump_loc(ch_zero(), 1);
  415.         else
  416.             jump_loc(initial_scrpos.pos, initial_scrpos.ln);
  417.     } else if (screen_trashed)
  418.         repaint();
  419.  
  420.     /*
  421.      * If the -E flag is set and we've hit EOF on the last file, quit.
  422.      */
  423.     if (quit_at_eof == 2 && hit_eof &&
  424.         next_ifile(curr_ifile) == NULL_IFILE)
  425.         quit(0);
  426.  
  427.     /*
  428.      * Select the proper prompt and display it.
  429.      */
  430.     lower_left();
  431.     clear_eol();
  432.     p = pr_string();
  433.     if (p == NULL)
  434.         putchr(':');
  435.     else
  436.     {
  437.         so_enter();
  438.         putstr(p);
  439.         so_exit();
  440.     }
  441. #if __MSDOS__
  442.     scroll_bar();
  443. #endif
  444. }
  445.  
  446. /*
  447.  * Get command character.
  448.  * The character normally comes from the keyboard,
  449.  * but may come from ungotten characters
  450.  * (characters previously given to ungetcc or ungetsc).
  451.  */
  452.     static int
  453. getcc()
  454. {
  455.     if (ungotp == NULL)
  456.         /*
  457.          * Normal case: no ungotten chars, so get one from the user.
  458.          */
  459.         return (getchr());
  460.  
  461.     if (ungotp > ungot)
  462.         /*
  463.          * Return the next ungotten char.
  464.          */
  465.         return (*--ungotp);
  466.  
  467.     /*
  468.      * We have just run out of ungotten chars.
  469.      */
  470.     ungotp = NULL;
  471.     if (len_cmdbuf() == 0 || !empty_screen())
  472.         return (getchr());
  473.     /*
  474.      * Command is incomplete, so try to complete it.
  475.      */
  476.     switch (mca)
  477.     {
  478.     case A_DIGIT:
  479.         /*
  480.          * We have a number but no command.  Treat as #g.
  481.          */
  482.         return ('g');
  483.  
  484.     case A_F_SEARCH:
  485.     case A_B_SEARCH:
  486.         /*
  487.          * We have "/string" but no newline.  Add the \n.
  488.          */
  489.         return ('\n');
  490.  
  491.     default:
  492.         /*
  493.          * Some other incomplete command.  Let user complete it.
  494.          */
  495.         return (getchr());
  496.     }
  497. }
  498.  
  499. /*
  500.  * "Unget" a command character.
  501.  * The next getcc() will return this character.
  502.  */
  503.     public void
  504. ungetcc(c)
  505.     int c;
  506. {
  507.     if (ungotp == NULL)
  508.         ungotp = ungot;
  509.     if (ungotp >= ungot + sizeof(ungot))
  510.     {
  511.         error("ungetcc overflow", NULL_PARG);
  512.         quit(1);
  513.     }
  514.     *ungotp++ = c;
  515. }
  516.  
  517. /*
  518.  * Unget a whole string of command characters.
  519.  * The next sequence of getcc()'s will return this string.
  520.  */
  521.     public void
  522. ungetsc(s)
  523.     char *s;
  524. {
  525.     register char *p;
  526.  
  527.     for (p = s + strlen(s) - 1;  p >= s;  p--)
  528.         ungetcc(*p);
  529. }
  530.  
  531. /*
  532.  * Search for a pattern, possibly in multiple files.
  533.  * If SRCH_FIRST_FILE is set, begin searching at the first file.
  534.  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
  535.  */
  536.     static void
  537. multi_search(pattern, n)
  538.     char *pattern;
  539.     int n;
  540. {
  541.     register int nomore;
  542.     char *curr_filename;
  543.     int changed_file;
  544.     struct scrpos scrpos;
  545.  
  546.     changed_file = 0;
  547.     curr_filename = get_filename(curr_ifile);
  548.  
  549.     if (search_type & SRCH_FIRST_FILE)
  550.     {
  551.         /*
  552.          * Start at the first (or last) file
  553.          * in the command line list.
  554.          */
  555.         if (SRCH_DIR(search_type) == SRCH_FORW)
  556.             nomore = edit_first();
  557.         else
  558.             nomore = edit_last();
  559.         if (nomore)
  560.             return;
  561.         changed_file = 1;
  562.         search_type &= ~SRCH_FIRST_FILE;
  563.     }
  564.  
  565.     for (;;)
  566.     {
  567.         if ((n = search(search_type, pattern, n)) == 0)
  568.             /*
  569.              * Found it.
  570.              */
  571.             return;
  572.  
  573.         if (n < 0)
  574.             /*
  575.              * Some kind of error in the search.
  576.              * Error message has been printed by search().
  577.              */
  578.             break;
  579.  
  580.         if ((search_type & SRCH_PAST_EOF) == 0)
  581.             /*
  582.              * We didn't find a match, but we're
  583.              * supposed to search only one file.
  584.              */
  585.             break;
  586.         /*
  587.          * Move on to the next file.
  588.          */
  589.         if (SRCH_DIR(search_type) == SRCH_BACK)
  590.             nomore = edit_prev(1);
  591.         else
  592.             nomore = edit_next(1);
  593.         if (nomore)
  594.             break;
  595.         changed_file = 1;
  596.     }
  597.  
  598.     /*
  599.      * Didn't find it.
  600.      * Print an error message if we haven't already.
  601.      */
  602.     if (n > 0)
  603.         error("Pattern not found", NULL_PARG);
  604.  
  605.     if (changed_file)
  606.         /*
  607.          * Restore the file we were originally viewing.
  608.          */
  609.         (void) edit(curr_filename, 0);
  610. }
  611.  
  612. /*
  613.  * Main command processor.
  614.  * Accept and execute commands until a quit command.
  615.  */
  616.     public void
  617. commands()
  618. {
  619.     register int c;
  620.     register int action;
  621.     register char *cbuf;
  622.     char *s;
  623.     char tbuf[2];
  624.     PARG parg;
  625.  
  626.     search_type = SRCH_FORW;
  627.     scroll = (sc_height + 1) / 2;
  628.  
  629.     for (;;)
  630.     {
  631.         mca = 0;
  632.         number = 0;
  633.         optchar = '\0';
  634.  
  635.         /*
  636.          * See if any signals need processing.
  637.          */
  638.         if (sigs)
  639.         {
  640.             psignals();
  641.             if (quitting)
  642.                 quit(-1);
  643.         }
  644.  
  645.         /*
  646.          * Display prompt and accept a character.
  647.          */
  648.         cmd_reset();
  649.         prompt();
  650.         if (sigs)
  651.             continue;
  652.         c = getcc();
  653.  
  654.     again:
  655.         if (sigs)
  656.             continue;
  657.  
  658.         /*
  659.          * If we are in a multicharacter command, call mca_char.
  660.          * Otherwise we call cmd_decode to determine the
  661.          * action to be performed.
  662.          */
  663.         if (mca)
  664.             switch (mca_char(c))
  665.             {
  666.             case MCA_MORE:
  667.                 /*
  668.                  * Need another character.
  669.                  */
  670.                 c = getcc();
  671.                 goto again;
  672.             case MCA_DONE:
  673.                 /*
  674.                  * Command has been handled by mca_char.
  675.                  * Start clean with a prompt.
  676.                  */
  677.                 continue;
  678.             case NO_MCA:
  679.                 /*
  680.                  * Not a multi-char command
  681.                  * (at least, not anymore).
  682.                  */
  683.                 break;
  684.             }
  685.  
  686.         /*
  687.          * Decode the command character and decide what to do.
  688.          */
  689.         if (mca)
  690.         {
  691.             /*
  692.              * We're in a multichar command.
  693.              * Add the character to the command buffer
  694.              * and display it on the screen.
  695.              * If the user backspaces past the start
  696.              * of the line, abort the command.
  697.              */
  698.             if (cmd_char(c) || len_cmdbuf() == 0)
  699.                 continue;
  700.             cbuf = get_cmdbuf();
  701.         } else
  702.         {
  703.             /*
  704.              * Don't use cmd_char if we're starting fresh
  705.              * at the beginning of a command, because we
  706.              * don't want to echo the command until we know
  707.              * it is a multichar command.  We also don't
  708.              * want erase_char/kill_char to be treated
  709.              * as line editing characters.
  710.              */
  711.             tbuf[0] = c;
  712.             tbuf[1] = '\0';
  713.             cbuf = tbuf;
  714.         }
  715.         s = NULL;
  716.         action = cmd_decode(cbuf, &s);
  717.         /*
  718.          * If an "extra" string was returned,
  719.          * process it as a string of command characters.
  720.          */
  721.         if (s != NULL)
  722.             ungetsc(s);
  723.         /*
  724.          * Clear the cmdbuf string.
  725.          * (But not if we're in the prefix of a command,
  726.          * because the partial command string is kept there.)
  727.          */
  728.         if (action != A_PREFIX)
  729.             cmd_reset();
  730.  
  731.         switch (action)
  732.         {
  733.         case A_DIGIT:
  734.             /*
  735.              * First digit of a number.
  736.              */
  737.             start_mca(A_DIGIT, ":");
  738.             goto again;
  739.  
  740.         case A_F_WINDOW:
  741.             /*
  742.              * Forward one window (and set the window size).
  743.              */
  744.             if (number > 0)
  745.                 swindow = number;
  746.             /* FALLTHRU */
  747.         case A_F_SCREEN:
  748.             /*
  749.              * Forward one screen.
  750.              */
  751.             if (number <= 0)
  752.                 number = swindow;
  753.             cmd_exec();
  754.             forward(number, 0, 1);
  755.             break;
  756.  
  757.         case A_B_WINDOW:
  758.             /*
  759.              * Backward one window (and set the window size).
  760.              */
  761.             if (number > 0)
  762.                 swindow = number;
  763.             /* FALLTHRU */
  764.         case A_B_SCREEN:
  765.             /*
  766.              * Backward one screen.
  767.              */
  768.             if (number <= 0)
  769.                 number = swindow;
  770.             cmd_exec();
  771.             backward(number, 0, 1);
  772.             break;
  773.  
  774.         case A_F_LINE:
  775.             /*
  776.              * Forward N (default 1) line.
  777.              */
  778.             if (number <= 0)
  779.                 number = 1;
  780.             cmd_exec();
  781.             forward(number, 0, 0);
  782.             break;
  783.  
  784.         case A_B_LINE:
  785.             /*
  786.              * Backward N (default 1) line.
  787.              */
  788.             if (number <= 0)
  789.                 number = 1;
  790.             cmd_exec();
  791.             backward(number, 0, 0);
  792.             break;
  793.  
  794.         case A_FF_LINE:
  795.             /*
  796.              * Force forward N (default 1) line.
  797.              */
  798.             if (number <= 0)
  799.                 number = 1;
  800.             cmd_exec();
  801.             forward(number, 1, 0);
  802.             break;
  803.  
  804.         case A_BF_LINE:
  805.             /*
  806.              * Force backward N (default 1) line.
  807.              */
  808.             if (number <= 0)
  809.                 number = 1;
  810.             cmd_exec();
  811.             backward(number, 1, 0);
  812.             break;
  813.  
  814.         case A_F_FOREVER:
  815.             /*
  816.              * Forward forever, ignoring EOF.
  817.              */
  818.             cmd_exec();
  819.             ignore_eoi = 1;
  820.             while (sigs == 0)
  821.                 forward(1, 0, 0);
  822.             ignore_eoi = 0;
  823.             break;
  824.  
  825.         case A_F_SCROLL:
  826.             /*
  827.              * Forward N lines
  828.              * (default same as last 'd' or 'u' command).
  829.              */
  830.             if (number > 0)
  831.                 scroll = number;
  832.             cmd_exec();
  833.             forward(scroll, 0, 0);
  834.             break;
  835.  
  836.         case A_B_SCROLL:
  837.             /*
  838.              * Forward N lines
  839.              * (default same as last 'd' or 'u' command).
  840.              */
  841.             if (number > 0)
  842.                 scroll = number;
  843.             cmd_exec();
  844.             backward(scroll, 0, 0);
  845.             break;
  846.  
  847.         case A_FREPAINT:
  848.             /*
  849.              * Flush buffers, then repaint screen.
  850.              * Don't flush the buffers on a pipe!
  851.              */
  852.             ch_flush();
  853.             if (!ispipe)
  854.                 clr_linenum();
  855.             /* FALLTHRU */
  856.         case A_REPAINT:
  857.             /*
  858.              * Repaint screen.
  859.              */
  860.             cmd_exec();
  861.             repaint();
  862.             break;
  863.  
  864.         case A_GOLINE:
  865.             /*
  866.              * Go to line N, default beginning of file.
  867.              */
  868.             if (number <= 0)
  869.                 number = 1;
  870.             cmd_exec();
  871.             jump_back(number);
  872.             break;
  873.  
  874.         case A_PERCENT:
  875.             /*
  876.              * Go to a specified percentage into the file.
  877.              */
  878.             if (number < 0)
  879.                 number = 0;
  880.             if (number > 100)
  881.                 number = 100;
  882.             cmd_exec();
  883.             jump_percent(number);
  884.             break;
  885.  
  886.         case A_GOEND:
  887.             /*
  888.              * Go to line N, default end of file.
  889.              */
  890.             cmd_exec();
  891.             if (number <= 0)
  892.                 jump_forw();
  893.             else
  894.                 jump_back(number);
  895.             break;
  896.  
  897.         case A_GOPOS:
  898.             /*
  899.              * Go to a specified byte position in the file.
  900.              */
  901.             cmd_exec();
  902.             if (number < 0)
  903.                 number = 0;
  904.             jump_line_loc((POSITION)number, jump_sline);
  905.             break;
  906.  
  907.         case A_STAT:
  908.             /*
  909.              * Print file name, etc.
  910.              */
  911.             cmd_exec();
  912.             parg.p_string = eq_message();
  913.             error("%s", &parg);
  914.             break;
  915.  
  916.         case A_VERSION:
  917.             /*
  918.              * Print version number, without the "@(#)".
  919.              */
  920.             cmd_exec();
  921.             parg.p_string = version+4;
  922.             error("%s", &parg);
  923.             break;
  924.  
  925.         case A_QUIT:
  926.             /*
  927.              * Exit.
  928.              */
  929.             quit(0);
  930.  
  931.         case A_B_SEARCH:
  932.             search_type = SRCH_BACK;
  933.             goto do_search;
  934.         case A_F_SEARCH:
  935.             search_type = SRCH_FORW;
  936.         do_search:
  937.             /*
  938.              * Search for a pattern.
  939.              * Get the first char of the pattern.
  940.              */
  941.             if (number <= 0)
  942.                 number = 1;
  943.             search_mca();
  944.             c = getcc();
  945.             goto again;
  946.  
  947.         case A_T_REVERSE_SEARCH:
  948.             search_type |= SRCH_PAST_EOF;
  949.             /* FALLTHRU */
  950.  
  951.         case A_REVERSE_SEARCH:
  952.             /*
  953.              * Repeat previous search, in reverse direction.
  954.              */
  955.             c = SRCH_FLAG(search_type);
  956.             if (SRCH_DIR(search_type) == SRCH_BACK)
  957.                 search_type = SRCH_FORW;
  958.             else
  959.                 search_type = SRCH_BACK;
  960.             search_type |= c;
  961.             goto do_again_search;
  962.  
  963.         case A_T_AGAIN_SEARCH:
  964.             search_type |= SRCH_PAST_EOF;
  965.             goto do_again_search;
  966.  
  967.         case A_AGAIN_SEARCH:
  968.             /*
  969.              * Repeat previous search.
  970.              */
  971.         do_again_search:
  972.             if (number <= 0)
  973.                 number = 1;
  974.             search_mca();
  975.             cmd_exec();
  976.             multi_search((char *)NULL, number);
  977.             break;
  978.  
  979.         case A_HELP:
  980.             /*
  981.              * Help.
  982.              */
  983.             if (nohelp)
  984.             {
  985.                 bell();
  986.                 break;
  987.             }
  988.             lower_left();
  989.             clear_eol();
  990.             putstr("help");
  991.             cmd_exec();
  992.             help();
  993.             break;
  994.  
  995.         case A_EXAMINE:
  996.             /*
  997.              * Edit a new file.  Get the filename.
  998.              */
  999.             start_mca(A_EXAMINE, "Examine: ");
  1000.             c = getcc();
  1001.             goto again;
  1002.  
  1003.         case A_VISUAL:
  1004.             /*
  1005.              * Invoke an editor on the input file.
  1006.              */
  1007. #if EDITOR
  1008.             if (strcmp(get_filename(curr_ifile), "-") == 0)
  1009.             {
  1010.                 error("Cannot edit standard input", NULL_PARG);
  1011.                 break;
  1012.             }
  1013.             /*
  1014.              * Expand the editor prototype string
  1015.              * and pass it to the system to execute.
  1016.              */
  1017.             cmd_exec();
  1018.             lsystem(pr_expand(editproto, 0));
  1019.             /*
  1020.              * Re-edit the file, since data may have changed.
  1021.              * Some editors even recreate the file, so flushing
  1022.              * buffers is not sufficient.
  1023.              */
  1024.             (void) edit(get_filename(curr_ifile), 0);
  1025.             break;
  1026. #else
  1027.             error("Command not available", NULL_PARG);
  1028.             break;
  1029. #endif
  1030.  
  1031.         case A_NEXT_FILE:
  1032.             /*
  1033.              * Examine next file.
  1034.              */
  1035.             if (number <= 0)
  1036.                 number = 1;
  1037.             if (edit_next(number))
  1038.             {
  1039.                 if (quit_at_eof && hit_eof)
  1040.                     quit(0);
  1041.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1042.                 error("No %snext file", &parg);
  1043.             }
  1044.             break;
  1045.  
  1046.         case A_PREV_FILE:
  1047.             /*
  1048.              * Examine previous file.
  1049.              */
  1050.             if (number <= 0)
  1051.                 number = 1;
  1052.             if (edit_prev(number))
  1053.             {
  1054.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1055.                 error("No %sprevious file", &parg);
  1056.             }
  1057.             break;
  1058.  
  1059.         case A_INDEX_FILE:
  1060.             /*
  1061.              * Examine a particular file.
  1062.              */
  1063.             if (number <= 0)
  1064.                 number = 1;
  1065.             if (edit_index(number))
  1066.                 error("No such file", NULL_PARG);
  1067.             break;
  1068.  
  1069.         case A_OPT_TOGGLE:
  1070.             start_mca(A_OPT_TOGGLE, "-");
  1071.             optflag = OPT_TOGGLE;
  1072.             c = getcc();
  1073.             goto again;
  1074.  
  1075.         case A_DISP_OPTION:
  1076.             /*
  1077.              * Report a flag setting.
  1078.              */
  1079.             start_mca(A_DISP_OPTION, "_");
  1080.             c = getcc();
  1081.             if (c == erase_char || c == kill_char)
  1082.                 break;
  1083.             toggle_option(c, "", OPT_NO_TOGGLE);
  1084.             break;
  1085.  
  1086.         case A_FIRSTCMD:
  1087.             /*
  1088.              * Set an initial command for new files.
  1089.              */
  1090.             start_mca(A_FIRSTCMD, "+");
  1091.             c = getcc();
  1092.             goto again;
  1093.  
  1094.         case A_SHELL:
  1095.             /*
  1096.              * Shell escape.
  1097.              */
  1098. #if SHELL_ESCAPE
  1099.             start_mca(A_SHELL, "!");
  1100.             c = getcc();
  1101.             goto again;
  1102. #else
  1103.             error("Command not available", NULL_PARG);
  1104.             break;
  1105. #endif
  1106.  
  1107.         case A_SETMARK:
  1108.             /*
  1109.              * Set a mark.
  1110.              */
  1111.             start_mca(A_SETMARK, "mark: ");
  1112.             c = getcc();
  1113.             if (c == erase_char || c == kill_char ||
  1114.                 c == '\n' || c == '\r')
  1115.                 break;
  1116.             setmark(c);
  1117.             break;
  1118.  
  1119.         case A_GOMARK:
  1120.             /*
  1121.              * Go to a mark.
  1122.              */
  1123.             start_mca(A_GOMARK, "goto mark: ");
  1124.             c = getcc();
  1125.             if (c == erase_char || c == kill_char ||
  1126.                 c == '\n' || c == '\r')
  1127.                 break;
  1128.             gomark(c);
  1129.             break;
  1130.  
  1131. #if PIPEC
  1132.         case A_PIPE:
  1133.             start_mca(A_PIPE, "|mark: ");
  1134.             c = getcc();
  1135.             if (c == erase_char || c == kill_char)
  1136.                 break;
  1137.             if (c == '\n' || c == '\r')
  1138.                 c = '.';
  1139.             if (badmark(c))
  1140.                 break;
  1141.             pipec = c;
  1142.             start_mca(A_PIPE, "!");
  1143.             c = getcc();
  1144.             goto again;
  1145. #endif
  1146.  
  1147.         case A_B_BRACKET:
  1148.         case A_F_BRACKET:
  1149.             start_mca(action, "Brackets: ");
  1150.             c = getcc();
  1151.             goto again;
  1152.  
  1153.         case A_PREFIX:
  1154.             /*
  1155.              * The command is incomplete (more chars are needed).
  1156.              * Display the current char, so the user knows
  1157.              * what's going on, and get another character.
  1158.              */
  1159.             if (mca != A_PREFIX)
  1160.             {
  1161.                 start_mca(A_PREFIX, " ");
  1162.                 cmd_reset();
  1163.                 (void) cmd_char(c);
  1164.             }
  1165.             c = getcc();
  1166.             goto again;
  1167.  
  1168.         case A_NOACTION:
  1169.             break;
  1170.  
  1171.         default:
  1172.             bell();
  1173.             break;
  1174.         }
  1175.     }
  1176. }
  1177.