diff options
-rw-r--r-- | libbb/lineedit.c | 67 |
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 | |||
587 | static void free_tab_completion_data(void) | 593 | static 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 | ||
662 | enum { | 668 | enum { |
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 | ||
1018 | static char *add_quote_for_spec_chars(char *found) | 1024 | static const char *is_special_char(char c) |
1025 | { | ||
1026 | return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); | ||
1027 | } | ||
1028 | |||
1029 | static 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); |