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 / cmdbuf.c < prev    next >
Text File  |  1998-03-03  |  20KB  |  1,040 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.  * Functions which manipulate the command buffer.
  30.  * Used only by command() and related functions.
  31.  */
  32.  
  33. #include "less.h"
  34. #include "cmd.h"
  35.  
  36. extern int sc_width;
  37.  
  38. static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
  39. static int cmd_col;        /* Current column of the cursor */
  40. static int prompt_col;        /* Column of cursor just after prompt */
  41. static char *cp;        /* Pointer into cmdbuf */
  42. static int cmd_offset;        /* Index into cmdbuf of first displayed char */
  43. static int literal;        /* Next input char should not be interpreted */
  44.  
  45. #if TAB_COMPLETE_FILENAME
  46. static int cmd_complete();
  47. /*
  48.  * These variables are statics used by cmd_complete.
  49.  */
  50. static int in_completion = 0;
  51. static char *tk_text;
  52. static char *tk_original;
  53. static char *tk_ipoint;
  54. static char *tk_trial;
  55. static struct textlist tk_tlist;
  56. #endif
  57.  
  58. static int cmd_left();
  59. static int cmd_right();
  60.  
  61. #if SPACES_IN_FILENAMES
  62. public char openquote = '"';
  63. public char closequote = '"';
  64. #endif
  65.  
  66. #if CMD_HISTORY
  67. /*
  68.  * A mlist structure represents a command history.
  69.  */
  70. struct mlist
  71. {
  72.     struct mlist *next;
  73.     struct mlist *prev;
  74.     struct mlist *curr_mp;
  75.     char *string;
  76. };
  77.  
  78. /*
  79.  * These are the various command histories that exist.
  80.  */
  81. struct mlist mlist_search =  
  82.     { &mlist_search,  &mlist_search,  &mlist_search,  NULL };
  83. public void constant *ml_search = (void *) &mlist_search;
  84.  
  85. struct mlist mlist_examine = 
  86.     { &mlist_examine, &mlist_examine, &mlist_examine, NULL };
  87. public void constant *ml_examine = (void *) &mlist_examine;
  88.  
  89. #if SHELL_ESCAPE || PIPEC
  90. struct mlist mlist_shell =   
  91.     { &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL };
  92. public void constant *ml_shell = (void *) &mlist_shell;
  93. #endif
  94.  
  95. #else /* CMD_HISTORY */
  96.  
  97. /* If CMD_HISTORY is off, these are just flags. */
  98. public void constant *ml_search = (void *)1;
  99. public void constant *ml_examine = (void *)2;
  100. #if SHELL_ESCAPE || PIPEC
  101. public void constant *ml_shell = (void *)3;
  102. #endif
  103.  
  104. #endif /* CMD_HISTORY */
  105.  
  106. /*
  107.  * History for the current command.
  108.  */
  109. static struct mlist *curr_mlist = NULL;
  110.  
  111.  
  112. /*
  113.  * Reset command buffer (to empty).
  114.  */
  115.     public void
  116. cmd_reset()
  117. {
  118.     cp = cmdbuf;
  119.     *cp = '\0';
  120.     cmd_col = 0;
  121.     cmd_offset = 0;
  122.     literal = 0;
  123. }
  124.  
  125. /*
  126.  * Clear command line on display.
  127.  */
  128.     public void
  129. clear_cmd()
  130. {
  131.     clear_bot();
  132.     cmd_col = prompt_col = 0;
  133. }
  134.  
  135. /*
  136.  * Display a string, usually as a prompt for input into the command buffer.
  137.  */
  138.     public void
  139. cmd_putstr(s)
  140.     char *s;
  141. {
  142.     putstr(s);
  143.     cmd_col += strlen(s);
  144.     prompt_col += strlen(s);
  145. }
  146.  
  147. /*
  148.  * How many characters are in the command buffer?
  149.  */
  150.     public int
  151. len_cmdbuf()
  152. {
  153.     return (strlen(cmdbuf));
  154. }
  155.  
  156. /*
  157.  * Repaint the line from cp onwards.
  158.  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
  159.  */
  160.     static void
  161. cmd_repaint(old_cp)
  162.     char *old_cp;
  163. {
  164.     char *p;
  165.  
  166.     /*
  167.      * Repaint the line from the current position.
  168.      */
  169.     clear_eol();
  170.     for ( ;  *cp != '\0';  cp++)
  171.     {
  172.         p = prchar(*cp);
  173.         if (cmd_col + strlen(p) >= sc_width)
  174.             break;
  175.         putstr(p);
  176.         cmd_col += strlen(p);
  177.     }
  178.  
  179.     /*
  180.      * Back up the cursor to the correct position.
  181.      */
  182.     while (cp > old_cp)
  183.         cmd_left();
  184. }
  185.  
  186. /*
  187.  * Put the cursor at "home" (just after the prompt),
  188.  * and set cp to the corresponding char in cmdbuf.
  189.  */
  190.     static void
  191. cmd_home()
  192. {
  193.     while (cmd_col > prompt_col)
  194.     {
  195.         putbs();
  196.         cmd_col--;
  197.     }
  198.  
  199.     cp = &cmdbuf[cmd_offset];
  200. }
  201.  
  202. /*
  203.  * Shift the cmdbuf display left a half-screen.
  204.  */
  205.     static void
  206. cmd_lshift()
  207. {
  208.     char *s;
  209.     char *save_cp;
  210.     int cols;
  211.  
  212.     /*
  213.      * Start at the first displayed char, count how far to the
  214.      * right we'd have to move to reach the center of the screen.
  215.      */
  216.     s = cmdbuf + cmd_offset;
  217.     cols = 0;
  218.     while (cols < (sc_width - prompt_col) / 2 && *s != '\0')
  219.         cols += strlen(prchar(*s++));
  220.  
  221.     cmd_offset = s - cmdbuf;
  222.     save_cp = cp;
  223.     cmd_home();
  224.     cmd_repaint(save_cp);
  225. }
  226.  
  227. /*
  228.  * Shift the cmdbuf display right a half-screen.
  229.  */
  230.     static void
  231. cmd_rshift()
  232. {
  233.     char *s;
  234.     char *p;
  235.     char *save_cp;
  236.     int cols;
  237.  
  238.     /*
  239.      * Start at the first displayed char, count how far to the
  240.      * left we'd have to move to traverse a half-screen width
  241.      * of displayed characters.
  242.      */
  243.     s = cmdbuf + cmd_offset;
  244.     cols = 0;
  245.     while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf)
  246.     {
  247.         p = prchar(*--s);
  248.         cols += strlen(p);
  249.     }
  250.  
  251.     cmd_offset = s - cmdbuf;
  252.     save_cp = cp;
  253.     cmd_home();
  254.     cmd_repaint(save_cp);
  255. }
  256.  
  257. /*
  258.  * Move cursor right one character.
  259.  */
  260.     static int
  261. cmd_right()
  262. {
  263.     char *p;
  264.     
  265.     if (*cp == '\0')
  266.     {
  267.         /* 
  268.          * Already at the end of the line.
  269.          */
  270.         return (CC_OK);
  271.     }
  272.     p = prchar(*cp);
  273.     if (cmd_col + strlen(p) >= sc_width)
  274.         cmd_lshift();
  275.     else if (cmd_col + strlen(p) == sc_width - 1 && cp[1] != '\0')
  276.         cmd_lshift();
  277.     cp++;
  278.     putstr(p);
  279.     cmd_col += strlen(p);
  280.     return (CC_OK);
  281. }
  282.  
  283. /*
  284.  * Move cursor left one character.
  285.  */
  286.     static int
  287. cmd_left()
  288. {
  289.     char *p;
  290.     
  291.     if (cp <= cmdbuf)
  292.     {
  293.         /* Already at the beginning of the line */
  294.         return (CC_OK);
  295.     }
  296.     p = prchar(cp[-1]);
  297.     if (cmd_col < prompt_col + strlen(p))
  298.         cmd_rshift();
  299.     cp--;
  300.     cmd_col -= strlen(p);
  301.     while (*p++ != '\0')
  302.         putbs();
  303.     return (CC_OK);
  304. }
  305.  
  306. /*
  307.  * Insert a char into the command buffer, at the current position.
  308.  */
  309.     static int
  310. cmd_ichar(c)
  311.     int c;
  312. {
  313.     char *s;
  314.     
  315.     if (strlen(cmdbuf) >= sizeof(cmdbuf)-2)
  316.     {
  317.         /*
  318.          * No room in the command buffer for another char.
  319.          */
  320.         bell();
  321.         return (CC_ERROR);
  322.     }
  323.         
  324.     /*
  325.      * Insert the character into the buffer.
  326.      */
  327.     for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
  328.         s[1] = s[0];
  329.     *cp = c;
  330.     /*
  331.      * Reprint the tail of the line from the inserted char.
  332.      */
  333.     cmd_repaint(cp);
  334.     cmd_right();
  335.     return (CC_OK);
  336. }
  337.  
  338. /*
  339.  * Backspace in the command buffer.
  340.  * Delete the char to the left of the cursor.
  341.  */
  342.     static int
  343. cmd_erase()
  344. {
  345.     register char *s;
  346.  
  347.     if (cp == cmdbuf)
  348.     {
  349.         /*
  350.          * Backspace past beginning of the buffer:
  351.          * this usually means abort the command.
  352.          */
  353.         return (CC_QUIT);
  354.     }
  355.     /*
  356.      * Move cursor left (to the char being erased).
  357.      */
  358.     cmd_left();
  359.     /*
  360.      * Remove the char from the buffer (shift the buffer left).
  361.      */
  362.     for (s = cp;  *s != '\0';  s++)
  363.         s[0] = s[1];
  364.     /*
  365.      * Repaint the buffer after the erased char.
  366.      */
  367.     cmd_repaint(cp);
  368.     
  369.     /*
  370.      * This is rather weird.
  371.      * We say that erasing the entire command string causes us
  372.      * to abort the current command, BUT ONLY IF there is no history
  373.      * for this type of command.  This causes commands like search (/)
  374.      * and edit (:e) to stay active even if we erase the entire string,
  375.      * but commands like <digit> and - go away when we erase the string.
  376.      * (See same thing in cmd_kill.)
  377.      */
  378.     if (curr_mlist == NULL && cp == cmdbuf && *cp == '\0')
  379.         return (CC_QUIT);
  380.     return (CC_OK);
  381. }
  382.  
  383. /*
  384.  * Delete the char under the cursor.
  385.  */
  386.     static int
  387. cmd_delete()
  388. {
  389.     if (*cp == '\0')
  390.     {
  391.         /*
  392.          * At end of string; there is no char under the cursor.
  393.          */
  394.         return (CC_OK);
  395.     }
  396.     /*
  397.      * Move right, then use cmd_erase.
  398.      */
  399.     cmd_right();
  400.     cmd_erase();
  401.     return (CC_OK);
  402. }
  403.  
  404. /*
  405.  * Delete the "word" to the left of the cursor.
  406.  */
  407.     static int
  408. cmd_werase()
  409. {
  410.     if (cp > cmdbuf && cp[-1] == ' ')
  411.     {
  412.         /*
  413.          * If the char left of cursor is a space,
  414.          * erase all the spaces left of cursor (to the first non-space).
  415.          */
  416.         while (cp > cmdbuf && cp[-1] == ' ')
  417.             (void) cmd_erase();
  418.     } else
  419.     {
  420.         /*
  421.          * If the char left of cursor is not a space,
  422.          * erase all the nonspaces left of cursor (the whole "word").
  423.          */
  424.         while (cp > cmdbuf && cp[-1] != ' ')
  425.             (void) cmd_erase();
  426.     }
  427.     return (CC_OK);
  428. }
  429.  
  430. /*
  431.  * Delete the "word" under the cursor.
  432.  */
  433.     static int
  434. cmd_wdelete()
  435. {
  436.     if (*cp == ' ')
  437.     {
  438.         /*
  439.          * If the char under the cursor is a space,
  440.          * delete it and all the spaces right of cursor.
  441.          */
  442.         while (*cp == ' ')
  443.             (void) cmd_delete();
  444.     } else
  445.     {
  446.         /*
  447.          * If the char under the cursor is not a space,
  448.          * delete it and all nonspaces right of cursor (the whole word).
  449.          */
  450.         while (*cp != ' ' && *cp != '\0')
  451.             (void) cmd_delete();
  452.     }
  453.     return (CC_OK);
  454. }
  455.  
  456. /*
  457.  * Delete all chars in the command buffer.
  458.  */
  459.     static int
  460. cmd_kill()
  461. {
  462.     if (cmdbuf[0] == '\0')
  463.     {
  464.         /*
  465.          * Buffer is already empty; abort the current command.
  466.          */
  467.         return (CC_QUIT);
  468.     }
  469.     cmd_offset = 0;
  470.     cmd_home();
  471.     *cp = '\0';
  472.     cmd_repaint(cp);
  473.     /*
  474.      * Same weirdness as in cmd_erase.
  475.      * If the current command has no history, abort the current command.
  476.      */
  477.     if (curr_mlist == NULL)
  478.         return (CC_QUIT);
  479.     return (CC_OK);
  480. }
  481.  
  482. /*
  483.  * Select an mlist structure to be the current command history.
  484.  */
  485.     public void
  486. set_mlist(mlist)
  487.     void *mlist;
  488. {
  489.     curr_mlist = (struct mlist *) mlist;
  490. }
  491.  
  492. #if CMD_HISTORY
  493. /*
  494.  * Move up or down in the currently selected command history list.
  495.  */
  496.     static int
  497. cmd_updown(action)
  498.     int action;
  499. {
  500.     char *s;
  501.     
  502.     if (curr_mlist == NULL)
  503.     {
  504.         /*
  505.          * The current command has no history list.
  506.          */
  507.         bell();
  508.         return (CC_OK);
  509.     }
  510.     cmd_home();
  511.     clear_eol();
  512.     /*
  513.      * Move curr_mp to the next/prev entry.
  514.      */
  515.     if (action == EC_UP)
  516.         curr_mlist->curr_mp = curr_mlist->curr_mp->prev;
  517.     else
  518.         curr_mlist->curr_mp = curr_mlist->curr_mp->next;
  519.     /*
  520.      * Copy the entry into cmdbuf and echo it on the screen.
  521.      */
  522.     s = curr_mlist->curr_mp->string;
  523.     if (s == NULL)
  524.         s = "";
  525.     for (cp = cmdbuf;  *s != '\0';  s++)
  526.     {
  527.         *cp = *s;
  528.         cmd_right();
  529.     }
  530.     *cp = '\0';
  531.     return (CC_OK);
  532. }
  533. #endif
  534.  
  535. /*
  536.  * Add a string to a history list.
  537.  */
  538.     public void
  539. cmd_addhist(mlist, cmd)
  540.     struct mlist *mlist;
  541.     char *cmd;
  542. {
  543. #if CMD_HISTORY
  544.     struct mlist *ml;
  545.     
  546.     /*
  547.      * Don't save a trivial command.
  548.      */
  549.     if (strlen(cmd) == 0)
  550.         return;
  551.     /*
  552.      * Don't save if a duplicate of a command which is already 
  553.      * in the history.
  554.      * But select the one already in the history to be current.
  555.      */
  556.     for (ml = mlist->next;  ml != mlist;  ml = ml->next)
  557.     {
  558.         if (strcmp(ml->string, cmd) == 0)
  559.             break;
  560.     }
  561.     if (ml == mlist)
  562.     {
  563.         /*
  564.          * Did not find command in history.
  565.          * Save the command and put it at the end of the history list.
  566.          */
  567.         ml = (struct mlist *) ecalloc(1, sizeof(struct mlist));
  568.         ml->string = save(cmd);
  569.         ml->next = mlist;
  570.         ml->prev = mlist->prev;
  571.         mlist->prev->next = ml;
  572.         mlist->prev = ml;
  573.     }
  574.     /*
  575.      * Point to the cmd just after the just-accepted command.
  576.      * Thus, an UPARROW will always retrieve the previous command.
  577.      */
  578.     mlist->curr_mp = ml->next;
  579. #endif
  580. }
  581.  
  582. /*
  583.  * Accept the command in the command buffer.
  584.  * Add it to the currently selected history list.
  585.  */
  586.     public void
  587. cmd_accept()
  588. {
  589. #if CMD_HISTORY
  590.     /*
  591.      * Nothing to do if there is no currently selected history list.
  592.      */
  593.     if (curr_mlist == NULL)
  594.         return;
  595.     cmd_addhist(curr_mlist, cmdbuf);
  596. #endif
  597. }
  598.  
  599. /*
  600.  * Try to perform a line-edit function on the command buffer,
  601.  * using a specified char as a line-editing command.
  602.  * Returns:
  603.  *    CC_PASS    The char does not invoke a line edit function.
  604.  *    CC_OK    Line edit function done.
  605.  *    CC_QUIT    The char requests the current command to be aborted.
  606.  */
  607.     static int
  608. cmd_edit(c)
  609.     int c;
  610. {
  611.     int action;
  612.     int flags;
  613.  
  614. #if TAB_COMPLETE_FILENAME
  615. #define    not_in_completion()    in_completion = 0
  616. #else
  617. #define    not_in_completion()
  618. #endif
  619.     
  620.     /*
  621.      * See if the char is indeed a line-editing command.
  622.      */
  623.     flags = 0;
  624. #if CMD_HISTORY
  625.     if (curr_mlist == NULL)
  626.         /*
  627.          * No current history; don't accept history manipulation cmds.
  628.          */
  629.         flags |= EC_NOHISTORY;
  630. #endif
  631. #if TAB_COMPLETE_FILENAME
  632.     if (curr_mlist == ml_search)
  633.         /*
  634.          * In a search command; don't accept file-completion cmds.
  635.          */
  636.         flags |= EC_NOCOMPLETE;
  637. #endif
  638.  
  639.     action = editchar(c, flags);
  640.  
  641.     switch (action)
  642.     {
  643.     case EC_RIGHT:
  644.         not_in_completion();
  645.         return (cmd_right());
  646.     case EC_LEFT:
  647.         not_in_completion();
  648.         return (cmd_left());
  649.     case EC_W_RIGHT:
  650.         not_in_completion();
  651.         while (*cp != '\0' && *cp != ' ')
  652.             cmd_right();
  653.         while (*cp == ' ')
  654.             cmd_right();
  655.         return (CC_OK);
  656.     case EC_W_LEFT:
  657.         not_in_completion();
  658.         while (cp > cmdbuf && cp[-1] == ' ')
  659.             cmd_left();
  660.         while (cp > cmdbuf && cp[-1] != ' ')
  661.             cmd_left();
  662.         return (CC_OK);
  663.     case EC_HOME:
  664.         not_in_completion();
  665.         cmd_offset = 0;
  666.         cmd_home();
  667.         cmd_repaint(cp);
  668.         return (CC_OK);
  669.     case EC_END:
  670.         not_in_completion();
  671.         while (*cp != '\0')
  672.             cmd_right();
  673.         return (CC_OK);
  674.     case EC_INSERT:
  675.         not_in_completion();
  676.         return (CC_OK);
  677.     case EC_BACKSPACE:
  678.         not_in_completion();
  679.         return (cmd_erase());
  680.     case EC_LINEKILL:
  681.         not_in_completion();
  682.         return (cmd_kill());
  683.     case EC_W_BACKSPACE:
  684.         not_in_completion();
  685.         return (cmd_werase());
  686.     case EC_DELETE:
  687.         not_in_completion();
  688.         return (cmd_delete());
  689.     case EC_W_DELETE:
  690.         not_in_completion();
  691.         return (cmd_wdelete());
  692.     case EC_LITERAL:
  693.         literal = 1;
  694.         return (CC_OK);
  695. #if CMD_HISTORY
  696.     case EC_UP:
  697.     case EC_DOWN:
  698.         not_in_completion();
  699.         return (cmd_updown(action));
  700. #endif
  701. #if TAB_COMPLETE_FILENAME
  702.     case EC_F_COMPLETE:
  703.     case EC_B_COMPLETE:
  704.     case EC_EXPAND:
  705.         return (cmd_complete(action));
  706. #endif
  707.     case EC_NOACTION:
  708.         return (CC_OK);
  709.     default:
  710.         not_in_completion();
  711.         return (CC_PASS);
  712.     }
  713. }
  714.  
  715. #if TAB_COMPLETE_FILENAME
  716. /*
  717.  * Insert a string into the command buffer, at the current position.
  718.  */
  719.     static int
  720. cmd_istr(str)
  721.     char *str;
  722. {
  723.     char *s;
  724.     int action;
  725.     
  726.     for (s = str;  *s != '\0';  s++)
  727.     {
  728.         action = cmd_ichar(*s);
  729.         if (action != CC_OK)
  730.         {
  731.             bell();
  732.             return (action);
  733.         }
  734.     }
  735.     return (CC_OK);
  736. }
  737.  
  738. /*
  739.  * Find the beginning and end of the "current" word.
  740.  * This is the word which the cursor (cp) is inside or at the end of.
  741.  * Return pointer to the beginning of the word and put the
  742.  * cursor at the end of the word.
  743.  */
  744.     static char *
  745. delimit_word()
  746. {
  747.     char *word;
  748. #if SPACES_IN_FILENAMES
  749.     char *p;
  750.     int quoted;
  751. #endif
  752.     
  753.     /*
  754.      * Move cursor to end of word.
  755.      */
  756.     if (*cp != ' ' && *cp != '\0')
  757.     {
  758.         /*
  759.          * Cursor is on a nonspace.
  760.          * Move cursor right to the next space.
  761.          */
  762.         while (*cp != ' ' && *cp != '\0')
  763.             cmd_right();
  764.     } else if (cp > cmdbuf && cp[-1] != ' ')
  765.     {
  766.         /*
  767.          * Cursor is on a space, and char to the left is a nonspace.
  768.          * We're already at the end of the word.
  769.          */
  770.         ;
  771.     } else
  772.     {
  773.         /*
  774.          * Cursor is on a space and char to the left is a space.
  775.          * Huh? There's no word here.
  776.          */
  777.         return (NULL);
  778.     }
  779.     /*
  780.      * Search backwards for beginning of the word.
  781.      */
  782.     if (cp == cmdbuf)
  783.         return (NULL);
  784. #if SPACES_IN_FILENAMES
  785.     /*
  786.      * If we have an unbalanced quote (that is, an open quote
  787.      * without a corresponding close quote), we return everything
  788.      * from the open quote, including spaces.
  789.      */
  790.     quoted = 0;
  791.     for (p = cmdbuf;  p < cp;  p++)
  792.     {
  793.         if (!quoted && *p == openquote)
  794.         {
  795.             quoted = 1;
  796.             word = p;
  797.         } else if (quoted && *p == closequote)
  798.         {
  799.             quoted = 0;
  800.         }
  801.     }
  802.     if (quoted)
  803.         return (word);
  804. #endif
  805.     for (word = cp-1;  word > cmdbuf;  word--)
  806.         if (word[-1] == ' ')
  807.             break;
  808.     return (word);
  809. }
  810.  
  811. /*
  812.  * Set things up to enter completion mode.
  813.  * Expand the word under the cursor into a list of filenames 
  814.  * which start with that word, and set tk_text to that list.
  815.  */
  816.     static void
  817. init_compl()
  818. {
  819.     char *word;
  820.     char c;
  821.     
  822.     /*
  823.      * Get rid of any previous tk_text.
  824.      */
  825.     if (tk_text != NULL)
  826.     {
  827.         free(tk_text);
  828.         tk_text = NULL;
  829.     }
  830.     /*
  831.      * Find the original (uncompleted) word in the command buffer.
  832.      */
  833.     word = delimit_word();
  834.     if (word == NULL)
  835.         return;
  836.     /*
  837.      * Set the insertion point to the point in the command buffer
  838.      * where the original (uncompleted) word now sits.
  839.      */
  840.     tk_ipoint = word;
  841.     /*
  842.      * Save the original (uncompleted) word
  843.      */
  844.     if (tk_original != NULL)
  845.         free(tk_original);
  846.     tk_original = (char *) ecalloc(cp-word+1, sizeof(char));
  847.     strncpy(tk_original, word, cp-word);
  848.     /*
  849.      * Get the expanded filename.
  850.      * This may result in a single filename, or
  851.      * a blank-separated list of filenames.
  852.      */
  853.     c = *cp;
  854.     *cp = '\0';
  855. #if SPACES_IN_FILENAMES
  856.     if (*word == openquote)
  857.         word++;
  858. #endif
  859.     tk_text = fcomplete(word);
  860.     *cp = c;
  861. }
  862.  
  863. /*
  864.  * Return the next word in the current completion list.
  865.  */
  866.     static char *
  867. next_compl(action, prev)
  868.          int action;
  869.     char *prev;
  870. {
  871.     switch (action)
  872.     {
  873.     case EC_F_COMPLETE:
  874.         return (forw_textlist(&tk_tlist, prev));
  875.     case EC_B_COMPLETE:
  876.         return (back_textlist(&tk_tlist, prev));
  877.     }
  878.     /* Cannot happen */
  879.     return ("?");
  880. }
  881.  
  882. /*
  883.  * Complete the filename before (or under) the cursor.
  884.  * cmd_complete may be called multiple times.  The global in_completion
  885.  * remembers whether this call is the first time (create the list),
  886.  * or a subsequent time (step thru the list).
  887.  */
  888.     static int
  889. cmd_complete(action)
  890.     int action;
  891. {
  892.     char *s;
  893.  
  894.     if (!in_completion || action == EC_EXPAND)
  895.     {
  896.         /*
  897.          * Expand the word under the cursor and 
  898.          * use the first word in the expansion 
  899.          * (or the entire expansion if we're doing EC_EXPAND).
  900.          */
  901.         init_compl();
  902.         if (tk_text == NULL)
  903.         {
  904.             bell();
  905.             return (CC_OK);
  906.         }
  907.         if (action == EC_EXPAND)
  908.         {
  909.             /*
  910.              * Use the whole list.
  911.              */
  912.             tk_trial = tk_text;
  913.         } else
  914.         {
  915.             /*
  916.              * Use the first filename in the list.
  917.              */
  918.             in_completion = 1;
  919.             init_textlist(&tk_tlist, tk_text);
  920.             tk_trial = next_compl(action, (char*)NULL);
  921.         }
  922.     } else
  923.     {
  924.         /*
  925.          * We already have a completion list.
  926.          * Use the next/previous filename from the list.
  927.          */
  928.         tk_trial = next_compl(action, tk_trial);
  929.     }
  930.     
  931.       /*
  932.        * Remove the original word, or the previous trial completion.
  933.        */
  934.     while (cp > tk_ipoint)
  935.         (void) cmd_erase();
  936.     
  937.     if (tk_trial == NULL)
  938.     {
  939.         /*
  940.          * There are no more trial completions.
  941.          * Insert the original (uncompleted) filename.
  942.          */
  943.         in_completion = 0;
  944.         if (cmd_istr(tk_original) != CC_OK)
  945.             goto fail;
  946.     } else
  947.     {
  948.         /*
  949.          * Insert trial completion.
  950.          */
  951.         if (cmd_istr(tk_trial) != CC_OK)
  952.             goto fail;
  953.         /*
  954.          * If it is a directory, append a slash.
  955.          */
  956.         if (is_dir(tk_trial))
  957.         {
  958.             if (cp > cmdbuf && cp[-1] == closequote)
  959.                 (void) cmd_erase();
  960.             s = lgetenv("LESSSEPARATOR");
  961.             if (s == NULL)
  962.                 s = PATHNAME_SEP;
  963.             if (cmd_istr(s) != CC_OK)
  964.                 goto fail;
  965.         }
  966.     }
  967.     
  968.     return (CC_OK);
  969.     
  970. fail:
  971.     in_completion = 0;
  972.     bell();
  973.     return (CC_OK);
  974. }
  975.  
  976. #endif /* TAB_COMPLETE_FILENAME */
  977.  
  978. /*
  979.  * Process a single character of a multi-character command, such as
  980.  * a number, or the pattern of a search command.
  981.  * Returns:
  982.  *    CC_OK        The char was accepted.
  983.  *    CC_QUIT        The char requests the command to be aborted.
  984.  *    CC_ERROR    The char could not be accepted due to an error.
  985.  */
  986.     public int
  987. cmd_char(c)
  988.     int c;
  989. {
  990.     int action;
  991.  
  992.     if (literal)
  993.     {
  994.         /*
  995.          * Insert the char, even if it is a line-editing char.
  996.          */
  997.         literal = 0;
  998.         return (cmd_ichar(c));
  999.     }
  1000.         
  1001.     /*
  1002.      * See if it is a special line-editing character.
  1003.      */
  1004.     if (in_mca())
  1005.     {
  1006.         action = cmd_edit(c);
  1007.         switch (action)
  1008.         {
  1009.         case CC_OK:
  1010.         case CC_QUIT:
  1011.             return (action);
  1012.         case CC_PASS:
  1013.             break;
  1014.         }
  1015.     }
  1016.     
  1017.     /*
  1018.      * Insert the char into the command buffer.
  1019.      */
  1020.     return (cmd_ichar(c));
  1021. }
  1022.  
  1023. /*
  1024.  * Return the number currently in the command buffer.
  1025.  */
  1026.     public int
  1027. cmd_int()
  1028. {
  1029.     return (atoi(cmdbuf));
  1030. }
  1031.  
  1032. /*
  1033.  * Return a pointer to the command buffer.
  1034.  */
  1035.     public char *
  1036. get_cmdbuf()
  1037. {
  1038.     return (cmdbuf);
  1039. }
  1040.