aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:56:15 +0700
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:56:15 +0700
commit5f6f2162512106adf120d4b528bb125e93e34429 (patch)
tree7d7449f755633c263be7125ad58d21cc3ca5b8a7 /libbb/lineedit.c
parent9db69882bee2d528d706d61d34ef7741122330be (diff)
parenta116552869db5e7793ae10968eb3c962c69b3d8c (diff)
downloadbusybox-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.c99
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
101enum { 103enum {
@@ -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)
501static void redraw(int y, int back_cursor) 503static 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
585static void free_tab_completion_data(void) 593static 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
660enum { 668enum {
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
1024static char *add_quote_for_spec_chars(char *found) 1032static const char *is_special_char(char c)
1033{
1034 return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
1035}
1036
1037static 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