home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / LESS177.ZIP / src / command.c < prev    next >
C/C++ Source or Header  |  1992-07-18  |  21KB  |  1,193 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.     scroll_bar();
  435. #endif
  436. }
  437.  
  438. /*
  439.  * Get command character.
  440.  * The character normally comes from the keyboard,
  441.  * but may come from ungotten characters
  442.  * (characters previously given to ungetcc or ungetsc).
  443.  */
  444.     static int
  445. getcc()
  446. {
  447.     if (ungotp == NULL)
  448.         /*
  449.          * Normal case: no ungotten chars, so get one from the user.
  450.          */
  451.         return (getchr());
  452.  
  453.     if (ungotp > ungot)
  454.         /*
  455.          * Return the next ungotten char.
  456.          */
  457.         return (*--ungotp);
  458.  
  459.     /*
  460.      * We have just run out of ungotten chars.
  461.      */
  462.     ungotp = NULL;
  463.     if (len_cmdbuf() == 0 || !empty_screen())
  464.         return (getchr());
  465.     /*
  466.      * Command is incomplete, so try to complete it.
  467.      */
  468.     switch (mca)
  469.     {
  470.     case A_DIGIT:
  471.         /*
  472.          * We have a number but no command.  Treat as #g.
  473.          */
  474.         return ('g');
  475.  
  476.     case A_F_SEARCH:
  477.     case A_B_SEARCH:
  478.         /*
  479.          * We have "/string" but no newline.  Add the \n.
  480.          */
  481.         return ('\n'); 
  482.  
  483.     default:
  484.         /*
  485.          * Some other incomplete command.  Let user complete it.
  486.          */
  487.         return (getchr());
  488.     }
  489. }
  490.  
  491. /*
  492.  * "Unget" a command character.
  493.  * The next getcc() will return this character.
  494.  */
  495.     public void
  496. ungetcc(c)
  497.     int c;
  498. {
  499.     if (ungotp == NULL)
  500.         ungotp = ungot;
  501.     if (ungotp >= ungot + sizeof(ungot))
  502.     {
  503.         error("ungetcc overflow", NULL_PARG);
  504.         quit(1);
  505.     }
  506.     *ungotp++ = c;
  507. }
  508.  
  509. /*
  510.  * Unget a whole string of command characters.
  511.  * The next sequence of getcc()'s will return this string.
  512.  */
  513.     public void
  514. ungetsc(s)
  515.     char *s;
  516. {
  517.     register char *p;
  518.  
  519.     for (p = s + strlen(s) - 1;  p >= s;  p--)
  520.         ungetcc(*p);
  521. }
  522.  
  523. /*
  524.  * Search for a pattern, possibly in multiple files.
  525.  * If SRCH_FIRST_FILE is set, begin searching at the first file.
  526.  * If SRCH_PAST_EOF is set, continue the search thru multiple files.
  527.  */
  528.     static void
  529. multi_search(pattern, n)
  530.     char *pattern;
  531.     int n;
  532. {
  533.     register int nomore;
  534.     char *curr_filename;
  535.     int changed_file;
  536.  
  537.     changed_file = 0;
  538.     curr_filename = get_filename(curr_ifile);
  539.  
  540.     if (search_type & SRCH_FIRST_FILE)
  541.     {
  542.         /*
  543.          * Start at the first (or last) file 
  544.          * in the command line list.
  545.          */
  546.         if (SRCH_DIR(search_type) == SRCH_FORW)
  547.             nomore = edit_first();
  548.         else
  549.             nomore = edit_last();
  550.         if (nomore)
  551.             return;
  552.         changed_file = 1;
  553.         search_type &= ~SRCH_FIRST_FILE;
  554.     }
  555.  
  556.     for (;;)
  557.     {
  558.         if ((n = search(search_type, pattern, n)) == 0)
  559.             /*
  560.              * Found it.
  561.              */
  562.             return;
  563.  
  564.         if (n < 0)
  565.             /*
  566.              * Some kind of error in the search.
  567.              * Error message has been printed by search().
  568.              */
  569.             break;
  570.  
  571.         if ((search_type & SRCH_PAST_EOF) == 0)
  572.             /*
  573.              * We didn't find a match, but we're
  574.              * supposed to search only one file.
  575.              */
  576.             break;
  577.         /*
  578.          * Move on to the next file.
  579.          */
  580.         if (SRCH_DIR(search_type) == SRCH_BACK)
  581.             nomore = edit_prev(1);
  582.         else
  583.             nomore = edit_next(1);
  584.         if (nomore)
  585.             break;
  586.         changed_file = 1;
  587.     }
  588.  
  589.     /*
  590.      * Didn't find it.
  591.      * Print an error message if we haven't already.
  592.      */
  593.     if (n > 0)
  594.         error("Pattern not found", NULL_PARG);
  595.  
  596.     if (changed_file)
  597.         /*
  598.          * Restore the file we were originally viewing.
  599.          */
  600.         (void) edit(curr_filename, 0);
  601. }
  602.  
  603. /*
  604.  * Main command processor.
  605.  * Accept and execute commands until a quit command.
  606.  */
  607.     public void
  608. commands()
  609. {
  610.     register int c;
  611.     register int action;
  612.     register char *cbuf;
  613.     int save_search_type;
  614.     char *s;
  615.     char tbuf[2];
  616.     PARG parg;
  617.  
  618.     search_type = SRCH_FORW;
  619.     scroll = (sc_height + 1) / 2;
  620.  
  621.     for (;;)
  622.     {
  623.         mca = 0;
  624.         number = 0;
  625.         optchar = '\0';
  626.  
  627.         /*
  628.          * See if any signals need processing.
  629.          */
  630.         if (sigs)
  631.         {
  632.             psignals();
  633.             if (quitting)
  634.                 quit(-1);
  635.         }
  636.             
  637.         /*
  638.          * Display prompt and accept a character.
  639.          */
  640.         cmd_reset();
  641.         prompt();
  642.         if (sigs)
  643.             continue;
  644.         c = getcc();
  645.  
  646.     again:
  647.         if (sigs)
  648.             continue;
  649.  
  650.         /*
  651.          * If we are in a multicharacter command, call mca_char.
  652.          * Otherwise we call cmd_decode to determine the
  653.          * action to be performed.
  654.          */
  655.         if (mca)
  656.             switch (mca_char(c))
  657.             {
  658.             case MCA_MORE:
  659.                 /*
  660.                  * Need another character.
  661.                  */
  662.                 c = getcc();
  663.                 goto again;
  664.             case MCA_DONE:
  665.                 /*
  666.                  * Command has been handled by mca_char.
  667.                  * Start clean with a prompt.
  668.                  */
  669.                 continue;
  670.             case NO_MCA:
  671.                 /*
  672.                  * Not a multi-char command
  673.                  * (at least, not anymore).
  674.                  */
  675.                 break;
  676.             }
  677.  
  678.         /*
  679.          * Decode the command character and decide what to do.
  680.          */
  681.         if (mca)
  682.         {
  683.             /*
  684.              * We're in a multichar command.
  685.              * Add the character to the command buffer
  686.              * and display it on the screen.
  687.              * If the user backspaces past the start 
  688.              * of the line, abort the command.
  689.              */
  690.             if (cmd_char(c) || len_cmdbuf() == 0)
  691.                 continue;
  692.             cbuf = get_cmdbuf();
  693.         } else
  694.         {
  695.             /*
  696.              * Don't use cmd_char if we're starting fresh
  697.              * at the beginning of a command, because we
  698.              * don't want to echo the command until we know
  699.              * it is a multichar command.  We also don't
  700.              * want erase_char/kill_char to be treated
  701.              * as line editing characters.
  702.              */
  703.             tbuf[0] = c;
  704.             tbuf[1] = '\0';
  705.             cbuf = tbuf;
  706.         }
  707.         s = NULL;
  708.         action = cmd_decode(cbuf, &s);
  709.         /*
  710.          * If an "extra" string was returned,
  711.          * process it as a string of command characters.
  712.          */
  713.         if (s != NULL)
  714.             ungetsc(s);
  715.         /*
  716.          * Clear the cmdbuf string.
  717.          * (But not if we're in the prefix of a command,
  718.          * because the partial command string is kept there.)
  719.          */
  720.         if (action != A_PREFIX)
  721.             cmd_reset();
  722.  
  723.         switch (action)
  724.         {
  725.         case A_DIGIT:
  726.             /*
  727.              * First digit of a number.
  728.              */
  729.             start_mca(A_DIGIT, ":");
  730.             goto again;
  731.  
  732.         case A_F_WINDOW:
  733.             /*
  734.              * Forward one window (and set the window size).
  735.              */
  736.             if (number > 0)
  737.                 swindow = number;
  738.             /* FALLTHRU */
  739.         case A_F_SCREEN:
  740.             /*
  741.              * Forward one screen.
  742.              */
  743.             if (number <= 0)
  744.                 number = swindow;
  745.             cmd_exec();
  746.             forward(number, 0, 1);
  747.             break;
  748.  
  749.         case A_B_WINDOW:
  750.             /*
  751.              * Backward one window (and set the window size).
  752.              */
  753.             if (number > 0)
  754.                 swindow = number;
  755.             /* FALLTHRU */
  756.         case A_B_SCREEN:
  757.             /*
  758.              * Backward one screen.
  759.              */
  760.             if (number <= 0)
  761.                 number = swindow;
  762.             cmd_exec();
  763.             backward(number, 0, 1);
  764.             break;
  765.  
  766.         case A_F_LINE:
  767.             /*
  768.              * Forward N (default 1) line.
  769.              */
  770.             if (number <= 0)
  771.                 number = 1;
  772.             cmd_exec();
  773.             forward(number, 0, 0);
  774.             break;
  775.  
  776.         case A_B_LINE:
  777.             /*
  778.              * Backward N (default 1) line.
  779.              */
  780.             if (number <= 0)
  781.                 number = 1;
  782.             cmd_exec();
  783.             backward(number, 0, 0);
  784.             break;
  785.  
  786.         case A_FF_LINE:
  787.             /*
  788.              * Force forward N (default 1) line.
  789.              */
  790.             if (number <= 0)
  791.                 number = 1;
  792.             cmd_exec();
  793.             forward(number, 1, 0);
  794.             break;
  795.  
  796.         case A_BF_LINE:
  797.             /*
  798.              * Force backward N (default 1) line.
  799.              */
  800.             if (number <= 0)
  801.                 number = 1;
  802.             cmd_exec();
  803.             backward(number, 1, 0);
  804.             break;
  805.         
  806.         case A_F_FOREVER:
  807.             /*
  808.              * Forward forever, ignoring EOF.
  809.              */
  810.             cmd_exec();
  811.             jump_forw();
  812.             ignore_eoi = 1;
  813.             hit_eof = 0;
  814.             while (sigs == 0)
  815.                 forward(1, 0, 0);
  816.             ignore_eoi = 0;
  817.             break;
  818.  
  819.         case A_F_SCROLL:
  820.             /*
  821.              * Forward N lines 
  822.              * (default same as last 'd' or 'u' command).
  823.              */
  824.             if (number > 0)
  825.                 scroll = number;
  826.             cmd_exec();
  827.             forward(scroll, 0, 0);
  828.             break;
  829.  
  830.         case A_B_SCROLL:
  831.             /*
  832.              * Forward N lines 
  833.              * (default same as last 'd' or 'u' command).
  834.              */
  835.             if (number > 0)
  836.                 scroll = number;
  837.             cmd_exec();
  838.             backward(scroll, 0, 0);
  839.             break;
  840.  
  841.         case A_FREPAINT:
  842.             /*
  843.              * Flush buffers, then repaint screen.
  844.              * Don't flush the buffers on a pipe!
  845.              */
  846.             ch_flush();
  847.             if (!ispipe)
  848.                 clr_linenum();
  849.             /* FALLTHRU */
  850.         case A_REPAINT:
  851.             /*
  852.              * Repaint screen.
  853.              */
  854.             cmd_exec();
  855.             repaint();
  856.             break;
  857.  
  858.         case A_GOLINE:
  859.             /*
  860.              * Go to line N, default beginning of file.
  861.              */
  862.             if (number <= 0)
  863.                 number = 1;
  864.             cmd_exec();
  865.             jump_back(number);
  866.             break;
  867.  
  868.         case A_PERCENT:
  869.             /*
  870.              * Go to a specified percentage into the file.
  871.              */
  872.             if (number < 0)
  873.                 number = 0;
  874.             if (number > 100)
  875.                 number = 100;
  876.             cmd_exec();
  877.             jump_percent(number);
  878.             break;
  879.  
  880.         case A_GOEND:
  881.             /*
  882.              * Go to line N, default end of file.
  883.              */
  884.             cmd_exec();
  885.             if (number <= 0)
  886.                 jump_forw();
  887.             else
  888.                 jump_back(number);
  889.             break;
  890.  
  891.         case A_GOPOS:
  892.             /*
  893.              * Go to a specified byte position in the file.
  894.              */
  895.             cmd_exec();
  896.             if (number < 0)
  897.                 number = 0;
  898.             jump_line_loc((POSITION)number, jump_sline);
  899.             break;
  900.  
  901.         case A_STAT:
  902.             /*
  903.              * Print file name, etc.
  904.              */
  905.             cmd_exec();
  906.             parg.p_string = eq_message();
  907.             error("%s", &parg);
  908.             break;
  909.             
  910.         case A_VERSION:
  911.             /*
  912.              * Print version number, without the "@(#)".
  913.              */
  914.             cmd_exec();
  915.             parg.p_string = version+4;
  916.             error("%s", &parg);
  917.             break;
  918.  
  919.         case A_QUIT:
  920.             /*
  921.              * Exit.
  922.              */
  923.             quit(0);
  924.  
  925. /*
  926.  * Define abbreviation for a commonly used sequence below.
  927.  */
  928. #define    DO_SEARCH()    if (number <= 0) number = 1;    \
  929.             mca_search();            \
  930.             cmd_exec();            \
  931.             multi_search((char *)NULL, number);
  932.  
  933.  
  934.         case A_F_SEARCH:
  935.             /*
  936.              * Search forward for a pattern.
  937.              * Get the first char of the pattern.
  938.              */
  939.             search_type = SRCH_FORW;
  940.             if (number <= 0)
  941.                 number = 1;
  942.             mca_search();
  943.             c = getcc();
  944.             goto again;
  945.  
  946.         case A_B_SEARCH:
  947.             /*
  948.              * Search backward for a pattern.
  949.              * Get the first char of the pattern.
  950.              */
  951.             search_type = SRCH_BACK;
  952.             if (number <= 0)
  953.                 number = 1;
  954.             mca_search();
  955.             c = getcc();
  956.             goto again;
  957.  
  958.         case A_AGAIN_SEARCH:
  959.             /*
  960.              * Repeat previous search.
  961.              */
  962.             DO_SEARCH();
  963.             break;
  964.         
  965.         case A_T_AGAIN_SEARCH:
  966.             /*
  967.              * Repeat previous search, multiple files.
  968.              */
  969.             search_type |= SRCH_PAST_EOF;
  970.             DO_SEARCH();
  971.             break;
  972.  
  973.         case A_REVERSE_SEARCH:
  974.             /*
  975.              * Repeat previous search, in reverse direction.
  976.              */
  977.             save_search_type = search_type;
  978.             search_type = SRCH_REVERSE(search_type);
  979.             DO_SEARCH();
  980.             search_type = save_search_type;
  981.             break;
  982.  
  983.         case A_T_REVERSE_SEARCH:
  984.             /* 
  985.              * Repeat previous search, 
  986.              * multiple files in reverse direction.
  987.              */
  988.             save_search_type = search_type;
  989.             search_type = SRCH_REVERSE(search_type);
  990.             search_type |= SRCH_PAST_EOF;
  991.             DO_SEARCH();
  992.             search_type = save_search_type;
  993.             break;
  994.  
  995.         case A_HELP:
  996.             /*
  997.              * Help.
  998.              */
  999.             if (nohelp)
  1000.             {
  1001.                 bell();
  1002.                 break;
  1003.             }
  1004.             lower_left();
  1005.             clear_eol();
  1006.             putstr("help");
  1007.             cmd_exec();
  1008.             help();
  1009.             break;
  1010.  
  1011.         case A_EXAMINE:
  1012.             /*
  1013.              * Edit a new file.  Get the filename.
  1014.              */
  1015.             start_mca(A_EXAMINE, "Examine: ");
  1016.             c = getcc();
  1017.             goto again;
  1018.             
  1019.         case A_VISUAL:
  1020.             /*
  1021.              * Invoke an editor on the input file.
  1022.              */
  1023. #if EDITOR
  1024.             if (strcmp(get_filename(curr_ifile), "-") == 0)
  1025.             {
  1026.                 error("Cannot edit standard input", NULL_PARG);
  1027.                 break;
  1028.             }
  1029.             /*
  1030.              * Expand the editor prototype string
  1031.              * and pass it to the system to execute.
  1032.              */
  1033.             cmd_exec();
  1034.             lsystem(pr_expand(editproto, 0));
  1035.             /*
  1036.              * Re-edit the file, since data may have changed.
  1037.              * Some editors even recreate the file, so flushing
  1038.              * buffers is not sufficient.
  1039.              */
  1040.             (void) edit(get_filename(curr_ifile), 0);
  1041.             break;
  1042. #else
  1043.             error("Command not available", NULL_PARG);
  1044.             break;
  1045. #endif
  1046.  
  1047.         case A_NEXT_FILE:
  1048.             /*
  1049.              * Examine next file.
  1050.              */
  1051.             if (number <= 0)
  1052.                 number = 1;
  1053.             if (edit_next(number))
  1054.             {
  1055.                 if (quit_at_eof && hit_eof)
  1056.                     quit(0);
  1057.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1058.                 error("No %snext file", &parg);
  1059.             }
  1060.             break;
  1061.  
  1062.         case A_PREV_FILE:
  1063.             /*
  1064.              * Examine previous file.
  1065.              */
  1066.             if (number <= 0)
  1067.                 number = 1;
  1068.             if (edit_prev(number))
  1069.             {
  1070.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1071.                 error("No %sprevious file", &parg);
  1072.             }
  1073.             break;
  1074.  
  1075.         case A_INDEX_FILE:
  1076.             /*
  1077.              * Examine a particular file.
  1078.              */
  1079.             if (number <= 0)
  1080.                 number = 1;
  1081.             if (edit_index(number))
  1082.                 error("No such file", NULL_PARG);
  1083.             break;
  1084.  
  1085.         case A_OPT_TOGGLE:
  1086.             start_mca(A_OPT_TOGGLE, "-");
  1087.             optflag = OPT_TOGGLE;
  1088.             c = getcc();
  1089.             goto again;
  1090.  
  1091.         case A_DISP_OPTION:
  1092.             /*
  1093.              * Report a flag setting.
  1094.              */
  1095.             start_mca(A_DISP_OPTION, "_");
  1096.             c = getcc();
  1097.             if (c == erase_char || c == kill_char)
  1098.                 break;
  1099.             toggle_option(c, "", OPT_NO_TOGGLE);
  1100.             break;
  1101.  
  1102.         case A_FIRSTCMD:
  1103.             /*
  1104.              * Set an initial command for new files.
  1105.              */
  1106.             start_mca(A_FIRSTCMD, "+");
  1107.             c = getcc();
  1108.             goto again;
  1109.  
  1110.         case A_SHELL:
  1111.             /*
  1112.              * Shell escape.
  1113.              */
  1114. #if SHELL_ESCAPE
  1115.             start_mca(A_SHELL, "!");
  1116.             c = getcc();
  1117.             goto again;
  1118. #else
  1119.             error("Command not available", NULL_PARG);
  1120.             break;
  1121. #endif
  1122.  
  1123.         case A_SETMARK:
  1124.             /*
  1125.              * Set a mark.
  1126.              */
  1127.             start_mca(A_SETMARK, "mark: ");
  1128.             c = getcc();
  1129.             if (c == erase_char || c == kill_char ||
  1130.                 c == '\n' || c == '\r')
  1131.                 break;
  1132.             setmark(c);
  1133.             break;
  1134.  
  1135.         case A_GOMARK:
  1136.             /*
  1137.              * Go to a mark.
  1138.              */
  1139.             start_mca(A_GOMARK, "goto mark: ");
  1140.             c = getcc();
  1141.             if (c == erase_char || c == kill_char || 
  1142.                 c == '\n' || c == '\r')
  1143.                 break;
  1144.             gomark(c);
  1145.             break;
  1146.  
  1147. #if PIPEC
  1148.         case A_PIPE:
  1149.             start_mca(A_PIPE, "|mark: ");
  1150.             c = getcc();
  1151.             if (c == erase_char || c == kill_char)
  1152.                 break;
  1153.             if (c == '\n' || c == '\r')
  1154.                 c = '.';
  1155.             if (badmark(c))
  1156.                 break;
  1157.             pipec = c;
  1158.             start_mca(A_PIPE, "!");
  1159.             c = getcc();
  1160.             goto again;
  1161. #endif
  1162.  
  1163.         case A_B_BRACKET:
  1164.         case A_F_BRACKET:
  1165.             start_mca(action, "Brackets: ");
  1166.             c = getcc();
  1167.             goto again;
  1168.  
  1169.         case A_PREFIX:
  1170.             /*
  1171.              * The command is incomplete (more chars are needed).
  1172.              * Display the current char, so the user knows
  1173.              * what's going on, and get another character.
  1174.              */
  1175.             if (mca != A_PREFIX)
  1176.             {
  1177.                 start_mca(A_PREFIX, " ");
  1178.                 cmd_reset();
  1179.                 (void) cmd_char(c);
  1180.             }
  1181.             c = getcc();
  1182.             goto again;
  1183.  
  1184.         case A_NOACTION:
  1185.             break;
  1186.  
  1187.         default:
  1188.             bell();
  1189.             break;
  1190.         }
  1191.     }
  1192. }
  1193.