aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);