I noticed some strange behavior of ksh in emacs mode when completing
file names that contain spaces (or other characters that need
escaping).

To illustrate the problem, consider two files 'a b c test1' and
'a b c test2'. ksh will correctly complete `a` and `a\ b\ c\ ` to
the common prefix `a\ b\ c\ test`. However, all of the following
do not get completed:

* `a\ b\ c\ t`
* `a\ b\ c\ te`
* `a\ b\ c\ tes`

I did some debugging, and I think this is due to emacs.c:do_complete()
using `end - start` for olen (which counts the backslashes), but
nlen (the longest prefix length) does not. So the completion only
occurs when the current word is shorter than the common prefix
length minus the number of backslashes in the word.

I don't believe vi.c:complete_word() has this issue since it always
replaces the word with the longest prefix.

I wrote a (only lightly tested) patch to make x_cf_glob return the
unescaped length as well as the start and end positions, and use
that for olen instead of `end - start`. This seems to fix the issue,
but it is longer than I had hoped. Perhaps there is a simpler patch
to make emacs.c:do_complete() use the same approach as vi completion.

diff --git a/bin/ksh/edit.c b/bin/ksh/edit.c
index 3089d195d20..d44afc258ba 100644
--- a/bin/ksh/edit.c
+++ b/bin/ksh/edit.c
@@ -29,7 +29,7 @@ static void check_sigwinch(void);
 
 static int     x_file_glob(int, const char *, int, char ***);
 static int     x_command_glob(int, const char *, int, char ***);
-static int     x_locate_word(const char *, int, int, int *, int *);
+static int     x_locate_word(const char *, int, int, int *, int *, int *);
 
 
 /* Called from main */
@@ -524,15 +524,16 @@ x_command_glob(int flags, const char *str, int slen, char 
***wordsp)
                            (c) == '`' || (c) == '=' || (c) == ':' )
 
 static int
-x_locate_word(const char *buf, int buflen, int pos, int *startp,
+x_locate_word(const char *buf, int buflen, int pos, int *startp, int *endp,
     int *is_commandp)
 {
        int p;
-       int start, end;
+       int start, end, len;
 
        /* Bad call?  Probably should report error */
        if (pos < 0 || pos > buflen) {
                *startp = pos;
+               *endp = pos;
                *is_commandp = 0;
                return 0;
        }
@@ -546,10 +547,13 @@ x_locate_word(const char *buf, int buflen, int pos, int 
*startp,
            (start > 1 && buf[start-2] == '\\'); start--)
                ;
        /* Go forwards to end of word */
+       len = 0;
        for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
                if (buf[end] == '\\' && (end+1) < buflen)
                        end++;
+               len++;
        }
+       *endp = end;
 
        if (is_commandp) {
                int iscmd;
@@ -574,7 +578,7 @@ x_locate_word(const char *buf, int buflen, int pos, int 
*startp,
 
        *startp = start;
 
-       return end - start;
+       return len;
 }
 
 static int
@@ -654,14 +658,14 @@ x_try_array(const char *buf, int buflen, const char 
*want, int wantlen,
 
 int
 x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
-    int *endp, char ***wordsp, int *is_commandp)
+    int *endp, int *lenp, char ***wordsp, int *is_commandp)
 {
-       int len;
+       int len, esclen;
        int nwords;
        char **words = NULL;
        int is_command;
 
-       len = x_locate_word(buf, buflen, pos, startp, &is_command);
+       len = x_locate_word(buf, buflen, pos, startp, endp, &is_command);
        if (!(flags & XCF_COMMAND))
                is_command = 0;
        /* Don't do command globing on zero length strings - it takes too
@@ -671,10 +675,11 @@ x_cf_glob(int flags, const char *buf, int buflen, int 
pos, int *startp,
        if (len == 0 && is_command)
                return 0;
 
+       esclen = *endp - *startp;
        if (is_command)
-               nwords = x_command_glob(flags, buf + *startp, len, &words);
-       else if (!x_try_array(buf, buflen, buf + *startp, len, &nwords, &words))
-               nwords = x_file_glob(flags, buf + *startp, len, &words);
+               nwords = x_command_glob(flags, buf + *startp, esclen, &words);
+       else if (!x_try_array(buf, buflen, buf + *startp, esclen, &nwords, 
&words))
+               nwords = x_file_glob(flags, buf + *startp, esclen, &words);
        if (nwords == 0) {
                *wordsp = NULL;
                return 0;
@@ -683,7 +688,7 @@ x_cf_glob(int flags, const char *buf, int buflen, int pos, 
int *startp,
        if (is_commandp)
                *is_commandp = is_command;
        *wordsp = words;
-       *endp = *startp + len;
+       *lenp = len;
 
        return nwords;
 }
diff --git a/bin/ksh/edit.h b/bin/ksh/edit.h
index 0b604cd64fb..f988d92c4b2 100644
--- a/bin/ksh/edit.h
+++ b/bin/ksh/edit.h
@@ -43,7 +43,7 @@ bool  x_mode(bool);
 int    promptlen(const char *, const char **);
 int    x_do_comment(char *, int, int *);
 void   x_print_expansions(int, char *const *, int);
-int    x_cf_glob(int, const char *, int, int, int *, int *, char ***, int *);
+int    x_cf_glob(int, const char *, int, int, int *, int *, int *, char ***, 
int *);
 int    x_longest_prefix(int , char *const *);
 int    x_basename(const char *, const char *);
 void   x_free_words(int, char **);
diff --git a/bin/ksh/emacs.c b/bin/ksh/emacs.c
index 694c402ff6b..bb05cd7fad0 100644
--- a/bin/ksh/emacs.c
+++ b/bin/ksh/emacs.c
@@ -1706,12 +1706,12 @@ x_expand(int c)
 {
        char **words;
        int nwords = 0;
-       int start, end;
+       int start, end, len;
        int is_command;
        int i;
 
        nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
-           &start, &end, &words, &is_command);
+           &start, &end, &len, &words, &is_command);
 
        if (nwords == 0) {
                x_e_putc(BEL);
@@ -1744,7 +1744,7 @@ do_complete(int flags,    /* 
XCF_{COMMAND,FILE,COMMAND_FILE} */
        int completed = 0;
 
        nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
-           &start, &end, &words, &is_command);
+           &start, &end, &olen, &words, &is_command);
        /* no match */
        if (nwords == 0) {
                x_e_putc(BEL);
@@ -1758,12 +1758,11 @@ do_complete(int flags,  /* 
XCF_{COMMAND,FILE,COMMAND_FILE} */
                return;
        }
 
-       olen = end - start;
        nlen = x_longest_prefix(nwords, words);
        /* complete */
        if (nwords == 1 || nlen > olen) {
                x_goto(xbuf + start);
-               x_delete(olen, false);
+               x_delete(end - start, false);
                x_escape(words[0], nlen, x_do_ins);
                x_adjust();
                completed = 1;
diff --git a/bin/ksh/vi.c b/bin/ksh/vi.c
index 53be5a76d50..ad985d6c6b5 100644
--- a/bin/ksh/vi.c
+++ b/bin/ksh/vi.c
@@ -2013,7 +2013,7 @@ expand_word(int command)
        static struct edstate *buf;
        int rval = 0;
        int nwords;
-       int start, end;
+       int start, end, len;
        char **words;
        int i;
 
@@ -2031,7 +2031,7 @@ expand_word(int command)
 
        nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
            es->cbuf, es->linelen, es->cursor,
-           &start, &end, &words, NULL);
+           &start, &end, &len, &words, NULL);
        if (nwords == 0) {
                vi_error();
                return -1;
@@ -2067,7 +2067,7 @@ complete_word(int command, int count)
        static struct edstate *buf;
        int rval = 0;
        int nwords;
-       int start, end;
+       int start, end, len;
        char **words;
        char *match;
        int match_len;
@@ -2096,7 +2096,7 @@ complete_word(int command, int count)
         */
        nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0),
            es->cbuf, es->linelen, es->cursor,
-           &start, &end, &words, &is_command);
+           &start, &end, &len, &words, &is_command);
        if (nwords == 0) {
                vi_error();
                return -1;
@@ -2170,13 +2170,13 @@ static int
 print_expansions(struct edstate *e)
 {
        int nwords;
-       int start, end;
+       int start, end, len;
        char **words;
        int is_command;
 
        nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH,
            e->cbuf, e->linelen, e->cursor,
-           &start, &end, &words, &is_command);
+           &start, &end, &len, &words, &is_command);
        if (nwords == 0) {
                vi_error();
                return -1;

Reply via email to