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>
This commit is contained in:
Denys Vlasenko 2024-04-13 18:40:20 +02:00
parent 681e4f5d92
commit fd47f05676

View file

@ -345,7 +345,7 @@ static unsigned save_string(char *dst, unsigned maxsize)
return i; 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) static void BB_PUTCHAR(wchar_t c)
{ {
if (unicode_status == UNICODE_ON) { if (unicode_status == UNICODE_ON) {
@ -354,11 +354,11 @@ static void BB_PUTCHAR(wchar_t c)
ssize_t len = wcrtomb(buf, c, &mbst); ssize_t len = wcrtomb(buf, c, &mbst);
if (len > 0) { if (len > 0) {
buf[len] = '\0'; buf[len] = '\0';
fputs_stdout(buf); fputs(buf, stderr);
} }
} else { } else {
/* In this case, c is always one byte */ /* In this case, c is always one byte */
putchar(c); bb_putchar_stderr(c);
} }
} }
# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS # 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); safe_strncpy(dst, command_ps, maxsize);
} }
# endif # endif
# define BB_PUTCHAR(c) bb_putchar(c) # define BB_PUTCHAR(c) bb_putchar_stderr(c)
/* Should never be called: */ /* Should never be called: */
int adjust_width_and_validate_wc(unsigned *width_adj, int wc); int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
#endif #endif
@ -463,7 +463,7 @@ static void put_cur_glyph_and_inc_cursor(void)
if (c == BB_NUL) if (c == BB_NUL)
c = ' '; c = ' ';
BB_PUTCHAR(c); BB_PUTCHAR(c);
bb_putchar('\b'); bb_putchar_stderr('\b');
#endif #endif
cmdedit_y++; cmdedit_y++;
if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { 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(); put_till_end_and_adv_cursor();
/* "cursor == 0" is only if prompt is "" and user input is empty */ /* "cursor == 0" is only if prompt is "" and user input is empty */
if (cursor == 0 || cmdedit_x != 0) if (cursor == 0 || cmdedit_x != 0)
bb_putchar('\n'); bb_putchar_stderr('\n');
} }
static void beep(void) static void beep(void)
{ {
bb_putchar('\007'); bb_putchar_stderr('\007');
} }
/* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. /* 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) 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; cursor = 0;
cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
cmdedit_x = cmdedit_prmt_len % cmdedit_termw; 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. /* This is longer by 5 bytes on x86.
* Also gets miscompiled for ARM users * Also gets miscompiled for ARM users
* (busybox.net/bugs/view.php?id=2274). * (busybox.net/bugs/view.php?id=2274).
* printf(("\b\b\b\b" + 4) - num); * fprintf(("\b\b\b\b" + 4) - num, stderr);
* return; * return;
*/ */
do { do {
bb_putchar('\b'); bb_putchar_stderr('\b');
} while (--num); } while (--num);
return; return;
} }
printf(ESC"[%uD", num); fprintf(stderr, ESC"[%uD", num);
return; return;
} }
@ -572,7 +575,7 @@ static void input_backward(unsigned num)
*/ */
unsigned sv_cursor; unsigned sv_cursor;
/* go to 1st column; go up to first line */ /* 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; cmdedit_y = 0;
sv_cursor = cursor; sv_cursor = cursor;
put_prompt_last_line(); /* sets cursor to 0 */ 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_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw;
cmdedit_y -= lines_up; cmdedit_y -= lines_up;
/* go to 1st column; go up */ /* go to 1st column; go up */
printf("\r" ESC"[%uA", lines_up); fprintf(stderr, "\r" ESC"[%uA", lines_up);
/* go to correct column. /* go to correct column.
* xterm, konsole, Linux VT interpret 0 as 1 below! wow. * xterm, konsole, Linux VT interpret 0 as 1 below! wow.
* need to *make sure* we skip it if cmdedit_x == 0 */ * need to *make sure* we skip it if cmdedit_x == 0 */
if (cmdedit_x) 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) static void draw_custom(int y, int back_cursor, bool is_full)
{ {
if (y > 0) /* up y lines */ if (y > 0) /* up y lines */
printf(ESC"[%uA", y); fprintf(stderr, ESC"[%uA", y);
bb_putchar('\r'); bb_putchar_stderr('\r');
put_prompt_custom(is_full); put_prompt_custom(is_full);
put_till_end_and_adv_cursor(); 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); input_backward(back_cursor);
} }
@ -649,7 +652,7 @@ static void input_delete(int save)
command_len--; command_len--;
put_till_end_and_adv_cursor(); put_till_end_and_adv_cursor();
/* Last char is still visible, erase it (and more) */ /* 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 */ 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) { if (dbg_bmp) {
int i; int i;
for (i = 0; int_buf[i]; i++) for (i = 0; int_buf[i]; i++)
bb_putchar((unsigned char)int_buf[i]); bb_putchar_stderr((unsigned char)int_buf[i]);
bb_putchar('\n'); bb_putchar_stderr('\n');
} }
} }
/* Caller ensures that match_buf points to a malloced buffer /* Caller ensures that match_buf points to a malloced buffer
@ -1162,7 +1165,7 @@ static void showfiles(void)
int nc; int nc;
for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, 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])), "" (int)(column_width - unicode_strwidth(matches[n])), ""
); );
} }
@ -1460,7 +1463,7 @@ void FAST_FUNC show_history(const line_input_t *st)
if (!st) if (!st)
return; return;
for (i = 0; i < st->cnt_history; i++) 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 # if ENABLE_FEATURE_EDITING_SAVEHISTORY
@ -1900,7 +1903,7 @@ static void ask_terminal(void)
pfd.events = POLLIN; pfd.events = POLLIN;
if (safe_poll(&pfd, 1, 0) == 0) { if (safe_poll(&pfd, 1, 0) == 0) {
S.sent_ESC_br6n = 1; S.sent_ESC_br6n = 1;
fputs_stdout(ESC"[6n"); fputs(ESC"[6n", stderr);
fflush_all(); /* make terminal see it ASAP! */ 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 */ /* Control-k -- clear to end of line */
command_ps[cursor] = BB_NUL; command_ps[cursor] = BB_NUL;
command_len = cursor; command_len = cursor;
printf(SEQ_CLEAR_TILL_END_OF_SCREEN); fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr);
break; break;
case CTRL('L'): case CTRL('L'):
vi_case(CTRL('L')|VI_CMDMODE_BIT:) vi_case(CTRL('L')|VI_CMDMODE_BIT:)
/* Control-l -- clear screen */ /* Control-l -- clear screen */
/* cursor to top,left; clear to the end of 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); draw_full(command_len - cursor);
break; break;
#if MAX_HISTORY > 0 #if MAX_HISTORY > 0
@ -2832,8 +2835,8 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
beep(); beep();
} else { } else {
command_ps[cursor] = ic; command_ps[cursor] = ic;
bb_putchar(ic); bb_putchar_stderr(ic);
bb_putchar('\b'); bb_putchar_stderr('\b');
} }
break; break;
case '\x1b': /* ESC */ 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 #undef read_line_input
int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) 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(); fflush_all();
if (!fgets(command, maxsize, stdin)) if (!fgets(command, maxsize, stdin))
return -1; return -1;