diff --git a/libbb/lineedit.c b/libbb/lineedit.c index d6c33541e..5dd835cca 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -584,6 +584,12 @@ static void input_forward(void) #if ENABLE_FEATURE_TAB_COMPLETION +//FIXME: +//needs to be more clever: currently it thinks that "foo\ b +//matches the file named "foo bar", which is untrue. +//Also, perhaps "foo b needs to complete to "foo bar" , +//not "foo bar ... + static void free_tab_completion_data(void) { if (matches) { @@ -601,7 +607,7 @@ static void add_match(char *matched) num_matches++; } -#if ENABLE_FEATURE_USERNAME_COMPLETION +# if ENABLE_FEATURE_USERNAME_COMPLETION /* Replace "~user/..." with "/homedir/...". * The parameter is malloced, free it or return it * unchanged if no user is matched. @@ -657,7 +663,7 @@ static NOINLINE unsigned complete_username(const char *ud) return 1 + userlen; } -#endif /* FEATURE_USERNAME_COMPLETION */ +# endif /* FEATURE_USERNAME_COMPLETION */ enum { FIND_EXE_ONLY = 0, @@ -734,10 +740,10 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) pfind++; /* dirbuf = ".../.../.../" */ dirbuf = xstrndup(command, pfind - command); -#if ENABLE_FEATURE_USERNAME_COMPLETION +# if ENABLE_FEATURE_USERNAME_COMPLETION if (dirbuf[0] == '~') /* ~/... or ~user/... */ dirbuf = username_path_completion(dirbuf); -#endif +# endif path1[0] = dirbuf; } pf_len = strlen(pfind); @@ -1015,13 +1021,18 @@ static void showfiles(void) } } -static char *add_quote_for_spec_chars(char *found) +static const char *is_special_char(char c) +{ + return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); +} + +static char *quote_special_chars(char *found) { int l = 0; char *s = xzalloc((strlen(found) + 1) * 2); while (*found) { - if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found)) + if (is_special_char(*found)) s[l++] = '\\'; s[l++] = *found++; } @@ -1038,10 +1049,10 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* Length of string used for matching */ unsigned match_pfx_len = match_pfx_len; int find_type; -#if ENABLE_UNICODE_SUPPORT +# if ENABLE_UNICODE_SUPPORT /* cursor pos in command converted to multibyte form */ int cursor_mb; -#endif +# endif if (!(state->flags & TAB_COMPLETION)) return; @@ -1068,9 +1079,9 @@ static NOINLINE void input_tab(smallint *lastWasTab) * (we then also (ab)use this extra space later - see (**)) */ match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); -#if !ENABLE_UNICODE_SUPPORT +# if !ENABLE_UNICODE_SUPPORT save_string(match_buf, cursor + 1); /* +1 for NUL */ -#else +# else { CHAR_T wc = command_ps[cursor]; command_ps[cursor] = BB_NUL; @@ -1078,26 +1089,37 @@ static NOINLINE void input_tab(smallint *lastWasTab) command_ps[cursor] = wc; cursor_mb = strlen(match_buf); } -#endif +# endif find_type = build_match_prefix(match_buf); /* Free up any memory already allocated */ free_tab_completion_data(); -#if ENABLE_FEATURE_USERNAME_COMPLETION - /* If the word starts with `~' and there is no slash in the word, +# if ENABLE_FEATURE_USERNAME_COMPLETION + /* If the word starts with ~ and there is no slash in the word, * then try completing this word as a username. */ if (state->flags & USERNAME_COMPLETION) if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) match_pfx_len = complete_username(match_buf); -#endif - /* Try to match a command in $PATH, or a directory, or a file */ +# endif + /* If complete_username() did not match, + * try to match a command in $PATH, or a directory, or a file */ if (!matches) match_pfx_len = complete_cmd_dir_file(match_buf, find_type); + + /* Account for backslashes which will be inserted + * by quote_special_chars() later */ + { + const char *e = match_buf + strlen(match_buf); + const char *s = e - match_pfx_len; + while (s < e) + if (is_special_char(*s++)) + match_pfx_len++; + } + /* Remove duplicates */ if (matches) { - unsigned i; - unsigned n = 0; + unsigned i, n = 0; qsort_string_vector(matches, num_matches); for (i = 0; i < num_matches - 1; ++i) { //if (matches[i] && matches[i+1]) { /* paranoia */ @@ -1112,6 +1134,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) matches[n++] = matches[i]; num_matches = n; } + /* Did we find exactly one match? */ if (num_matches != 1) { /* no */ char *cp; @@ -1133,7 +1156,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) goto ret; /* no */ } *cp = '\0'; - cp = add_quote_for_spec_chars(chosen_match); + cp = quote_special_chars(chosen_match); free(chosen_match); chosen_match = cp; len_found = strlen(chosen_match); @@ -1141,7 +1164,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* Next is not a double-tab */ *lastWasTab = 0; - chosen_match = add_quote_for_spec_chars(matches[0]); + chosen_match = quote_special_chars(matches[0]); len_found = strlen(chosen_match); if (chosen_match[len_found-1] != '/') { chosen_match[len_found] = ' '; @@ -1149,7 +1172,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) } } -#if !ENABLE_UNICODE_SUPPORT +# if !ENABLE_UNICODE_SUPPORT /* Have space to place the match? */ /* The result consists of three parts with these lengths: */ /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ @@ -1166,7 +1189,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) /* write out the matched command */ redraw(cmdedit_y, command_len - pos); } -#else +# else { /* Use 2nd half of match_buf as scratch space - see (**) */ char *command = match_buf + MAX_LINELEN; @@ -1190,7 +1213,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) redraw(cmdedit_y, pos >= 0 ? pos : 0); } } -#endif +# endif ret: free(chosen_match); free(match_buf);