mirror of
https://github.com/sheumann/hush.git
synced 2024-10-12 01:24:10 +00:00
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:
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) {
|
||||||
@ -601,7 +607,7 @@ static void add_match(char *matched)
|
|||||||
num_matches++;
|
num_matches++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_FEATURE_USERNAME_COMPLETION
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
||||||
/* Replace "~user/..." with "/homedir/...".
|
/* Replace "~user/..." with "/homedir/...".
|
||||||
* The parameter is malloced, free it or return it
|
* The parameter is malloced, free it or return it
|
||||||
* unchanged if no user is matched.
|
* unchanged if no user is matched.
|
||||||
@ -657,7 +663,7 @@ static NOINLINE unsigned complete_username(const char *ud)
|
|||||||
|
|
||||||
return 1 + userlen;
|
return 1 + userlen;
|
||||||
}
|
}
|
||||||
#endif /* FEATURE_USERNAME_COMPLETION */
|
# endif /* FEATURE_USERNAME_COMPLETION */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
FIND_EXE_ONLY = 0,
|
FIND_EXE_ONLY = 0,
|
||||||
@ -734,10 +740,10 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
|
|||||||
pfind++;
|
pfind++;
|
||||||
/* dirbuf = ".../.../.../" */
|
/* dirbuf = ".../.../.../" */
|
||||||
dirbuf = xstrndup(command, pfind - command);
|
dirbuf = xstrndup(command, pfind - command);
|
||||||
#if ENABLE_FEATURE_USERNAME_COMPLETION
|
# if ENABLE_FEATURE_USERNAME_COMPLETION
|
||||||
if (dirbuf[0] == '~') /* ~/... or ~user/... */
|
if (dirbuf[0] == '~') /* ~/... or ~user/... */
|
||||||
dirbuf = username_path_completion(dirbuf);
|
dirbuf = username_path_completion(dirbuf);
|
||||||
#endif
|
# endif
|
||||||
path1[0] = dirbuf;
|
path1[0] = dirbuf;
|
||||||
}
|
}
|
||||||
pf_len = strlen(pfind);
|
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;
|
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++;
|
||||||
}
|
}
|
||||||
@ -1038,10 +1049,10 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
/* Length of string used for matching */
|
/* Length of string used for matching */
|
||||||
unsigned match_pfx_len = match_pfx_len;
|
unsigned match_pfx_len = match_pfx_len;
|
||||||
int find_type;
|
int find_type;
|
||||||
#if ENABLE_UNICODE_SUPPORT
|
# if ENABLE_UNICODE_SUPPORT
|
||||||
/* cursor pos in command converted to multibyte form */
|
/* cursor pos in command converted to multibyte form */
|
||||||
int cursor_mb;
|
int cursor_mb;
|
||||||
#endif
|
# endif
|
||||||
if (!(state->flags & TAB_COMPLETION))
|
if (!(state->flags & TAB_COMPLETION))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1068,9 +1079,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
* (we then also (ab)use this extra space later - see (**))
|
* (we then also (ab)use this extra space later - see (**))
|
||||||
*/
|
*/
|
||||||
match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
|
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 */
|
save_string(match_buf, cursor + 1); /* +1 for NUL */
|
||||||
#else
|
# else
|
||||||
{
|
{
|
||||||
CHAR_T wc = command_ps[cursor];
|
CHAR_T wc = command_ps[cursor];
|
||||||
command_ps[cursor] = BB_NUL;
|
command_ps[cursor] = BB_NUL;
|
||||||
@ -1078,26 +1089,37 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
command_ps[cursor] = wc;
|
command_ps[cursor] = wc;
|
||||||
cursor_mb = strlen(match_buf);
|
cursor_mb = strlen(match_buf);
|
||||||
}
|
}
|
||||||
#endif
|
# endif
|
||||||
find_type = build_match_prefix(match_buf);
|
find_type = build_match_prefix(match_buf);
|
||||||
|
|
||||||
/* Free up any memory already allocated */
|
/* Free up any memory already allocated */
|
||||||
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] = ' ';
|
||||||
@ -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? */
|
/* Have space to place the match? */
|
||||||
/* The result consists of three parts with these lengths: */
|
/* The result consists of three parts with these lengths: */
|
||||||
/* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
|
/* 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 */
|
/* write out the matched command */
|
||||||
redraw(cmdedit_y, command_len - pos);
|
redraw(cmdedit_y, command_len - pos);
|
||||||
}
|
}
|
||||||
#else
|
# else
|
||||||
{
|
{
|
||||||
/* Use 2nd half of match_buf as scratch space - see (**) */
|
/* Use 2nd half of match_buf as scratch space - see (**) */
|
||||||
char *command = match_buf + MAX_LINELEN;
|
char *command = match_buf + MAX_LINELEN;
|
||||||
@ -1190,7 +1213,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
|
|||||||
redraw(cmdedit_y, pos >= 0 ? pos : 0);
|
redraw(cmdedit_y, pos >= 0 ? pos : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
# endif
|
||||||
ret:
|
ret:
|
||||||
free(chosen_match);
|
free(chosen_match);
|
||||||
free(match_buf);
|
free(match_buf);
|
||||||
|
Loading…
Reference in New Issue
Block a user