diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2011-01-04 19:56:15 +0700 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2011-01-04 19:56:15 +0700 |
commit | 5f6f2162512106adf120d4b528bb125e93e34429 (patch) | |
tree | 7d7449f755633c263be7125ad58d21cc3ca5b8a7 /libbb/lineedit.c | |
parent | 9db69882bee2d528d706d61d34ef7741122330be (diff) | |
parent | a116552869db5e7793ae10968eb3c962c69b3d8c (diff) | |
download | busybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.tar.gz busybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.tar.bz2 busybox-w32-5f6f2162512106adf120d4b528bb125e93e34429.zip |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r-- | libbb/lineedit.c | 99 |
1 files changed, 63 insertions, 36 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 182dfac13..f9658711a 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -94,8 +94,10 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); } | |||
94 | #endif | 94 | #endif |
95 | 95 | ||
96 | 96 | ||
97 | #define SEQ_CLEAR_TILL_END_OF_SCREEN "\033[J" | 97 | #define ESC "\033" |
98 | //#define SEQ_CLEAR_TILL_END_OF_LINE "\033[K" | 98 | |
99 | #define SEQ_CLEAR_TILL_END_OF_SCREEN ESC"[J" | ||
100 | //#define SEQ_CLEAR_TILL_END_OF_LINE ESC"[K" | ||
99 | 101 | ||
100 | 102 | ||
101 | enum { | 103 | enum { |
@@ -446,7 +448,7 @@ static void input_backward(unsigned num) | |||
446 | } while (--num); | 448 | } while (--num); |
447 | return; | 449 | return; |
448 | } | 450 | } |
449 | printf("\033[%uD", num); | 451 | printf(ESC"[%uD", num); |
450 | return; | 452 | return; |
451 | } | 453 | } |
452 | 454 | ||
@@ -471,7 +473,7 @@ static void input_backward(unsigned num) | |||
471 | */ | 473 | */ |
472 | unsigned sv_cursor; | 474 | unsigned sv_cursor; |
473 | /* go to 1st column; go up to first line */ | 475 | /* go to 1st column; go up to first line */ |
474 | printf("\r" "\033[%uA", cmdedit_y); | 476 | printf("\r" ESC"[%uA", cmdedit_y); |
475 | cmdedit_y = 0; | 477 | cmdedit_y = 0; |
476 | sv_cursor = cursor; | 478 | sv_cursor = cursor; |
477 | put_prompt(); /* sets cursor to 0 */ | 479 | put_prompt(); /* sets cursor to 0 */ |
@@ -488,12 +490,12 @@ static void input_backward(unsigned num) | |||
488 | cmdedit_x = (width * cmdedit_y - num) % width; | 490 | cmdedit_x = (width * cmdedit_y - num) % width; |
489 | cmdedit_y -= lines_up; | 491 | cmdedit_y -= lines_up; |
490 | /* go to 1st column; go up */ | 492 | /* go to 1st column; go up */ |
491 | printf("\r" "\033[%uA", lines_up); | 493 | printf("\r" ESC"[%uA", lines_up); |
492 | /* go to correct column. | 494 | /* go to correct column. |
493 | * xterm, konsole, Linux VT interpret 0 as 1 below! wow. | 495 | * xterm, konsole, Linux VT interpret 0 as 1 below! wow. |
494 | * need to *make sure* we skip it if cmdedit_x == 0 */ | 496 | * need to *make sure* we skip it if cmdedit_x == 0 */ |
495 | if (cmdedit_x) | 497 | if (cmdedit_x) |
496 | printf("\033[%uC", cmdedit_x); | 498 | printf(ESC"[%uC", cmdedit_x); |
497 | } | 499 | } |
498 | } | 500 | } |
499 | 501 | ||
@@ -501,7 +503,7 @@ static void input_backward(unsigned num) | |||
501 | static void redraw(int y, int back_cursor) | 503 | static void redraw(int y, int back_cursor) |
502 | { | 504 | { |
503 | if (y > 0) /* up y lines */ | 505 | if (y > 0) /* up y lines */ |
504 | printf("\033[%uA", y); | 506 | printf(ESC"[%uA", y); |
505 | bb_putchar('\r'); | 507 | bb_putchar('\r'); |
506 | put_prompt(); | 508 | put_prompt(); |
507 | put_till_end_and_adv_cursor(); | 509 | put_till_end_and_adv_cursor(); |
@@ -582,6 +584,12 @@ static void input_forward(void) | |||
582 | 584 | ||
583 | #if ENABLE_FEATURE_TAB_COMPLETION | 585 | #if ENABLE_FEATURE_TAB_COMPLETION |
584 | 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 | |||
585 | static void free_tab_completion_data(void) | 593 | static void free_tab_completion_data(void) |
586 | { | 594 | { |
587 | if (matches) { | 595 | if (matches) { |
@@ -599,7 +607,7 @@ static void add_match(char *matched) | |||
599 | num_matches++; | 607 | num_matches++; |
600 | } | 608 | } |
601 | 609 | ||
602 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 610 | # if ENABLE_FEATURE_USERNAME_COMPLETION |
603 | /* Replace "~user/..." with "/homedir/...". | 611 | /* Replace "~user/..." with "/homedir/...". |
604 | * The parameter is malloced, free it or return it | 612 | * The parameter is malloced, free it or return it |
605 | * unchanged if no user is matched. | 613 | * unchanged if no user is matched. |
@@ -655,7 +663,7 @@ static NOINLINE unsigned complete_username(const char *ud) | |||
655 | 663 | ||
656 | return 1 + userlen; | 664 | return 1 + userlen; |
657 | } | 665 | } |
658 | #endif /* FEATURE_USERNAME_COMPLETION */ | 666 | # endif /* FEATURE_USERNAME_COMPLETION */ |
659 | 667 | ||
660 | enum { | 668 | enum { |
661 | FIND_EXE_ONLY = 0, | 669 | FIND_EXE_ONLY = 0, |
@@ -740,10 +748,10 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) | |||
740 | pfind++; | 748 | pfind++; |
741 | /* dirbuf = ".../.../.../" */ | 749 | /* dirbuf = ".../.../.../" */ |
742 | dirbuf = xstrndup(command, pfind - command); | 750 | dirbuf = xstrndup(command, pfind - command); |
743 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 751 | # if ENABLE_FEATURE_USERNAME_COMPLETION |
744 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ | 752 | if (dirbuf[0] == '~') /* ~/... or ~user/... */ |
745 | dirbuf = username_path_completion(dirbuf); | 753 | dirbuf = username_path_completion(dirbuf); |
746 | #endif | 754 | # endif |
747 | path1[0] = dirbuf; | 755 | path1[0] = dirbuf; |
748 | } | 756 | } |
749 | pf_len = strlen(pfind); | 757 | pf_len = strlen(pfind); |
@@ -1021,13 +1029,18 @@ static void showfiles(void) | |||
1021 | } | 1029 | } |
1022 | } | 1030 | } |
1023 | 1031 | ||
1024 | static char *add_quote_for_spec_chars(char *found) | 1032 | static const char *is_special_char(char c) |
1033 | { | ||
1034 | return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c); | ||
1035 | } | ||
1036 | |||
1037 | static char *quote_special_chars(char *found) | ||
1025 | { | 1038 | { |
1026 | int l = 0; | 1039 | int l = 0; |
1027 | char *s = xzalloc((strlen(found) + 1) * 2); | 1040 | char *s = xzalloc((strlen(found) + 1) * 2); |
1028 | 1041 | ||
1029 | while (*found) { | 1042 | while (*found) { |
1030 | if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found)) | 1043 | if (is_special_char(*found)) |
1031 | s[l++] = '\\'; | 1044 | s[l++] = '\\'; |
1032 | s[l++] = *found++; | 1045 | s[l++] = *found++; |
1033 | } | 1046 | } |
@@ -1044,10 +1057,10 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1044 | /* Length of string used for matching */ | 1057 | /* Length of string used for matching */ |
1045 | unsigned match_pfx_len = match_pfx_len; | 1058 | unsigned match_pfx_len = match_pfx_len; |
1046 | int find_type; | 1059 | int find_type; |
1047 | #if ENABLE_UNICODE_SUPPORT | 1060 | # if ENABLE_UNICODE_SUPPORT |
1048 | /* cursor pos in command converted to multibyte form */ | 1061 | /* cursor pos in command converted to multibyte form */ |
1049 | int cursor_mb; | 1062 | int cursor_mb; |
1050 | #endif | 1063 | # endif |
1051 | if (!(state->flags & TAB_COMPLETION)) | 1064 | if (!(state->flags & TAB_COMPLETION)) |
1052 | return; | 1065 | return; |
1053 | 1066 | ||
@@ -1074,9 +1087,9 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1074 | * (we then also (ab)use this extra space later - see (**)) | 1087 | * (we then also (ab)use this extra space later - see (**)) |
1075 | */ | 1088 | */ |
1076 | match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); | 1089 | match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t)); |
1077 | #if !ENABLE_UNICODE_SUPPORT | 1090 | # if !ENABLE_UNICODE_SUPPORT |
1078 | save_string(match_buf, cursor + 1); /* +1 for NUL */ | 1091 | save_string(match_buf, cursor + 1); /* +1 for NUL */ |
1079 | #else | 1092 | # else |
1080 | { | 1093 | { |
1081 | CHAR_T wc = command_ps[cursor]; | 1094 | CHAR_T wc = command_ps[cursor]; |
1082 | command_ps[cursor] = BB_NUL; | 1095 | command_ps[cursor] = BB_NUL; |
@@ -1084,26 +1097,37 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1084 | command_ps[cursor] = wc; | 1097 | command_ps[cursor] = wc; |
1085 | cursor_mb = strlen(match_buf); | 1098 | cursor_mb = strlen(match_buf); |
1086 | } | 1099 | } |
1087 | #endif | 1100 | # endif |
1088 | find_type = build_match_prefix(match_buf); | 1101 | find_type = build_match_prefix(match_buf); |
1089 | 1102 | ||
1090 | /* Free up any memory already allocated */ | 1103 | /* Free up any memory already allocated */ |
1091 | free_tab_completion_data(); | 1104 | free_tab_completion_data(); |
1092 | 1105 | ||
1093 | #if ENABLE_FEATURE_USERNAME_COMPLETION | 1106 | # if ENABLE_FEATURE_USERNAME_COMPLETION |
1094 | /* If the word starts with `~' and there is no slash in the word, | 1107 | /* If the word starts with ~ and there is no slash in the word, |
1095 | * then try completing this word as a username. */ | 1108 | * then try completing this word as a username. */ |
1096 | if (state->flags & USERNAME_COMPLETION) | 1109 | if (state->flags & USERNAME_COMPLETION) |
1097 | if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) | 1110 | if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL) |
1098 | match_pfx_len = complete_username(match_buf); | 1111 | match_pfx_len = complete_username(match_buf); |
1099 | #endif | 1112 | # endif |
1100 | /* Try to match a command in $PATH, or a directory, or a file */ | 1113 | /* If complete_username() did not match, |
1114 | * try to match a command in $PATH, or a directory, or a file */ | ||
1101 | if (!matches) | 1115 | if (!matches) |
1102 | match_pfx_len = complete_cmd_dir_file(match_buf, find_type); | 1116 | match_pfx_len = complete_cmd_dir_file(match_buf, find_type); |
1117 | |||
1118 | /* Account for backslashes which will be inserted | ||
1119 | * by quote_special_chars() later */ | ||
1120 | { | ||
1121 | const char *e = match_buf + strlen(match_buf); | ||
1122 | const char *s = e - match_pfx_len; | ||
1123 | while (s < e) | ||
1124 | if (is_special_char(*s++)) | ||
1125 | match_pfx_len++; | ||
1126 | } | ||
1127 | |||
1103 | /* Remove duplicates */ | 1128 | /* Remove duplicates */ |
1104 | if (matches) { | 1129 | if (matches) { |
1105 | unsigned i; | 1130 | unsigned i, n = 0; |
1106 | unsigned n = 0; | ||
1107 | qsort_string_vector(matches, num_matches); | 1131 | qsort_string_vector(matches, num_matches); |
1108 | for (i = 0; i < num_matches - 1; ++i) { | 1132 | for (i = 0; i < num_matches - 1; ++i) { |
1109 | //if (matches[i] && matches[i+1]) { /* paranoia */ | 1133 | //if (matches[i] && matches[i+1]) { /* paranoia */ |
@@ -1118,6 +1142,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1118 | matches[n++] = matches[i]; | 1142 | matches[n++] = matches[i]; |
1119 | num_matches = n; | 1143 | num_matches = n; |
1120 | } | 1144 | } |
1145 | |||
1121 | /* Did we find exactly one match? */ | 1146 | /* Did we find exactly one match? */ |
1122 | if (num_matches != 1) { /* no */ | 1147 | if (num_matches != 1) { /* no */ |
1123 | char *cp; | 1148 | char *cp; |
@@ -1139,7 +1164,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1139 | goto ret; /* no */ | 1164 | goto ret; /* no */ |
1140 | } | 1165 | } |
1141 | *cp = '\0'; | 1166 | *cp = '\0'; |
1142 | cp = add_quote_for_spec_chars(chosen_match); | 1167 | cp = quote_special_chars(chosen_match); |
1143 | free(chosen_match); | 1168 | free(chosen_match); |
1144 | chosen_match = cp; | 1169 | chosen_match = cp; |
1145 | len_found = strlen(chosen_match); | 1170 | len_found = strlen(chosen_match); |
@@ -1147,7 +1172,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1147 | /* Next <tab> is not a double-tab */ | 1172 | /* Next <tab> is not a double-tab */ |
1148 | *lastWasTab = 0; | 1173 | *lastWasTab = 0; |
1149 | 1174 | ||
1150 | chosen_match = add_quote_for_spec_chars(matches[0]); | 1175 | chosen_match = quote_special_chars(matches[0]); |
1151 | len_found = strlen(chosen_match); | 1176 | len_found = strlen(chosen_match); |
1152 | if (chosen_match[len_found-1] != '/') { | 1177 | if (chosen_match[len_found-1] != '/') { |
1153 | chosen_match[len_found] = ' '; | 1178 | chosen_match[len_found] = ' '; |
@@ -1155,7 +1180,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1155 | } | 1180 | } |
1156 | } | 1181 | } |
1157 | 1182 | ||
1158 | #if !ENABLE_UNICODE_SUPPORT | 1183 | # if !ENABLE_UNICODE_SUPPORT |
1159 | /* Have space to place the match? */ | 1184 | /* Have space to place the match? */ |
1160 | /* The result consists of three parts with these lengths: */ | 1185 | /* The result consists of three parts with these lengths: */ |
1161 | /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ | 1186 | /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */ |
@@ -1172,7 +1197,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1172 | /* write out the matched command */ | 1197 | /* write out the matched command */ |
1173 | redraw(cmdedit_y, command_len - pos); | 1198 | redraw(cmdedit_y, command_len - pos); |
1174 | } | 1199 | } |
1175 | #else | 1200 | # else |
1176 | { | 1201 | { |
1177 | /* Use 2nd half of match_buf as scratch space - see (**) */ | 1202 | /* Use 2nd half of match_buf as scratch space - see (**) */ |
1178 | char *command = match_buf + MAX_LINELEN; | 1203 | char *command = match_buf + MAX_LINELEN; |
@@ -1196,7 +1221,7 @@ static NOINLINE void input_tab(smallint *lastWasTab) | |||
1196 | redraw(cmdedit_y, pos >= 0 ? pos : 0); | 1221 | redraw(cmdedit_y, pos >= 0 ? pos : 0); |
1197 | } | 1222 | } |
1198 | } | 1223 | } |
1199 | #endif | 1224 | # endif |
1200 | ret: | 1225 | ret: |
1201 | free(chosen_match); | 1226 | free(chosen_match); |
1202 | free(match_buf); | 1227 | free(match_buf); |
@@ -1342,7 +1367,7 @@ static void save_history(char *str) | |||
1342 | int fd; | 1367 | int fd; |
1343 | int len, len2; | 1368 | int len, len2; |
1344 | 1369 | ||
1345 | fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666); | 1370 | fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); |
1346 | if (fd < 0) | 1371 | if (fd < 0) |
1347 | return; | 1372 | return; |
1348 | xlseek(fd, 0, SEEK_END); /* paranoia */ | 1373 | xlseek(fd, 0, SEEK_END); /* paranoia */ |
@@ -1357,10 +1382,8 @@ static void save_history(char *str) | |||
1357 | /* did we write so much that history file needs trimming? */ | 1382 | /* did we write so much that history file needs trimming? */ |
1358 | state->cnt_history_in_file++; | 1383 | state->cnt_history_in_file++; |
1359 | if (state->cnt_history_in_file > MAX_HISTORY * 4) { | 1384 | if (state->cnt_history_in_file > MAX_HISTORY * 4) { |
1360 | FILE *fp; | ||
1361 | char *new_name; | 1385 | char *new_name; |
1362 | line_input_t *st_temp; | 1386 | line_input_t *st_temp; |
1363 | int i; | ||
1364 | 1387 | ||
1365 | /* we may have concurrently written entries from others. | 1388 | /* we may have concurrently written entries from others. |
1366 | * load them */ | 1389 | * load them */ |
@@ -1370,8 +1393,12 @@ static void save_history(char *str) | |||
1370 | 1393 | ||
1371 | /* write out temp file and replace hist_file atomically */ | 1394 | /* write out temp file and replace hist_file atomically */ |
1372 | new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); | 1395 | new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); |
1373 | fp = fopen_for_write(new_name); | 1396 | fd = open(state->hist_file, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
1374 | if (fp) { | 1397 | if (fd >= 0) { |
1398 | FILE *fp; | ||
1399 | int i; | ||
1400 | |||
1401 | fp = xfdopen_for_write(fd); | ||
1375 | for (i = 0; i < st_temp->cnt_history; i++) | 1402 | for (i = 0; i < st_temp->cnt_history; i++) |
1376 | fprintf(fp, "%s\n", st_temp->history[i]); | 1403 | fprintf(fp, "%s\n", st_temp->history[i]); |
1377 | fclose(fp); | 1404 | fclose(fp); |
@@ -1634,7 +1661,7 @@ static void ask_terminal(void) | |||
1634 | pfd.events = POLLIN; | 1661 | pfd.events = POLLIN; |
1635 | if (safe_poll(&pfd, 1, 0) == 0) { | 1662 | if (safe_poll(&pfd, 1, 0) == 0) { |
1636 | S.sent_ESC_br6n = 1; | 1663 | S.sent_ESC_br6n = 1; |
1637 | fputs("\033" "[6n", stdout); | 1664 | fputs(ESC"[6n", stdout); |
1638 | fflush_all(); /* make terminal see it ASAP! */ | 1665 | fflush_all(); /* make terminal see it ASAP! */ |
1639 | } | 1666 | } |
1640 | } | 1667 | } |
@@ -2089,7 +2116,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
2089 | case CTRL('L'): | 2116 | case CTRL('L'): |
2090 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) | 2117 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) |
2091 | /* Control-l -- clear screen */ | 2118 | /* Control-l -- clear screen */ |
2092 | printf("\033[H"); /* cursor to top,left */ | 2119 | printf(ESC"[H"); /* cursor to top,left */ |
2093 | redraw(0, command_len - cursor); | 2120 | redraw(0, command_len - cursor); |
2094 | break; | 2121 | break; |
2095 | #if MAX_HISTORY > 0 | 2122 | #if MAX_HISTORY > 0 |