9 |
*/ |
*/ |
10 |
#include "libbb.h" |
#include "libbb.h" |
11 |
|
|
12 |
int FAST_FUNC read_key(int fd, smalluint *nbuffered, char *buffer) |
int64_t FAST_FUNC read_key(int fd, char *buffer) |
13 |
{ |
{ |
14 |
struct pollfd pfd; |
struct pollfd pfd; |
15 |
const char *seq; |
const char *seq; |
16 |
int n; |
int n; |
|
int c; |
|
17 |
|
|
18 |
/* Known escape sequences for cursor and function keys */ |
/* Known escape sequences for cursor and function keys */ |
19 |
static const char esccmds[] ALIGN1 = { |
static const char esccmds[] ALIGN1 = { |
26 |
#if 0 |
#if 0 |
27 |
'O','P' |0x80,KEYCODE_FUN1 , |
'O','P' |0x80,KEYCODE_FUN1 , |
28 |
/* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ |
/* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ |
29 |
/* Ctrl- seems to not affect sequences */ |
/* ESC [ O 1 ; 2 P - Shift-F1 */ |
30 |
|
/* ESC [ O 1 ; 3 P - Alt-F1 */ |
31 |
|
/* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ |
32 |
|
/* ESC [ O 1 ; 5 P - Ctrl-F1 */ |
33 |
|
/* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ |
34 |
'O','Q' |0x80,KEYCODE_FUN2 , |
'O','Q' |0x80,KEYCODE_FUN2 , |
35 |
'O','R' |0x80,KEYCODE_FUN3 , |
'O','R' |0x80,KEYCODE_FUN3 , |
36 |
'O','S' |0x80,KEYCODE_FUN4 , |
'O','S' |0x80,KEYCODE_FUN4 , |
39 |
'[','B' |0x80,KEYCODE_DOWN , |
'[','B' |0x80,KEYCODE_DOWN , |
40 |
'[','C' |0x80,KEYCODE_RIGHT , |
'[','C' |0x80,KEYCODE_RIGHT , |
41 |
'[','D' |0x80,KEYCODE_LEFT , |
'[','D' |0x80,KEYCODE_LEFT , |
42 |
|
/* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow> */ |
43 |
|
/* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> */ |
44 |
|
/* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow> */ |
45 |
|
/* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below */ |
46 |
|
/* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow> */ |
47 |
'[','H' |0x80,KEYCODE_HOME , /* xterm */ |
'[','H' |0x80,KEYCODE_HOME , /* xterm */ |
48 |
/* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */ |
/* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */ |
49 |
'[','F' |0x80,KEYCODE_END , /* xterm */ |
'[','F' |0x80,KEYCODE_END , /* xterm */ |
50 |
'[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ |
'[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ |
51 |
'[','2','~' |0x80,KEYCODE_INSERT , |
'[','2','~' |0x80,KEYCODE_INSERT , |
52 |
|
/* ESC [ 2 ; 3 ~ - Alt-Insert */ |
53 |
'[','3','~' |0x80,KEYCODE_DELETE , |
'[','3','~' |0x80,KEYCODE_DELETE , |
54 |
/* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ |
/* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ |
55 |
|
/* ESC [ 3 ; 3 ~ - Alt-Delete */ |
56 |
|
/* ESC [ 3 ; 5 ~ - Ctrl-Delete */ |
57 |
'[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ |
'[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ |
58 |
'[','5','~' |0x80,KEYCODE_PAGEUP , |
'[','5','~' |0x80,KEYCODE_PAGEUP , |
59 |
|
/* ESC [ 5 ; 3 ~ - Alt-PgUp */ |
60 |
|
/* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ |
61 |
|
/* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ |
62 |
'[','6','~' |0x80,KEYCODE_PAGEDOWN, |
'[','6','~' |0x80,KEYCODE_PAGEDOWN, |
63 |
'[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ |
'[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ |
64 |
'[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ |
'[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ |
76 |
'[','2','1','~'|0x80,KEYCODE_FUN10 , |
'[','2','1','~'|0x80,KEYCODE_FUN10 , |
77 |
'[','2','3','~'|0x80,KEYCODE_FUN11 , |
'[','2','3','~'|0x80,KEYCODE_FUN11 , |
78 |
'[','2','4','~'|0x80,KEYCODE_FUN12 , |
'[','2','4','~'|0x80,KEYCODE_FUN12 , |
79 |
|
/* ESC [ 2 4 ; 2 ~ - Shift-F12 */ |
80 |
|
/* ESC [ 2 4 ; 3 ~ - Alt-F12 */ |
81 |
|
/* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ |
82 |
|
/* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ |
83 |
|
/* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ |
84 |
#endif |
#endif |
85 |
|
/* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unused */ |
86 |
|
/* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */ |
87 |
|
'[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT, |
88 |
|
'[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT , |
89 |
0 |
0 |
90 |
|
/* ESC [ Z - Shift-Tab */ |
91 |
}; |
}; |
92 |
|
|
93 |
n = 0; |
buffer++; /* saved chars counter is in buffer[-1] now */ |
94 |
if (nbuffered) |
|
95 |
n = *nbuffered; |
start_over: |
96 |
|
errno = 0; |
97 |
|
n = (unsigned char)buffer[-1]; |
98 |
if (n == 0) { |
if (n == 0) { |
99 |
/* If no data, block waiting for input. If we read more |
/* If no data, block waiting for input. |
100 |
* than the minimal ESC sequence size, the "n=0" below |
* It is tempting to read more than one byte here, |
101 |
* would instead have to figure out how much to keep, |
* but it breaks pasting. Example: at shell prompt, |
102 |
* resulting in larger code. */ |
* user presses "c","a","t" and then pastes "\nline\n". |
103 |
n = safe_read(fd, buffer, 3); |
* When we were reading 3 bytes here, we were eating |
104 |
|
* "li" too, and cat was getting wrong input. |
105 |
|
*/ |
106 |
|
n = safe_read(fd, buffer, 1); |
107 |
if (n <= 0) |
if (n <= 0) |
108 |
return -1; |
return -1; |
109 |
} |
} |
110 |
|
|
111 |
/* Grab character to return from buffer */ |
{ |
112 |
c = (unsigned char)buffer[0]; |
unsigned char c = buffer[0]; |
113 |
n--; |
n--; |
114 |
if (n) |
if (n) |
115 |
memmove(buffer, buffer + 1, n); |
memmove(buffer, buffer + 1, n); |
116 |
|
/* Only ESC starts ESC sequences */ |
117 |
/* Only ESC starts ESC sequences */ |
if (c != 27) { |
118 |
if (c != 27) |
buffer[-1] = n; |
119 |
goto ret; |
return c; |
120 |
|
} |
121 |
|
} |
122 |
|
|
123 |
/* Loop through known ESC sequences */ |
/* Loop through known ESC sequences */ |
124 |
pfd.fd = fd; |
pfd.fd = fd; |
140 |
if (safe_poll(&pfd, 1, 50) == 0) { |
if (safe_poll(&pfd, 1, 50) == 0) { |
141 |
/* No more data! |
/* No more data! |
142 |
* Array is sorted from shortest to longest, |
* Array is sorted from shortest to longest, |
143 |
* we can't match anything later in array, |
* we can't match anything later in array - |
144 |
* break out of both loops. */ |
* anything later is longer than this seq. |
145 |
goto ret; |
* Break out of both loops. */ |
146 |
|
goto got_all; |
147 |
} |
} |
148 |
errno = 0; |
errno = 0; |
149 |
if (safe_read(fd, buffer + n, 1) <= 0) { |
if (safe_read(fd, buffer + n, 1) <= 0) { |
150 |
/* If EAGAIN, then fd is O_NONBLOCK and poll lied: |
/* If EAGAIN, then fd is O_NONBLOCK and poll lied: |
151 |
* in fact, there is no data. */ |
* in fact, there is no data. */ |
152 |
if (errno != EAGAIN) |
if (errno != EAGAIN) { |
153 |
c = -1; /* otherwise it's EOF/error */ |
/* otherwise: it's EOF/error */ |
154 |
goto ret; |
buffer[-1] = 0; |
155 |
|
return -1; |
156 |
|
} |
157 |
|
goto got_all; |
158 |
} |
} |
159 |
n++; |
n++; |
160 |
} |
} |
170 |
} |
} |
171 |
if (seq[i] & 0x80) { |
if (seq[i] & 0x80) { |
172 |
/* Entire seq matched */ |
/* Entire seq matched */ |
|
c = (signed char)seq[i+1]; |
|
173 |
n = 0; |
n = 0; |
174 |
/* n -= i; memmove(...); |
/* n -= i; memmove(...); |
175 |
* would be more correct, |
* would be more correct, |
176 |
* but we never read ahead that much, |
* but we never read ahead that much, |
177 |
* and n == i here. */ |
* and n == i here. */ |
178 |
goto ret; |
buffer[-1] = 0; |
179 |
|
return (signed char)seq[i+1]; |
180 |
} |
} |
181 |
i++; |
i++; |
182 |
} |
} |
183 |
} |
} |
184 |
/* We did not find matching sequence, it was a bare ESC. |
/* We did not find matching sequence. |
185 |
* We possibly read and stored more input in buffer[] |
* We possibly read and stored more input in buffer[] by now. |
186 |
* by now. */ |
* n = bytes read. Try to read more until we time out. |
187 |
|
*/ |
188 |
ret: |
while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ |
189 |
if (nbuffered) |
if (safe_poll(&pfd, 1, 50) == 0) { |
190 |
*nbuffered = n; |
/* No more data! */ |
191 |
return c; |
break; |
192 |
|
} |
193 |
|
errno = 0; |
194 |
|
if (safe_read(fd, buffer + n, 1) <= 0) { |
195 |
|
/* If EAGAIN, then fd is O_NONBLOCK and poll lied: |
196 |
|
* in fact, there is no data. */ |
197 |
|
if (errno != EAGAIN) { |
198 |
|
/* otherwise: it's EOF/error */ |
199 |
|
buffer[-1] = 0; |
200 |
|
return -1; |
201 |
|
} |
202 |
|
break; |
203 |
|
} |
204 |
|
n++; |
205 |
|
/* Try to decipher "ESC [ NNN ; NNN R" sequence */ |
206 |
|
if (ENABLE_FEATURE_EDITING_ASK_TERMINAL |
207 |
|
&& n >= 5 |
208 |
|
&& buffer[0] == '[' |
209 |
|
&& buffer[n-1] == 'R' |
210 |
|
&& isdigit(buffer[1]) |
211 |
|
) { |
212 |
|
char *end; |
213 |
|
unsigned long row, col; |
214 |
|
|
215 |
|
row = strtoul(buffer + 1, &end, 10); |
216 |
|
if (*end != ';' || !isdigit(end[1])) |
217 |
|
continue; |
218 |
|
col = strtoul(end + 1, &end, 10); |
219 |
|
if (*end != 'R') |
220 |
|
continue; |
221 |
|
if (row < 1 || col < 1 || (row | col) > 0x7fff) |
222 |
|
continue; |
223 |
|
|
224 |
|
buffer[-1] = 0; |
225 |
|
/* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ |
226 |
|
col |= (((-1 << 15) | row) << 16); |
227 |
|
/* Return it in high-order word */ |
228 |
|
return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; |
229 |
|
} |
230 |
|
} |
231 |
|
got_all: |
232 |
|
|
233 |
|
if (n <= 1) { |
234 |
|
/* Alt-x is usually returned as ESC x. |
235 |
|
* Report ESC, x is remembered for the next call. |
236 |
|
*/ |
237 |
|
buffer[-1] = n; |
238 |
|
return 27; |
239 |
|
} |
240 |
|
|
241 |
|
/* We were doing "buffer[-1] = n; return c;" here, but this results |
242 |
|
* in unknown key sequences being interpreted as ESC + garbage. |
243 |
|
* This was not useful. Pretend there was no key pressed, |
244 |
|
* go and wait for a new keypress: |
245 |
|
*/ |
246 |
|
buffer[-1] = 0; |
247 |
|
goto start_over; |
248 |
} |
} |