Hi,

On Fri, Jun 30, 2017 at 09:50:12PM -0700, Ilya Kaliman wrote:
> Hi!
> 
> The tab-autocomplete does not seem to work for some strings in ksh.
> How to reproduce:
> 
> mkdir "a a" "(bbb)" "(c c)"
> cd a\ a && mkdir abc{1,2,3} && cd ..
> cd \(bbb\) && mkdir abc{1,2,3} && cd ..
> cd \(c\ c\) && mkdir abc{1,2,3} && cd ..
> 
> type "cd a\ a/" hit tab -> auto-completes to abc, offers abc1 abc2 abc3
> type "cd \(bbb\)/" hit tab -> auto-completes to abc, offers abc1 abc2 abc3
> type "cd \(c\ c\)/" hit tab -> does not autocomplete; expected - same
> as previous.

Nice catch. As I see it, the bug you're seeing happens inside
do_complete() while comparing the length of the input buffer and the
results from the completion. Since the completions are passed through
expand() causing escaped characters to be unescaped to their literal
form while the input buffer remains escaped. You managed to find the
perfect set of input causing the condition to when a completion
succeeded to be invalidated:

  olen = strlen("\(c\ c\)/") = 9;
  nlen = strlen("(c c)/abc") = 9;

Since `olen == nlen` no completion is performed. Please try out the diff
below in which slashes are discarded when comparing the length. I don't
know if any other character should be discarded as well, if true then it
might be worth passing the input buffer through ksh's own lexer and
parser in order to properly handle special characters, just like in
x_file_glob().

Comments? OK?

Index: emacs.c
===================================================================
RCS file: /cvs/src/bin/ksh/emacs.c,v
retrieving revision 1.70
diff -u -p -r1.70 emacs.c
--- emacs.c     25 Jun 2017 17:28:39 -0000      1.70
+++ emacs.c     2 Jul 2017 20:43:00 -0000
@@ -1754,6 +1754,7 @@ do_complete(int flags,    /* XCF_{COMMAND,F
        char **words;
        int nwords;
        int start, end, nlen, olen;
+       int i, ndiscard;
        int is_command;
        int completed = 0;
 
@@ -1773,9 +1774,13 @@ do_complete(int flags,   /* XCF_{COMMAND,F
        }
 
        olen = end - start;
+       ndiscard = 0;
+       for (i = start; i < end; i++)
+               if (xbuf[i] == '\\')
+                       ndiscard++;
        nlen = x_longest_prefix(nwords, words);
        /* complete */
-       if (nwords == 1 || nlen > olen) {
+       if (nwords == 1 || nlen > olen - ndiscard) {
                x_goto(xbuf + start);
                x_delete(olen, false);
                x_escape(words[0], nlen, x_do_ins);

Reply via email to