lineedit: fix tab-completion of filenames with spaces
Using ash in busybox git version dea28e1e
, tab completion doesn't seem
to work properly for filenames that have special characters (such as
spaces) in them. For example, with filenames "foo bar" and "foo zap",
typing "ls fo<TAB>" correctly expands to "ls foo\ ", but then
continuing to type "b<TAB>" will produce "ls foo\ bbar", which is not
correct (the 'b' is duplicated).
Signed-off-by: Mike Shal <marfey@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
committed by
Denys Vlasenko
parent
7c6ed78aaa
commit
f3763033e4
@@ -584,6 +584,12 @@ static void input_forward(void)
|
|||||||
|
|
||||||
#if ENABLE_FEATURE_TAB_COMPLETION
|
#if ENABLE_FEATURE_TAB_COMPLETION
|
||||||
|
|
||||||
|
//FIXME:
|
||||||
|
//needs to be more clever: currently it thinks that "foo\ b<TAB>
|
||||||
|
//matches the file named "foo bar", which is untrue.
|
||||||
|
//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
|
||||||
|
//not "foo bar <cursor>...
|
||||||
|
|
||||||
static void free_tab_completion_data(void)
|
static void free_tab_completion_data(void)
|
||||||
{
|
{
|
||||||
if (matches) {
|
if (matches) {
|
||||||
@@ -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;
|
int l = 0;
|
||||||
char *s = xzalloc((strlen(found) + 1) * 2);
|
char *s = xzalloc((strlen(found) + 1) * 2);
|
||||||
|
|
||||||
while (*found) {
|
while (*found) {
|
||||||
if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found))
|
if (is_special_char(*found))
|
||||||
s[l++] = '\\';
|
s[l++] = '\\';
|
||||||
s[l++] = *found++;
|
s[l++] = *found++;
|
||||||
}
|
}
|
||||||
@@ -1085,19 +1096,30 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
free_tab_completion_data();
|
free_tab_completion_data();
|
||||||
|
|
||||||
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
||||||
/* If the word starts with `~' and there is no slash in the word,
|
/* If the word starts with ~ and there is no slash in the word,
|
||||||
* then try completing this word as a username. */
|
* then try completing this word as a username. */
|
||||||
if (state->flags & USERNAME_COMPLETION)
|
if (state->flags & USERNAME_COMPLETION)
|
||||||
if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
|
if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
|
||||||
match_pfx_len = complete_username(match_buf);
|
match_pfx_len = complete_username(match_buf);
|
||||||
# endif
|
# endif
|
||||||
/* Try to match a command in $PATH, or a directory, or a file */
|
/* If complete_username() did not match,
|
||||||
|
* try to match a command in $PATH, or a directory, or a file */
|
||||||
if (!matches)
|
if (!matches)
|
||||||
match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
|
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 */
|
/* Remove duplicates */
|
||||||
if (matches) {
|
if (matches) {
|
||||||
unsigned i;
|
unsigned i, n = 0;
|
||||||
unsigned n = 0;
|
|
||||||
qsort_string_vector(matches, num_matches);
|
qsort_string_vector(matches, num_matches);
|
||||||
for (i = 0; i < num_matches - 1; ++i) {
|
for (i = 0; i < num_matches - 1; ++i) {
|
||||||
//if (matches[i] && matches[i+1]) { /* paranoia */
|
//if (matches[i] && matches[i+1]) { /* paranoia */
|
||||||
@@ -1112,6 +1134,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
matches[n++] = matches[i];
|
matches[n++] = matches[i];
|
||||||
num_matches = n;
|
num_matches = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Did we find exactly one match? */
|
/* Did we find exactly one match? */
|
||||||
if (num_matches != 1) { /* no */
|
if (num_matches != 1) { /* no */
|
||||||
char *cp;
|
char *cp;
|
||||||
@@ -1133,7 +1156,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
goto ret; /* no */
|
goto ret; /* no */
|
||||||
}
|
}
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
cp = add_quote_for_spec_chars(chosen_match);
|
cp = quote_special_chars(chosen_match);
|
||||||
free(chosen_match);
|
free(chosen_match);
|
||||||
chosen_match = cp;
|
chosen_match = cp;
|
||||||
len_found = strlen(chosen_match);
|
len_found = strlen(chosen_match);
|
||||||
@@ -1141,7 +1164,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
/* Next <tab> is not a double-tab */
|
/* Next <tab> is not a double-tab */
|
||||||
*lastWasTab = 0;
|
*lastWasTab = 0;
|
||||||
|
|
||||||
chosen_match = add_quote_for_spec_chars(matches[0]);
|
chosen_match = quote_special_chars(matches[0]);
|
||||||
len_found = strlen(chosen_match);
|
len_found = strlen(chosen_match);
|
||||||
if (chosen_match[len_found-1] != '/') {
|
if (chosen_match[len_found-1] != '/') {
|
||||||
chosen_match[len_found] = ' ';
|
chosen_match[len_found] = ' ';
|
||||||
|
Reference in New Issue
Block a user