summaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorDenys Vlasenko2009-05-17 16:44:54 +0200
committerDenys Vlasenko2009-05-17 16:44:54 +0200
commit020f40693a08b836abdea74f3823a0bce0378ec5 (patch)
tree4ae11c9b2156cccc1af8904c11c195b25b2886b0 /libbb
parent071ede1e5de784820f39c2546000c08d74b12f6d (diff)
downloadbusybox-020f40693a08b836abdea74f3823a0bce0378ec5.zip
busybox-020f40693a08b836abdea74f3823a0bce0378ec5.tar.gz
line editing: add an option to emit ESC [ 6 n and use results
This makes line editing able to recognize case when cursor was not at the beginning of the line. It may also be adapted later to find out display size (serial line users would love it). Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb')
-rw-r--r--libbb/Config.in12
-rw-r--r--libbb/lineedit.c42
-rw-r--r--libbb/read_key.c57
3 files changed, 94 insertions, 17 deletions
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;
}