home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / cmdbuf.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-01-14  |  18.8 KB  |  1,047 lines

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