From fd47f056765aed515f4c71118813f07be1402bee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2024 18:40:20 +0200 Subject: 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 --- libbb/lineedit.c | 62 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/libbb/lineedit.c b/libbb/lineedit.c index bdae109..543a3f1 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -345,7 +345,7 @@ static unsigned save_string(char *dst, unsigned maxsize) return i; } } -/* I thought just fputwc(c, stdout) would work. But no... */ +/* I thought just fputwc(c, stderr) would work. But no... */ static void BB_PUTCHAR(wchar_t c) { if (unicode_status == UNICODE_ON) { @@ -354,11 +354,11 @@ static void BB_PUTCHAR(wchar_t c) ssize_t len = wcrtomb(buf, c, &mbst); if (len > 0) { buf[len] = '\0'; - fputs_stdout(buf); + fputs(buf, stderr); } } else { /* In this case, c is always one byte */ - putchar(c); + bb_putchar_stderr(c); } } # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS @@ -404,7 +404,7 @@ static void save_string(char *dst, unsigned maxsize) safe_strncpy(dst, command_ps, maxsize); } # endif -# define BB_PUTCHAR(c) bb_putchar(c) +# define BB_PUTCHAR(c) bb_putchar_stderr(c) /* Should never be called: */ int adjust_width_and_validate_wc(unsigned *width_adj, int wc); #endif @@ -463,7 +463,7 @@ static void put_cur_glyph_and_inc_cursor(void) if (c == BB_NUL) c = ' '; BB_PUTCHAR(c); - bb_putchar('\b'); + bb_putchar_stderr('\b'); #endif cmdedit_y++; if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { @@ -489,12 +489,12 @@ static void goto_new_line(void) put_till_end_and_adv_cursor(); /* "cursor == 0" is only if prompt is "" and user input is empty */ if (cursor == 0 || cmdedit_x != 0) - bb_putchar('\n'); + bb_putchar_stderr('\n'); } static void beep(void) { - bb_putchar('\007'); + bb_putchar_stderr('\007'); } /* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. @@ -502,7 +502,10 @@ static void beep(void) */ static void put_prompt_custom(bool is_full) { - fputs_stdout((is_full ? cmdedit_prompt : prompt_last_line)); + /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + * says that shells must write $PSn to stderr, not stdout. + */ + fputs((is_full ? cmdedit_prompt : prompt_last_line), stderr); cursor = 0; cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ cmdedit_x = cmdedit_prmt_len % cmdedit_termw; @@ -539,15 +542,15 @@ static void input_backward(unsigned num) /* This is longer by 5 bytes on x86. * Also gets miscompiled for ARM users * (busybox.net/bugs/view.php?id=2274). - * printf(("\b\b\b\b" + 4) - num); + * fprintf(("\b\b\b\b" + 4) - num, stderr); * return; */ do { - bb_putchar('\b'); + bb_putchar_stderr('\b'); } while (--num); return; } - printf(ESC"[%uD", num); + fprintf(stderr, ESC"[%uD", num); return; } @@ -572,7 +575,7 @@ static void input_backward(unsigned num) */ unsigned sv_cursor; /* go to 1st column; go up to first line */ - printf("\r" ESC"[%uA", cmdedit_y); + fprintf(stderr, "\r" ESC"[%uA", cmdedit_y); cmdedit_y = 0; sv_cursor = cursor; put_prompt_last_line(); /* sets cursor to 0 */ @@ -587,12 +590,12 @@ static void input_backward(unsigned num) cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw; cmdedit_y -= lines_up; /* go to 1st column; go up */ - printf("\r" ESC"[%uA", lines_up); + fprintf(stderr, "\r" ESC"[%uA", lines_up); /* go to correct column. * xterm, konsole, Linux VT interpret 0 as 1 below! wow. * need to *make sure* we skip it if cmdedit_x == 0 */ if (cmdedit_x) - printf(ESC"[%uC", cmdedit_x); + fprintf(stderr, ESC"[%uC", cmdedit_x); } } @@ -600,11 +603,11 @@ static void input_backward(unsigned num) static void draw_custom(int y, int back_cursor, bool is_full) { if (y > 0) /* up y lines */ - printf(ESC"[%uA", y); - bb_putchar('\r'); + fprintf(stderr, ESC"[%uA", y); + bb_putchar_stderr('\r'); put_prompt_custom(is_full); put_till_end_and_adv_cursor(); - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); input_backward(back_cursor); } @@ -649,7 +652,7 @@ static void input_delete(int save) command_len--; put_till_end_and_adv_cursor(); /* Last char is still visible, erase it (and more) */ - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); input_backward(cursor - j); /* back to old pos cursor */ } @@ -984,8 +987,8 @@ static void remove_chunk(int16_t *int_buf, int beg, int end) if (dbg_bmp) { int i; for (i = 0; int_buf[i]; i++) - bb_putchar((unsigned char)int_buf[i]); - bb_putchar('\n'); + bb_putchar_stderr((unsigned char)int_buf[i]); + bb_putchar_stderr('\n'); } } /* Caller ensures that match_buf points to a malloced buffer @@ -1162,7 +1165,7 @@ static void showfiles(void) int nc; for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { - printf("%s%-*s", matches[n], + fprintf(stderr, "%s%-*s", matches[n], (int)(column_width - unicode_strwidth(matches[n])), "" ); } @@ -1460,7 +1463,7 @@ void FAST_FUNC show_history(const line_input_t *st) if (!st) return; for (i = 0; i < st->cnt_history; i++) - printf("%4d %s\n", i, st->history[i]); + fprintf(stderr, "%4d %s\n", i, st->history[i]); } # if ENABLE_FEATURE_EDITING_SAVEHISTORY @@ -1900,7 +1903,7 @@ static void ask_terminal(void) pfd.events = POLLIN; if (safe_poll(&pfd, 1, 0) == 0) { S.sent_ESC_br6n = 1; - fputs_stdout(ESC"[6n"); + fputs(ESC"[6n", stderr); fflush_all(); /* make terminal see it ASAP! */ } } @@ -2639,13 +2642,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman /* Control-k -- clear to end of line */ command_ps[cursor] = BB_NUL; command_len = cursor; - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); break; case CTRL('L'): vi_case(CTRL('L')|VI_CMDMODE_BIT:) /* Control-l -- clear screen */ /* cursor to top,left; clear to the end of screen */ - printf(ESC"[H" ESC"[J"); + fputs(ESC"[H" ESC"[J", stderr); draw_full(command_len - cursor); break; #if MAX_HISTORY > 0 @@ -2832,8 +2835,8 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman beep(); } else { command_ps[cursor] = ic; - bb_putchar(ic); - bb_putchar('\b'); + bb_putchar_stderr(ic); + bb_putchar_stderr('\b'); } break; case '\x1b': /* ESC */ @@ -3027,7 +3030,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman #undef read_line_input int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) { - fputs_stdout(prompt); + /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + * says that shells must write $PSn to stderr, not stdout. + */ + fputs(prompt, stderr); fflush_all(); if (!fgets(command, maxsize, stdin)) return -1; -- cgit v1.1