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