aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r--libbb/lineedit.c153
1 files changed, 148 insertions, 5 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index e8d721e61..7c46fa5db 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -371,7 +371,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
371/* Put 'command_ps[cursor]', cursor++. 371/* Put 'command_ps[cursor]', cursor++.
372 * Advance cursor on screen. If we reached right margin, scroll text up 372 * Advance cursor on screen. If we reached right margin, scroll text up
373 * and remove terminal margin effect by printing 'next_char' */ 373 * and remove terminal margin effect by printing 'next_char' */
374#define HACK_FOR_WRONG_WIDTH 1 374#define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32
375static void put_cur_glyph_and_inc_cursor(void) 375static void put_cur_glyph_and_inc_cursor(void)
376{ 376{
377 CHAR_T c = command_ps[cursor]; 377 CHAR_T c = command_ps[cursor];
@@ -434,6 +434,42 @@ static void put_cur_glyph_and_inc_cursor(void)
434 } 434 }
435} 435}
436 436
437#if ENABLE_PLATFORM_MINGW32
438static void inc_cursor(void)
439{
440 CHAR_T c = command_ps[cursor];
441 unsigned width = 0;
442 int ofs_to_right;
443
444 /* advance cursor */
445 cursor++;
446 if (unicode_status == UNICODE_ON) {
447 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
448 c = adjust_width_and_validate_wc(&cmdedit_x, c);
449 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
450 } else {
451 cmdedit_x++;
452 }
453
454 ofs_to_right = cmdedit_x - cmdedit_termw;
455 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
456 /* cursor remains on this line */
457 printf(ESC"[1C");
458 }
459
460 if (ofs_to_right >= 0) {
461 /* we go to the next line */
462 printf(ESC"[1B");
463 bb_putchar('\r');
464 cmdedit_y++;
465 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
466 width = 0;
467 }
468 cmdedit_x = width;
469 }
470}
471#endif
472
437/* Move to end of line (by printing all chars till the end) */ 473/* Move to end of line (by printing all chars till the end) */
438static void put_till_end_and_adv_cursor(void) 474static void put_till_end_and_adv_cursor(void)
439{ 475{
@@ -493,6 +529,7 @@ static void input_backward(unsigned num)
493 529
494 if (cmdedit_x >= num) { 530 if (cmdedit_x >= num) {
495 cmdedit_x -= num; 531 cmdedit_x -= num;
532#if !ENABLE_PLATFORM_MINGW32
496 if (num <= 4) { 533 if (num <= 4) {
497 /* This is longer by 5 bytes on x86. 534 /* This is longer by 5 bytes on x86.
498 * Also gets miscompiled for ARM users 535 * Also gets miscompiled for ARM users
@@ -505,6 +542,7 @@ static void input_backward(unsigned num)
505 } while (--num); 542 } while (--num);
506 return; 543 return;
507 } 544 }
545#endif
508 printf(ESC"[%uD", num); 546 printf(ESC"[%uD", num);
509 return; 547 return;
510 } 548 }
@@ -643,7 +681,11 @@ static void input_backspace(void)
643static void input_forward(void) 681static void input_forward(void)
644{ 682{
645 if (cursor < command_len) 683 if (cursor < command_len)
684#if !ENABLE_PLATFORM_MINGW32
646 put_cur_glyph_and_inc_cursor(); 685 put_cur_glyph_and_inc_cursor();
686#else
687 inc_cursor();
688#endif
647} 689}
648 690
649#if ENABLE_FEATURE_TAB_COMPLETION 691#if ENABLE_FEATURE_TAB_COMPLETION
@@ -654,6 +696,14 @@ static void input_forward(void)
654//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 696//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
655//not "foo bar <cursor>... 697//not "foo bar <cursor>...
656 698
699# if ENABLE_PLATFORM_MINGW32
700/* use case-insensitive comparisons for filenames */
701# define is_prefixed_with(s, k) is_prefixed_with_case(s, k)
702# define qsort_string_vector(s, c) qsort_string_vector_case(s, c)
703# define strcmp(s, t) strcasecmp(s, t)
704# define strncmp(s, t, n) strncasecmp(s, t, n)
705# endif
706
657static void free_tab_completion_data(void) 707static void free_tab_completion_data(void)
658{ 708{
659 if (matches) { 709 if (matches) {
@@ -670,8 +720,12 @@ static void add_match(char *matched)
670 while (*p) { 720 while (*p) {
671 /* ESC attack fix: drop any string with control chars */ 721 /* ESC attack fix: drop any string with control chars */
672 if (*p < ' ' 722 if (*p < ' '
723# if !ENABLE_PLATFORM_MINGW32
673 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f) 724 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
674 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f) 725 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
726# else
727 || *p == 0x7f
728# endif
675 ) { 729 ) {
676 free(matched); 730 free(matched);
677 return; 731 return;
@@ -690,13 +744,16 @@ static void add_match(char *matched)
690 */ 744 */
691static char *username_path_completion(char *ud) 745static char *username_path_completion(char *ud)
692{ 746{
747# if !ENABLE_PLATFORM_MINGW32
693 struct passwd *entry; 748 struct passwd *entry;
749#endif
694 char *tilde_name = ud; 750 char *tilde_name = ud;
695 char *home = NULL; 751 char *home = NULL;
696 752
697 ud++; /* skip ~ */ 753 ud++; /* skip ~ */
698 if (*ud == '/') { /* "~/..." */ 754 if (*ud == '/') { /* "~/..." */
699 home = home_pwd_buf; 755 home = home_pwd_buf;
756# if !ENABLE_PLATFORM_MINGW32
700 } else { 757 } else {
701 /* "~user/..." */ 758 /* "~user/..." */
702 ud = strchr(ud, '/'); 759 ud = strchr(ud, '/');
@@ -705,6 +762,7 @@ static char *username_path_completion(char *ud)
705 *ud = '/'; /* restore "~user/..." */ 762 *ud = '/'; /* restore "~user/..." */
706 if (entry) 763 if (entry)
707 home = entry->pw_dir; 764 home = entry->pw_dir;
765# endif
708 } 766 }
709 if (home) { 767 if (home) {
710 ud = concat_path_file(home, ud); 768 ud = concat_path_file(home, ud);
@@ -714,6 +772,7 @@ static char *username_path_completion(char *ud)
714 return tilde_name; 772 return tilde_name;
715} 773}
716 774
775# if !ENABLE_PLATFORM_MINGW32
717/* ~use<tab> - find all users with this prefix. 776/* ~use<tab> - find all users with this prefix.
718 * Return the length of the prefix used for matching. 777 * Return the length of the prefix used for matching.
719 */ 778 */
@@ -736,6 +795,7 @@ static NOINLINE unsigned complete_username(const char *ud)
736 795
737 return 1 + userlen; 796 return 1 + userlen;
738} 797}
798# endif
739# endif /* FEATURE_USERNAME_COMPLETION */ 799# endif /* FEATURE_USERNAME_COMPLETION */
740 800
741enum { 801enum {
@@ -765,7 +825,7 @@ static unsigned path_parse(char ***p)
765 tmp = (char*)pth; 825 tmp = (char*)pth;
766 npth = 1; /* path component count */ 826 npth = 1; /* path component count */
767 while (1) { 827 while (1) {
768 tmp = strchr(tmp, ':'); 828 tmp = strchr(tmp, PATH_SEP);
769 if (!tmp) 829 if (!tmp)
770 break; 830 break;
771 tmp++; 831 tmp++;
@@ -776,7 +836,7 @@ static unsigned path_parse(char ***p)
776 res[0] = tmp = xstrdup(pth); 836 res[0] = tmp = xstrdup(pth);
777 npth = 1; 837 npth = 1;
778 while (1) { 838 while (1) {
779 tmp = strchr(tmp, ':'); 839 tmp = strchr(tmp, PATH_SEP);
780 if (!tmp) 840 if (!tmp)
781 break; 841 break;
782 *tmp++ = '\0'; /* ':' -> '\0' */ 842 *tmp++ = '\0'; /* ':' -> '\0' */
@@ -804,6 +864,17 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
804 path1[0] = (char*)"."; 864 path1[0] = (char*)".";
805 865
806 basecmd = strrchr(command, '/'); 866 basecmd = strrchr(command, '/');
867#if ENABLE_PLATFORM_MINGW32
868 if (!basecmd && has_dos_drive_prefix(command) && command[2] != '\0') {
869 char buffer[PATH_MAX];
870
871 /* path is of form c:path with no '/' */
872 if (get_drive_cwd(command, buffer, PATH_MAX)) {
873 basecmd = command + 2;
874 path1[0] = dirbuf = xstrdup(buffer);
875 }
876 } else
877#endif
807 if (!basecmd) { 878 if (!basecmd) {
808 if (type == FIND_EXE_ONLY) 879 if (type == FIND_EXE_ONLY)
809 npaths = path_parse(&paths); 880 npaths = path_parse(&paths);
@@ -861,6 +932,9 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
861 } 932 }
862 933
863 lpath = *paths[i] ? paths[i] : "."; 934 lpath = *paths[i] ? paths[i] : ".";
935#if ENABLE_PLATFORM_MINGW32
936 lpath = auto_string(alloc_system_drive(lpath));
937#endif
864 dir = opendir(lpath); 938 dir = opendir(lpath);
865 if (!dir) 939 if (!dir)
866 continue; /* don't print an error */ 940 continue; /* don't print an error */
@@ -883,6 +957,12 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
883 if (stat(found, &st) && lstat(found, &st)) 957 if (stat(found, &st) && lstat(found, &st))
884 goto cont; /* hmm, remove in progress? */ 958 goto cont; /* hmm, remove in progress? */
885 959
960# if ENABLE_PLATFORM_MINGW32
961 if (type == FIND_EXE_ONLY && S_ISREG(st.st_mode) &&
962 !(st.st_mode & S_IXUSR))
963 goto cont;
964# endif
965
886 /* Save only name */ 966 /* Save only name */
887 len = strlen(name_found); 967 len = strlen(name_found);
888 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 968 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
@@ -966,7 +1046,13 @@ static NOINLINE int build_match_prefix(char *match_buf)
966 1046
967 /* Mark every \c as "quoted c" */ 1047 /* Mark every \c as "quoted c" */
968 for (i = 0; int_buf[i]; i++) { 1048 for (i = 0; int_buf[i]; i++) {
1049#if ENABLE_PLATFORM_MINGW32
1050 /* Trailing backslash is effectively removed which confuses
1051 * the code to display case-preserved filenames. */
1052 if (int_buf[i] == '\\' && int_buf[i+1] != '\0') {
1053#else
969 if (int_buf[i] == '\\') { 1054 if (int_buf[i] == '\\') {
1055#endif
970 remove_chunk(int_buf, i, i + 1); 1056 remove_chunk(int_buf, i, i + 1);
971 int_buf[i] |= QUOT; 1057 int_buf[i] |= QUOT;
972 } 1058 }
@@ -1167,6 +1253,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1167 size_t len_found; 1253 size_t len_found;
1168 /* Length of string used for matching */ 1254 /* Length of string used for matching */
1169 unsigned match_pfx_len = match_pfx_len; 1255 unsigned match_pfx_len = match_pfx_len;
1256#if ENABLE_PLATFORM_MINGW32
1257 unsigned orig_pfx_len;
1258 char *target;
1259 const char *source;
1260#endif
1170 int find_type; 1261 int find_type;
1171# if ENABLE_UNICODE_SUPPORT 1262# if ENABLE_UNICODE_SUPPORT
1172 /* cursor pos in command converted to multibyte form */ 1263 /* cursor pos in command converted to multibyte form */
@@ -1214,7 +1305,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1214 /* Free up any memory already allocated */ 1305 /* Free up any memory already allocated */
1215 free_tab_completion_data(); 1306 free_tab_completion_data();
1216 1307
1217# if ENABLE_FEATURE_USERNAME_COMPLETION 1308# if ENABLE_FEATURE_USERNAME_COMPLETION && !ENABLE_PLATFORM_MINGW32
1218 /* If the word starts with ~ and there is no slash in the word, 1309 /* If the word starts with ~ and there is no slash in the word,
1219 * then try completing this word as a username. */ 1310 * then try completing this word as a username. */
1220 if (state->flags & USERNAME_COMPLETION) 1311 if (state->flags & USERNAME_COMPLETION)
@@ -1231,6 +1322,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1231 { 1322 {
1232 const char *e = match_buf + strlen(match_buf); 1323 const char *e = match_buf + strlen(match_buf);
1233 const char *s = e - match_pfx_len; 1324 const char *s = e - match_pfx_len;
1325#if ENABLE_PLATFORM_MINGW32
1326 orig_pfx_len = match_pfx_len;
1327#endif
1234 while (s < e) 1328 while (s < e)
1235 if (is_special_char(*s++)) 1329 if (is_special_char(*s++))
1236 match_pfx_len++; 1330 match_pfx_len++;
@@ -1265,7 +1359,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1265 for (cp = chosen_match; *cp; cp++) { 1359 for (cp = chosen_match; *cp; cp++) {
1266 unsigned n; 1360 unsigned n;
1267 for (n = 1; n < num_matches; n++) { 1361 for (n = 1; n < num_matches; n++) {
1362# if !ENABLE_PLATFORM_MINGW32
1268 if (matches[n][cp - chosen_match] != *cp) { 1363 if (matches[n][cp - chosen_match] != *cp) {
1364# else
1365 if (tolower(matches[n][cp - chosen_match]) != tolower(*cp)) {
1366# endif
1269 goto stop; 1367 goto stop;
1270 } 1368 }
1271 } 1369 }
@@ -1301,7 +1399,21 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1301 /* save tail */ 1399 /* save tail */
1302 strcpy(match_buf, &command_ps[cursor]); 1400 strcpy(match_buf, &command_ps[cursor]);
1303 /* add match and tail */ 1401 /* add match and tail */
1402# if ENABLE_PLATFORM_MINGW32
1403 if (match_pfx_len == orig_pfx_len) {
1404 /* replace match prefix to allow for altered case */
1405 target = &command_ps[cursor-match_pfx_len];
1406 source = chosen_match;
1407 }
1408 else {
1409 /* only replace tail of match if special characters are quoted */
1410 target = &command_ps[cursor];
1411 source = chosen_match + match_pfx_len;
1412 }
1413 strcpy(stpcpy(target, source), match_buf);
1414# else
1304 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); 1415 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1416# endif
1305 command_len = strlen(command_ps); 1417 command_len = strlen(command_ps);
1306 /* new pos */ 1418 /* new pos */
1307 pos = cursor + len_found - match_pfx_len; 1419 pos = cursor + len_found - match_pfx_len;
@@ -1338,6 +1450,13 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1338 free(match_buf); 1450 free(match_buf);
1339} 1451}
1340 1452
1453# if ENABLE_PLATFORM_MINGW32
1454# undef is_prefixed_with
1455# undef qsort_string_vector
1456# undef strcmp
1457# undef strncmp
1458# endif
1459
1341#endif /* FEATURE_TAB_COMPLETION */ 1460#endif /* FEATURE_TAB_COMPLETION */
1342 1461
1343 1462
@@ -2383,7 +2502,7 @@ static void sigaction2(int sig, struct sigaction *act)
2383 */ 2502 */
2384int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize) 2503int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
2385{ 2504{
2386 int len, n; 2505 int len IF_NOT_PLATFORM_MINGW32(, n);
2387 int timeout; 2506 int timeout;
2388#if ENABLE_FEATURE_TAB_COMPLETION 2507#if ENABLE_FEATURE_TAB_COMPLETION
2389 smallint lastWasTab = 0; 2508 smallint lastWasTab = 0;
@@ -2393,7 +2512,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2393 smallint vi_cmdmode = 0; 2512 smallint vi_cmdmode = 0;
2394#endif 2513#endif
2395 struct termios initial_settings; 2514 struct termios initial_settings;
2515#if !ENABLE_PLATFORM_MINGW32
2396 struct termios new_settings; 2516 struct termios new_settings;
2517#endif
2397 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 2518 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2398 2519
2399 INIT_S(); 2520 INIT_S();
@@ -2403,10 +2524,16 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2403 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) 2524 IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;)
2404 IF_FEATURE_EDITING_VI(delptr = delbuf;) 2525 IF_FEATURE_EDITING_VI(delptr = delbuf;)
2405 2526
2527#if !ENABLE_PLATFORM_MINGW32
2406 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0 2528 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
2407 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */ 2529 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
2408 ); 2530 );
2409 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) { 2531 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
2532#else
2533 initial_settings.c_cc[VINTR] = CTRL('C');
2534 initial_settings.c_cc[VEOF] = CTRL('D');
2535 if (!isatty(0) || !isatty(1)) {
2536#endif
2410 /* Happens when e.g. stty -echo was run before. 2537 /* Happens when e.g. stty -echo was run before.
2411 * But if ICANON is not set, we don't come here. 2538 * But if ICANON is not set, we don't come here.
2412 * (example: interactive python ^Z-backgrounded, 2539 * (example: interactive python ^Z-backgrounded,
@@ -2458,7 +2585,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2458#endif 2585#endif
2459#define command command_must_not_be_used 2586#define command command_must_not_be_used
2460 2587
2588#if !ENABLE_PLATFORM_MINGW32
2461 tcsetattr_stdin_TCSANOW(&new_settings); 2589 tcsetattr_stdin_TCSANOW(&new_settings);
2590#endif
2462 2591
2463#if ENABLE_USERNAME_OR_HOMEDIR 2592#if ENABLE_USERNAME_OR_HOMEDIR
2464 { 2593 {
@@ -2511,6 +2640,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2511 } 2640 }
2512#endif 2641#endif
2513 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 2642 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2643#if ENABLE_PLATFORM_MINGW32
2644 /* scroll to cursor position on any keypress */
2645 if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
2646 move_cursor_row(0);
2647#endif
2514 2648
2515#if ENABLE_FEATURE_REVERSE_SEARCH 2649#if ENABLE_FEATURE_REVERSE_SEARCH
2516 again: 2650 again:
@@ -2572,6 +2706,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2572 input_tab(&lastWasTab); 2706 input_tab(&lastWasTab);
2573 break; 2707 break;
2574#endif 2708#endif
2709#if ENABLE_PLATFORM_MINGW32
2710 case CTRL('Z'):
2711 command_ps[command_len] = '\0';
2712 bs_to_slash(command_ps);
2713 redraw(cmdedit_y, 0);
2714 break;
2715#endif
2575 case CTRL('K'): 2716 case CTRL('K'):
2576 /* Control-k -- clear to end of line */ 2717 /* Control-k -- clear to end of line */
2577 command_ps[cursor] = BB_NUL; 2718 command_ps[cursor] = BB_NUL;
@@ -2945,8 +3086,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2945 free_tab_completion_data(); 3086 free_tab_completion_data();
2946#endif 3087#endif
2947 3088
3089#if !ENABLE_PLATFORM_MINGW32
2948 /* restore initial_settings */ 3090 /* restore initial_settings */
2949 tcsetattr_stdin_TCSANOW(&initial_settings); 3091 tcsetattr_stdin_TCSANOW(&initial_settings);
3092#endif
2950#if ENABLE_FEATURE_EDITING_WINCH 3093#if ENABLE_FEATURE_EDITING_WINCH
2951 /* restore SIGWINCH handler */ 3094 /* restore SIGWINCH handler */
2952 sigaction_set(SIGWINCH, &S.SIGWINCH_handler); 3095 sigaction_set(SIGWINCH, &S.SIGWINCH_handler);