home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume16 / less5 / part02 / command.c next >
Encoding:
C/C++ Source or Header  |  1988-09-22  |  14.4 KB  |  860 lines

  1. /*
  2.  * User-level command processor.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #include "cmd.h"
  8.  
  9. #define    NO_MCA        0
  10. #define    MCA_DONE    1
  11. #define    MCA_MORE    2
  12.  
  13. extern int erase_char, kill_char;
  14. extern int ispipe;
  15. extern int sigs;
  16. extern int quit_at_eof;
  17. extern int hit_eof;
  18. extern int sc_width;
  19. extern int sc_height;
  20. extern int sc_window;
  21. extern int curr_ac;
  22. extern int ac;
  23. extern int quitting;
  24. extern int scroll;
  25. extern char *first_cmd;
  26. extern char *every_first_cmd;
  27. extern char version[];
  28. extern char *current_file;
  29. #if EDITOR
  30. extern char *editor;
  31. #endif
  32. extern int screen_trashed;    /* The screen has been overwritten */
  33.  
  34. static char cmdbuf[120];    /* Buffer for holding a multi-char command */
  35. #if SHELL_ESCAPE
  36. static char *shellcmd = NULL;    /* For holding last shell command for "!!" */
  37. #endif
  38. static char *cp;        /* Pointer into cmdbuf */
  39. static int cmd_col;        /* Current column of the multi-char command */
  40. static int mca;            /* The multicharacter command (action) */
  41. static int last_mca;        /* The previous mca */
  42. static int number;        /* The number typed by the user */
  43. static int wsearch;        /* Search for matches (1) or non-matches (0) */
  44.  
  45. /*
  46.  * Reset command buffer (to empty).
  47.  */
  48. cmd_reset()
  49. {
  50.     cp = cmdbuf;
  51. }
  52.  
  53. /*
  54.  * Backspace in command buffer.
  55.  */
  56.     static int
  57. cmd_erase()
  58. {
  59.     if (cp == cmdbuf)
  60.         /*
  61.          * Backspace past beginning of the string:
  62.          * this usually means abort the command.
  63.          */
  64.         return (1);
  65.  
  66.     if (control_char(*--cp))
  67.     {
  68.         /*
  69.          * Erase an extra character, for the carat.
  70.          */
  71.         backspace();
  72.         cmd_col--;
  73.     }
  74.     backspace();
  75.     cmd_col--;
  76.     return (0);
  77. }
  78.  
  79. /*
  80.  * Set up the display to start a new multi-character command.
  81.  */
  82. start_mca(action, prompt)
  83.     int action;
  84.     char *prompt;
  85. {
  86.     lower_left();
  87.     clear_eol();
  88.     putstr(prompt);
  89.     cmd_col = strlen(prompt);
  90.     mca = action;
  91. }
  92.  
  93. /*
  94.  * Process a single character of a multi-character command, such as
  95.  * a number, or the pattern of a search command.
  96.  */
  97.     static int
  98. cmd_char(c)
  99.     int c;
  100. {
  101.     if (c == erase_char)
  102.     {
  103.         if (cmd_erase())
  104.             return (1);
  105.     } else if (c == kill_char)
  106.     {
  107.         /* {{ Could do this faster, but who cares? }} */
  108.         while (cmd_erase() == 0)
  109.             ;
  110.     } else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
  111.     {
  112.         /*
  113.          * No room in the command buffer.
  114.          */
  115.         bell();
  116.     } else if (cmd_col >= sc_width-3)
  117.     {
  118.         /*
  119.          * No room on the screen.
  120.          * {{ Could get fancy here; maybe shift the displayed
  121.          *    line and make room for more chars, like ksh. }}
  122.          */
  123.         bell();
  124.     } else
  125.     {
  126.         /*
  127.          * Append the character to the string.
  128.          */
  129.         *cp++ = c;
  130.         if (control_char(c))
  131.         {
  132.             putchr('^');
  133.             cmd_col++;
  134.             c = carat_char(c);
  135.         }
  136.         putchr(c);
  137.         cmd_col++;
  138.     }
  139.     return (0);
  140. }
  141.  
  142. /*
  143.  * Return the number currently in the command buffer.
  144.  */
  145.     static int
  146. cmd_int()
  147. {
  148.     *cp = '\0';
  149.     cp = cmdbuf;
  150.     return (atoi(cmdbuf));
  151. }
  152.  
  153. /*
  154.  * Move the cursor to lower left before executing a command.
  155.  * This looks nicer if the command takes a long time before
  156.  * updating the screen.
  157.  */
  158.     static void
  159. cmd_exec()
  160. {
  161.     lower_left();
  162.     flush();
  163. }
  164.  
  165. /*
  166.  * Display the appropriate prompt.
  167.  */
  168.     static void
  169. prompt()
  170. {
  171.     register char *p;
  172.  
  173.     if (first_cmd != NULL && *first_cmd != '\0')
  174.     {
  175.         /*
  176.          * No prompt necessary if commands are from first_cmd
  177.          * rather than from the user.
  178.          */
  179.         return;
  180.     }
  181.  
  182.     /*
  183.      * If nothing is displayed yet, display starting from line 1.
  184.      */
  185.     if (position(TOP) == NULL_POSITION)
  186.         jump_back(1);
  187.     else if (screen_trashed)
  188.         repaint();
  189.  
  190.     /*
  191.      * If the -E flag is set and we've hit EOF on the last file, quit.
  192.      */
  193.     if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
  194.         quit();
  195.  
  196.     /*
  197.      * Select the proper prompt and display it.
  198.      */
  199.     lower_left();
  200.     clear_eol();
  201.     p = pr_string();
  202.     if (p == NULL)
  203.         putchr(':');
  204.     else
  205.     {
  206.         so_enter();
  207.         putstr(p);
  208.         so_exit();
  209.     }
  210. }
  211.  
  212. /*
  213.  * Get command character.
  214.  * The character normally comes from the keyboard,
  215.  * but may come from the "first_cmd" string.
  216.  */
  217.     static int
  218. getcc()
  219. {
  220.     if (first_cmd == NULL)
  221.         return (getchr());
  222.  
  223.     if (*first_cmd == '\0')
  224.     {
  225.         /*
  226.          * Reached end of first_cmd input.
  227.          */
  228.         first_cmd = NULL;
  229.         if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  230.         {
  231.             /*
  232.              * Command is incomplete, so try to complete it.
  233.              * There are only two cases:
  234.              * 1. We have "/string" but no newline.  Add the \n.
  235.              * 2. We have a number but no command.  Treat as #g.
  236.              * (This is all pretty hokey.)
  237.              */
  238.             if (mca != A_DIGIT)
  239.                 /* Not a number; must be search string */
  240.                 return ('\n'); 
  241.             else
  242.                 /* A number; append a 'g' */
  243.                 return ('g');
  244.         }
  245.         return (getchr());
  246.     }
  247.     return (*first_cmd++);
  248. }
  249.  
  250. /*
  251.  * Execute a multicharacter command.
  252.  */
  253.     static void
  254. exec_mca()
  255. {
  256.     register char *p;
  257.     register int n;
  258.  
  259.     *cp = '\0';
  260.     cmd_exec();
  261.     switch (mca)
  262.     {
  263.     case A_F_SEARCH:
  264.         search(1, cmdbuf, number, wsearch);
  265.         break;
  266.     case A_B_SEARCH:
  267.         search(0, cmdbuf, number, wsearch);
  268.         break;
  269.     case A_FIRSTCMD:
  270.         /*
  271.          * Skip leading spaces or + signs in the string.
  272.          */
  273.         for (p = cmdbuf;  *p == '+' || *p == ' ';  p++)
  274.             ;
  275.         if (every_first_cmd != NULL)
  276.             free(every_first_cmd);
  277.         if (*p == '\0')
  278.             every_first_cmd = NULL;
  279.         else
  280.             every_first_cmd = save(p);
  281.         break;
  282.     case A_TOGGLE_OPTION:
  283.         toggle_option(cmdbuf, 1);
  284.         break;
  285.     case A_EXAMINE:
  286.         /*
  287.          * Ignore leading spaces in the filename.
  288.          */
  289.         for (p = cmdbuf;  *p == ' ';  p++)
  290.             ;
  291.         edit(glob(p));
  292.         break;
  293. #if SHELL_ESCAPE
  294.     case A_SHELL:
  295.         /*
  296.          * !! just uses whatever is in shellcmd.
  297.          * Otherwise, copy cmdbuf to shellcmd,
  298.          * replacing any '%' with the current
  299.          * file name.
  300.          */
  301.         if (*cmdbuf != '!')
  302.         {
  303.             register char *fr, *to;
  304.  
  305.             /*
  306.              * Make one pass to see how big a buffer we 
  307.              * need to allocate for the expanded shell cmd.
  308.              */
  309.             for (fr = cmdbuf;  *fr != '\0';  fr++)
  310.                 if (*fr == '%')
  311.                     n += strlen(current_file);
  312.                 else
  313.                     n++;
  314.  
  315.             if (shellcmd != NULL)
  316.                 free(shellcmd);
  317.             shellcmd = calloc(n+1, sizeof(char));
  318.             if (shellcmd == NULL)
  319.             {
  320.                 error("cannot allocate memory");
  321.                 break;
  322.             }
  323.  
  324.             /*
  325.              * Now copy the shell cmd, expanding any "%"
  326.              * into the current filename.
  327.              */
  328.             to = shellcmd;
  329.             for (fr = cmdbuf;  *fr != '\0';  fr++)
  330.             {
  331.                 if (*fr != '%')
  332.                     *to++ = *fr;
  333.                 else
  334.                 {
  335.                     strcpy(to, current_file);
  336.                     to += strlen(to);
  337.                 }
  338.             }
  339.             *to = '\0';
  340.         }
  341.  
  342.         if (shellcmd == NULL)
  343.             lsystem("");
  344.         else
  345.             lsystem(shellcmd);
  346.         error("!done");
  347.         break;
  348. #endif
  349.     }
  350. }
  351.  
  352. /*
  353.  * Add a character to a multi-character command.
  354.  */
  355.     static int
  356. mca_char(c)
  357.     int c;
  358. {
  359.     switch (mca)
  360.     {
  361.     case 0:
  362.         /*
  363.          * Not in a multicharacter command.
  364.          */
  365.         return (NO_MCA);
  366.  
  367.     case A_PREFIX:
  368.         /*
  369.          * In the prefix of a command.
  370.          */
  371.         return (NO_MCA);
  372.  
  373.     case A_DIGIT:
  374.         /*
  375.          * Entering digits of a number.
  376.          * Terminated by a non-digit.
  377.          */
  378.         if ((c < '0' || c > '9') &&
  379.             c != erase_char && c != kill_char)
  380.         {
  381.             /*
  382.              * Not part of the number.
  383.              * Treat as a normal command character.
  384.              */
  385.             number = cmd_int();
  386.             mca = 0;
  387.             return (NO_MCA);
  388.         }
  389.         break;
  390.  
  391.     case A_TOGGLE_OPTION:
  392.         /*
  393.          * Special case for the TOGGLE_OPTION command.
  394.          * if the option letter which was entered is a
  395.          * single-char option, execute the command immediately,
  396.          * so he doesn't have to hit RETURN.
  397.          */
  398.         if (cp == cmdbuf && c != erase_char && c != kill_char &&
  399.             single_char_option(c))
  400.         {
  401.             cmdbuf[0] = c;
  402.             cmdbuf[1] = '\0';
  403.             toggle_option(cmdbuf, 1);
  404.             return (MCA_DONE);
  405.         }
  406.         break;
  407.     }
  408.  
  409.     /*
  410.      * Any other multicharacter command
  411.      * is terminated by a newline.
  412.      */
  413.     if (c == '\n' || c == '\r')
  414.     {
  415.         /*
  416.          * Execute the command.
  417.          */
  418.         exec_mca();
  419.         return (MCA_DONE);
  420.     }
  421.     /*
  422.      * Append the char to the command buffer.
  423.      */
  424.     if (cmd_char(c))
  425.         /*
  426.          * Abort the multi-char command.
  427.          */
  428.         return (MCA_DONE);
  429.     /*
  430.      * Need another character.
  431.      */
  432.     return (MCA_MORE);
  433. }
  434.  
  435. /*
  436.  * Main command processor.
  437.  * Accept and execute commands until a quit command, then return.
  438.  */
  439.     public void
  440. commands()
  441. {
  442.     register int c;
  443.     register int action;
  444.  
  445.     last_mca = 0;
  446.     scroll = (sc_height + 1) / 2;
  447.  
  448.     for (;;)
  449.     {
  450.         mca = 0;
  451.         number = 0;
  452.  
  453.         /*
  454.          * See if any signals need processing.
  455.          */
  456.         if (sigs)
  457.         {
  458.             psignals();
  459.             if (quitting)
  460.                 quit();
  461.         }
  462.             
  463.         /*
  464.          * Display prompt and accept a character.
  465.          */
  466.         cmd_reset();
  467.         prompt();
  468.         noprefix();
  469.         c = getcc();
  470.  
  471.     again:
  472.         if (sigs)
  473.             continue;
  474.  
  475.         /*
  476.          * If we are in a multicharacter command, call mca_char.
  477.          * Otherwise we call cmd_decode to determine the
  478.          * action to be performed.
  479.          */
  480.         if (mca)
  481.             switch (mca_char(c))
  482.             {
  483.             case MCA_MORE:
  484.                 /*
  485.                  * Need another character.
  486.                  */
  487.                 c = getcc();
  488.                 goto again;
  489.             case MCA_DONE:
  490.                 /*
  491.                  * Command has been handled by mca_char.
  492.                  * Start clean with a prompt.
  493.                  */
  494.                 continue;
  495.             case NO_MCA:
  496.                 /*
  497.                  * Not a multi-char command
  498.                  * (at least, not anymore).
  499.                  */
  500.                 break;
  501.             }
  502.  
  503.         /*
  504.          * Decode the command character and decide what to do.
  505.          */
  506.         switch (action = cmd_decode(c))
  507.         {
  508.         case A_DIGIT:
  509.             /*
  510.              * First digit of a number.
  511.              */
  512.             start_mca(A_DIGIT, ":");
  513.             goto again;
  514.  
  515.         case A_F_SCREEN:
  516.             /*
  517.              * Forward one screen.
  518.              */
  519.             if (number <= 0)
  520.                 number = sc_window;
  521.             if (number <= 0)
  522.                 number = sc_height - 1;
  523.             cmd_exec();
  524.             forward(number, 1);
  525.             break;
  526.  
  527.         case A_B_SCREEN:
  528.             /*
  529.              * Backward one screen.
  530.              */
  531.             if (number <= 0)
  532.                 number = sc_window;
  533.             if (number <= 0)
  534.                 number = sc_height - 1;
  535.             cmd_exec();
  536.             backward(number, 1);
  537.             break;
  538.  
  539.         case A_F_LINE:
  540.             /*
  541.              * Forward N (default 1) line.
  542.              */
  543.             if (number <= 0)
  544.                 number = 1;
  545.             cmd_exec();
  546.             forward(number, 0);
  547.             break;
  548.  
  549.         case A_B_LINE:
  550.             /*
  551.              * Backward N (default 1) line.
  552.              */
  553.             if (number <= 0)
  554.                 number = 1;
  555.             cmd_exec();
  556.             backward(number, 0);
  557.             break;
  558.  
  559.         case A_F_SCROLL:
  560.             /*
  561.              * Forward N lines 
  562.              * (default same as last 'd' or 'u' command).
  563.              */
  564.             if (number > 0)
  565.                 scroll = number;
  566.             cmd_exec();
  567.             forward(scroll, 0);
  568.             break;
  569.  
  570.         case A_B_SCROLL:
  571.             /*
  572.              * Forward N lines 
  573.              * (default same as last 'd' or 'u' command).
  574.              */
  575.             if (number > 0)
  576.                 scroll = number;
  577.             cmd_exec();
  578.             backward(scroll, 0);
  579.             break;
  580.  
  581.         case A_FREPAINT:
  582.             /*
  583.              * Flush buffers, then repaint screen.
  584.              * Don't flush the buffers on a pipe!
  585.              */
  586.             if (!ispipe)
  587.             {
  588.                 ch_init(0, 0);
  589.                 clr_linenum();
  590.             }
  591.             /* FALLTHRU */
  592.         case A_REPAINT:
  593.             /*
  594.              * Repaint screen.
  595.              */
  596.             cmd_exec();
  597.             repaint();
  598.             break;
  599.  
  600.         case A_GOLINE:
  601.             /*
  602.              * Go to line N, default beginning of file.
  603.              */
  604.             if (number <= 0)
  605.                 number = 1;
  606.             cmd_exec();
  607.             jump_back(number);
  608.             break;
  609.  
  610.         case A_PERCENT:
  611.             /*
  612.              * Go to a specified percentage into the file.
  613.              */
  614.             if (number < 0)
  615.                 number = 0;
  616.             if (number > 100)
  617.                 number = 100;
  618.             cmd_exec();
  619.             jump_percent(number);
  620.             break;
  621.  
  622.         case A_GOEND:
  623.             /*
  624.              * Go to line N, default end of file.
  625.              */
  626.             cmd_exec();
  627.             if (number <= 0)
  628.                 jump_forw();
  629.             else
  630.                 jump_back(number);
  631.             break;
  632.  
  633.         case A_STAT:
  634.             /*
  635.              * Print file name, etc.
  636.              */
  637.             cmd_exec();
  638.             error(eq_message());
  639.             break;
  640.             
  641.         case A_VERSION:
  642.             /*
  643.              * Print version number, without the "@(#)".
  644.              */
  645.             cmd_exec();
  646.             error(version+4);
  647.             break;
  648.  
  649.         case A_QUIT:
  650.             /*
  651.              * Exit.
  652.              */
  653.             quit();
  654.  
  655.         case A_F_SEARCH:
  656.         case A_B_SEARCH:
  657.             /*
  658.              * Search for a pattern.
  659.              * Accept chars of the pattern until \n.
  660.              */
  661.             if (number <= 0)
  662.                 number = 1;
  663.             start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
  664.             last_mca = mca;
  665.             wsearch = 1;
  666.             c = getcc();
  667.             if (c == '!')
  668.             {
  669.                 /*
  670.                  * Invert the sense of the search.
  671.                  * Set wsearch to 0 and get a new
  672.                  * character for the start of the pattern.
  673.                  */
  674.                 start_mca(action, 
  675.                     (action==A_F_SEARCH) ? "!/" : "!?");
  676.                 wsearch = 0;
  677.                 c = getcc();
  678.             }
  679.             goto again;
  680.  
  681.         case A_AGAIN_SEARCH:
  682.             /*
  683.              * Repeat previous search.
  684.              */
  685.             if (number <= 0)
  686.                 number = 1;
  687.             if (wsearch)
  688.                 start_mca(last_mca, 
  689.                     (last_mca==A_F_SEARCH) ? "/" : "?");
  690.             else
  691.                 start_mca(last_mca, 
  692.                     (last_mca==A_F_SEARCH) ? "!/" : "!?");
  693.             cmd_exec();
  694.             search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
  695.             break;
  696.  
  697.         case A_HELP:
  698.             /*
  699.              * Help.
  700.              */
  701.             lower_left();
  702.             clear_eol();
  703.             putstr("help");
  704.             cmd_exec();
  705.             help();
  706.             break;
  707.  
  708.         case A_EXAMINE:
  709.             /*
  710.              * Edit a new file.  Get the filename.
  711.              */
  712.             cmd_reset();
  713.             start_mca(A_EXAMINE, "Examine: ");
  714.             c = getcc();
  715.             goto again;
  716.             
  717.         case A_VISUAL:
  718.             /*
  719.              * Invoke an editor on the input file.
  720.              */
  721. #if EDITOR
  722.             if (ispipe)
  723.             {
  724.                 error("Cannot edit standard input");
  725.                 break;
  726.             }
  727.             /*
  728.              * Try to pass the line number to the editor.
  729.              */
  730.             cmd_exec();
  731.             c = currline(MIDDLE);
  732.             if (c == 0)
  733.                 sprintf(cmdbuf, "%s %s",
  734.                     editor, current_file);
  735.             else
  736.                 sprintf(cmdbuf, "%s +%d %s",
  737.                     editor, c, current_file);
  738.             lsystem(cmdbuf);
  739.             ch_init(0, 0);
  740.             clr_linenum();
  741.             break;
  742. #else
  743.             error("Command not available");
  744.             break;
  745. #endif
  746.  
  747.         case A_NEXT_FILE:
  748.             /*
  749.              * Examine next file.
  750.              */
  751.             if (number <= 0)
  752.                 number = 1;
  753.             next_file(number);
  754.             break;
  755.  
  756.         case A_PREV_FILE:
  757.             /*
  758.              * Examine previous file.
  759.              */
  760.             if (number <= 0)
  761.                 number = 1;
  762.             prev_file(number);
  763.             break;
  764.  
  765.         case A_TOGGLE_OPTION:
  766.             /*
  767.              * Toggle a flag setting.
  768.              */
  769.             cmd_reset();
  770.             start_mca(A_TOGGLE_OPTION, "-");
  771.             c = getcc();
  772.             goto again;
  773.  
  774.         case A_DISP_OPTION:
  775.             /*
  776.              * Report a flag setting.
  777.              */
  778.             cmd_reset();
  779.             start_mca(A_DISP_OPTION, "_");
  780.             c = getcc();
  781.             if (c == erase_char || c == kill_char)
  782.                 break;
  783.             cmdbuf[0] = c;
  784.             cmdbuf[1] = '\0';
  785.             toggle_option(cmdbuf, 0);
  786.             break;
  787.  
  788.         case A_FIRSTCMD:
  789.             /*
  790.              * Set an initial command for new files.
  791.              */
  792.             cmd_reset();
  793.             start_mca(A_FIRSTCMD, "+");
  794.             c = getcc();
  795.             goto again;
  796.  
  797.         case A_SHELL:
  798.             /*
  799.              * Shell escape.
  800.              */
  801. #if SHELL_ESCAPE
  802.             cmd_reset();
  803.             start_mca(A_SHELL, "!");
  804.             c = getcc();
  805.             goto again;
  806. #else
  807.             error("Command not available");
  808.             break;
  809. #endif
  810.  
  811.         case A_SETMARK:
  812.             /*
  813.              * Set a mark.
  814.              */
  815.             lower_left();
  816.             clear_eol();
  817.             start_mca(A_SETMARK, "mark: ");
  818.             c = getcc();
  819.             if (c == erase_char || c == kill_char)
  820.                 break;
  821.             setmark(c);
  822.             break;
  823.  
  824.         case A_GOMARK:
  825.             /*
  826.              * Go to a mark.
  827.              */
  828.             lower_left();
  829.             clear_eol();
  830.             start_mca(A_GOMARK, "goto mark: ");
  831.             c = getcc();
  832.             if (c == erase_char || c == kill_char)
  833.                 break;
  834.             gomark(c);
  835.             break;
  836.  
  837.         case A_PREFIX:
  838.             /*
  839.              * The command is incomplete (more chars are needed).
  840.              * Display the current char so the user knows
  841.              * what's going on and get another character.
  842.              */
  843.             if (mca != A_PREFIX)
  844.                 start_mca(A_PREFIX, "& ");
  845.             if (control_char(c))
  846.             {
  847.                 putchr('^');
  848.                 c = carat_char(c);
  849.             }
  850.             putchr(c);
  851.             c = getcc();
  852.             goto again;
  853.  
  854.         default:
  855.             bell();
  856.             break;
  857.         }
  858.     }
  859. }
  860.