line editing: add an option to emit ESC [ 6 n and use results
No binary (build failed)
—
(log yok)
| @@ -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) | |
| @@ -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 */ | |
| @@ -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 | |
| @@ -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) { | |
| @@ -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; | |
| } | |
| @@ -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; | |