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 ccc53fb58..ee5b5d98a 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -151,7 +151,6 @@ struct globals { | |||
151 | char erase_char; // the users erase character | 151 | char erase_char; // the users erase character |
152 | char last_input_char; // last char read from user | 152 | char last_input_char; // last char read from user |
153 | 153 | ||
154 | smalluint chars_to_parse; | ||
155 | #if ENABLE_FEATURE_VI_DOT_CMD | 154 | #if ENABLE_FEATURE_VI_DOT_CMD |
156 | smallint adding2q; // are we currently adding user input to q | 155 | smallint adding2q; // are we currently adding user input to q |
157 | int lmc_len; // length of last_modifying_cmd | 156 | int lmc_len; // length of last_modifying_cmd |
@@ -235,7 +234,6 @@ struct globals { | |||
235 | #define last_forward_char (G.last_forward_char ) | 234 | #define last_forward_char (G.last_forward_char ) |
236 | #define erase_char (G.erase_char ) | 235 | #define erase_char (G.erase_char ) |
237 | #define last_input_char (G.last_input_char ) | 236 | #define last_input_char (G.last_input_char ) |
238 | #define chars_to_parse (G.chars_to_parse ) | ||
239 | #if ENABLE_FEATURE_VI_READONLY | 237 | #if ENABLE_FEATURE_VI_READONLY |
240 | #define readonly_mode (G.readonly_mode ) | 238 | #define readonly_mode (G.readonly_mode ) |
241 | #else | 239 | #else |
@@ -620,7 +618,7 @@ static void edit_file(char *fn) | |||
620 | // poll to see if there is input already waiting. if we are | 618 | // poll to see if there is input already waiting. if we are |
621 | // not able to display output fast enough to keep up, skip | 619 | // not able to display output fast enough to keep up, skip |
622 | // the display update until we catch up with input. | 620 | // the display update until we catch up with input. |
623 | if (!chars_to_parse && mysleep(0) == 0) { | 621 | if (!readbuffer[0] && mysleep(0) == 0) { |
624 | // no input pending - so update output | 622 | // no input pending - so update output |
625 | refresh(FALSE); | 623 | refresh(FALSE); |
626 | show_status_line(); | 624 | show_status_line(); |
@@ -2206,7 +2204,7 @@ static int readit(void) // read (maybe cursor) key from stdin | |||
2206 | int c; | 2204 | int c; |
2207 | 2205 | ||
2208 | fflush(stdout); | 2206 | fflush(stdout); |
2209 | c = read_key(STDIN_FILENO, &chars_to_parse, readbuffer); | 2207 | c = read_key(STDIN_FILENO, readbuffer); |
2210 | if (c == -1) { // EOF/error | 2208 | if (c == -1) { // EOF/error |
2211 | go_bottom_and_clear_to_eol(); | 2209 | go_bottom_and_clear_to_eol(); |
2212 | cookmode(); // terminal to "cooked" | 2210 | cookmode(); // terminal to "cooked" |
@@ -3851,10 +3849,11 @@ static void crash_dummy() | |||
3851 | cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL"; | 3849 | cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL"; |
3852 | 3850 | ||
3853 | // is there already a command running? | 3851 | // is there already a command running? |
3854 | if (chars_to_parse > 0) | 3852 | if (readbuffer[0] > 0) |
3855 | goto cd1; | 3853 | goto cd1; |
3856 | cd0: | 3854 | cd0: |
3857 | startrbi = rbi = 0; | 3855 | readbuffer[0] = 'X'; |
3856 | startrbi = rbi = 1; | ||
3858 | sleeptime = 0; // how long to pause between commands | 3857 | sleeptime = 0; // how long to pause between commands |
3859 | memset(readbuffer, '\0', sizeof(readbuffer)); | 3858 | memset(readbuffer, '\0', sizeof(readbuffer)); |
3860 | // generate a command by percentages | 3859 | // generate a command by percentages |
@@ -3928,7 +3927,7 @@ static void crash_dummy() | |||
3928 | } | 3927 | } |
3929 | strcat(readbuffer, "\033"); | 3928 | strcat(readbuffer, "\033"); |
3930 | } | 3929 | } |
3931 | chars_to_parse = strlen(readbuffer); | 3930 | readbuffer[0] = strlen(readbuffer + 1); |
3932 | cd1: | 3931 | cd1: |
3933 | totalcmds++; | 3932 | totalcmds++; |
3934 | if (sleeptime > 0) | 3933 | if (sleeptime > 0) |
diff --git a/include/libbb.h b/include/libbb.h index bae7efb00..788140d14 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -962,16 +962,23 @@ enum { | |||
962 | KEYCODE_FUN11 = -22, | 962 | KEYCODE_FUN11 = -22, |
963 | KEYCODE_FUN12 = -23, | 963 | KEYCODE_FUN12 = -23, |
964 | #endif | 964 | #endif |
965 | /* How long the longest ESC sequence we know? */ | 965 | KEYCODE_CURSOR_POS = -0x100, |
966 | KEYCODE_BUFFER_SIZE = 4 | 966 | /* How long is the longest ESC sequence we know? |
967 | * We want it big enough to be able to contain | ||
968 | * cursor position sequence "ESC [ 9999 ; 9999 R" | ||
969 | */ | ||
970 | KEYCODE_BUFFER_SIZE = 16 | ||
967 | }; | 971 | }; |
968 | /* Note: fd may be in blocking or non-blocking mode, both make sense. | 972 | /* Note: fd may be in blocking or non-blocking mode, both make sense. |
969 | * For one, less uses non-blocking mode. | 973 | * For one, less uses non-blocking mode. |
970 | * Only the first read syscall inside read_key may block indefinitely | 974 | * Only the first read syscall inside read_key may block indefinitely |
971 | * (unless fd is in non-blocking mode), | 975 | * (unless fd is in non-blocking mode), |
972 | * subsequent reads will time out after a few milliseconds. | 976 | * subsequent reads will time out after a few milliseconds. |
977 | * Return of -1 means EOF or error (errno == 0 on EOF). | ||
978 | * buffer[0] is used as a counter of buffered chars and must be 0 | ||
979 | * on first call. | ||
973 | */ | 980 | */ |
974 | int read_key(int fd, smalluint *nbuffered, char *buffer) FAST_FUNC; | 981 | int64_t read_key(int fd, char *buffer) FAST_FUNC; |
975 | 982 | ||
976 | 983 | ||
977 | /* Networking */ | 984 | /* Networking */ |
diff --git a/libbb/Config.in b/libbb/Config.in index f5b804ff8..7ced387a9 100644 --- a/libbb/Config.in +++ b/libbb/Config.in | |||
@@ -102,6 +102,18 @@ config FEATURE_EDITING_FANCY_PROMPT | |||
102 | Setting this option allows for prompts to use things like \w and | 102 | Setting this option allows for prompts to use things like \w and |
103 | \$ and escape codes. | 103 | \$ and escape codes. |
104 | 104 | ||
105 | config FEATURE_EDITING_ASK_TERMINAL | ||
106 | bool "Query cursor position from terminal" | ||
107 | default n | ||
108 | depends on FEATURE_EDITING | ||
109 | help | ||
110 | Allow usage of "ESC [ 6 n" sequence. Terminal answers back with | ||
111 | current cursor position. This information is used to make line | ||
112 | editing more robust in some cases. | ||
113 | If you are not sure whether your terminals respond to this code | ||
114 | correctly, or want to save on code size (about 300 bytes), | ||
115 | then do not turn this option on. | ||
116 | |||
105 | config FEATURE_VERBOSE_CP_MESSAGE | 117 | config FEATURE_VERBOSE_CP_MESSAGE |
106 | bool "Give more precise messages when copy fails (cp, mv etc)" | 118 | bool "Give more precise messages when copy fails (cp, mv etc)" |
107 | default n | 119 | default n |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index ccf3e0dc8..e1404fb68 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -86,8 +86,8 @@ struct lineedit_statics { | |||
86 | volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ | 86 | volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ |
87 | sighandler_t previous_SIGWINCH_handler; | 87 | sighandler_t previous_SIGWINCH_handler; |
88 | 88 | ||
89 | unsigned cmdedit_x; /* real x terminal position */ | 89 | unsigned cmdedit_x; /* real x (col) terminal position */ |
90 | unsigned cmdedit_y; /* pseudoreal y terminal position */ | 90 | unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ |
91 | unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ | 91 | unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ |
92 | 92 | ||
93 | unsigned cursor; | 93 | unsigned cursor; |
@@ -299,6 +299,16 @@ static void input_backward(unsigned num) | |||
299 | 299 | ||
300 | static void put_prompt(void) | 300 | static void put_prompt(void) |
301 | { | 301 | { |
302 | #if ENABLE_FEATURE_EDITING_ASK_TERMINAL | ||
303 | /* Ask terminal where is cursor now. | ||
304 | * lineedit_read_key handles response and corrects | ||
305 | * our idea of current cursor position. | ||
306 | * Testcase: run "echo -n long_line_long_line_long_line", | ||
307 | * then type in a long, wrapping command and try to | ||
308 | * delete it using backspace key. | ||
309 | */ | ||
310 | out1str("\033" "[6n"); | ||
311 | #endif | ||
302 | out1str(cmdedit_prompt); | 312 | out1str(cmdedit_prompt); |
303 | cursor = 0; | 313 | cursor = 0; |
304 | { | 314 | { |
@@ -1430,18 +1440,33 @@ static void win_changed(int nsig) | |||
1430 | signal(SIGWINCH, win_changed); /* rearm ourself */ | 1440 | signal(SIGWINCH, win_changed); /* rearm ourself */ |
1431 | } | 1441 | } |
1432 | 1442 | ||
1433 | static int lineedit_read_key(smalluint *read_key_bufsize, char *read_key_buffer) | 1443 | static int lineedit_read_key(char *read_key_buffer) |
1434 | { | 1444 | { |
1435 | int ic; | 1445 | int64_t ic; |
1436 | struct pollfd pfd; | 1446 | struct pollfd pfd; |
1447 | |||
1437 | pfd.fd = STDIN_FILENO; | 1448 | pfd.fd = STDIN_FILENO; |
1438 | pfd.events = POLLIN; | 1449 | pfd.events = POLLIN; |
1439 | do { | 1450 | do { |
1451 | poll_again: | ||
1440 | /* Wait for input. Can't just call read_key, it will return | 1452 | /* Wait for input. Can't just call read_key, it will return |
1441 | * at once if stdin is in non-blocking mode. */ | 1453 | * at once if stdin is in non-blocking mode. */ |
1442 | safe_poll(&pfd, 1, -1); | 1454 | safe_poll(&pfd, 1, -1); |
1443 | /* note: read_key sets errno to 0 on success: */ | 1455 | /* note: read_key sets errno to 0 on success: */ |
1444 | ic = read_key(STDIN_FILENO, read_key_bufsize, read_key_buffer); | 1456 | ic = read_key(STDIN_FILENO, read_key_buffer); |
1457 | if (ENABLE_FEATURE_EDITING_ASK_TERMINAL | ||
1458 | && (int32_t)ic == KEYCODE_CURSOR_POS | ||
1459 | ) { | ||
1460 | int col = ((ic >> 32) & 0x7fff) - 1; | ||
1461 | if (col > 0) { | ||
1462 | cmdedit_x += col; | ||
1463 | while (cmdedit_x >= cmdedit_termw) { | ||
1464 | cmdedit_x -= cmdedit_termw; | ||
1465 | cmdedit_y++; | ||
1466 | } | ||
1467 | } | ||
1468 | goto poll_again; | ||
1469 | } | ||
1445 | } while (errno == EAGAIN); | 1470 | } while (errno == EAGAIN); |
1446 | return ic; | 1471 | return ic; |
1447 | } | 1472 | } |
@@ -1482,7 +1507,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
1482 | #endif | 1507 | #endif |
1483 | struct termios initial_settings; | 1508 | struct termios initial_settings; |
1484 | struct termios new_settings; | 1509 | struct termios new_settings; |
1485 | smalluint read_key_bufsize; | ||
1486 | char read_key_buffer[KEYCODE_BUFFER_SIZE]; | 1510 | char read_key_buffer[KEYCODE_BUFFER_SIZE]; |
1487 | 1511 | ||
1488 | INIT_S(); | 1512 | INIT_S(); |
@@ -1561,7 +1585,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
1561 | 1585 | ||
1562 | while (1) { | 1586 | while (1) { |
1563 | fflush(NULL); | 1587 | fflush(NULL); |
1564 | ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); | 1588 | ic = lineedit_read_key(read_key_buffer); |
1565 | 1589 | ||
1566 | #if ENABLE_FEATURE_EDITING_VI | 1590 | #if ENABLE_FEATURE_EDITING_VI |
1567 | newdelflag = 1; | 1591 | newdelflag = 1; |
@@ -1738,7 +1762,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
1738 | sc = cursor; | 1762 | sc = cursor; |
1739 | prev_ic = ic; | 1763 | prev_ic = ic; |
1740 | 1764 | ||
1741 | ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); | 1765 | ic = lineedit_read_key(read_key_buffer); |
1742 | if (errno) /* error */ | 1766 | if (errno) /* error */ |
1743 | goto prepare_to_die; | 1767 | goto prepare_to_die; |
1744 | 1768 | ||
@@ -1801,7 +1825,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li | |||
1801 | put(); | 1825 | put(); |
1802 | break; | 1826 | break; |
1803 | case 'r'|VI_CMDMODE_BIT: | 1827 | case 'r'|VI_CMDMODE_BIT: |
1804 | ic = lineedit_read_key(&read_key_bufsize, read_key_buffer); | 1828 | ic = lineedit_read_key(read_key_buffer); |
1805 | if (errno) /* error */ | 1829 | if (errno) /* error */ |
1806 | goto prepare_to_die; | 1830 | goto prepare_to_die; |
1807 | if (ic < ' ' || ic > 255) { | 1831 | if (ic < ' ' || ic > 255) { |
diff --git a/libbb/read_key.c b/libbb/read_key.c index fd100b0ec..3771045d2 100644 --- a/libbb/read_key.c +++ b/libbb/read_key.c | |||
@@ -9,7 +9,7 @@ | |||
9 | */ | 9 | */ |
10 | #include "libbb.h" | 10 | #include "libbb.h" |
11 | 11 | ||
12 | int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) | 12 | int64_t FAST_FUNC read_key(int fd, char *buffer) |
13 | { | 13 | { |
14 | struct pollfd pfd; | 14 | struct pollfd pfd; |
15 | const char *seq; | 15 | const char *seq; |
@@ -67,9 +67,7 @@ int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) | |||
67 | }; | 67 | }; |
68 | 68 | ||
69 | errno = 0; | 69 | errno = 0; |
70 | n = 0; | 70 | n = (unsigned char) *buffer++; |
71 | if (nbuffered) | ||
72 | n = *nbuffered; | ||
73 | if (n == 0) { | 71 | if (n == 0) { |
74 | /* If no data, block waiting for input. If we read more | 72 | /* If no data, block waiting for input. If we read more |
75 | * than the minimal ESC sequence size, the "n=0" below | 73 | * 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) | |||
148 | } | 146 | } |
149 | } | 147 | } |
150 | /* We did not find matching sequence, it was a bare ESC. | 148 | /* We did not find matching sequence, it was a bare ESC. |
151 | * We possibly read and stored more input in buffer[] | 149 | * We possibly read and stored more input in buffer[] by now. */ |
152 | * by now. */ | 150 | |
151 | /* Try to decipher "ESC [ NNN ; NNN R" sequence */ | ||
152 | if (ENABLE_FEATURE_EDITING_ASK_TERMINAL | ||
153 | && n != 0 | ||
154 | && buffer[0] == '[' | ||
155 | ) { | ||
156 | char *end; | ||
157 | unsigned long row, col; | ||
158 | |||
159 | while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for cnt */ | ||
160 | if (safe_poll(&pfd, 1, 50) == 0) { | ||
161 | /* No more data! */ | ||
162 | break; | ||
163 | } | ||
164 | errno = 0; | ||
165 | if (safe_read(fd, buffer + n, 1) <= 0) { | ||
166 | /* If EAGAIN, then fd is O_NONBLOCK and poll lied: | ||
167 | * in fact, there is no data. */ | ||
168 | if (errno != EAGAIN) | ||
169 | c = -1; /* otherwise it's EOF/error */ | ||
170 | goto ret; | ||
171 | } | ||
172 | if (buffer[n++] == 'R') | ||
173 | goto got_R; | ||
174 | } | ||
175 | goto ret; | ||
176 | got_R: | ||
177 | if (!isdigit(buffer[1])) | ||
178 | goto ret; | ||
179 | row = strtoul(buffer + 1, &end, 10); | ||
180 | if (*end != ';' || !isdigit(end[1])) | ||
181 | goto ret; | ||
182 | col = strtoul(end + 1, &end, 10); | ||
183 | if (*end != 'R') | ||
184 | goto ret; | ||
185 | if (row < 1 || col < 1 || (row | col) > 0x7fff) | ||
186 | goto ret; | ||
187 | |||
188 | buffer[-1] = 0; | ||
189 | |||
190 | /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ | ||
191 | c = (((-1 << 15) | row) << 16) | col; | ||
192 | /* Return it in high-order word */ | ||
193 | return ((int64_t) c << 32) | (uint32_t)KEYCODE_CURSOR_POS; | ||
194 | } | ||
153 | 195 | ||
154 | ret: | 196 | ret: |
155 | if (nbuffered) | 197 | buffer[-1] = n; |
156 | *nbuffered = n; | ||
157 | return c; | 198 | return c; |
158 | } | 199 | } |
diff --git a/miscutils/less.c b/miscutils/less.c index 702c4a891..bd855066f 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -96,7 +96,6 @@ struct globals { | |||
96 | smallint pattern_valid; | 96 | smallint pattern_valid; |
97 | #endif | 97 | #endif |
98 | smallint terminated; | 98 | smallint terminated; |
99 | smalluint kbd_input_size; | ||
100 | struct termios term_orig, term_less; | 99 | struct termios term_orig, term_less; |
101 | char kbd_input[KEYCODE_BUFFER_SIZE]; | 100 | char kbd_input[KEYCODE_BUFFER_SIZE]; |
102 | }; | 101 | }; |
@@ -135,7 +134,6 @@ struct globals { | |||
135 | #define terminated (G.terminated ) | 134 | #define terminated (G.terminated ) |
136 | #define term_orig (G.term_orig ) | 135 | #define term_orig (G.term_orig ) |
137 | #define term_less (G.term_less ) | 136 | #define term_less (G.term_less ) |
138 | #define kbd_input_size (G.kbd_input_size ) | ||
139 | #define kbd_input (G.kbd_input ) | 137 | #define kbd_input (G.kbd_input ) |
140 | #define INIT_G() do { \ | 138 | #define INIT_G() do { \ |
141 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | 139 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
@@ -806,7 +804,7 @@ static void reinitialize(void) | |||
806 | buffer_fill_and_print(); | 804 | buffer_fill_and_print(); |
807 | } | 805 | } |
808 | 806 | ||
809 | static ssize_t getch_nowait(void) | 807 | static int getch_nowait(void) |
810 | { | 808 | { |
811 | int rd; | 809 | int rd; |
812 | struct pollfd pfd[2]; | 810 | struct pollfd pfd[2]; |
@@ -839,7 +837,7 @@ static ssize_t getch_nowait(void) | |||
839 | move_cursor(max_displayed_line + 2, less_gets_pos + 1); | 837 | move_cursor(max_displayed_line + 2, less_gets_pos + 1); |
840 | fflush(stdout); | 838 | fflush(stdout); |
841 | 839 | ||
842 | if (kbd_input_size == 0) { | 840 | if (kbd_input[0] == 0) { /* if nothing is buffered */ |
843 | #if ENABLE_FEATURE_LESS_WINCH | 841 | #if ENABLE_FEATURE_LESS_WINCH |
844 | while (1) { | 842 | while (1) { |
845 | int r; | 843 | int r; |
@@ -856,7 +854,7 @@ static ssize_t getch_nowait(void) | |||
856 | 854 | ||
857 | /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() | 855 | /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() |
858 | * would not block even if there is no input available */ | 856 | * would not block even if there is no input available */ |
859 | rd = read_key(kbd_fd, &kbd_input_size, kbd_input); | 857 | rd = read_key(kbd_fd, kbd_input); |
860 | if (rd == -1) { | 858 | if (rd == -1) { |
861 | if (errno == EAGAIN) { | 859 | if (errno == EAGAIN) { |
862 | /* No keyboard input available. Since poll() did return, | 860 | /* No keyboard input available. Since poll() did return, |
@@ -872,9 +870,9 @@ static ssize_t getch_nowait(void) | |||
872 | return rd; | 870 | return rd; |
873 | } | 871 | } |
874 | 872 | ||
875 | /* Grab a character from input without requiring the return key. If the | 873 | /* Grab a character from input without requiring the return key. |
876 | * character is ASCII \033, get more characters and assign certain sequences | 874 | * May return KEYCODE_xxx values. |
877 | * special return codes. Note that this function works best with raw input. */ | 875 | * Note that this function works best with raw input. */ |
878 | static int less_getch(int pos) | 876 | static int less_getch(int pos) |
879 | { | 877 | { |
880 | int i; | 878 | int i; |