home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less3292.zip / command.c < prev    next >
C/C++ Source or Header  |  1996-10-04  |  26KB  |  1,372 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.          * Display prompt and accept a character.
  724.          */
  725.         cmd_reset();
  726.         prompt();
  727.         if (sigs)
  728.             continue;
  729.         if (newaction == A_NOACTION)
  730.             c = getcc();
  731.  
  732.     again:
  733.         if (sigs)
  734.             continue;
  735.  
  736.         if (newaction != A_NOACTION)
  737.         {
  738.             action = newaction;
  739.             newaction = A_NOACTION;
  740.         } else
  741.         {
  742.             /*
  743.              * If we are in a multicharacter command, call mca_char.
  744.              * Otherwise we call fcmd_decode to determine the
  745.              * action to be performed.
  746.              */
  747.             if (mca)
  748.                 switch (mca_char(c))
  749.                 {
  750.                 case MCA_MORE:
  751.                     /*
  752.                      * Need another character.
  753.                      */
  754.                     c = getcc();
  755.                     goto again;
  756.                 case MCA_DONE:
  757.                     /*
  758.                      * Command has been handled by mca_char.
  759.                      * Start clean with a prompt.
  760.                      */
  761.                     continue;
  762.                 case NO_MCA:
  763.                     /*
  764.                      * Not a multi-char command
  765.                      * (at least, not anymore).
  766.                      */
  767.                     break;
  768.                 }
  769.  
  770.             /*
  771.              * Decode the command character and decide what to do.
  772.              */
  773.             if (mca)
  774.             {
  775.                 /*
  776.                  * We're in a multichar command.
  777.                  * Add the character to the command buffer
  778.                  * and display it on the screen.
  779.                  * If the user backspaces past the start 
  780.                  * of the line, abort the command.
  781.                  */
  782.                 if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0)
  783.                     continue;
  784.                 cbuf = get_cmdbuf();
  785.             } else
  786.             {
  787.                 /*
  788.                  * Don't use cmd_char if we're starting fresh
  789.                  * at the beginning of a command, because we
  790.                  * don't want to echo the command until we know
  791.                  * it is a multichar command.  We also don't
  792.                  * want erase_char/kill_char to be treated
  793.                  * as line editing characters.
  794.                  */
  795.                 tbuf[0] = c;
  796.                 tbuf[1] = '\0';
  797.                 cbuf = tbuf;
  798.             }
  799.             extra = NULL;
  800.             action = fcmd_decode(cbuf, &extra);
  801.             /*
  802.              * If an "extra" string was returned,
  803.              * process it as a string of command characters.
  804.              */
  805.             if (extra != NULL)
  806.                 ungetsc(extra);
  807.         }
  808.         /*
  809.          * Clear the cmdbuf string.
  810.          * (But not if we're in the prefix of a command,
  811.          * because the partial command string is kept there.)
  812.          */
  813.         if (action != A_PREFIX)
  814.             cmd_reset();
  815.  
  816.         switch (action)
  817.         {
  818.         case A_DIGIT:
  819.             /*
  820.              * First digit of a number.
  821.              */
  822.             start_mca(A_DIGIT, ":", (void*)NULL);
  823.             goto again;
  824.  
  825.         case A_F_WINDOW:
  826.             /*
  827.              * Forward one window (and set the window size).
  828.              */
  829.             if (number > 0)
  830.                 swindow = number;
  831.             /* FALLTHRU */
  832.         case A_F_SCREEN:
  833.             /*
  834.              * Forward one screen.
  835.              */
  836.             if (number <= 0)
  837.                 number = get_swindow();
  838.             cmd_exec();
  839.             forward(number, 0, 1);
  840.             break;
  841.  
  842.         case A_B_WINDOW:
  843.             /*
  844.              * Backward one window (and set the window size).
  845.              */
  846.             if (number > 0)
  847.                 swindow = number;
  848.             /* FALLTHRU */
  849.         case A_B_SCREEN:
  850.             /*
  851.              * Backward one screen.
  852.              */
  853.             if (number <= 0)
  854.                 number = get_swindow();
  855.             cmd_exec();
  856.             backward(number, 0, 1);
  857.             break;
  858.  
  859.         case A_F_LINE:
  860.             /*
  861.              * Forward N (default 1) line.
  862.              */
  863.             if (number <= 0)
  864.                 number = 1;
  865.             cmd_exec();
  866.             forward(number, 0, 0);
  867.             break;
  868.  
  869.         case A_B_LINE:
  870.             /*
  871.              * Backward N (default 1) line.
  872.              */
  873.             if (number <= 0)
  874.                 number = 1;
  875.             cmd_exec();
  876.             backward(number, 0, 0);
  877.             break;
  878.  
  879.         case A_FF_LINE:
  880.             /*
  881.              * Force forward N (default 1) line.
  882.              */
  883.             if (number <= 0)
  884.                 number = 1;
  885.             cmd_exec();
  886.             forward(number, 1, 0);
  887.             break;
  888.  
  889.         case A_BF_LINE:
  890.             /*
  891.              * Force backward N (default 1) line.
  892.              */
  893.             if (number <= 0)
  894.                 number = 1;
  895.             cmd_exec();
  896.             backward(number, 1, 0);
  897.             break;
  898.         
  899.         case A_FF_SCREEN:
  900.             /*
  901.              * Force forward one screen.
  902.              */
  903.             if (number <= 0)
  904.                 number = get_swindow();
  905.             cmd_exec();
  906.             forward(number, 1, 0);
  907.             break;
  908.  
  909.         case A_F_FOREVER:
  910.             /*
  911.              * Forward forever, ignoring EOF.
  912.              */
  913.             if (ch_getflags() & CH_HELPFILE)
  914.                 break;
  915.             cmd_exec();
  916.             jump_forw();
  917.             ignore_eoi = 1;
  918.             hit_eof = 0;
  919.             while (!sigs)
  920.                 forward(1, 0, 0);
  921.             ignore_eoi = 0;
  922.             /*
  923.              * This gets us back in "F mode" after processing 
  924.              * a non-abort signal (e.g. window-change).  
  925.              */
  926.             if (sigs && !ABORT_SIGS())
  927.                 newaction = A_F_FOREVER;
  928.             break;
  929.  
  930.         case A_F_SCROLL:
  931.             /*
  932.              * Forward N lines 
  933.              * (default same as last 'd' or 'u' command).
  934.              */
  935.             if (number > 0)
  936.                 wscroll = number;
  937.             cmd_exec();
  938.             forward(wscroll, 0, 0);
  939.             break;
  940.  
  941.         case A_B_SCROLL:
  942.             /*
  943.              * Forward N lines 
  944.              * (default same as last 'd' or 'u' command).
  945.              */
  946.             if (number > 0)
  947.                 wscroll = number;
  948.             cmd_exec();
  949.             backward(wscroll, 0, 0);
  950.             break;
  951.  
  952.         case A_FREPAINT:
  953.             /*
  954.              * Flush buffers, then repaint screen.
  955.              * Don't flush the buffers on a pipe!
  956.              */
  957.             if (ch_getflags() & CH_CANSEEK)
  958.             {
  959.                 ch_flush();
  960.                 clr_linenum();
  961.             }
  962.             /* FALLTHRU */
  963.         case A_REPAINT:
  964.             /*
  965.              * Repaint screen.
  966.              */
  967.             cmd_exec();
  968.             repaint();
  969.             break;
  970.  
  971.         case A_GOLINE:
  972.             /*
  973.              * Go to line N, default beginning of file.
  974.              */
  975.             if (number <= 0)
  976.                 number = 1;
  977.             cmd_exec();
  978.             jump_back(number);
  979.             break;
  980.  
  981.         case A_PERCENT:
  982.             /*
  983.              * Go to a specified percentage into the file.
  984.              */
  985.             if (number < 0)
  986.                 number = 0;
  987.             if (number > 100)
  988.                 number = 100;
  989.             cmd_exec();
  990.             jump_percent(number);
  991.             break;
  992.  
  993.         case A_GOEND:
  994.             /*
  995.              * Go to line N, default end of file.
  996.              */
  997.             cmd_exec();
  998.             if (number <= 0)
  999.                 jump_forw();
  1000.             else
  1001.                 jump_back(number);
  1002.             break;
  1003.  
  1004.         case A_GOPOS:
  1005.             /*
  1006.              * Go to a specified byte position in the file.
  1007.              */
  1008.             cmd_exec();
  1009.             if (number < 0)
  1010.                 number = 0;
  1011.             jump_line_loc((POSITION)number, jump_sline);
  1012.             break;
  1013.  
  1014.         case A_STAT:
  1015.             /*
  1016.              * Print file name, etc.
  1017.              */
  1018.             if (ch_getflags() & CH_HELPFILE)
  1019.                 break;
  1020.             cmd_exec();
  1021.             parg.p_string = eq_message();
  1022.             error("%s", &parg);
  1023.             break;
  1024.  
  1025.         case A_VERSION:
  1026.             /*
  1027.              * Print version number, without the "@(#)".
  1028.              */
  1029.             cmd_exec();
  1030.             dispversion();
  1031.             break;
  1032.  
  1033.         case A_QUIT:
  1034.             /*
  1035.              * Exit.
  1036.              */
  1037.             if (curr_ifile != NULL_IFILE && 
  1038.                 ch_getflags() & CH_HELPFILE)
  1039.             {
  1040.                 /*
  1041.                  * Quit while viewing the help file
  1042.                  * just means return to viewing the
  1043.                  * previous file.
  1044.                  */
  1045.                 if (edit_prev(1) == 0)
  1046.                     break;
  1047.             }
  1048.             if (extra != NULL)
  1049.                 quit(*extra);
  1050.             quit(QUIT_OK);
  1051.             break;
  1052.  
  1053. /*
  1054.  * Define abbreviation for a commonly used sequence below.
  1055.  */
  1056. #define    DO_SEARCH()    if (number <= 0) number = 1;    \
  1057.             mca_search();            \
  1058.             cmd_exec();            \
  1059.             multi_search((char *)NULL, number);
  1060.  
  1061.  
  1062.         case A_F_SEARCH:
  1063.             /*
  1064.              * Search forward for a pattern.
  1065.              * Get the first char of the pattern.
  1066.              */
  1067.             search_type = SRCH_FORW;
  1068.             if (number <= 0)
  1069.                 number = 1;
  1070.             mca_search();
  1071.             c = getcc();
  1072.             goto again;
  1073.  
  1074.         case A_B_SEARCH:
  1075.             /*
  1076.              * Search backward for a pattern.
  1077.              * Get the first char of the pattern.
  1078.              */
  1079.             search_type = SRCH_BACK;
  1080.             if (number <= 0)
  1081.                 number = 1;
  1082.             mca_search();
  1083.             c = getcc();
  1084.             goto again;
  1085.  
  1086.         case A_AGAIN_SEARCH:
  1087.             /*
  1088.              * Repeat previous search.
  1089.              */
  1090.             DO_SEARCH();
  1091.             break;
  1092.         
  1093.         case A_T_AGAIN_SEARCH:
  1094.             /*
  1095.              * Repeat previous search, multiple files.
  1096.              */
  1097.             search_type |= SRCH_PAST_EOF;
  1098.             DO_SEARCH();
  1099.             break;
  1100.  
  1101.         case A_REVERSE_SEARCH:
  1102.             /*
  1103.              * Repeat previous search, in reverse direction.
  1104.              */
  1105.             save_search_type = search_type;
  1106.             search_type = SRCH_REVERSE(search_type);
  1107.             DO_SEARCH();
  1108.             search_type = save_search_type;
  1109.             break;
  1110.  
  1111.         case A_T_REVERSE_SEARCH:
  1112.             /* 
  1113.              * Repeat previous search, 
  1114.              * multiple files in reverse direction.
  1115.              */
  1116.             save_search_type = search_type;
  1117.             search_type = SRCH_REVERSE(search_type);
  1118.             search_type |= SRCH_PAST_EOF;
  1119.             DO_SEARCH();
  1120.             search_type = save_search_type;
  1121.             break;
  1122.  
  1123.         case A_UNDO_SEARCH:
  1124.             undo_search();
  1125.             break;
  1126.  
  1127.         case A_HELP:
  1128.             /*
  1129.              * Help.
  1130.              */
  1131.             if (ch_getflags() & CH_HELPFILE)
  1132.                 break;
  1133.             cmd_exec();
  1134.             (void) edit(FAKE_HELPFILE);
  1135.             break;
  1136.  
  1137.         case A_EXAMINE:
  1138. #if EXAMINE
  1139.             /*
  1140.              * Edit a new file.  Get the filename.
  1141.              */
  1142.             if (secure)
  1143.             {
  1144.                 error("Command not available", NULL_PARG);
  1145.                 break;
  1146.             }
  1147.             start_mca(A_EXAMINE, "Examine: ", ml_examine);
  1148.             c = getcc();
  1149.             goto again;
  1150. #else
  1151.             error("Command not available", NULL_PARG);
  1152.             break;
  1153. #endif
  1154.             
  1155.         case A_VISUAL:
  1156.             /*
  1157.              * Invoke an editor on the input file.
  1158.              */
  1159. #if EDITOR
  1160.             if (secure)
  1161.             {
  1162.                 error("Command not available", NULL_PARG);
  1163.                 break;
  1164.             }
  1165.             if (ch_getflags() & CH_HELPFILE)
  1166.                 break;
  1167.             if (strcmp(get_filename(curr_ifile), "-") == 0)
  1168.             {
  1169.                 error("Cannot edit standard input", NULL_PARG);
  1170.                 break;
  1171.             }
  1172.             if (curr_altfilename != NULL)
  1173.             {
  1174.                 error("Cannot edit file processed with LESSOPEN", 
  1175.                     NULL_PARG);
  1176.                 break;
  1177.             }
  1178.             start_mca(A_SHELL, "!", ml_shell);
  1179.             /*
  1180.              * Expand the editor prototype string
  1181.              * and pass it to the system to execute.
  1182.              * (Make sure the screen is displayed so the
  1183.              * expansion of "+%lm" works.)
  1184.              */
  1185.             make_display();
  1186.             cmd_exec();
  1187.             lsystem(pr_expand(editproto, 0), (char*)NULL);
  1188.             break;
  1189. #else
  1190.             error("Command not available", NULL_PARG);
  1191.             break;
  1192. #endif
  1193.  
  1194.         case A_NEXT_FILE:
  1195.             /*
  1196.              * Examine next file.
  1197.              */
  1198.             if (number <= 0)
  1199.                 number = 1;
  1200.             if (edit_next(number))
  1201.             {
  1202.                 if (quit_at_eof && hit_eof && 
  1203.                     !(ch_getflags() & CH_HELPFILE))
  1204.                     quit(QUIT_OK);
  1205.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1206.                 error("No %snext file", &parg);
  1207.             }
  1208.             break;
  1209.  
  1210.         case A_PREV_FILE:
  1211.             /*
  1212.              * Examine previous file.
  1213.              */
  1214.             if (number <= 0)
  1215.                 number = 1;
  1216.             if (edit_prev(number))
  1217.             {
  1218.                 parg.p_string = (number > 1) ? "(N-th) " : "";
  1219.                 error("No %sprevious file", &parg);
  1220.             }
  1221.             break;
  1222.  
  1223.         case A_INDEX_FILE:
  1224.             /*
  1225.              * Examine a particular file.
  1226.              */
  1227.             if (number <= 0)
  1228.                 number = 1;
  1229.             if (edit_index(number))
  1230.                 error("No such file", NULL_PARG);
  1231.             break;
  1232.  
  1233.         case A_OPT_TOGGLE:
  1234.             start_mca(A_OPT_TOGGLE, "-", (void*)NULL);
  1235.             optflag = OPT_TOGGLE;
  1236.             c = getcc();
  1237.             goto again;
  1238.  
  1239.         case A_DISP_OPTION:
  1240.             /*
  1241.              * Report a flag setting.
  1242.              */
  1243.             start_mca(A_DISP_OPTION, "_", (void*)NULL);
  1244.             c = getcc();
  1245.             if (c == erase_char || c == kill_char)
  1246.                 break;
  1247.             toggle_option(c, "", OPT_NO_TOGGLE);
  1248.             break;
  1249.  
  1250.         case A_FIRSTCMD:
  1251.             /*
  1252.              * Set an initial command for new files.
  1253.              */
  1254.             start_mca(A_FIRSTCMD, "+", (void*)NULL);
  1255.             c = getcc();
  1256.             goto again;
  1257.  
  1258.         case A_SHELL:
  1259.             /*
  1260.              * Shell escape.
  1261.              */
  1262. #if SHELL_ESCAPE
  1263.             if (secure)
  1264.             {
  1265.                 error("Command not available", NULL_PARG);
  1266.                 break;
  1267.             }
  1268.             start_mca(A_SHELL, "!", ml_shell);
  1269.             c = getcc();
  1270.             goto again;
  1271. #else
  1272.             error("Command not available", NULL_PARG);
  1273.             break;
  1274. #endif
  1275.  
  1276.         case A_SETMARK:
  1277.             /*
  1278.              * Set a mark.
  1279.              */
  1280.             if (ch_getflags() & CH_HELPFILE)
  1281.                 break;
  1282.             start_mca(A_SETMARK, "mark: ", (void*)NULL);
  1283.             c = getcc();
  1284.             if (c == erase_char || c == kill_char ||
  1285.                 c == '\n' || c == '\r')
  1286.                 break;
  1287.             setmark(c);
  1288.             break;
  1289.  
  1290.         case A_GOMARK:
  1291.             /*
  1292.              * Go to a mark.
  1293.              */
  1294.             start_mca(A_GOMARK, "goto mark: ", (void*)NULL);
  1295.             c = getcc();
  1296.             if (c == erase_char || c == kill_char || 
  1297.                 c == '\n' || c == '\r')
  1298.                 break;
  1299.             gomark(c);
  1300.             break;
  1301.  
  1302.         case A_PIPE:
  1303. #if PIPEC
  1304.             if (secure)
  1305.             {
  1306.                 error("Command not available", NULL_PARG);
  1307.                 break;
  1308.             }
  1309.             start_mca(A_PIPE, "|mark: ", (void*)NULL);
  1310.             c = getcc();
  1311.             if (c == erase_char || c == kill_char)
  1312.                 break;
  1313.             if (c == '\n' || c == '\r')
  1314.                 c = '.';
  1315.             if (badmark(c))
  1316.                 break;
  1317.             pipec = c;
  1318.             start_mca(A_PIPE, "!", ml_shell);
  1319.             c = getcc();
  1320.             goto again;
  1321. #else
  1322.             error("Command not available", NULL_PARG);
  1323.             break;
  1324. #endif
  1325.  
  1326.         case A_B_BRACKET:
  1327.         case A_F_BRACKET:
  1328.             start_mca(action, "Brackets: ", (void*)NULL);
  1329.             c = getcc();
  1330.             goto again;
  1331.  
  1332.         case A_LSHIFT:
  1333.             if (number <= 0)
  1334.                 number = 8;
  1335.             if (number > hshift)
  1336.                 number = hshift;
  1337.             hshift -= number;
  1338.             screen_trashed = 1;
  1339.             break;
  1340.  
  1341.         case A_RSHIFT:
  1342.             if (number <= 0)
  1343.                 number = 8;
  1344.             hshift += number;
  1345.             screen_trashed = 1;
  1346.             break;
  1347.  
  1348.         case A_PREFIX:
  1349.             /*
  1350.              * The command is incomplete (more chars are needed).
  1351.              * Display the current char, so the user knows
  1352.              * what's going on, and get another character.
  1353.              */
  1354.             if (mca != A_PREFIX)
  1355.             {
  1356.                 cmd_reset();
  1357.                 start_mca(A_PREFIX, " ", (void*)NULL);
  1358.                 (void) cmd_char(c);
  1359.             }
  1360.             c = getcc();
  1361.             goto again;
  1362.  
  1363.         case A_NOACTION:
  1364.             break;
  1365.  
  1366.         default:
  1367.             bell();
  1368.             break;
  1369.         }
  1370.     }
  1371. }
  1372.