diff options
-rw-r--r-- | editors/vi.c | 13 | ||||
-rw-r--r-- | include/libbb.h | 13 | ||||
-rw-r--r-- | libbb/Config.in | 12 | ||||
-rw-r--r-- | libbb/lineedit.c | 42 | ||||
-rw-r--r-- | libbb/read_key.c | 57 | ||||
-rw-r--r-- | miscutils/less.c | 14 |
6 files changed, 116 insertions, 35 deletions
diff --git a/editors/vi.c b/editors/vi.c index ccc53fb..ee5b5d9 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -151,7 +151,6 @@ struct globals { char erase_char; // the users erase character char last_input_char; // last char read from user - smalluint chars_to_parse; #if ENABLE_FEATURE_VI_DOT_CMD smallint adding2q; // are we currently adding user input to q int lmc_len; // length of last_modifying_cmd @@ -235,7 +234,6 @@ struct globals { #define last_forward_char (G.last_forward_char ) #define erase_char (G.erase_char ) #define last_input_char (G.last_input_char ) -#define chars_to_parse (G.chars_to_parse ) #if ENABLE_FEATURE_VI_READONLY #define readonly_mode (G.readonly_mode ) #else @@ -620,7 +618,7 @@ static void edit_file(char *fn) // poll to see if there is input already waiting. if we are // not able to display output fast enough to keep up, skip // the display update until we catch up with input. - if (!chars_to_parse && mysleep(0) == 0) { + if (!readbuffer[0] && mysleep(0) == 0) { // no input pending - so update output refresh(FALSE); show_status_line(); @@ -2206,7 +2204,7 @@ static int readit(void) // read (maybe cursor) key from stdin int c; fflush(stdout); - c = read_key(STDIN_FILENO, &chars_to_parse, readbuffer); + c = read_key(STDIN_FILENO, readbuffer); if (c == -1) { // EOF/error go_bottom_and_clear_to_eol(); cookmode(); // terminal to "cooked" @@ -3851,10 +3849,11 @@ static void crash_dummy() cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL"; // is there already a command running? - if (chars_to_parse > 0) + if (readbuffer[0] > 0) goto cd1; cd0: - startrbi = rbi = 0; + readbuffer[0] = 'X'; + startrbi = rbi = 1; sleeptime = 0; // how long to pause between commands memset(readbuffer, '\0', sizeof(readbuffer)); // generate a command by percentages @@ -3928,7 +3927,7 @@ static void crash_dummy() } strcat(readbuffer, "\033"); } - chars_to_parse = strlen(readbuffer); + readbuffer[0] = strlen(readbuffer + 1); cd1: totalcmds++; if (sleeptime > 0) diff --git a/include/libbb.h b/include/libbb.h index bae7efb..788140d 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -962,16 +962,23 @@ enum { KEYCODE_FUN11 = -22, KEYCODE_FUN12 = -23, #endif - /* How long the longest ESC sequence we know? */ - KEYCODE_BUFFER_SIZE = 4 + KEYCODE_CURSOR_POS = -0x100, + /* How long is the longest ESC sequence we know? + * We want it big enough to be able to contain + * cursor position sequence "ESC [ 9999 ; 9999 R" + */ + KEYCODE_BUFFER_SIZE = 16 }; /* Note: fd may be in blocking or non-blocking mode, both make sense. * For one, less uses non-blocking mode. * Only the first read syscall inside read_key may block indefinitely * (unless fd is in non-blocking mode), * subsequent reads will time out after a few milliseconds. + * Return of -1 means EOF or error (errno == 0 on EOF). + * buffer[0] is used as a counter of buffered chars and must be 0 + * on first call. */ -int read_key(int fd, smalluint *nbuffered, char *buffer) FAST_FUNC; +int64_t read_key(int fd, char *buffer) FAST_FUNC; /* Networking */ diff --git a/libbb/Config.in b/libbb/Config.in index f5b804f..7ced387 100644 --- a/libbb/Config.in +++ b/libbb/Config.in @@ -102,6 +102,18 @@ config FEATURE_EDITING_FANCY_PROMPT Setting this option allows for prompts to use things like \w and \$ and escape codes. +config FEATURE_EDITING_ASK_TERMINAL + bool "Query cursor position from terminal" + default n + depends on FEATURE_EDITING + help + Allow usage of "ESC [ 6 n" sequence. Terminal answers back with + current cursor position. This information is used to make line + editing more robust in some cases. + If you are not sure whether your terminals respond to this code + correctly, or want to save on code size (about 300 bytes), + then do not turn this option on. + config FEATURE_VERBOSE_CP_MESSAGE bool "Give more precise messages when copy fails (cp, mv etc)" default n diff --git a/libbb/lineedit.c b/libbb/lineedit.c index ccf3e0d..e1404fb 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -86,8 +86,8 @@ struct lineedit_statics { volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ sighandler_t previous_SIGWINCH_handler; - unsigned cmdedit_x; /* real x terminal position */ - unsigned cmdedit_y; /* pseudoreal y terminal position */ + unsigned cmdedit_x; /* real x (col) terminal position */ + unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ unsigned cursor; @@ -299,6 +299,16 @@ static void input_backward(unsigned num) static void put_prompt(void) { +#if ENABLE_FEATURE_EDITING_ASK_TERMINAL + /* Ask terminal where is 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. + */ + out1str("\033" "[6n"); +#endif out1str(cmdedit_prompt); cursor = 0; { @@ -1430,18 +1440,33 @@ static void win_changed(int nsig) signal(SIGWINCH, win_changed); /* rearm ourself */ } -static int lineedit_read_key(smalluint *read_key_bufsize, char *read_key_buffer) +static int lineedit_read_key(char *read_key_buffer) { - int ic; + int64_t ic; struct pollfd pfd; + pfd.fd = STDIN_FILENO; pfd.events = POLLIN; do { + poll_again: /* Wait for input. Can't just call read_key, it will return * at once if stdin is in non-blocking mode. */ safe_poll(&pfd, 1, -1); /* note: read_key sets errno to 0 on success: */ - ic = read_key(STDIN_FILENO, read_key_bufsize, read_key_buffer); + ic = read_key(STDIN_FILENO, read_key_buffer); + if (ENABLE_FEATURE_EDITING_ASK_TERMINAL + && (int32_t)ic == KEYCODE_CURSOR_POS + ) { + int col = ((ic >> 32) & 0x7fff) - 1; + if (col > 0) { + cmdedit_x += col; + while (cmdedit_x >= cmdedit_termw) { + cmdedit_x -= cmdedit_termw; + cmdedit_y++; + } + } + goto poll_again; + } } while (errno == EAGAIN); return ic; } @@ -1482,7 +1507,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li #endif struct termios initial_settings; struct termios new_settings; - smalluint read_key_bufsize; char read_key_buffer[KEYCODE_BUFFER_SIZE]; INIT_S(); @@ -1561,7 +1585,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li while (1) { fflush(NULL); - ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); + ic = lineedit_read_key(read_key_buffer); #if ENABLE_FEATURE_EDITING_VI newdelflag = 1; @@ -1738,7 +1762,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li sc = cursor; prev_ic = ic; - ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); + ic = lineedit_read_key(read_key_buffer); if (errno) /* error */ goto prepare_to_die; @@ -1801,7 +1825,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li put(); break; case 'r'|VI_CMDMODE_BIT: - ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); + ic = lineedit_read_key(read_key_buffer); if (errno) /* error */ goto prepare_to_die; if (ic < ' ' || ic > 255) { diff --git a/libbb/read_key.c b/libbb/read_key.c index fd100b0..3771045 100644 --- a/libbb/read_key.c +++ b/libbb/read_key.c @@ -9,7 +9,7 @@ */ #include "libbb.h" -int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) +int64_t FAST_FUNC read_key(int fd, char *buffer) { struct pollfd pfd; const char *seq; @@ -67,9 +67,7 @@ int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) }; errno = 0; - n = 0; - if (nbuffered) - n = *nbuffered; + n = (unsigned char) *buffer++; if (n == 0) { /* If no data, block waiting for input. If we read more * than the minimal ESC sequence size, the "n=0" below @@ -148,11 +146,54 @@ int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) } } /* We did not find matching sequence, it was a bare ESC. - * We possibly read and stored more input in buffer[] - * by now. */ + * We possibly read and stored more input in buffer[] by now. */ + + /* Try to decipher "ESC [ NNN ; NNN R" sequence */ + if (ENABLE_FEATURE_EDITING_ASK_TERMINAL + && n != 0 + && buffer[0] == '[' + ) { + char *end; + unsigned long row, col; + + while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for cnt */ + if (safe_poll(&pfd, 1, 50) == 0) { + /* No more data! */ + break; + } + errno = 0; + if (safe_read(fd, buffer + n, 1) <= 0) { + /* If EAGAIN, then fd is O_NONBLOCK and poll lied: + * in fact, there is no data. */ + if (errno != EAGAIN) + c = -1; /* otherwise it's EOF/error */ + goto ret; + } + if (buffer[n++] == 'R') + goto got_R; + } + goto ret; + got_R: + if (!isdigit(buffer[1])) + goto ret; + row = strtoul(buffer + 1, &end, 10); + if (*end != ';' || !isdigit(end[1])) + goto ret; + col = strtoul(end + 1, &end, 10); + if (*end != 'R') + goto ret; + if (row < 1 || col < 1 || (row | col) > 0x7fff) + goto ret; + + buffer[-1] = 0; + + /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ + c = (((-1 << 15) | row) << 16) | col; + /* Return it in high-order word */ + return ((int64_t) c << 32) | (uint32_t)KEYCODE_CURSOR_POS; + } ret: - if (nbuffered) - *nbuffered = n; + buffer[-1] = n; return c; } diff --git a/miscutils/less.c b/miscutils/less.c index 702c4a8..bd85506 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -96,7 +96,6 @@ struct globals { smallint pattern_valid; #endif smallint terminated; - smalluint kbd_input_size; struct termios term_orig, term_less; char kbd_input[KEYCODE_BUFFER_SIZE]; }; @@ -135,7 +134,6 @@ struct globals { #define terminated (G.terminated ) #define term_orig (G.term_orig ) #define term_less (G.term_less ) -#define kbd_input_size (G.kbd_input_size ) #define kbd_input (G.kbd_input ) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ @@ -806,7 +804,7 @@ static void reinitialize(void) buffer_fill_and_print(); } -static ssize_t getch_nowait(void) +static int getch_nowait(void) { int rd; struct pollfd pfd[2]; @@ -839,7 +837,7 @@ static ssize_t getch_nowait(void) move_cursor(max_displayed_line + 2, less_gets_pos + 1); fflush(stdout); - if (kbd_input_size == 0) { + if (kbd_input[0] == 0) { /* if nothing is buffered */ #if ENABLE_FEATURE_LESS_WINCH while (1) { int r; @@ -856,7 +854,7 @@ static ssize_t getch_nowait(void) /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() * would not block even if there is no input available */ - rd = read_key(kbd_fd, &kbd_input_size, kbd_input); + rd = read_key(kbd_fd, kbd_input); if (rd == -1) { if (errno == EAGAIN) { /* No keyboard input available. Since poll() did return, @@ -872,9 +870,9 @@ static ssize_t getch_nowait(void) return rd; } -/* Grab a character from input without requiring the return key. If the - * character is ASCII \033, get more characters and assign certain sequences - * special return codes. Note that this function works best with raw input. */ +/* Grab a character from input without requiring the return key. + * May return KEYCODE_xxx values. + * Note that this function works best with raw input. */ static int less_getch(int pos) { int i; |