aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Shal <marfey@gmail.com>2010-11-22 03:49:18 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2010-11-22 03:49:18 +0100
commitf3763033e457b100a863731c16a80648d85995c9 (patch)
tree0910f9751c6c750f3725438ec8f24f2a88b0bea6
parent7c6ed78aaa6f7252212869ab576eae1559645a3e (diff)
downloadbusybox-w32-f3763033e457b100a863731c16a80648d85995c9.tar.gz
busybox-w32-f3763033e457b100a863731c16a80648d85995c9.tar.bz2
busybox-w32-f3763033e457b100a863731c16a80648d85995c9.zip
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>
-rw-r--r--libbb/lineedit.c67
1 files changed, 45 insertions, 22 deletions
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)
584 584
585#if ENABLE_FEATURE_TAB_COMPLETION 585#if ENABLE_FEATURE_TAB_COMPLETION
586 586
587//FIXME:
588//needs to be more clever: currently it thinks that "foo\ b<TAB>
589//matches the file named "foo bar", which is untrue.
590//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
591//not "foo bar <cursor>...
592
587static void free_tab_completion_data(void) 593static void free_tab_completion_data(void)
588{ 594{
589 if (matches) { 595 if (matches) {
@@ -601,7 +607,7 @@ static void add_match(char *matched)
601 num_matches++; 607 num_matches++;
602} 608}
603 609
604#if ENABLE_FEATURE_USERNAME_COMPLETION 610# if ENABLE_FEATURE_USERNAME_COMPLETION
605/* Replace "~user/..." with "/homedir/...". 611/* Replace "~user/..." with "/homedir/...".
606 * The parameter is malloced, free it or return it 612 * The parameter is malloced, free it or return it
607 * unchanged if no user is matched. 613 * unchanged if no user is matched.
@@ -657,7 +663,7 @@ static NOINLINE unsigned complete_username(const char *ud)
657 663
658 return 1 + userlen; 664 return 1 + userlen;
659} 665}
660#endif /* FEATURE_USERNAME_COMPLETION */ 666# endif /* FEATURE_USERNAME_COMPLETION */
661 667
662enum { 668enum {
663 FIND_EXE_ONLY = 0, 669 FIND_EXE_ONLY = 0,
@@ -734,10 +740,10 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
734 pfind++; 740 pfind++;
735 /* dirbuf = ".../.../.../" */ 741 /* dirbuf = ".../.../.../" */
736 dirbuf = xstrndup(command, pfind - command); 742 dirbuf = xstrndup(command, pfind - command);
737#if ENABLE_FEATURE_USERNAME_COMPLETION 743# if ENABLE_FEATURE_USERNAME_COMPLETION
738 if (dirbuf[0] == '~') /* ~/... or ~user/... */ 744 if (dirbuf[0] == '~') /* ~/... or ~user/... */
739 dirbuf = username_path_completion(dirbuf); 745 dirbuf = username_path_completion(dirbuf);
740#endif 746# endif
741 path1[0] = dirbuf; 747 path1[0] = dirbuf;
742 } 748 }
743 pf_len = strlen(pfind); 749 pf_len = strlen(pfind);
@@ -1015,13 +1021,18 @@ static void showfiles(void)
1015 } 1021 }
1016} 1022}
1017 1023
1018static char *add_quote_for_spec_chars(char *found) 1024static const char *is_special_char(char c)
1025{
1026 return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
1027}
1028
1029static char *quote_special_chars(char *found)
1019{ 1030{
1020 int l = 0; 1031 int l = 0;
1021 char *s = xzalloc((strlen(found) + 1) * 2); 1032 char *s = xzalloc((strlen(found) + 1) * 2);
1022 1033
1023 while (*found) { 1034 while (*found) {
1024 if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found)) 1035 if (is_special_char(*found))
1025 s[l++] = '\\'; 1036 s[l++] = '\\';
1026 s[l++] = *found++; 1037 s[l++] = *found++;
1027 } 1038 }
@@ -1038,10 +1049,10 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1038 /* Length of string used for matching */ 1049 /* Length of string used for matching */
1039 unsigned match_pfx_len = match_pfx_len; 1050 unsigned match_pfx_len = match_pfx_len;
1040 int find_type; 1051 int find_type;
1041#if ENABLE_UNICODE_SUPPORT 1052# if ENABLE_UNICODE_SUPPORT
1042 /* cursor pos in command converted to multibyte form */ 1053 /* cursor pos in command converted to multibyte form */
1043 int cursor_mb; 1054 int cursor_mb;
1044#endif 1055# endif
1045 if (!(state->flags & TAB_COMPLETION)) 1056 if (!(state->flags & TAB_COMPLETION))
1046 return; 1057 return;
1047 1058
@@ -1068,9 +1079,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1068 * (we then also (ab)use this extra space later - see (**)) 1079 * (we then also (ab)use this extra space later - see (**))
1069 */ 1080 */
1070 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); 1081 match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
1071#if !ENABLE_UNICODE_SUPPORT 1082# if !ENABLE_UNICODE_SUPPORT
1072 save_string(match_buf, cursor + 1); /* +1 for NUL */ 1083 save_string(match_buf, cursor + 1); /* +1 for NUL */
1073#else 1084# else
1074 { 1085 {
1075 CHAR_T wc = command_ps[cursor]; 1086 CHAR_T wc = command_ps[cursor];
1076 command_ps[cursor] = BB_NUL; 1087 command_ps[cursor] = BB_NUL;
@@ -1078,26 +1089,37 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1078 command_ps[cursor] = wc; 1089 command_ps[cursor] = wc;
1079 cursor_mb = strlen(match_buf); 1090 cursor_mb = strlen(match_buf);
1080 } 1091 }
1081#endif 1092# endif
1082 find_type = build_match_prefix(match_buf); 1093 find_type = build_match_prefix(match_buf);
1083 1094
1084 /* Free up any memory already allocated */ 1095 /* Free up any memory already allocated */
1085 free_tab_completion_data(); 1096 free_tab_completion_data();
1086 1097
1087#if ENABLE_FEATURE_USERNAME_COMPLETION 1098# if ENABLE_FEATURE_USERNAME_COMPLETION
1088 /* If the word starts with `~' and there is no slash in the word, 1099 /* If the word starts with ~ and there is no slash in the word,
1089 * then try completing this word as a username. */ 1100 * then try completing this word as a username. */
1090 if (state->flags & USERNAME_COMPLETION) 1101 if (state->flags & USERNAME_COMPLETION)
1091 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) 1102 if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
1092 match_pfx_len = complete_username(match_buf); 1103 match_pfx_len = complete_username(match_buf);
1093#endif 1104# endif
1094 /* Try to match a command in $PATH, or a directory, or a file */ 1105 /* If complete_username() did not match,
1106 * try to match a command in $PATH, or a directory, or a file */
1095 if (!matches) 1107 if (!matches)
1096 match_pfx_len = complete_cmd_dir_file(match_buf, find_type); 1108 match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
1109
1110 /* Account for backslashes which will be inserted
1111 * by quote_special_chars() later */
1112 {
1113 const char *e = match_buf + strlen(match_buf);
1114 const char *s = e - match_pfx_len;
1115 while (s < e)
1116 if (is_special_char(*s++))
1117 match_pfx_len++;
1118 }
1119
1097 /* Remove duplicates */ 1120 /* Remove duplicates */
1098 if (matches) { 1121 if (matches) {
1099 unsigned i; 1122 unsigned i, n = 0;
1100 unsigned n = 0;
1101 qsort_string_vector(matches, num_matches); 1123 qsort_string_vector(matches, num_matches);
1102 for (i = 0; i < num_matches - 1; ++i) { 1124 for (i = 0; i < num_matches - 1; ++i) {
1103 //if (matches[i] && matches[i+1]) { /* paranoia */ 1125 //if (matches[i] && matches[i+1]) { /* paranoia */
@@ -1112,6 +1134,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1112 matches[n++] = matches[i]; 1134 matches[n++] = matches[i];
1113 num_matches = n; 1135 num_matches = n;
1114 } 1136 }
1137
1115 /* Did we find exactly one match? */ 1138 /* Did we find exactly one match? */
1116 if (num_matches != 1) { /* no */ 1139 if (num_matches != 1) { /* no */
1117 char *cp; 1140 char *cp;
@@ -1133,7 +1156,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1133 goto ret; /* no */ 1156 goto ret; /* no */
1134 } 1157 }
1135 *cp = '\0'; 1158 *cp = '\0';
1136 cp = add_quote_for_spec_chars(chosen_match); 1159 cp = quote_special_chars(chosen_match);
1137 free(chosen_match); 1160 free(chosen_match);
1138 chosen_match = cp; 1161 chosen_match = cp;
1139 len_found = strlen(chosen_match); 1162 len_found = strlen(chosen_match);
@@ -1141,7 +1164,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1141 /* Next <tab> is not a double-tab */ 1164 /* Next <tab> is not a double-tab */
1142 *lastWasTab = 0; 1165 *lastWasTab = 0;
1143 1166
1144 chosen_match = add_quote_for_spec_chars(matches[0]); 1167 chosen_match = quote_special_chars(matches[0]);
1145 len_found = strlen(chosen_match); 1168 len_found = strlen(chosen_match);
1146 if (chosen_match[len_found-1] != '/') { 1169 if (chosen_match[len_found-1] != '/') {
1147 chosen_match[len_found] = ' '; 1170 chosen_match[len_found] = ' ';
@@ -1149,7 +1172,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1149 } 1172 }
1150 } 1173 }
1151 1174
1152#if !ENABLE_UNICODE_SUPPORT 1175# if !ENABLE_UNICODE_SUPPORT
1153 /* Have space to place the match? */ 1176 /* Have space to place the match? */
1154 /* The result consists of three parts with these lengths: */ 1177 /* The result consists of three parts with these lengths: */
1155 /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ 1178 /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
@@ -1166,7 +1189,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1166 /* write out the matched command */ 1189 /* write out the matched command */
1167 redraw(cmdedit_y, command_len - pos); 1190 redraw(cmdedit_y, command_len - pos);
1168 } 1191 }
1169#else 1192# else
1170 { 1193 {
1171 /* Use 2nd half of match_buf as scratch space - see (**) */ 1194 /* Use 2nd half of match_buf as scratch space - see (**) */
1172 char *command = match_buf + MAX_LINELEN; 1195 char *command = match_buf + MAX_LINELEN;
@@ -1190,7 +1213,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1190 redraw(cmdedit_y, pos >= 0 ? pos : 0); 1213 redraw(cmdedit_y, pos >= 0 ? pos : 0);
1191 } 1214 }
1192 } 1215 }
1193#endif 1216# endif
1194 ret: 1217 ret:
1195 free(chosen_match); 1218 free(chosen_match);
1196 free(match_buf); 1219 free(match_buf);