X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=libbb%2Fread_key.c;h=ace23defb88a5f70cadff88e2acb1713f935519e;hb=688a7e3f0454363e1dfed481e95afcdb818b1c91;hp=6f6c39e450f17719aa76492b4ca9593244d6de07;hpb=4b7db4f2ca232c630e334fa56b1eb89848d5fcc5;p=oweals%2Fbusybox.git diff --git a/libbb/read_key.c b/libbb/read_key.c index 6f6c39e45..ace23defb 100644 --- a/libbb/read_key.c +++ b/libbb/read_key.c @@ -5,18 +5,20 @@ * Copyright (C) 2008 Rob Landley * Copyright (C) 2008 Denys Vlasenko * - * Licensed under GPL version 2, see file LICENSE in this tarball for details. + * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "libbb.h" -int64_t FAST_FUNC read_key(int fd, char *buffer) +int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) { struct pollfd pfd; const char *seq; int n; - int c; - /* Known escape sequences for cursor and function keys */ + /* Known escape sequences for cursor and function keys. + * See "Xterm Control Sequences" + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + */ static const char esccmds[] ALIGN1 = { 'O','A' |0x80,KEYCODE_UP , 'O','B' |0x80,KEYCODE_DOWN , @@ -27,7 +29,11 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) #if 0 'O','P' |0x80,KEYCODE_FUN1 , /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ - /* Ctrl- seems to not affect sequences */ + /* ESC [ O 1 ; 2 P - Shift-F1 */ + /* ESC [ O 1 ; 3 P - Alt-F1 */ + /* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ + /* ESC [ O 1 ; 5 P - Ctrl-F1 */ + /* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ 'O','Q' |0x80,KEYCODE_FUN2 , 'O','R' |0x80,KEYCODE_FUN3 , 'O','S' |0x80,KEYCODE_FUN4 , @@ -36,23 +42,37 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) '[','B' |0x80,KEYCODE_DOWN , '[','C' |0x80,KEYCODE_RIGHT , '[','D' |0x80,KEYCODE_LEFT , + /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift- */ + /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt- - implemented below */ + /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift- */ + /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl- - implemented below */ + /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift- */ + /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt- */ + /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift- */ '[','H' |0x80,KEYCODE_HOME , /* xterm */ - /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */ '[','F' |0x80,KEYCODE_END , /* xterm */ + /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */ + /* '[','Z' |0x80,KEYCODE_SHIFT_TAB, */ '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','2','~' |0x80,KEYCODE_INSERT , + /* ESC [ 2 ; 3 ~ - Alt-Insert */ '[','3','~' |0x80,KEYCODE_DELETE , /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ + /* ESC [ 3 ; 3 ~ - Alt-Delete */ + /* ESC [ 3 ; 5 ~ - Ctrl-Delete */ '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ '[','5','~' |0x80,KEYCODE_PAGEUP , + /* ESC [ 5 ; 3 ~ - Alt-PgUp */ + /* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ + /* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ '[','6','~' |0x80,KEYCODE_PAGEDOWN, '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ #if 0 - '[','1','1','~'|0x80,KEYCODE_FUN1 , - '[','1','2','~'|0x80,KEYCODE_FUN2 , - '[','1','3','~'|0x80,KEYCODE_FUN3 , - '[','1','4','~'|0x80,KEYCODE_FUN4 , + '[','1','1','~'|0x80,KEYCODE_FUN1 , /* old xterm, deprecated by ESC O P */ + '[','1','2','~'|0x80,KEYCODE_FUN2 , /* old xterm... */ + '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */ + '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */ '[','1','5','~'|0x80,KEYCODE_FUN5 , /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ '[','1','7','~'|0x80,KEYCODE_FUN6 , @@ -62,15 +82,45 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) '[','2','1','~'|0x80,KEYCODE_FUN10 , '[','2','3','~'|0x80,KEYCODE_FUN11 , '[','2','4','~'|0x80,KEYCODE_FUN12 , + /* ESC [ 2 4 ; 2 ~ - Shift-F12 */ + /* ESC [ 2 4 ; 3 ~ - Alt-F12 */ + /* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ + /* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ + /* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ #endif + /* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused */ + /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */ + '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT, + '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT , + /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unused */ + /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unused */ + '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT, + '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT , + /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unused */ 0 }; + pfd.fd = fd; + pfd.events = POLLIN; + + buffer++; /* saved chars counter is in buffer[-1] now */ + + start_over: errno = 0; - n = (unsigned char) *buffer++; + n = (unsigned char)buffer[-1]; if (n == 0) { - /* If no data, block waiting for input. - * It is tempting to read more than one byte here, + /* If no data, wait for input. + * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful + * if fd can be in non-blocking mode. + */ + if (timeout >= -1) { + if (safe_poll(&pfd, 1, timeout) == 0) { + /* Timed out */ + errno = EAGAIN; + return -1; + } + } + /* It is tempting to read more than one byte here, * but it breaks pasting. Example: at shell prompt, * user presses "c","a","t" and then pastes "\nline\n". * When we were reading 3 bytes here, we were eating @@ -81,19 +131,19 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) return -1; } - /* Grab character to return from buffer */ - c = (unsigned char)buffer[0]; - n--; - if (n) - memmove(buffer, buffer + 1, n); - - /* Only ESC starts ESC sequences */ - if (c != 27) - goto ret; + { + unsigned char c = buffer[0]; + n--; + if (n) + memmove(buffer, buffer + 1, n); + /* Only ESC starts ESC sequences */ + if (c != 27) { + buffer[-1] = n; + return c; + } + } /* Loop through known ESC sequences */ - pfd.fd = fd; - pfd.events = POLLIN; seq = esccmds; while (*seq != '\0') { /* n - position in sequence we did not read yet */ @@ -111,17 +161,21 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! * Array is sorted from shortest to longest, - * we can't match anything later in array, - * break out of both loops. */ - goto ret; + * we can't match anything later in array - + * anything later is longer than this seq. + * Break out of both loops. */ + goto got_all; } 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 (errno != EAGAIN) { + /* otherwise: it's EOF/error */ + buffer[-1] = 0; + return -1; + } + goto got_all; } n++; } @@ -137,66 +191,91 @@ int64_t FAST_FUNC read_key(int fd, char *buffer) } if (seq[i] & 0x80) { /* Entire seq matched */ - c = (signed char)seq[i+1]; n = 0; /* n -= i; memmove(...); * would be more correct, * but we never read ahead that much, * and n == i here. */ - goto ret; + buffer[-1] = 0; + return (signed char)seq[i+1]; } i++; } } - /* We did not find matching sequence, it was a bare ESC. - * 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; + /* We did not find matching sequence. + * We possibly read and stored more input in buffer[] by now. + * n = bytes read. Try to read more until we time out. + */ + while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ + 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) { + /* otherwise: it's EOF/error */ + buffer[-1] = 0; + return -1; } - if (buffer[n++] == 'R') - goto got_R; + break; } - 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; + n++; + /* Try to decipher "ESC [ NNN ; NNN R" sequence */ + if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL + || ENABLE_FEATURE_VI_ASK_TERMINAL + || ENABLE_FEATURE_LESS_ASK_TERMINAL + ) + && n >= 5 + && buffer[0] == '[' + && buffer[n-1] == 'R' + && isdigit(buffer[1]) + ) { + char *end; + unsigned long row, col; + + row = strtoul(buffer + 1, &end, 10); + if (*end != ';' || !isdigit(end[1])) + continue; + col = strtoul(end + 1, &end, 10); + if (*end != 'R') + continue; + if (row < 1 || col < 1 || (row | col) > 0x7fff) + continue; - buffer[-1] = 0; + buffer[-1] = 0; + /* Pack into "1 " 32-bit sequence */ + col |= (((-1 << 15) | row) << 16); + /* Return it in high-order word */ + return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; + } + } + got_all: - /* Pack into "1 " 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; + if (n <= 1) { + /* Alt-x is usually returned as ESC x. + * Report ESC, x is remembered for the next call. + */ + buffer[-1] = n; + return 27; } - ret: - buffer[-1] = n; - return c; + /* We were doing "buffer[-1] = n; return c;" here, but this results + * in unknown key sequences being interpreted as ESC + garbage. + * This was not useful. Pretend there was no key pressed, + * go and wait for a new keypress: + */ + buffer[-1] = 0; + goto start_over; +} + +void FAST_FUNC read_key_ungets(char *buffer, const char *str, unsigned len) +{ + unsigned cur_len = (unsigned char)buffer[0]; + if (len > KEYCODE_BUFFER_SIZE-1 - cur_len) + len = KEYCODE_BUFFER_SIZE-1 - cur_len; + memcpy(buffer + 1 + cur_len, str, len); + buffer[0] += len; }