home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume6 / less.patch / command.c next >
C/C++ Source or Header  |  1986-11-30  |  10KB  |  597 lines

  1. /*
  2.  * User-level command processor.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #include <setjmp.h>
  8.  
  9. extern jmp_buf main_loop;
  10. extern int erase_char, kill_char;
  11. extern int pr_type;
  12. extern int sigs;
  13. extern int ispipe;
  14. extern int quit_at_eof;
  15. extern int hit_eof;
  16. extern int sc_width, sc_height;
  17. extern int sc_window;
  18. extern char *first_cmd;
  19. extern char version[];
  20. extern char current_file[];
  21. extern char *editor;
  22.  
  23. static char cmdbuf[90];        /* Buffer for holding a multi-char command */
  24. static char *cp;        /* Pointer into cmdbuf */
  25. static int cmd_col;        /* Current column of the multi-char command */
  26. static char mcc;        /* The multi-char command letter (e.g. '/') */
  27. static char last_mcc;        /* The previous mcc */
  28.  
  29. /*
  30.  * Reset command buffer (to empty).
  31.  */
  32. cmd_reset()
  33. {
  34.     cp = cmdbuf;
  35. }
  36.  
  37. /*
  38.  * Backspace in command buffer.
  39.  */
  40.     static int
  41. cmd_erase()
  42. {
  43.     if (cp == cmdbuf)
  44.         /*
  45.          * Backspace past beginning of the string:
  46.          * this usually means abort the command.
  47.          */
  48.         return (1);
  49.  
  50.     if (control_char(*--cp))
  51.     {
  52.         /*
  53.          * Erase an extra character, for the carat.
  54.          */
  55.         backspace();
  56.         cmd_col--;
  57.     }
  58.     backspace();
  59.     cmd_col--;
  60.     return (0);
  61. }
  62.  
  63. /*
  64.  * Set up the display to start a new multi-character command.
  65.  */
  66. start_mcc()
  67. {
  68.     lower_left();
  69.     clear_eol();
  70.     putc(mcc);
  71.     cmd_col = 1;
  72. }
  73.  
  74. /*
  75.  * Process a single character of a multi-character command, such as
  76.  * a number, or the pattern of a search command.
  77.  */
  78.     static int
  79. cmd_char(c)
  80.     int c;
  81. {
  82.     if (c == erase_char)
  83.     {
  84.         if (cmd_erase())
  85.             return (1);
  86.     } else if (c == kill_char)
  87.     {
  88.         /* {{ Could do this faster, but who cares? }} */
  89.         while (cmd_erase() == 0)
  90.             ;
  91.     } else
  92.     {
  93.         /*
  94.          * Append the character to the string,
  95.          * if there is room in the buffer and on the screen.
  96.          */
  97.         if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
  98.         {
  99.             *cp++ = c;
  100.             if (control_char(c))
  101.             {
  102.                 putc('^');
  103.                 cmd_col++;
  104.                 c = carat_char(c);
  105.             }
  106.             putc(c);
  107.             cmd_col++;
  108.         } else
  109.             bell();
  110.     }
  111.     return (0);
  112. }
  113.  
  114. /*
  115.  * Return the number currently in the command buffer.
  116.  */
  117.     static int
  118. cmd_int()
  119. {
  120.     *cp = '\0';
  121.     cp = cmdbuf;
  122.     return (atoi(cmdbuf));
  123. }
  124.  
  125. /*
  126.  * Move the cursor to lower left before executing a command.
  127.  * This looks nicer if the command takes a long time before
  128.  * updating the screen.
  129.  */
  130.     static void
  131. cmd_exec()
  132. {
  133.     lower_left();
  134.     flush();
  135. }
  136.  
  137. /*
  138.  * Display the appropriate prompt.
  139.  */
  140.     static void
  141. prompt()
  142. {
  143.     register char *p;
  144.  
  145.     if (first_cmd != NULL && *first_cmd != '\0')
  146.         /*
  147.          * No prompt necessary if commands are from first_cmd
  148.          * rather than from the user.
  149.          */
  150.         return;
  151.  
  152.     /*
  153.      * Select the proper prompt and display it.
  154.      */
  155.     p = pr_string();
  156.     if (p == NULL)
  157.         putc(':');
  158.     else
  159.     {
  160.         so_enter();
  161.         puts(p);
  162.         so_exit();
  163.     }
  164. }
  165.  
  166. /*
  167.  * Get command character.
  168.  * The character normally comes from the keyboard,
  169.  * but may come from the "first_cmd" string.
  170.  */
  171.     static int
  172. getcc()
  173. {
  174.     if (first_cmd == NULL)
  175.         return (getc());
  176.  
  177.     if (*first_cmd == '\0')
  178.     {
  179.         /*
  180.          * Reached end of first_cmd input.
  181.          */
  182.         first_cmd = NULL;
  183.         if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  184.         {
  185.             /*
  186.              * Command is incomplete, so try to complete it.
  187.              * There are only two cases:
  188.              * 1. We have "/string" but no newline.  Add the \n.
  189.              * 2. We have a number but no command.  Treat as #g.
  190.              * (This is all pretty hokey.)
  191.              */
  192.             if (mcc != ':')
  193.                 return ('\n'); 
  194.             else
  195.                 return ('g');
  196.         }
  197.         return (getc());
  198.     }
  199.     return (*first_cmd++);
  200. }
  201.  
  202. /*
  203.  * Main command processor.
  204.  * Accept and execute commands until a quit command, then return.
  205.  */
  206.     public void
  207. commands()
  208. {
  209.     register int c;
  210.     register int n;
  211.     register int scroll = 10;
  212.  
  213.     mcc = last_mcc = 0;
  214.  
  215.     setjmp(main_loop);
  216.     for (;;)
  217.     {
  218.         /*
  219.          * Display prompt and accept a character.
  220.          */
  221.         psignals();    /* See if any signals need processing */
  222.  
  223.         if (quit_at_eof && hit_eof > 1)
  224.             /*
  225.              * After hitting end-of-file for the second time,
  226.              * automatically advance to the next file.
  227.              * If there are no more files, quit.
  228.              */
  229.             next_file(1);
  230.  
  231.         cmd_reset();
  232.         lower_left();
  233.         clear_eol();
  234.         prompt();
  235.         c = getcc();
  236.  
  237.     again:
  238.         if (sigs)
  239.             continue;
  240.  
  241.         if (mcc)
  242.         {
  243.             /*
  244.              * We are in a multi-character command.  
  245.              * All chars until newline go into the command buffer.
  246.              * (Note that mcc == ':' is a special case that
  247.              *  means a number is being entered.)
  248.              */
  249.             if (mcc != ':' && (c == '\n' || c == '\r'))
  250.             {
  251.                 /*
  252.                  * Execute the command.
  253.                  */
  254.                 *cp = '\0';
  255.                 cmd_exec();
  256.                 if (mcc == 'E')
  257.                 {
  258.                     char *p;
  259.                     /*
  260.                      * Ignore leading spaces 
  261.                      * in the filename.
  262.                      */
  263.                     for (p = cmdbuf;  *p == ' ';  p++) ;
  264.                     edit(p);
  265. #if SHELL_ESCAPE
  266.                 } else if (mcc == '!')
  267.                 {
  268.                     lsystem(cmdbuf);
  269.                     error("!done");
  270.                     first_cmd = "r";    /* Repaint */
  271. #endif
  272.                 } else
  273.                     search(mcc, cmdbuf, n);
  274.                 mcc = 0;
  275.             } else
  276.             {
  277.                 if (mcc == ':' && (c < '0' || c > '9') &&
  278.                     c != erase_char && c != kill_char)
  279.                 {
  280.                     /*
  281.                      * This is not part of the number
  282.                      * we were entering.  Process
  283.                      * it as a regular character.
  284.                      */
  285.                     mcc = 0;
  286.                     goto again;
  287.                 }
  288.  
  289.                 /*
  290.                  * Append the char to the command buffer.
  291.                  */
  292.                 if (cmd_char(c))
  293.                 {
  294.                     /* Abort the multi-char command. */
  295.                     mcc = 0;
  296.                     continue;
  297.                 }
  298.                 c = getcc();
  299.                 goto again;
  300.             }
  301.         } else switch (c)
  302.         {
  303.         case '0': case '1': case '2': case '3': case '4':
  304.         case '5': case '6': case '7': case '8': case '9':
  305.             /*
  306.              * First digit of a number.
  307.              */
  308.             mcc = ':';
  309.             start_mcc();
  310.             goto again;
  311.  
  312.         case 'f':
  313.         case ' ':
  314.         case CONTROL('F'):
  315.             /*
  316.              * Forward one screen.
  317.              */
  318.             n = cmd_int();
  319.             if (n <= 0)
  320.                 n = sc_window;
  321.             forward(n, 1);
  322.             break;
  323.  
  324.         case 'b':
  325.         case CONTROL('B'):
  326.             /*
  327.              * Backward one screen.
  328.              */
  329.             n = cmd_int();
  330.             if (n <= 0)
  331.                 n = sc_window;
  332.             backward(n, 1);
  333.             break;
  334.  
  335.         case 'e':
  336.         case 'j':
  337.         case '\r':
  338.         case '\n':
  339.         case CONTROL('E'):
  340.             /*
  341.              * Forward N (default 1) line.
  342.              */
  343.             n = cmd_int();
  344.             if (n <= 0)
  345.                 n = 1;
  346.             forward(n, 0);
  347.             break;
  348.  
  349.         case 'y':
  350.         case 'k':
  351.         case CONTROL('K'):
  352.         case CONTROL('Y'):
  353.             /*
  354.              * Backward N (default 1) line.
  355.              */
  356.             n = cmd_int();
  357.             if (n <= 0)
  358.                 n = 1;
  359.             backward(n, 0);
  360.             break;
  361.  
  362.         case 'd':
  363.         case CONTROL('D'):
  364.             /*
  365.              * Forward N lines 
  366.              * (default same as last 'd' or 'u' command).
  367.              */
  368.             n = cmd_int();
  369.             if (n > 0)
  370.                 scroll = n;
  371.             forward(scroll, 0);
  372.             break;
  373.  
  374.         case 'u':
  375.         case CONTROL('U'):
  376.             /*
  377.              * Forward N lines 
  378.              * (default same as last 'd' or 'u' command).
  379.              */
  380.             n = cmd_int();
  381.             if (n > 0)
  382.                 scroll = n;
  383.             backward(scroll, 0);
  384.             break;
  385.  
  386.         case 'R':
  387.             /*
  388.              * Flush buffers, then repaint screen.
  389.              */
  390.             ch_init(0);
  391.             /* Fall thru */
  392.         case 'r':
  393.         case CONTROL('R'):
  394.         case CONTROL('L'):
  395.             /*
  396.              * Repaint screen.
  397.              */
  398.             repaint();
  399.             break;
  400.  
  401.         case 'g':
  402.             /*
  403.              * Go to line N, default beginning of file.
  404.              */
  405.             n = cmd_int();
  406.             if (n <= 0)
  407.                 n = 1;
  408.             cmd_exec();
  409.             jump_back(n);
  410.             break;
  411.  
  412.         case 'p':
  413.         case '%':
  414.             /*
  415.              * Go to a specified percentage into the file.
  416.              */
  417.             n = cmd_int();
  418.             if (n < 0)
  419.                 n = 0;
  420.             if (n > 100)
  421.                 n = 100;
  422.             cmd_exec();
  423.             jump_percent(n);
  424.             break;
  425.  
  426.         case 'G':
  427.             /*
  428.              * Go to line N, default end of file.
  429.              */
  430.             n = cmd_int();
  431.             cmd_exec();
  432.             if (n <= 0)
  433.                 jump_forw();
  434.             else
  435.                 jump_back(n);
  436.             break;
  437.  
  438.         case '=':
  439.         case CONTROL('G'):
  440.             /*
  441.              * Print file name, etc.
  442.              */
  443.             error(eq_message());
  444.             break;
  445.             
  446.         case 'V':
  447.             /*
  448.              * Print version number, without the "@(#)".
  449.              */
  450.             error(version+4);
  451.             break;
  452.  
  453.         case 'q':
  454.             /*
  455.              * Exit.
  456.              */
  457.             return;
  458.  
  459.         case '/':
  460.         case '?':
  461.             /*
  462.              * Search for a pattern.
  463.              * Accept chars of the pattern until \n.
  464.              */
  465.             n = cmd_int();
  466.             if (n <= 0)
  467.                 n = 1;
  468.             mcc = last_mcc = c;
  469.             start_mcc();
  470.             c = getcc();
  471.             goto again;
  472.  
  473.         case 'n':
  474.             /*
  475.              * Repeat previous search.
  476.              */
  477.             n = cmd_int();
  478.             if (n <= 0)
  479.                 n = 1;
  480.             mcc = last_mcc;
  481.             start_mcc();
  482.             cmd_exec();
  483.             search(mcc, (char *)NULL, n);
  484.             mcc = 0;
  485.             break;
  486.  
  487.         case 'h':
  488.             /*
  489.              * Help.
  490.              */
  491.             help();
  492.             repaint();
  493.             break;
  494.  
  495.         case 'E':
  496.             /*
  497.              * Edit a new file.  Get the filename.
  498.              */
  499.             cmd_reset();
  500.             mcc = 'E';
  501.             start_mcc();
  502.             puts("dit: ");    /* This looks nicer */
  503.             cmd_col += 5;
  504.             c = getcc();
  505.             goto again;
  506.             
  507. #if SHELL_ESCAPE
  508.         case '!':
  509.             /*
  510.              * Shell escape.
  511.              */
  512.             cmd_reset();
  513.             mcc = '!';
  514.             start_mcc();
  515.             c = getcc();
  516.             goto again;
  517. #endif
  518.  
  519. #if EDITOR
  520.         case 'v':
  521.             if (ispipe)
  522.             {
  523.                 error("Cannot edit standard input");
  524.                 break;
  525.             }
  526.             sprintf(cmdbuf, "%s %s", editor, current_file);
  527.             lsystem(cmdbuf);
  528.             first_cmd = "R";
  529.             break;
  530. #endif
  531.  
  532.         case 'N':
  533.             /*
  534.              * Examine next file.
  535.              */
  536.             n = cmd_int();
  537.             if (n <= 0)
  538.                 n = 1;
  539.             next_file(n);
  540.             break;
  541.  
  542.         case 'P':
  543.             /*
  544.              * Examine previous file.
  545.              */
  546.             n = cmd_int();
  547.             if (n <= 0)
  548.                 n = 1;
  549.             prev_file(n);
  550.             break;
  551.  
  552.         case '-':
  553.             /*
  554.              * Toggle a flag setting.
  555.              */
  556.             mcc = '-';
  557.             start_mcc();
  558.             c = getcc();
  559.             mcc = 0;
  560.             if (c == erase_char || c == kill_char)
  561.                 break;
  562.             toggle_option(c);
  563.             break;
  564.  
  565.         case 'm':
  566.             /*
  567.              * Set a mark.
  568.              */
  569.             lower_left();
  570.             clear_eol();
  571.             puts("mark: ");
  572.             c = getcc();
  573.             if (c == erase_char || c == kill_char)
  574.                 break;
  575.             setmark(c);
  576.             break;
  577.  
  578.         case '\'':
  579.             /*
  580.              * Go to a mark.
  581.              */
  582.             lower_left();
  583.             clear_eol();
  584.             puts("goto mark: ");
  585.             c = getcc();
  586.             if (c == erase_char || c == kill_char)
  587.                 break;
  588.             gomark(c);
  589.             break;
  590.  
  591.         default:
  592.             bell();
  593.             break;
  594.         }
  595.     }
  596. }
  597.