diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2024-04-13 18:40:20 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-04-13 18:40:20 +0200 |
commit | fd47f056765aed515f4c71118813f07be1402bee (patch) | |
tree | 5e2d6f5c01003989629aa898344b278d0cd3c6ec /libbb | |
parent | 681e4f5d922b9f0ea968238750d5c5d748eac809 (diff) | |
download | busybox-w32-fd47f056765aed515f4c71118813f07be1402bee.tar.gz busybox-w32-fd47f056765aed515f4c71118813f07be1402bee.tar.bz2 busybox-w32-fd47f056765aed515f4c71118813f07be1402bee.zip |
lineedit: print prompt and editing operations to stderr
For shells, this is mandated by standards
function old new delta
input_backward 215 231 +16
read_line_input 3015 3028 +13
draw_custom 66 78 +12
put_cur_glyph_and_inc_cursor 149 159 +10
put_prompt_custom 47 56 +9
show_history 40 46 +6
input_tab 927 933 +6
input_delete 136 142 +6
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 8/0 up/down: 78/0) Total: 78 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/lineedit.c | 62 |
1 files changed, 34 insertions, 28 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index bdae10914..543a3f11c 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -345,7 +345,7 @@ static unsigned save_string(char *dst, unsigned maxsize) | |||
345 | return i; | 345 | return i; |
346 | } | 346 | } |
347 | } | 347 | } |
348 | /* I thought just fputwc(c, stdout) would work. But no... */ | 348 | /* I thought just fputwc(c, stderr) would work. But no... */ |
349 | static void BB_PUTCHAR(wchar_t c) | 349 | static void BB_PUTCHAR(wchar_t c) |
350 | { | 350 | { |
351 | if (unicode_status == UNICODE_ON) { | 351 | if (unicode_status == UNICODE_ON) { |
@@ -354,11 +354,11 @@ static void BB_PUTCHAR(wchar_t c) | |||
354 | ssize_t len = wcrtomb(buf, c, &mbst); | 354 | ssize_t len = wcrtomb(buf, c, &mbst); |
355 | if (len > 0) { | 355 | if (len > 0) { |
356 | buf[len] = '\0'; | 356 | buf[len] = '\0'; |
357 | fputs_stdout(buf); | 357 | fputs(buf, stderr); |
358 | } | 358 | } |
359 | } else { | 359 | } else { |
360 | /* In this case, c is always one byte */ | 360 | /* In this case, c is always one byte */ |
361 | putchar(c); | 361 | bb_putchar_stderr(c); |
362 | } | 362 | } |
363 | } | 363 | } |
364 | # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS | 364 | # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS |
@@ -404,7 +404,7 @@ static void save_string(char *dst, unsigned maxsize) | |||
404 | safe_strncpy(dst, command_ps, maxsize); | 404 | safe_strncpy(dst, command_ps, maxsize); |
405 | } | 405 | } |
406 | # endif | 406 | # endif |
407 | # define BB_PUTCHAR(c) bb_putchar(c) | 407 | # define BB_PUTCHAR(c) bb_putchar_stderr(c) |
408 | /* Should never be called: */ | 408 | /* Should never be called: */ |
409 | int adjust_width_and_validate_wc(unsigned *width_adj, int wc); | 409 | int adjust_width_and_validate_wc(unsigned *width_adj, int wc); |
410 | #endif | 410 | #endif |
@@ -463,7 +463,7 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
463 | if (c == BB_NUL) | 463 | if (c == BB_NUL) |
464 | c = ' '; | 464 | c = ' '; |
465 | BB_PUTCHAR(c); | 465 | BB_PUTCHAR(c); |
466 | bb_putchar('\b'); | 466 | bb_putchar_stderr('\b'); |
467 | #endif | 467 | #endif |
468 | cmdedit_y++; | 468 | cmdedit_y++; |
469 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { | 469 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { |
@@ -489,12 +489,12 @@ static void goto_new_line(void) | |||
489 | put_till_end_and_adv_cursor(); | 489 | put_till_end_and_adv_cursor(); |
490 | /* "cursor == 0" is only if prompt is "" and user input is empty */ | 490 | /* "cursor == 0" is only if prompt is "" and user input is empty */ |
491 | if (cursor == 0 || cmdedit_x != 0) | 491 | if (cursor == 0 || cmdedit_x != 0) |
492 | bb_putchar('\n'); | 492 | bb_putchar_stderr('\n'); |
493 | } | 493 | } |
494 | 494 | ||
495 | static void beep(void) | 495 | static void beep(void) |
496 | { | 496 | { |
497 | bb_putchar('\007'); | 497 | bb_putchar_stderr('\007'); |
498 | } | 498 | } |
499 | 499 | ||
500 | /* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. | 500 | /* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. |
@@ -502,7 +502,10 @@ static void beep(void) | |||
502 | */ | 502 | */ |
503 | static void put_prompt_custom(bool is_full) | 503 | static void put_prompt_custom(bool is_full) |
504 | { | 504 | { |
505 | fputs_stdout((is_full ? cmdedit_prompt : prompt_last_line)); | 505 | /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
506 | * says that shells must write $PSn to stderr, not stdout. | ||
507 | */ | ||
508 | fputs((is_full ? cmdedit_prompt : prompt_last_line), stderr); | ||
506 | cursor = 0; | 509 | cursor = 0; |
507 | cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ | 510 | cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ |
508 | cmdedit_x = cmdedit_prmt_len % cmdedit_termw; | 511 | cmdedit_x = cmdedit_prmt_len % cmdedit_termw; |
@@ -539,15 +542,15 @@ static void input_backward(unsigned num) | |||
539 | /* This is longer by 5 bytes on x86. | 542 | /* This is longer by 5 bytes on x86. |
540 | * Also gets miscompiled for ARM users | 543 | * Also gets miscompiled for ARM users |
541 | * (busybox.net/bugs/view.php?id=2274). | 544 | * (busybox.net/bugs/view.php?id=2274). |
542 | * printf(("\b\b\b\b" + 4) - num); | 545 | * fprintf(("\b\b\b\b" + 4) - num, stderr); |
543 | * return; | 546 | * return; |
544 | */ | 547 | */ |
545 | do { | 548 | do { |
546 | bb_putchar('\b'); | 549 | bb_putchar_stderr('\b'); |
547 | } while (--num); | 550 | } while (--num); |
548 | return; | 551 | return; |
549 | } | 552 | } |
550 | printf(ESC"[%uD", num); | 553 | fprintf(stderr, ESC"[%uD", num); |
551 | return; | 554 | return; |
552 | } | 555 | } |
553 | 556 | ||
@@ -572,7 +575,7 @@ static void input_backward(unsigned num) | |||
572 | */ | 575 | */ |
573 | unsigned sv_cursor; | 576 | unsigned sv_cursor; |
574 | /* go to 1st column; go up to first line */ | 577 | /* go to 1st column; go up to first line */ |
575 | printf("\r" ESC"[%uA", cmdedit_y); | 578 | fprintf(stderr, "\r" ESC"[%uA", cmdedit_y); |
576 | cmdedit_y = 0; | 579 | cmdedit_y = 0; |
577 | sv_cursor = cursor; | 580 | sv_cursor = cursor; |
578 | put_prompt_last_line(); /* sets cursor to 0 */ | 581 | put_prompt_last_line(); /* sets cursor to 0 */ |
@@ -587,12 +590,12 @@ static void input_backward(unsigned num) | |||
587 | cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw; | 590 | cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw; |
588 | cmdedit_y -= lines_up; | 591 | cmdedit_y -= lines_up; |
589 | /* go to 1st column; go up */ | 592 | /* go to 1st column; go up */ |
590 | printf("\r" ESC"[%uA", lines_up); | 593 | fprintf(stderr, "\r" ESC"[%uA", lines_up); |
591 | /* go to correct column. | 594 | /* go to correct column. |
592 | * xterm, konsole, Linux VT interpret 0 as 1 below! wow. | 595 | * xterm, konsole, Linux VT interpret 0 as 1 below! wow. |
593 | * need to *make sure* we skip it if cmdedit_x == 0 */ | 596 | * need to *make sure* we skip it if cmdedit_x == 0 */ |
594 | if (cmdedit_x) | 597 | if (cmdedit_x) |
595 | printf(ESC"[%uC", cmdedit_x); | 598 | fprintf(stderr, ESC"[%uC", cmdedit_x); |
596 | } | 599 | } |
597 | } | 600 | } |
598 | 601 | ||
@@ -600,11 +603,11 @@ static void input_backward(unsigned num) | |||
600 | static void draw_custom(int y, int back_cursor, bool is_full) | 603 | static void draw_custom(int y, int back_cursor, bool is_full) |
601 | { | 604 | { |
602 | if (y > 0) /* up y lines */ | 605 | if (y > 0) /* up y lines */ |
603 | printf(ESC"[%uA", y); | 606 | fprintf(stderr, ESC"[%uA", y); |
604 | bb_putchar('\r'); | 607 | bb_putchar_stderr('\r'); |
605 | put_prompt_custom(is_full); | 608 | put_prompt_custom(is_full); |
606 | put_till_end_and_adv_cursor(); | 609 | put_till_end_and_adv_cursor(); |
607 | printf(SEQ_CLEAR_TILL_END_OF_SCREEN); | 610 | fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); |
608 | input_backward(back_cursor); | 611 | input_backward(back_cursor); |
609 | } | 612 | } |
610 | 613 | ||
@@ -649,7 +652,7 @@ static void input_delete(int save) | |||
649 | command_len--; | 652 | command_len--; |
650 | put_till_end_and_adv_cursor(); | 653 | put_till_end_and_adv_cursor(); |
651 | /* Last char is still visible, erase it (and more) */ | 654 | /* Last char is still visible, erase it (and more) */ |
652 | printf(SEQ_CLEAR_TILL_END_OF_SCREEN); | 655 | fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); |
653 | input_backward(cursor - j); /* back to old pos cursor */ | 656 | input_backward(cursor - j); /* back to old pos cursor */ |
654 | } | 657 | } |
655 | 658 | ||
@@ -984,8 +987,8 @@ static void remove_chunk(int16_t *int_buf, int beg, int end) | |||
984 | if (dbg_bmp) { | 987 | if (dbg_bmp) { |
985 | int i; | 988 | int i; |
986 | for (i = 0; int_buf[i]; i++) | 989 | for (i = 0; int_buf[i]; i++) |
987 | bb_putchar((unsigned char)int_buf[i]); | 990 | bb_putchar_stderr((unsigned char)int_buf[i]); |
988 | bb_putchar('\n'); | 991 | bb_putchar_stderr('\n'); |
989 | } | 992 | } |
990 | } | 993 | } |
991 | /* Caller ensures that match_buf points to a malloced buffer | 994 | /* Caller ensures that match_buf points to a malloced buffer |
@@ -1162,7 +1165,7 @@ static void showfiles(void) | |||
1162 | int nc; | 1165 | int nc; |
1163 | 1166 | ||
1164 | for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { | 1167 | for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { |
1165 | printf("%s%-*s", matches[n], | 1168 | fprintf(stderr, "%s%-*s", matches[n], |
1166 | (int)(column_width - unicode_strwidth(matches[n])), "" | 1169 | (int)(column_width - unicode_strwidth(matches[n])), "" |
1167 | ); | 1170 | ); |
1168 | } | 1171 | } |
@@ -1460,7 +1463,7 @@ void FAST_FUNC show_history(const line_input_t *st) | |||
1460 | if (!st) | 1463 | if (!st) |
1461 | return; | 1464 | return; |
1462 | for (i = 0; i < st->cnt_history; i++) | 1465 | for (i = 0; i < st->cnt_history; i++) |
1463 | printf("%4d %s\n", i, st->history[i]); | 1466 | fprintf(stderr, "%4d %s\n", i, st->history[i]); |
1464 | } | 1467 | } |
1465 | 1468 | ||
1466 | # if ENABLE_FEATURE_EDITING_SAVEHISTORY | 1469 | # if ENABLE_FEATURE_EDITING_SAVEHISTORY |
@@ -1900,7 +1903,7 @@ static void ask_terminal(void) | |||
1900 | pfd.events = POLLIN; | 1903 | pfd.events = POLLIN; |
1901 | if (safe_poll(&pfd, 1, 0) == 0) { | 1904 | if (safe_poll(&pfd, 1, 0) == 0) { |
1902 | S.sent_ESC_br6n = 1; | 1905 | S.sent_ESC_br6n = 1; |
1903 | fputs_stdout(ESC"[6n"); | 1906 | fputs(ESC"[6n", stderr); |
1904 | fflush_all(); /* make terminal see it ASAP! */ | 1907 | fflush_all(); /* make terminal see it ASAP! */ |
1905 | } | 1908 | } |
1906 | } | 1909 | } |
@@ -2639,13 +2642,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2639 | /* Control-k -- clear to end of line */ | 2642 | /* Control-k -- clear to end of line */ |
2640 | command_ps[cursor] = BB_NUL; | 2643 | command_ps[cursor] = BB_NUL; |
2641 | command_len = cursor; | 2644 | command_len = cursor; |
2642 | printf(SEQ_CLEAR_TILL_END_OF_SCREEN); | 2645 | fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); |
2643 | break; | 2646 | break; |
2644 | case CTRL('L'): | 2647 | case CTRL('L'): |
2645 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) | 2648 | vi_case(CTRL('L')|VI_CMDMODE_BIT:) |
2646 | /* Control-l -- clear screen */ | 2649 | /* Control-l -- clear screen */ |
2647 | /* cursor to top,left; clear to the end of screen */ | 2650 | /* cursor to top,left; clear to the end of screen */ |
2648 | printf(ESC"[H" ESC"[J"); | 2651 | fputs(ESC"[H" ESC"[J", stderr); |
2649 | draw_full(command_len - cursor); | 2652 | draw_full(command_len - cursor); |
2650 | break; | 2653 | break; |
2651 | #if MAX_HISTORY > 0 | 2654 | #if MAX_HISTORY > 0 |
@@ -2832,8 +2835,8 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2832 | beep(); | 2835 | beep(); |
2833 | } else { | 2836 | } else { |
2834 | command_ps[cursor] = ic; | 2837 | command_ps[cursor] = ic; |
2835 | bb_putchar(ic); | 2838 | bb_putchar_stderr(ic); |
2836 | bb_putchar('\b'); | 2839 | bb_putchar_stderr('\b'); |
2837 | } | 2840 | } |
2838 | break; | 2841 | break; |
2839 | case '\x1b': /* ESC */ | 2842 | case '\x1b': /* ESC */ |
@@ -3027,7 +3030,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
3027 | #undef read_line_input | 3030 | #undef read_line_input |
3028 | int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) | 3031 | int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) |
3029 | { | 3032 | { |
3030 | fputs_stdout(prompt); | 3033 | /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
3034 | * says that shells must write $PSn to stderr, not stdout. | ||
3035 | */ | ||
3036 | fputs(prompt, stderr); | ||
3031 | fflush_all(); | 3037 | fflush_all(); |
3032 | if (!fgets(command, maxsize, stdin)) | 3038 | if (!fgets(command, maxsize, stdin)) |
3033 | return -1; | 3039 | return -1; |