home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / COMMAND.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  23KB  |  1,239 lines

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