]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cli_readline.c
catch weird escape sequence: 'SS3 F' --> KEY_END
[z180-stamp.git] / avr / cli_readline.c
1 /*
2 * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * (C) Copyright 2000
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 *
7 * Add to readline cmdline-editing by
8 * (C) Copyright 2005
9 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
10 *
11 * SPDX-License-Identifier: GPL-2.0
12 */
13
14 #include "cli_readline.h"
15 #include "common.h"
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdbool.h>
20 #include <ctype.h>
21
22 #include "config.h"
23 #include "con-utils.h"
24 #include "print-utils.h"
25 #include "command.h"
26
27
28
29 /************************************************************************************************/
30 /* TODO:
31 *
32 */
33
34 #define ESC 0x1b
35
36 #define KEY_TAB '\t' // TAB key
37 #define KEY_CR '\r' // RETURN key
38 #define KEY_BACKSPACE '\b' // Backspace key
39 #define KEY_ESCAPE 0x1B // ESCAPE (pressed twice)
40
41 #define KEY_DOWN 0x80 // Down arrow key
42 #define KEY_UP 0x81 // Up arrow key
43 #define KEY_LEFT 0x82 // Left arrow key
44 #define KEY_RIGHT 0x83 // Right arrow key
45 #define KEY_HOME 0x84 // Home key
46 #define KEY_DC 0x85 // Delete character key
47 #define KEY_IC 0x86 // Ins char/toggle ins mode key
48 #define KEY_NPAGE 0x87 // Next-page key
49 #define KEY_PPAGE 0x88 // Previous-page key
50 #define KEY_END 0x89 // End key
51 #define KEY_BTAB 0x8A // Back tab key
52 #define KEY_F1 0x8B // Function key F1
53 #define KEY_F(n) (KEY_F1+(n)-1) // Space for additional 12 function keys
54
55
56 struct fkey_tbl_s {
57 const FLASH char *sequence; /* ESC Sequence */
58 int code; /* Keycode */
59 };
60
61 //typedef const FLASH struct fkey_tbl_s fkey_tbl_t;
62
63 #define FKEY_TBL_ITEM(_seq, _code) { FSTR(#_seq), _code }
64
65
66
67 static const FLASH struct fkey_tbl_s fkey_table[] = {
68
69 FKEY_TBL_ITEM(B, KEY_DOWN), // Down arrow key
70 FKEY_TBL_ITEM(A, KEY_UP), // Up arrow key
71 FKEY_TBL_ITEM(D, KEY_LEFT), // Left arrow key
72 FKEY_TBL_ITEM(C, KEY_RIGHT), // Right arrow key
73 FKEY_TBL_ITEM(1~, KEY_HOME), // Home key
74 FKEY_TBL_ITEM(3~, KEY_DC), // Delete character key
75 FKEY_TBL_ITEM(2~, KEY_IC), // Ins char/toggle ins mode key
76 FKEY_TBL_ITEM(6~, KEY_NPAGE), // Next-page key
77 FKEY_TBL_ITEM(5~, KEY_PPAGE), // Previous-page key
78 FKEY_TBL_ITEM(4~, KEY_END), // End key
79 FKEY_TBL_ITEM(Z, KEY_BTAB), // Back tab key
80 /* VT400: */
81 FKEY_TBL_ITEM(11~, KEY_F(1)), // Function key F1
82 FKEY_TBL_ITEM(12~, KEY_F(2)), // Function key F2
83 FKEY_TBL_ITEM(13~, KEY_F(3)), // Function key F3
84 FKEY_TBL_ITEM(14~, KEY_F(4)), // Function key F4
85 FKEY_TBL_ITEM(15~, KEY_F(5)), // Function key F5
86 /* Linux consoe */
87 FKEY_TBL_ITEM([A, KEY_F(1)), // Function key F1
88 FKEY_TBL_ITEM([B, KEY_F(2)), // Function key F2
89 FKEY_TBL_ITEM([C, KEY_F(3)), // Function key F3
90 FKEY_TBL_ITEM([D, KEY_F(4)), // Function key F4
91 FKEY_TBL_ITEM([E, KEY_F(5)), // Function key F5
92
93 FKEY_TBL_ITEM(17~, KEY_F(6)), // Function key F6
94 FKEY_TBL_ITEM(18~, KEY_F(7)), // Function key F7
95 FKEY_TBL_ITEM(19~, KEY_F(8)), // Function key F8
96 FKEY_TBL_ITEM(20~, KEY_F(9)), // Function key F9
97 FKEY_TBL_ITEM(21~, KEY_F(10)), // Function key F10
98 FKEY_TBL_ITEM(23~, KEY_F(11)), // Function key F11
99 FKEY_TBL_ITEM(24~, KEY_F(12)), // Function key F12
100 { NULL } /* Mark end of table */
101 };
102
103
104
105 typedef enum {
106 STATE_GROUND,
107 STATE_ESCAPE,
108 STATE_CSI_ENTRY,
109 STATE_SS3
110 } vtparse_state_t;
111
112 #define CHB_SIZE 15
113
114 static
115 int vt_parse (void)
116 {
117 static vtparse_state_t state = STATE_GROUND;
118 char buf[CHB_SIZE+1];
119 uint8_t param[2];
120 uint8_t i_buf;
121 uint8_t i_param;
122 int ch;
123
124
125 while (1) {
126 ch = my_getchar(1);
127 // debug_getch(state, ch);
128
129 switch (state) {
130 case STATE_GROUND:
131 if (ch == ESC) {
132 state = STATE_ESCAPE;
133 continue;
134 }
135 if (ch == 0x7F) // BACKSPACE on VT200 sends DEL char
136 ch = KEY_BACKSPACE; // map it to '\b'
137 break;
138 case STATE_ESCAPE:
139 if (ch < 0)
140 continue;
141
142 if (ch == '[') {
143 state = STATE_CSI_ENTRY;
144 param[0] = param[1] = 0;
145 i_buf = 0;
146 i_param = 0;
147 continue;
148 }
149 if (ch == 'O') {
150 state = STATE_SS3;
151 continue;
152 }
153 state = STATE_GROUND;
154 break;
155 case STATE_SS3:
156 if (ch == 'F') /* weird */
157 ch = KEY_END;
158 state = STATE_GROUND;
159 break;
160 case STATE_CSI_ENTRY:
161 if (ch < 0)
162 continue;
163
164 buf[i_buf] = ch;
165 if (i_buf < CHB_SIZE)
166 i_buf++;
167 if (ch == ';') {
168 i_param++;
169 continue;
170 }
171 if (isdigit(ch)) {
172 if (i_param < 2)
173 param[i_param] = param[i_param] * 10 + ch - '0';
174 continue;
175 }
176 if (ch >= '@' && ch <= '~' && ch != '[') {
177 buf[i_buf] = '\0';
178 int_fast8_t i = 0;
179 while (fkey_table[i].sequence) {
180 if (! strcmp_P (buf, fkey_table[i].sequence)) {
181 ch = fkey_table[i].code;
182 break;
183 }
184 i++;
185 }
186 if (fkey_table[i].sequence == NULL) {
187 ch = '$'; /* KEY_ESCAPE; */
188 }
189 }
190 state = STATE_GROUND;
191 break;
192 }
193 break; /* while */
194 }
195
196 return ch;
197 }
198
199 /************************************************************************************************/
200
201
202 char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
203
204 /*
205 * cmdline-editing related codes from vivi.
206 * Author: Janghoon Lyu <nandy@mizi.com>
207 */
208
209 static void putnstr(char *str, int n)
210 {
211 /* printf_P(PSTR("%.*s"), (int)n, str) */
212 while (n-- && *str)
213 putchar(*str++);
214 }
215
216
217 #define CTL_CH(c) ((c) - 'a' + 1)
218 #define CTL_BACKSPACE ('\b')
219 #define DEL ((char)255)
220 #define DEL7 ((char)127)
221
222 #define getcmd_putch(ch) putchar(ch)
223 #define getcmd_getch() vt_parse()
224 #define getcmd_cbeep() getcmd_putch('\a')
225
226
227 struct hist_node_s {
228 struct hist_node_s *next;
229 char line[];
230 };
231 typedef struct hist_node_s hist_node;
232
233
234 static hist_node *hist_head;
235 static hist_node *hist_cur;
236
237 static hist_node *hist_search_node(char *line)
238 {
239 hist_node *p = hist_head;
240
241 while (p && strcmp(p->line, line))
242 p = p->next;
243 return p;
244 }
245
246 #if 0
247 static hist_node *hist_insert(char *line)
248 {
249 hist_node *p = (hist_node *) malloc(sizeof (hist_node) + strlen(line) + 1);
250
251 if (p) {
252 strcpy(p->line, line);
253 p->next = hist_head;
254 hist_head = p;
255 }
256 return p;
257 }
258 #endif
259
260 static hist_node *hist_new(char *line)
261 {
262 hist_node *p = (hist_node *) malloc(sizeof (hist_node) + strlen(line) + 1);
263
264 if (p) {
265 strcpy(p->line, line);
266 p->next = NULL;
267 }
268 return p;
269 }
270
271 static hist_node *hist_delete(void)
272 {
273 hist_node *p = NULL;
274 hist_node *q = hist_head;
275
276 if (q) {
277 while(q->next) {
278 p = q;
279 q = q->next;
280 }
281 free(q);
282 if (p)
283 p->next = NULL;
284 }
285 return p;
286 }
287
288 static hist_node *hist_unlink(hist_node *pos)
289 {
290 hist_node *p = NULL;
291 hist_node *q = hist_head;
292
293 while(q && q != pos) {
294 p = q;
295 q = q->next;
296 }
297 if (q) {
298 if (p)
299 p->next = q->next;
300 else
301 hist_head = q->next;
302 q->next = NULL;
303 }
304 return q;
305 }
306
307 static uint_fast8_t hist_count(void)
308 {
309 hist_node *p = hist_head;
310 uint_fast8_t n = 0;
311
312 while (p) {
313 ++n;
314 p = p->next;
315 }
316 return n;
317 }
318
319 static hist_node *cread_add_to_hist(char *line)
320 {
321 hist_node * p;
322
323 p = hist_search_node(line);
324 if (p)
325 hist_unlink(p);
326 else
327 p = hist_new(line);
328
329 if (p) {
330 p->next = hist_head;
331 hist_head = p;
332 }
333
334 if (hist_count() > CONFIG_SYS_HIST_MAX)
335 hist_delete();
336 return p;
337 }
338
339 static char *hist_prev(void)
340 {
341 hist_node *p = hist_cur;
342
343 if (p == NULL)
344 return NULL;
345
346 hist_cur = hist_cur->next;
347
348 return p->line;
349 }
350
351 static char *hist_next(void)
352 {
353 hist_node *p = NULL;
354 hist_node *q = hist_head;
355
356 if(q == hist_cur)
357 return NULL;
358
359 while(q->next != hist_cur) {
360 p = q;
361 q = q->next;
362 }
363 hist_cur = q;
364
365 return p ? p->line : "";
366 }
367
368
369 #define BEGINNING_OF_LINE() { \
370 while (num) { \
371 getcmd_putch(CTL_BACKSPACE); \
372 num--; \
373 } \
374 }
375
376 #define ERASE_TO_EOL() { \
377 if (num < eol_num) { \
378 /* printf_P(PSTR("%*S"), (int)(eol_num - num), PSTR("")); */ \
379 print_blanks(eol_num - num); \
380 do { \
381 getcmd_putch(CTL_BACKSPACE); \
382 } while (--eol_num > num); \
383 } \
384 }
385
386 #define REFRESH_TO_EOL() { \
387 if (num < eol_num) { \
388 uint_fast8_t wlen = eol_num - num; \
389 putnstr(buf + num, wlen); \
390 num = eol_num; \
391 } \
392 }
393
394 static void cread_add_char(char ichar, bool insert, uint_fast8_t *num,
395 uint_fast8_t *eol_num, char *buf, uint_fast8_t len)
396 {
397 uint_fast8_t wlen;
398
399 /* room ??? */
400 if (insert || *num == *eol_num) {
401 if (*eol_num > len - 1) {
402 getcmd_cbeep();
403 return;
404 }
405 (*eol_num)++;
406 }
407
408 if (insert) {
409 wlen = *eol_num - *num;
410 if (wlen > 1)
411 memmove(&buf[*num+1], &buf[*num], wlen-1);
412
413 buf[*num] = ichar;
414 putnstr(buf + *num, wlen);
415 (*num)++;
416 while (--wlen)
417 getcmd_putch(CTL_BACKSPACE);
418 } else {
419 /* echo the character */
420 wlen = 1;
421 buf[*num] = ichar;
422 putnstr(buf + *num, wlen);
423 (*num)++;
424 }
425 }
426
427 static void cread_add_str(char *str, uint_fast8_t strsize, bool insert,
428 uint_fast8_t *num, uint_fast8_t *eol_num,
429 char *buf, uint_fast8_t len)
430 {
431 while (strsize--) {
432 cread_add_char(*str, insert, num, eol_num, buf, len);
433 str++;
434 }
435 }
436
437 static int cread_line(const FLASH char *const prompt, char *buf,
438 uint_fast8_t *len, bool enable_history)
439 {
440 uint_fast8_t num = 0;
441 uint_fast8_t eol_num = 0;
442 bool insert = 1;
443
444 (void) prompt;
445
446 uint_fast8_t init_len = strlen(buf);
447 if (init_len)
448 cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
449
450 while (1) {
451 int ichar = getcmd_getch();
452
453 if ((ichar == '\n') || (ichar == '\r')) {
454 putchar('\n');
455 break;
456 }
457
458
459 switch (ichar) {
460
461 case KEY_HOME:
462 case CTL_CH('a'):
463 BEGINNING_OF_LINE();
464 break;
465 case CTL_CH('c'): /* ^C - break */
466 *buf = '\0'; /* discard input */
467 return -1;
468 case KEY_RIGHT:
469 case CTL_CH('f'): /* forward-char */
470 if (num < eol_num) {
471 getcmd_putch(buf[num]);
472 num++;
473 }
474 break;
475 case KEY_LEFT:
476 case CTL_CH('b'): /* backward-char */
477 if (num) {
478 getcmd_putch(CTL_BACKSPACE);
479 num--;
480 }
481 break;
482 case KEY_DC:
483 case CTL_CH('d'): /* delete-char */
484 if (num < eol_num) {
485 uint_fast8_t wlen = eol_num - num - 1;
486 if (wlen) {
487 memmove(&buf[num], &buf[num+1], wlen);
488 putnstr(buf + num, wlen);
489 }
490
491 getcmd_putch(' ');
492 do {
493 getcmd_putch(CTL_BACKSPACE);
494 } while (wlen--);
495 eol_num--;
496 }
497 break;
498 case CTL_CH('k'): /* kill-line */
499 ERASE_TO_EOL();
500 break;
501 case KEY_END:
502 case CTL_CH('e'):
503 REFRESH_TO_EOL();
504 break;
505 case KEY_IC:
506 case CTL_CH('o'):
507 insert = !insert;
508 break;
509 case CTL_CH('x'):
510 case CTL_CH('u'): /* kill-whole-line */
511 BEGINNING_OF_LINE();
512 ERASE_TO_EOL();
513 break;
514 case DEL:
515 case DEL7:
516 case 8: /* backward-delete-char */
517 if (num) {
518 uint_fast8_t wlen = eol_num - num;
519 num--;
520 memmove(&buf[num], &buf[num+1], wlen);
521 getcmd_putch(CTL_BACKSPACE);
522 putnstr(buf + num, wlen);
523 getcmd_putch(' ');
524 do {
525 getcmd_putch(CTL_BACKSPACE);
526 } while (wlen--);
527 eol_num--;
528 }
529 break;
530 case KEY_UP:
531 case CTL_CH('p'): /* previous-history */
532 case KEY_DOWN:
533 case CTL_CH('n'): /* next-history */
534 if (enable_history) {
535 char *hline;
536
537 if (ichar == CTL_CH('p') || ichar == KEY_UP)
538 hline = hist_prev();
539 else
540 hline = hist_next();
541
542 if (hline) {
543 /* nuke the current line */
544 /* first, go home */
545 BEGINNING_OF_LINE();
546
547 /* erase to end of line */
548 ERASE_TO_EOL();
549
550 /* copy new line into place and display */
551 strcpy(buf, hline);
552 eol_num = strlen(buf);
553 REFRESH_TO_EOL();
554 } else {
555 getcmd_cbeep();
556 }
557 } else {
558 getcmd_cbeep();
559 }
560 break;
561 #if 0
562 case KEY_PPAGE: /* history-search-backward */
563 case KEY_NPAGE: /* history-search-forward */
564 if (enable_history) {
565 char *hline;
566
567 if (ichar == KEY_PPAGE)
568 hline = hist_search_backward();
569 else
570 hline = hist_search_forward();
571
572 if (hline) {
573 /* nuke the current line */
574 /* first, go home */
575 BEGINNING_OF_LINE();
576
577 /* erase to end of line */
578 ERASE_TO_EOL();
579
580 /* copy new line into place and display */
581 strcpy(buf, hline);
582 eol_num = strlen(buf);
583 REFRESH_TO_EOL();
584 } else {
585 getcmd_cbeep();
586 }
587 } else {
588 getcmd_cbeep();
589 }
590 break;
591 #endif
592 #ifdef CONFIG_AUTO_COMPLETE
593 case '\t': {
594 int num2, col;
595
596 /* do not autocomplete when in the middle */
597 if (num < eol_num) {
598 getcmd_cbeep();
599 break;
600 }
601
602 buf[num] = '\0';
603 col = strlen_P(prompt) + eol_num;
604 num2 = num;
605 if (cmd_auto_complete(prompt, buf, &num2, &col)) {
606 col = num2 - num;
607 num += col;
608 eol_num += col;
609 }
610 break;
611 }
612 #endif
613 default:
614 if (isprint(ichar))
615 cread_add_char(ichar, insert, &num, &eol_num, buf,
616 *len);
617 break;
618 }
619 }
620 *len = eol_num;
621 buf[eol_num] = '\0'; /* lose the newline */
622
623 if (enable_history) {
624 if (buf[0])
625 hist_cur = cread_add_to_hist(buf);
626 }
627 return 0;
628 }
629
630 /****************************************************************************/
631
632 static int cli_readline_into_buffer(const FLASH char *const prompt,
633 char *buffer, bool enable_history)
634 {
635 char *p = buffer;
636 uint_fast8_t len = CONFIG_SYS_CBSIZE;
637 int rc;
638
639 if (prompt)
640 my_puts_P(prompt);
641
642 rc = cread_line(prompt, p, &len, enable_history);
643 return rc < 0 ? rc : (int) len;
644 }
645
646 int cli_readline(const FLASH char *const prompt, bool enable_history)
647 {
648 /*
649 * If console_buffer isn't 0-length the user will be prompted to modify
650 * it instead of entering it from scratch as desired.
651 */
652 console_buffer[0] = '\0';
653
654 return cli_readline_into_buffer(prompt, console_buffer, enable_history);
655 }