home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / utilities / text / less-278.lha / less-278 / src.lha / source / command.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-01  |  22.7 KB  |  1,249 lines

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