[git commit] lineedit: print prompt and editing operations to stderr

Denys Vlasenko vda.linux at googlemail.com
Sat Apr 13 16:40:20 UTC 2024


commit: https://git.busybox.net/busybox/commit/?id=fd47f056765aed515f4c71118813f07be1402bee
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 libbb/lineedit.c | 62 +++++++++++++++++++++++++++++++-------------------------
 1 file 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)
 		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;


More information about the busybox-cvs mailing list