smallint newdelflag; /* whether delbuf should be reused yet */
CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
#endif
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+ smallint sent_ESC_br6n;
+#endif
/* Formerly these were big buffers on stack: */
#if ENABLE_FEATURE_TAB_COMPLETION
static void put_prompt(void)
{
- out1str(cmdedit_prompt);
- fflush(NULL);
- if (ENABLE_FEATURE_EDITING_ASK_TERMINAL) {
- /* Ask terminal where is the cursor now.
- * lineedit_read_key handles response and corrects
- * our idea of current cursor position.
- * Testcase: run "echo -n long_line_long_line_long_line",
- * then type in a long, wrapping command and try to
- * delete it using backspace key.
- * Note: we print it _after_ prompt, because
- * prompt may contain CR. Example: PS1='\[\r\n\]\w '
- */
- /* Problem: if there is buffered input on stdin,
- * the response will be delivered later,
- * possibly to an unsuspecting application.
- * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
- * Result:
- * ~/srcdevel/bbox/fix/busybox.t4 #
- * ~/srcdevel/bbox/fix/busybox.t4 #
- * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage
- * ~/srcdevel/bbox/fix/busybox.t4 #
- *
- * Checking for input with poll only makes the race narrower,
- * I still can trigger it. Strace:
- *
- * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
- * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists
- * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick!
- * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}])
- * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first
- */
- struct pollfd pfd;
+ unsigned w;
- pfd.fd = STDIN_FILENO;
- pfd.events = POLLIN;
- if (safe_poll(&pfd, 1, 0) == 0) {
- out1str("\033" "[6n");
- fflush(NULL); /* make terminal see it ASAP! */
- }
- }
+ out1str(cmdedit_prompt);
+ fflush_all();
cursor = 0;
- {
- unsigned w = cmdedit_termw; /* volatile var */
- cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
- cmdedit_x = cmdedit_prmt_len % w;
- }
+ w = cmdedit_termw; /* read volatile var once */
+ cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
+ cmdedit_x = cmdedit_prmt_len % w;
}
/* draw prompt, editor line, and clear tail */
* read_line_input and its helpers
*/
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+static void ask_terminal(void)
+{
+ /* Ask terminal where is the cursor now.
+ * lineedit_read_key handles response and corrects
+ * our idea of current cursor position.
+ * Testcase: run "echo -n long_line_long_line_long_line",
+ * then type in a long, wrapping command and try to
+ * delete it using backspace key.
+ * Note: we print it _after_ prompt, because
+ * prompt may contain CR. Example: PS1='\[\r\n\]\w '
+ */
+ /* Problem: if there is buffered input on stdin,
+ * the response will be delivered later,
+ * possibly to an unsuspecting application.
+ * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
+ * Result:
+ * ~/srcdevel/bbox/fix/busybox.t4 #
+ * ~/srcdevel/bbox/fix/busybox.t4 #
+ * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 # <-- garbage
+ * ~/srcdevel/bbox/fix/busybox.t4 #
+ *
+ * Checking for input with poll only makes the race narrower,
+ * I still can trigger it. Strace:
+ *
+ * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
+ * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout) <-- no input exists
+ * write(1, "\33[6n", 4) = 4 <-- send the ESC sequence, quick!
+ * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}])
+ * read(0, "\n", 1) = 1 <-- oh crap, user's input got in first
+ */
+ struct pollfd pfd;
+
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ if (safe_poll(&pfd, 1, 0) == 0) {
+ S.sent_ESC_br6n = 1;
+ out1str("\033" "[6n");
+ fflush_all(); /* make terminal see it ASAP! */
+ }
+}
+#else
+#define ask_terminal() ((void)0)
+#endif
+
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
static void parse_and_put_prompt(const char *prmt_ptr)
{
int new_y = (cursor + cmdedit_prmt_len) / w;
/* redraw */
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
- fflush(NULL);
+ fflush_all();
}
}
pfd.fd = STDIN_FILENO;
pfd.events = POLLIN;
do {
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_ASSUME_UNICODE
poll_again:
+#endif
if (read_key_buffer[0] == 0) {
/* Wait for input. Can't just call read_key,
* it returns at once if stdin
/* Note: read_key sets errno to 0 on success: */
ic = read_key(STDIN_FILENO, read_key_buffer);
- if (ENABLE_FEATURE_EDITING_ASK_TERMINAL
- && cursor == 0 /* otherwise it may be bogus */
- && (int32_t)ic == KEYCODE_CURSOR_POS
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+ if ((int32_t)ic == KEYCODE_CURSOR_POS
+ && S.sent_ESC_br6n
) {
- int col = ((ic >> 32) & 0x7fff) - 1;
- if (col > cmdedit_prmt_len) {
- cmdedit_x += (col - cmdedit_prmt_len);
- while (cmdedit_x >= cmdedit_termw) {
- cmdedit_x -= cmdedit_termw;
- cmdedit_y++;
+ S.sent_ESC_br6n = 0;
+ if (cursor == 0) { /* otherwise it may be bogus */
+ int col = ((ic >> 32) & 0x7fff) - 1;
+ if (col > cmdedit_prmt_len) {
+ cmdedit_x += (col - cmdedit_prmt_len);
+ while (cmdedit_x >= cmdedit_termw) {
+ cmdedit_x -= cmdedit_termw;
+ cmdedit_y++;
+ }
}
}
goto poll_again;
}
+#endif
#if ENABLE_FEATURE_ASSUME_UNICODE
{
) {
/* Happens when e.g. stty -echo was run before */
parse_and_put_prompt(prompt);
- /* fflush(stdout); - done by parse_and_put_prompt */
+ /* fflush_all(); - done by parse_and_put_prompt */
if (fgets(command, maxsize, stdin) == NULL)
len = -1; /* EOF or error */
else
bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
#endif
- /* Print out the command prompt */
+ /* Print out the command prompt, optionally ask where cursor is */
parse_and_put_prompt(prompt);
+ ask_terminal();
read_key_buffer[0] = 0;
while (1) {
};
int32_t ic, ic_raw;
- fflush(NULL);
+ fflush_all();
ic = ic_raw = lineedit_read_key(read_key_buffer);
#if ENABLE_FEATURE_EDITING_VI
#endif
} /* while (1) */
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+ if (S.sent_ESC_br6n) {
+ /* "sleep 1; busybox ash" + hold [Enter] to trigger.
+ * We sent "ESC [ 6 n", but got '\n' first, and
+ * KEYCODE_CURSOR_POS response is now buffered from terminal.
+ * It's bad already and not much can be done with it
+ * (it _will_ be visible for the next process to read stdin),
+ * but without this delay it even shows up on the screen
+ * as garbage because we restore echo settings with tcsetattr
+ * before it comes in. UGLY!
+ */
+ usleep(20*1000);
+ }
+#endif
+
/* Stop bug catching using "command_must_not_be_used" trick */
#undef command
tcsetattr_stdin_TCSANOW(&initial_settings);
/* restore SIGWINCH handler */
signal(SIGWINCH, previous_SIGWINCH_handler);
- fflush(NULL);
+ fflush_all();
len = command_len;
DEINIT_S();
int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
{
fputs(prompt, stdout);
- fflush(NULL);
+ fflush_all();
fgets(command, maxsize, stdin);
return strlen(command);
}