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;