aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editors/vi.c13
-rw-r--r--include/libbb.h13
-rw-r--r--libbb/Config.in12
-rw-r--r--libbb/lineedit.c42
-rw-r--r--libbb/read_key.c57
-rw-r--r--miscutils/less.c14
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 {
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 bae7efb00..788140d14 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 f5b804ff8..7ced387a9 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 ccf3e0dc8..e1404fb68 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 fd100b0ec..3771045d2 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 702c4a891..bd855066f 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;