diff options
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/Config.in | 12 | ||||
-rw-r--r-- | libbb/lineedit.c | 42 | ||||
-rw-r--r-- | libbb/read_key.c | 57 |
3 files changed, 94 insertions, 17 deletions
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 | } |