home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / less_332.lzh / less_332 / command.c < prev    next >
Text File  |  1998-03-03  |  26KB  |  1,378 lines

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