aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--dumbsh.c16
-rw-r--r--handlekeys.c114
2 files changed, 83 insertions, 47 deletions
diff --git a/dumbsh.c b/dumbsh.c
index f0051c9..a282caf 100644
--- a/dumbsh.c
+++ b/dumbsh.c
@@ -2,7 +2,7 @@
2 * 2 *
3 * Copyright 2014 David Seikel <won_fang@yahoo.com.au> 3 * Copyright 2014 David Seikel <won_fang@yahoo.com.au>
4 * 4 *
5 * Not a real shell, so doesn't follow any standards, 5 * Not a real shell, so doesn't follow any standards,
6 * coz it wont implement them anyway. 6 * coz it wont implement them anyway.
7 7
8USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN)) 8USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN))
@@ -48,7 +48,8 @@ static void updateLine()
48 fflush(stdout); 48 fflush(stdout);
49 TT.y = TT.h; 49 TT.y = TT.h;
50 } 50 }
51 printf("\x1B[%d;0H%-*s\x1B[%d;%dH", TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1); 51 printf("\x1B[%d;0H%-*s\x1B[%d;%dH",
52 TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1);
52 fflush(stdout); 53 fflush(stdout);
53} 54}
54 55
@@ -58,11 +59,14 @@ static void handleCSI(long extra, char *command, int *params, int count)
58 // Is it a cursor location report? 59 // Is it a cursor location report?
59 if (strcmp("R", command) == 0) 60 if (strcmp("R", command) == 0)
60 { 61 {
61 // Parameters are cursor line and column. Note this may be sent at other times, not just during terminal resize. 62 // Parameters are cursor line and column.
63 // NOTE - This may be sent at other times, not just during terminal resize.
64 // We are assuming here that it's a resize.
62 // The defaults are 1, which get ignored by the heuristic below. 65 // The defaults are 1, which get ignored by the heuristic below.
63 int r = params[0], c = params[1]; 66 int r = params[0], c = params[1];
64 67
65 // Check it's not an F3 key variation, coz some of them use the same CSI function code. 68 // Check it's not an F3 key variation, coz some of them use
69 // the same CSI function command.
66 // This is a heuristic, we are checking against an unusable terminal size. 70 // This is a heuristic, we are checking against an unusable terminal size.
67 if ((2 == count) && (8 < r) && (8 < c)) 71 if ((2 == count) && (8 < r) && (8 < c))
68 { 72 {
@@ -96,8 +100,8 @@ static void backSpaceChar()
96 } 100 }
97} 101}
98 102
99// This is where we would actually deal with what ever command the user had typed in. 103// This is where we would actually deal with
100// For now we just move on. 104// what ever command the user had typed in.
101// For now we just move on to the next line. 105// For now we just move on to the next line.
102// TODO - We would want to redirect I/O, capture some keys (^C), 106// TODO - We would want to redirect I/O, capture some keys (^C),
103// but pass the rest on. 107// but pass the rest on.
diff --git a/handlekeys.c b/handlekeys.c
index 2fc6437..322d41e 100644
--- a/handlekeys.c
+++ b/handlekeys.c
@@ -18,7 +18,8 @@ struct key
18 char *name; 18 char *name;
19}; 19};
20 20
21// This table includes some variations I have found on some terminals, and the MC "Esc digit" versions. 21// This table includes some variations I have found on some terminals,
22// and the MC "Esc digit" versions.
22// http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. 23// http://rtfm.etla.org/xterm/ctlseq.html has a useful guide.
23// TODO - Don't think I got all the linux console variations. 24// TODO - Don't think I got all the linux console variations.
24// TODO - Add more shift variations, plus Ctrl & Alt variations when needed. 25// TODO - Add more shift variations, plus Ctrl & Alt variations when needed.
@@ -26,13 +27,15 @@ struct key
26// TODO - Add other miscelany that does not use an escape sequence. 27// TODO - Add other miscelany that does not use an escape sequence.
27 28
28// This is sorted by type, though there is some overlap. 29// This is sorted by type, though there is some overlap.
29// Human typing speeds wont need binary searching speeds on this small table. 30// Human typing speeds wont need fast searching speeds on this small table.
30// So simple wins out over speed, and sorting by terminal type wins the simple test. 31// So simple wins out over speed, and sorting by terminal type wins
32// the simple test.
31static struct key keys[] = 33static struct key keys[] =
32{ 34{
33 // Control characters. 35 // Control characters.
34// {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. 36 // Commented out coz it's the C string terminator, and may confuse things.
35 {"\x01", "^A"}, // SOH Apparently sometimes sent as Home 37 //{"\x00", "^@"}, // NUL
38 {"\x01", "^A"}, // SOH Apparently sometimes sent as Home.
36 {"\x02", "^B"}, // STX 39 {"\x02", "^B"}, // STX
37 {"\x03", "^C"}, // ETX SIGINT Emacs and vi. 40 {"\x03", "^C"}, // ETX SIGINT Emacs and vi.
38 {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. 41 {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano.
@@ -40,17 +43,17 @@ static struct key keys[] =
40 {"\x06", "^F"}, // ACK 43 {"\x06", "^F"}, // ACK
41 {"\x07", "^G"}, // BEL 44 {"\x07", "^G"}, // BEL
42 {"\x08", "Del"}, // BS Delete key, usually. 45 {"\x08", "Del"}, // BS Delete key, usually.
43 {"\x09", "Tab"}, // HT Tab key. 46 {"\x09", "Tab"}, // HT
44 {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. 47 {"\x0A", "Return"}, // LF Roxterm translates Ctrl-M to this.
45 {"\x0B", "^K"}, // VT 48 {"\x0B", "^K"}, // VT
46 {"\x0C", "^L"}, // FF 49 {"\x0C", "^L"}, // FF
47 {"\x0D", "^M"}, // CR Other Return key, usually. 50 {"\x0D", "^M"}, // CR Other Return key, usually.
48 {"\x0E", "^N"}, // SO 51 {"\x0E", "^N"}, // SO
49 {"\x0F", "^O"}, // SI DISCARD 52 {"\x0F", "^O"}, // SI DISCARD
50 {"\x10", "^P"}, // DLE 53 {"\x10", "^P"}, // DLE
51 {"\x11", "^Q"}, // DC1 SIGCONT Vi, and made up commands in MC, which seem to work anyway. 54 {"\x11", "^Q"}, // DC1 SIGCONT Vi.
52 {"\x12", "^R"}, // DC2 55 {"\x12", "^R"}, // DC2
53 {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi, so much for "can't be caught". 56 {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi.
54 {"\x14", "^T"}, // DC4 SIGINFO STATUS 57 {"\x14", "^T"}, // DC4 SIGINFO STATUS
55 {"\x15", "^U"}, // NAK KILL character 58 {"\x15", "^U"}, // NAK KILL character
56 {"\x16", "^V"}, // SYN LNEXT 59 {"\x16", "^V"}, // SYN LNEXT
@@ -58,17 +61,21 @@ static struct key keys[] =
58 {"\x18", "^X"}, // CAN KILL character 61 {"\x18", "^X"}, // CAN KILL character
59 {"\x19", "^Y"}, // EM DSUSP SIGTSTP 62 {"\x19", "^Y"}, // EM DSUSP SIGTSTP
60 {"\x1A", "^Z"}, // SUB SIGTSTP 63 {"\x1A", "^Z"}, // SUB SIGTSTP
61// {"\x1B", "^["}, // ESC Esc key. Commented out coz it's the ANSI start byte in the below multibyte keys. Handled in the code with a timeout. 64 // Commented out coz it's the ANSI start byte in the below multibyte keys.
62 {"\x1C", "^\\"}, // FS SIGQUIT Some say ^D is SIGQUIT, but my tests say it's this. 65 // Handled in the code with a timeout.
66 //{"\x1B", "^["}, // ESC Esc key.
67 {"\x1C", "^\\"}, // FS SIGQUIT
63 {"\x1D", "^]"}, // GS 68 {"\x1D", "^]"}, // GS
64 {"\x1E", "^^"}, // RS 69 {"\x1E", "^^"}, // RS
65 {"\x1F", "^_"}, // US 70 {"\x1F", "^_"}, // US
66 {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? 71 {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps?
67// {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". Commented out for the same reason Esc is. 72 // Commented out for the same reason Esc is.
73 //{"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [".
68 74
69 // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. 75 // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers.
70 // Even though we have a proper CSI parser, these should still be in this table. 76 // Even though we have a proper CSI parser,
71 // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. 77 // these should still be in this table. Coz we would need a table anyway
78 // in the CSI parser, so might as well keep them with the others.
72 // Also, less code, no need to have a separate scanner for that other table. 79 // Also, less code, no need to have a separate scanner for that other table.
73 {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. 80 {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere.
74 {"\x9B\x32~", "Ins"}, 81 {"\x9B\x32~", "Ins"},
@@ -113,7 +120,8 @@ static struct key keys[] =
113 {"\x9B\x32\x33;2~", "Shift F11"}, 120 {"\x9B\x32\x33;2~", "Shift F11"},
114 {"\x9B\x32\x34;2~", "Shift F12"}, 121 {"\x9B\x32\x34;2~", "Shift F12"},
115 122
116 // "Normal" Some terminals are special, and it seems they only have four function keys. 123 // "Normal" Some terminals are special, and it seems they only have
124 // four function keys.
117 {"\x9B\x41", "Up"}, 125 {"\x9B\x41", "Up"},
118 {"\x9B\x42", "Down"}, 126 {"\x9B\x42", "Down"},
119 {"\x9B\x43", "Right"}, 127 {"\x9B\x43", "Right"},
@@ -161,9 +169,12 @@ static struct key keys[] =
161 {"\x1BO1;2S", "Shift F4"}, 169 {"\x1BO1;2S", "Shift F4"},
162 170
163 // MC "Esc digit" specials. 171 // MC "Esc digit" specials.
164 // NOTE - The MC Esc variations might not be such a good idea, other programs want the Esc key for other things. 172 // NOTE - The MC Esc variations might not be such a good idea, other programs
165 // Notably seems that "Esc somekey" is used in place of "Alt somekey" AKA "Meta somekey" coz apparently some OSes swallow those. 173 // want the Esc key for other things.
166 // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". 174 // Notably seems that "Esc somekey" is used in place of "Alt somekey"
175 // AKA "Meta somekey" coz apparently some OSes swallow those.
176 // Conversely, some terminals send "Esc somekey" when you do
177 // "Alt somekey".
167 // MC Esc variants might be used on Macs for other things? 178 // MC Esc variants might be used on Macs for other things?
168 {"\x1B\x31", "F1"}, 179 {"\x1B\x31", "F1"},
169 {"\x1B\x32", "F2"}, 180 {"\x1B\x32", "F2"},
@@ -186,8 +197,11 @@ static void handleSIGWINCH(int signalNumber)
186} 197}
187 198
188// TODO - Unhandled complications - 199// TODO - Unhandled complications -
189// Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands. 200// Less and more have the "ZZ" command, but nothing else seems to have
190void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)) 201// multi ordinary character commands.
202void handle_keys(long extra,
203 int (*handle_sequence)(long extra, char *sequence),
204 void (*handle_CSI)(long extra, char *command, int *params, int count))
191{ 205{
192 fd_set selectFds; 206 fd_set selectFds;
193 struct timespec timeOut; 207 struct timespec timeOut;
@@ -201,13 +215,16 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
201 215
202 // Terminals send the SIGWINCH signal when they resize. 216 // Terminals send the SIGWINCH signal when they resize.
203 memset(&sigAction, 0, sizeof(sigAction)); 217 memset(&sigAction, 0, sizeof(sigAction));
204 sigAction.sa_flags = SA_RESTART;// Useless if we are using poll.
205 if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) perror_exit("can't set signal handler SIGWINCH");
206 sigAction.sa_handler = handleSIGWINCH; 218 sigAction.sa_handler = handleSIGWINCH;
219 sigAction.sa_flags = SA_RESTART; // Useless if we are using poll.
220 if (sigaction(SIGWINCH, &sigAction, &oldSigAction))
221 perror_exit("can't set signal handler for SIGWINCH");
207 sigemptyset(&signalMask); 222 sigemptyset(&signalMask);
208 sigaddset(&signalMask, SIGWINCH); 223 sigaddset(&signalMask, SIGWINCH);
209 224
210 // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. 225 // TODO - OS buffered keys might be a problem, but we can't do the
226 // usual timestamp filter for now.
227
211 stillRunning = 1; 228 stillRunning = 1;
212 while (stillRunning) 229 while (stillRunning)
213 { 230 {
@@ -218,20 +235,26 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
218 FD_SET(0, &selectFds); 235 FD_SET(0, &selectFds);
219 timeOut.tv_sec = 0; timeOut.tv_nsec = 100000000; // One tenth of a second. 236 timeOut.tv_sec = 0; timeOut.tv_nsec = 100000000; // One tenth of a second.
220 237
221// TODO - A bit unstable at the moment, something makes it go into a horrid CPU eating edit line flicker mode sometimes. And / or vi mode can crash on exit (stack smash). 238// TODO - A bit unstable at the moment, something makes it go into
239// a horrid CPU eating edit line flicker mode sometimes. And / or vi mode
240// can crash on exit (stack smash).
222// This might be fixed now. 241// This might be fixed now.
223 242
224 // We got a "terminal size changed" signal, ask the terminal how big it is now. 243 // We got a "terminal size changed" signal, ask the terminal
244 // how big it is now.
225 if (sigWinch) 245 if (sigWinch)
226 { 246 {
227 // Send - save cursor position, down 999, right 999, request cursor position, restore cursor position. 247 // Send - save cursor position, down 999, right 999,
248 // request cursor position, restore cursor position.
228 fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); 249 fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout);
229 fflush(stdout); 250 fflush(stdout);
230 sigWinch = 0; 251 sigWinch = 0;
231 } 252 }
232 253
233 // TODO - Should only ask for a time out after we get an Escape, or the user requested time ticks. 254 // TODO - Should only ask for a time out after we get an Escape, or
234 // I wanted to use poll, but that would mean using ppoll, which is Linux only, and involves defining swear words to get it. 255 // the user requested time ticks.
256 // I wanted to use poll, but that would mean using ppoll, which is
257 // Linux only, and involves defining swear words to get it.
235 p = pselect(0 + 1, &selectFds, NULL, NULL, &timeOut, &signalMask); 258 p = pselect(0 + 1, &selectFds, NULL, NULL, &timeOut, &signalMask);
236 if (0 > p) 259 if (0 > p)
237 { 260 {
@@ -239,21 +262,26 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
239 continue; 262 continue;
240 perror_exit("poll"); 263 perror_exit("poll");
241 } 264 }
242 else if (0 == p) // A timeout, trigger a time event. 265 else if (0 == p) // A timeout, trigger a time event.
243 { 266 {
244 if ((0 == buffer[1]) && ('\x1B' == buffer[0])) 267 if ((0 == buffer[1]) && ('\x1B' == buffer[0]))
245 { 268 {
246 // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. 269 // After a short delay to check, this is a real Escape key,
247 // TODO - so far the only uses of this have the escape at the start, but maybe a strcat is needed instead later? 270 // not part of an escape sequence, so deal with it.
271 // TODO - So far the only uses of this have the escape at the start,
272 // but maybe a strcat is needed instead later?
248 strcpy(sequence, "^["); 273 strcpy(sequence, "^[");
249 buffer[0] = buffIndex = 0; 274 buffer[0] = buffIndex = 0;
250 } 275 }
251 // TODO - Call some sort of timer tick callback. This wont be a precise timed event, but don't think we need one. 276 // TODO - Call some sort of timer tick callback. This wont be
277 // a precise timed event, but don't think we need one.
252 } 278 }
253 else if ((0 < p) && FD_ISSET(0, &selectFds)) 279 else if ((0 < p) && FD_ISSET(0, &selectFds))
254 { 280 {
255 // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. 281 // I am assuming that we get the input atomically, each multibyte key
256 // If that's not true (which is entirely likely), then we have to get complicated with circular buffers and stuff, or just one byte at a time. 282 // fits neatly into one read.
283 // If that's not true (which is entirely likely), then we have to get
284 // complicated with circular buffers and stuff, or just one byte at a time.
257 j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); 285 j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1));
258 if (j < 0) // An error happened. 286 if (j < 0) // An error happened.
259 { 287 {
@@ -301,9 +329,9 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
301 } 329 }
302 330
303 // Check for known key sequences. 331 // Check for known key sequences.
304 // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. 332 // For a real timeout checked Esc, buffer is now empty, so this for loop
305 // While it's true we could avoid it by checking, the user already had to wait for a time out, and this loop wont take THAT long. 333 // wont find it anyway. While it's true we could avoid it by checking,
306 for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. 334 // the user already had to wait for a time out, and this loop wont take THAT long.
307 for (j = 0; j < (sizeof(keys) / sizeof(*keys)); j++) 335 for (j = 0; j < (sizeof(keys) / sizeof(*keys)); j++)
308 { 336 {
309 if (strcmp(keys[j].code, buffer) == 0) 337 if (strcmp(keys[j].code, buffer) == 0)
@@ -343,7 +371,8 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
343 371
344 if ('M' == buffer[1]) 372 if ('M' == buffer[1])
345 { 373 {
346 // TODO - We have a mouse report, which is CSI M ..., where the rest is binary encoded, more or less. Not fitting into the CSI format. 374 // TODO - We have a mouse report, which is CSI M ..., where the rest is
375 // binary encoded, more or less. Not fitting into the CSI format.
347 } 376 }
348 else 377 else
349 { 378 {
@@ -379,10 +408,12 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
379 // Only stomp on the ; if it's really the ;. 408 // Only stomp on the ; if it's really the ;.
380 if (t) 409 if (t)
381 buffer[j] = 0; 410 buffer[j] = 0;
382 // Empty parameters are default parameters, so only deal with non defaults. 411 // Empty parameters are default parameters, so only deal with
412 // non defaults.
383 if (';' != buffer[csIndex] || (!t)) 413 if (';' != buffer[csIndex] || (!t))
384 { 414 {
385 // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. 415 // TODO - Might be ":" in the number somewhere, but we are not
416 // expecting any in anything we do.
386 csParams[p] = atoi(&buffer[csIndex]); 417 csParams[p] = atoi(&buffer[csIndex]);
387 } 418 }
388 p++; 419 p++;
@@ -399,7 +430,8 @@ void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence),
399 } 430 }
400 431
401 csi = 0; 432 csi = 0;
402 // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. 433 // Wether or not it's a CSI we understand, it's been handled either here
434 // or in the key sequence scanning above.
403 buffer[0] = buffIndex = 0; 435 buffer[0] = buffIndex = 0;
404 } 436 }
405 437