1 /*
2 * Command line editing and history
3 * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "includes.h"
10 #include <termios.h>
11
12 #include "common.h"
13 #include "eloop.h"
14 #include "list.h"
15 #include "edit.h"
16
17 #define CMD_BUF_LEN 4096
18 static char cmdbuf[CMD_BUF_LEN];
19 static int cmdbuf_pos = 0;
20 static int cmdbuf_len = 0;
21 static char currbuf[CMD_BUF_LEN];
22 static int currbuf_valid = 0;
23 static const char *ps2 = NULL;
24
25 #define HISTORY_MAX 100
26
27 struct edit_history {
28 struct dl_list list;
29 char str[1];
30 };
31
32 static struct dl_list history_list;
33 static struct edit_history *history_curr;
34
35 static void *edit_cb_ctx;
36 static void (*edit_cmd_cb)(void *ctx, char *cmd);
37 static void (*edit_eof_cb)(void *ctx);
38 static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
39 NULL;
40
41 static struct termios prevt, newt;
42
43
44 #define CLEAR_END_LINE "\e[K"
45
46
edit_clear_line(void)47 void edit_clear_line(void)
48 {
49 int i;
50 putchar('\r');
51 for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
52 putchar(' ');
53 }
54
55
move_start(void)56 static void move_start(void)
57 {
58 cmdbuf_pos = 0;
59 edit_redraw();
60 }
61
62
move_end(void)63 static void move_end(void)
64 {
65 cmdbuf_pos = cmdbuf_len;
66 edit_redraw();
67 }
68
69
move_left(void)70 static void move_left(void)
71 {
72 if (cmdbuf_pos > 0) {
73 cmdbuf_pos--;
74 edit_redraw();
75 }
76 }
77
78
move_right(void)79 static void move_right(void)
80 {
81 if (cmdbuf_pos < cmdbuf_len) {
82 cmdbuf_pos++;
83 edit_redraw();
84 }
85 }
86
87
move_word_left(void)88 static void move_word_left(void)
89 {
90 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
91 cmdbuf_pos--;
92 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
93 cmdbuf_pos--;
94 edit_redraw();
95 }
96
97
move_word_right(void)98 static void move_word_right(void)
99 {
100 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
101 cmdbuf_pos++;
102 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
103 cmdbuf_pos++;
104 edit_redraw();
105 }
106
107
delete_left(void)108 static void delete_left(void)
109 {
110 if (cmdbuf_pos == 0)
111 return;
112
113 edit_clear_line();
114 os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
115 cmdbuf_len - cmdbuf_pos);
116 cmdbuf_pos--;
117 cmdbuf_len--;
118 edit_redraw();
119 }
120
121
delete_current(void)122 static void delete_current(void)
123 {
124 if (cmdbuf_pos == cmdbuf_len)
125 return;
126
127 edit_clear_line();
128 os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
129 cmdbuf_len - cmdbuf_pos);
130 cmdbuf_len--;
131 edit_redraw();
132 }
133
134
delete_word(void)135 static void delete_word(void)
136 {
137 int pos;
138
139 edit_clear_line();
140 pos = cmdbuf_pos;
141 while (pos > 0 && cmdbuf[pos - 1] == ' ')
142 pos--;
143 while (pos > 0 && cmdbuf[pos - 1] != ' ')
144 pos--;
145 os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
146 cmdbuf_len -= cmdbuf_pos - pos;
147 cmdbuf_pos = pos;
148 edit_redraw();
149 }
150
151
clear_left(void)152 static void clear_left(void)
153 {
154 if (cmdbuf_pos == 0)
155 return;
156
157 edit_clear_line();
158 os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
159 cmdbuf_len -= cmdbuf_pos;
160 cmdbuf_pos = 0;
161 edit_redraw();
162 }
163
164
clear_right(void)165 static void clear_right(void)
166 {
167 if (cmdbuf_pos == cmdbuf_len)
168 return;
169
170 edit_clear_line();
171 cmdbuf_len = cmdbuf_pos;
172 edit_redraw();
173 }
174
175
history_add(const char * str)176 static void history_add(const char *str)
177 {
178 struct edit_history *h, *match = NULL, *last = NULL;
179 size_t len, count = 0;
180
181 if (str[0] == '\0')
182 return;
183
184 dl_list_for_each(h, &history_list, struct edit_history, list) {
185 if (os_strcmp(str, h->str) == 0) {
186 match = h;
187 break;
188 }
189 last = h;
190 count++;
191 }
192
193 if (match) {
194 dl_list_del(&h->list);
195 dl_list_add(&history_list, &h->list);
196 history_curr = h;
197 return;
198 }
199
200 if (count >= HISTORY_MAX && last) {
201 dl_list_del(&last->list);
202 os_free(last);
203 }
204
205 len = os_strlen(str);
206 h = os_zalloc(sizeof(*h) + len);
207 if (h == NULL)
208 return;
209 dl_list_add(&history_list, &h->list);
210 os_strlcpy(h->str, str, len + 1);
211 history_curr = h;
212 }
213
214
history_use(void)215 static void history_use(void)
216 {
217 edit_clear_line();
218 cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
219 os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
220 edit_redraw();
221 }
222
223
history_prev(void)224 static void history_prev(void)
225 {
226 if (history_curr == NULL)
227 return;
228
229 if (history_curr ==
230 dl_list_first(&history_list, struct edit_history, list)) {
231 if (!currbuf_valid) {
232 cmdbuf[cmdbuf_len] = '\0';
233 os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
234 currbuf_valid = 1;
235 history_use();
236 return;
237 }
238 }
239
240 if (history_curr ==
241 dl_list_last(&history_list, struct edit_history, list))
242 return;
243
244 history_curr = dl_list_entry(history_curr->list.next,
245 struct edit_history, list);
246 history_use();
247 }
248
249
history_next(void)250 static void history_next(void)
251 {
252 if (history_curr == NULL ||
253 history_curr ==
254 dl_list_first(&history_list, struct edit_history, list)) {
255 if (currbuf_valid) {
256 currbuf_valid = 0;
257 edit_clear_line();
258 cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
259 os_memcpy(cmdbuf, currbuf, cmdbuf_len);
260 edit_redraw();
261 }
262 return;
263 }
264
265 history_curr = dl_list_entry(history_curr->list.prev,
266 struct edit_history, list);
267 history_use();
268 }
269
270
history_read(const char * fname)271 static void history_read(const char *fname)
272 {
273 FILE *f;
274 char buf[CMD_BUF_LEN], *pos;
275
276 f = fopen(fname, "r");
277 if (f == NULL)
278 return;
279
280 while (fgets(buf, CMD_BUF_LEN, f)) {
281 for (pos = buf; *pos; pos++) {
282 if (*pos == '\r' || *pos == '\n') {
283 *pos = '\0';
284 break;
285 }
286 }
287 history_add(buf);
288 }
289
290 fclose(f);
291 }
292
293
history_write(const char * fname,int (* filter_cb)(void * ctx,const char * cmd))294 static void history_write(const char *fname,
295 int (*filter_cb)(void *ctx, const char *cmd))
296 {
297 FILE *f;
298 struct edit_history *h;
299
300 f = fopen(fname, "w");
301 if (f == NULL)
302 return;
303
304 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
305 if (filter_cb && filter_cb(edit_cb_ctx, h->str))
306 continue;
307 fprintf(f, "%s\n", h->str);
308 }
309
310 fclose(f);
311 }
312
313
history_debug_dump(void)314 static void history_debug_dump(void)
315 {
316 struct edit_history *h;
317 edit_clear_line();
318 printf("\r");
319 dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
320 printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
321 if (currbuf_valid)
322 printf("{%s}\n", currbuf);
323 edit_redraw();
324 }
325
326
insert_char(int c)327 static void insert_char(int c)
328 {
329 if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
330 return;
331 if (cmdbuf_len == cmdbuf_pos) {
332 cmdbuf[cmdbuf_pos++] = c;
333 cmdbuf_len++;
334 putchar(c);
335 fflush(stdout);
336 } else {
337 os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
338 cmdbuf_len - cmdbuf_pos);
339 cmdbuf[cmdbuf_pos++] = c;
340 cmdbuf_len++;
341 edit_redraw();
342 }
343 }
344
345
process_cmd(void)346 static void process_cmd(void)
347 {
348 currbuf_valid = 0;
349 if (cmdbuf_len == 0) {
350 printf("\n%s> ", ps2 ? ps2 : "");
351 fflush(stdout);
352 return;
353 }
354 printf("\n");
355 cmdbuf[cmdbuf_len] = '\0';
356 history_add(cmdbuf);
357 cmdbuf_pos = 0;
358 cmdbuf_len = 0;
359 edit_cmd_cb(edit_cb_ctx, cmdbuf);
360 printf("%s> ", ps2 ? ps2 : "");
361 fflush(stdout);
362 }
363
364
free_completions(char ** c)365 static void free_completions(char **c)
366 {
367 int i;
368 if (c == NULL)
369 return;
370 for (i = 0; c[i]; i++)
371 os_free(c[i]);
372 os_free(c);
373 }
374
375
filter_strings(char ** c,char * str,size_t len)376 static int filter_strings(char **c, char *str, size_t len)
377 {
378 int i, j;
379
380 for (i = 0, j = 0; c[j]; j++) {
381 if (os_strncasecmp(c[j], str, len) == 0) {
382 if (i != j) {
383 c[i] = c[j];
384 c[j] = NULL;
385 }
386 i++;
387 } else {
388 os_free(c[j]);
389 c[j] = NULL;
390 }
391 }
392 c[i] = NULL;
393 return i;
394 }
395
396
common_len(const char * a,const char * b)397 static int common_len(const char *a, const char *b)
398 {
399 int len = 0;
400 while (a[len] && a[len] == b[len])
401 len++;
402 return len;
403 }
404
405
max_common_length(char ** c)406 static int max_common_length(char **c)
407 {
408 int len, i;
409
410 len = os_strlen(c[0]);
411 for (i = 1; c[i]; i++) {
412 int same = common_len(c[0], c[i]);
413 if (same < len)
414 len = same;
415 }
416
417 return len;
418 }
419
420
cmp_str(const void * a,const void * b)421 static int cmp_str(const void *a, const void *b)
422 {
423 return os_strcmp(* (const char **) a, * (const char **) b);
424 }
425
complete(int list)426 static void complete(int list)
427 {
428 char **c;
429 int i, len, count;
430 int start, end;
431 int room, plen, add_space;
432
433 if (edit_completion_cb == NULL)
434 return;
435
436 cmdbuf[cmdbuf_len] = '\0';
437 c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
438 if (c == NULL)
439 return;
440
441 end = cmdbuf_pos;
442 start = end;
443 while (start > 0 && cmdbuf[start - 1] != ' ')
444 start--;
445 plen = end - start;
446
447 count = filter_strings(c, &cmdbuf[start], plen);
448 if (count == 0) {
449 free_completions(c);
450 return;
451 }
452
453 len = max_common_length(c);
454 if (len <= plen && count > 1) {
455 if (list) {
456 qsort(c, count, sizeof(char *), cmp_str);
457 edit_clear_line();
458 printf("\r");
459 for (i = 0; c[i]; i++)
460 printf("%s%s", i > 0 ? " " : "", c[i]);
461 printf("\n");
462 edit_redraw();
463 }
464 free_completions(c);
465 return;
466 }
467 len -= plen;
468
469 room = sizeof(cmdbuf) - 1 - cmdbuf_len;
470 if (room < len)
471 len = room;
472 add_space = count == 1 && len < room;
473
474 os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
475 cmdbuf_len - cmdbuf_pos);
476 os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
477 if (add_space)
478 cmdbuf[cmdbuf_pos + len] = ' ';
479
480 cmdbuf_pos += len + add_space;
481 cmdbuf_len += len + add_space;
482
483 edit_redraw();
484
485 free_completions(c);
486 }
487
488
489 enum edit_key_code {
490 EDIT_KEY_NONE = 256,
491 EDIT_KEY_TAB,
492 EDIT_KEY_UP,
493 EDIT_KEY_DOWN,
494 EDIT_KEY_RIGHT,
495 EDIT_KEY_LEFT,
496 EDIT_KEY_ENTER,
497 EDIT_KEY_BACKSPACE,
498 EDIT_KEY_INSERT,
499 EDIT_KEY_DELETE,
500 EDIT_KEY_HOME,
501 EDIT_KEY_END,
502 EDIT_KEY_PAGE_UP,
503 EDIT_KEY_PAGE_DOWN,
504 EDIT_KEY_F1,
505 EDIT_KEY_F2,
506 EDIT_KEY_F3,
507 EDIT_KEY_F4,
508 EDIT_KEY_F5,
509 EDIT_KEY_F6,
510 EDIT_KEY_F7,
511 EDIT_KEY_F8,
512 EDIT_KEY_F9,
513 EDIT_KEY_F10,
514 EDIT_KEY_F11,
515 EDIT_KEY_F12,
516 EDIT_KEY_CTRL_UP,
517 EDIT_KEY_CTRL_DOWN,
518 EDIT_KEY_CTRL_RIGHT,
519 EDIT_KEY_CTRL_LEFT,
520 EDIT_KEY_CTRL_A,
521 EDIT_KEY_CTRL_B,
522 EDIT_KEY_CTRL_D,
523 EDIT_KEY_CTRL_E,
524 EDIT_KEY_CTRL_F,
525 EDIT_KEY_CTRL_G,
526 EDIT_KEY_CTRL_H,
527 EDIT_KEY_CTRL_J,
528 EDIT_KEY_CTRL_K,
529 EDIT_KEY_CTRL_L,
530 EDIT_KEY_CTRL_N,
531 EDIT_KEY_CTRL_O,
532 EDIT_KEY_CTRL_P,
533 EDIT_KEY_CTRL_R,
534 EDIT_KEY_CTRL_T,
535 EDIT_KEY_CTRL_U,
536 EDIT_KEY_CTRL_V,
537 EDIT_KEY_CTRL_W,
538 EDIT_KEY_ALT_UP,
539 EDIT_KEY_ALT_DOWN,
540 EDIT_KEY_ALT_RIGHT,
541 EDIT_KEY_ALT_LEFT,
542 EDIT_KEY_SHIFT_UP,
543 EDIT_KEY_SHIFT_DOWN,
544 EDIT_KEY_SHIFT_RIGHT,
545 EDIT_KEY_SHIFT_LEFT,
546 EDIT_KEY_ALT_SHIFT_UP,
547 EDIT_KEY_ALT_SHIFT_DOWN,
548 EDIT_KEY_ALT_SHIFT_RIGHT,
549 EDIT_KEY_ALT_SHIFT_LEFT,
550 EDIT_KEY_EOF
551 };
552
show_esc_buf(const char * esc_buf,char c,int i)553 static void show_esc_buf(const char *esc_buf, char c, int i)
554 {
555 edit_clear_line();
556 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
557 edit_redraw();
558 }
559
560
esc_seq_to_key1_no(char last)561 static enum edit_key_code esc_seq_to_key1_no(char last)
562 {
563 switch (last) {
564 case 'A':
565 return EDIT_KEY_UP;
566 case 'B':
567 return EDIT_KEY_DOWN;
568 case 'C':
569 return EDIT_KEY_RIGHT;
570 case 'D':
571 return EDIT_KEY_LEFT;
572 default:
573 return EDIT_KEY_NONE;
574 }
575 }
576
577
esc_seq_to_key1_shift(char last)578 static enum edit_key_code esc_seq_to_key1_shift(char last)
579 {
580 switch (last) {
581 case 'A':
582 return EDIT_KEY_SHIFT_UP;
583 case 'B':
584 return EDIT_KEY_SHIFT_DOWN;
585 case 'C':
586 return EDIT_KEY_SHIFT_RIGHT;
587 case 'D':
588 return EDIT_KEY_SHIFT_LEFT;
589 default:
590 return EDIT_KEY_NONE;
591 }
592 }
593
594
esc_seq_to_key1_alt(char last)595 static enum edit_key_code esc_seq_to_key1_alt(char last)
596 {
597 switch (last) {
598 case 'A':
599 return EDIT_KEY_ALT_UP;
600 case 'B':
601 return EDIT_KEY_ALT_DOWN;
602 case 'C':
603 return EDIT_KEY_ALT_RIGHT;
604 case 'D':
605 return EDIT_KEY_ALT_LEFT;
606 default:
607 return EDIT_KEY_NONE;
608 }
609 }
610
611
esc_seq_to_key1_alt_shift(char last)612 static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
613 {
614 switch (last) {
615 case 'A':
616 return EDIT_KEY_ALT_SHIFT_UP;
617 case 'B':
618 return EDIT_KEY_ALT_SHIFT_DOWN;
619 case 'C':
620 return EDIT_KEY_ALT_SHIFT_RIGHT;
621 case 'D':
622 return EDIT_KEY_ALT_SHIFT_LEFT;
623 default:
624 return EDIT_KEY_NONE;
625 }
626 }
627
628
esc_seq_to_key1_ctrl(char last)629 static enum edit_key_code esc_seq_to_key1_ctrl(char last)
630 {
631 switch (last) {
632 case 'A':
633 return EDIT_KEY_CTRL_UP;
634 case 'B':
635 return EDIT_KEY_CTRL_DOWN;
636 case 'C':
637 return EDIT_KEY_CTRL_RIGHT;
638 case 'D':
639 return EDIT_KEY_CTRL_LEFT;
640 default:
641 return EDIT_KEY_NONE;
642 }
643 }
644
645
esc_seq_to_key1(int param1,int param2,char last)646 static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
647 {
648 /* ESC-[<param1>;<param2><last> */
649
650 if (param1 < 0 && param2 < 0)
651 return esc_seq_to_key1_no(last);
652
653 if (param1 == 1 && param2 == 2)
654 return esc_seq_to_key1_shift(last);
655
656 if (param1 == 1 && param2 == 3)
657 return esc_seq_to_key1_alt(last);
658
659 if (param1 == 1 && param2 == 4)
660 return esc_seq_to_key1_alt_shift(last);
661
662 if (param1 == 1 && param2 == 5)
663 return esc_seq_to_key1_ctrl(last);
664
665 if (param2 < 0) {
666 if (last != '~')
667 return EDIT_KEY_NONE;
668 switch (param1) {
669 case 2:
670 return EDIT_KEY_INSERT;
671 case 3:
672 return EDIT_KEY_DELETE;
673 case 5:
674 return EDIT_KEY_PAGE_UP;
675 case 6:
676 return EDIT_KEY_PAGE_DOWN;
677 case 15:
678 return EDIT_KEY_F5;
679 case 17:
680 return EDIT_KEY_F6;
681 case 18:
682 return EDIT_KEY_F7;
683 case 19:
684 return EDIT_KEY_F8;
685 case 20:
686 return EDIT_KEY_F9;
687 case 21:
688 return EDIT_KEY_F10;
689 case 23:
690 return EDIT_KEY_F11;
691 case 24:
692 return EDIT_KEY_F12;
693 }
694 }
695
696 return EDIT_KEY_NONE;
697 }
698
699
esc_seq_to_key2(int param1,int param2,char last)700 static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
701 {
702 /* ESC-O<param1>;<param2><last> */
703
704 if (param1 >= 0 || param2 >= 0)
705 return EDIT_KEY_NONE;
706
707 switch (last) {
708 case 'F':
709 return EDIT_KEY_END;
710 case 'H':
711 return EDIT_KEY_HOME;
712 case 'P':
713 return EDIT_KEY_F1;
714 case 'Q':
715 return EDIT_KEY_F2;
716 case 'R':
717 return EDIT_KEY_F3;
718 case 'S':
719 return EDIT_KEY_F4;
720 default:
721 return EDIT_KEY_NONE;
722 }
723 }
724
725
esc_seq_to_key(char * seq)726 static enum edit_key_code esc_seq_to_key(char *seq)
727 {
728 char last, *pos;
729 int param1 = -1, param2 = -1;
730 enum edit_key_code ret = EDIT_KEY_NONE;
731
732 last = '\0';
733 for (pos = seq; *pos; pos++)
734 last = *pos;
735
736 if (seq[1] >= '0' && seq[1] <= '9') {
737 param1 = atoi(&seq[1]);
738 pos = os_strchr(seq, ';');
739 if (pos)
740 param2 = atoi(pos + 1);
741 }
742
743 if (seq[0] == '[')
744 ret = esc_seq_to_key1(param1, param2, last);
745 else if (seq[0] == 'O')
746 ret = esc_seq_to_key2(param1, param2, last);
747
748 if (ret != EDIT_KEY_NONE)
749 return ret;
750
751 edit_clear_line();
752 printf("\rUnknown escape sequence '%s'\n", seq);
753 edit_redraw();
754 return EDIT_KEY_NONE;
755 }
756
757
edit_read_key(int sock)758 static enum edit_key_code edit_read_key(int sock)
759 {
760 int c;
761 unsigned char buf[1];
762 int res;
763 static int esc = -1;
764 static char esc_buf[7];
765
766 res = read(sock, buf, 1);
767 if (res < 0)
768 perror("read");
769 if (res <= 0)
770 return EDIT_KEY_EOF;
771
772 c = buf[0];
773
774 if (esc >= 0) {
775 if (c == 27 /* ESC */) {
776 esc = 0;
777 return EDIT_KEY_NONE;
778 }
779
780 if (esc == 6) {
781 show_esc_buf(esc_buf, c, 0);
782 esc = -1;
783 } else {
784 esc_buf[esc++] = c;
785 esc_buf[esc] = '\0';
786 }
787 }
788
789 if (esc == 1) {
790 if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
791 show_esc_buf(esc_buf, c, 1);
792 esc = -1;
793 return EDIT_KEY_NONE;
794 } else
795 return EDIT_KEY_NONE; /* Escape sequence continues */
796 }
797
798 if (esc > 1) {
799 if ((c >= '0' && c <= '9') || c == ';')
800 return EDIT_KEY_NONE; /* Escape sequence continues */
801
802 if (c == '~' || (c >= 'A' && c <= 'Z')) {
803 esc = -1;
804 return esc_seq_to_key(esc_buf);
805 }
806
807 show_esc_buf(esc_buf, c, 2);
808 esc = -1;
809 return EDIT_KEY_NONE;
810 }
811
812 switch (c) {
813 case 1:
814 return EDIT_KEY_CTRL_A;
815 case 2:
816 return EDIT_KEY_CTRL_B;
817 case 4:
818 return EDIT_KEY_CTRL_D;
819 case 5:
820 return EDIT_KEY_CTRL_E;
821 case 6:
822 return EDIT_KEY_CTRL_F;
823 case 7:
824 return EDIT_KEY_CTRL_G;
825 case 8:
826 return EDIT_KEY_CTRL_H;
827 case 9:
828 return EDIT_KEY_TAB;
829 case 10:
830 return EDIT_KEY_CTRL_J;
831 case 13: /* CR */
832 return EDIT_KEY_ENTER;
833 case 11:
834 return EDIT_KEY_CTRL_K;
835 case 12:
836 return EDIT_KEY_CTRL_L;
837 case 14:
838 return EDIT_KEY_CTRL_N;
839 case 15:
840 return EDIT_KEY_CTRL_O;
841 case 16:
842 return EDIT_KEY_CTRL_P;
843 case 18:
844 return EDIT_KEY_CTRL_R;
845 case 20:
846 return EDIT_KEY_CTRL_T;
847 case 21:
848 return EDIT_KEY_CTRL_U;
849 case 22:
850 return EDIT_KEY_CTRL_V;
851 case 23:
852 return EDIT_KEY_CTRL_W;
853 case 27: /* ESC */
854 esc = 0;
855 return EDIT_KEY_NONE;
856 case 127:
857 return EDIT_KEY_BACKSPACE;
858 default:
859 return c;
860 }
861 }
862
863
864 static char search_buf[21];
865 static int search_skip;
866
search_find(void)867 static char * search_find(void)
868 {
869 struct edit_history *h;
870 size_t len = os_strlen(search_buf);
871 int skip = search_skip;
872
873 if (len == 0)
874 return NULL;
875
876 dl_list_for_each(h, &history_list, struct edit_history, list) {
877 if (os_strstr(h->str, search_buf)) {
878 if (skip == 0)
879 return h->str;
880 skip--;
881 }
882 }
883
884 search_skip = 0;
885 return NULL;
886 }
887
888
search_redraw(void)889 static void search_redraw(void)
890 {
891 char *match = search_find();
892 printf("\rsearch '%s': %s" CLEAR_END_LINE,
893 search_buf, match ? match : "");
894 printf("\rsearch '%s", search_buf);
895 fflush(stdout);
896 }
897
898
search_start(void)899 static void search_start(void)
900 {
901 edit_clear_line();
902 search_buf[0] = '\0';
903 search_skip = 0;
904 search_redraw();
905 }
906
907
search_clear(void)908 static void search_clear(void)
909 {
910 search_redraw();
911 printf("\r" CLEAR_END_LINE);
912 }
913
914
search_stop(void)915 static void search_stop(void)
916 {
917 char *match = search_find();
918 search_buf[0] = '\0';
919 search_clear();
920 if (match) {
921 os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
922 cmdbuf_len = os_strlen(cmdbuf);
923 cmdbuf_pos = cmdbuf_len;
924 }
925 edit_redraw();
926 }
927
928
search_cancel(void)929 static void search_cancel(void)
930 {
931 search_buf[0] = '\0';
932 search_clear();
933 edit_redraw();
934 }
935
936
search_backspace(void)937 static void search_backspace(void)
938 {
939 size_t len;
940 len = os_strlen(search_buf);
941 if (len == 0)
942 return;
943 search_buf[len - 1] = '\0';
944 search_skip = 0;
945 search_redraw();
946 }
947
948
search_next(void)949 static void search_next(void)
950 {
951 search_skip++;
952 search_find();
953 search_redraw();
954 }
955
956
search_char(char c)957 static void search_char(char c)
958 {
959 size_t len;
960 len = os_strlen(search_buf);
961 if (len == sizeof(search_buf) - 1)
962 return;
963 search_buf[len] = c;
964 search_buf[len + 1] = '\0';
965 search_skip = 0;
966 search_redraw();
967 }
968
969
search_key(enum edit_key_code c)970 static enum edit_key_code search_key(enum edit_key_code c)
971 {
972 switch (c) {
973 case EDIT_KEY_ENTER:
974 case EDIT_KEY_CTRL_J:
975 case EDIT_KEY_LEFT:
976 case EDIT_KEY_RIGHT:
977 case EDIT_KEY_HOME:
978 case EDIT_KEY_END:
979 case EDIT_KEY_CTRL_A:
980 case EDIT_KEY_CTRL_E:
981 search_stop();
982 return c;
983 case EDIT_KEY_DOWN:
984 case EDIT_KEY_UP:
985 search_cancel();
986 return EDIT_KEY_EOF;
987 case EDIT_KEY_CTRL_H:
988 case EDIT_KEY_BACKSPACE:
989 search_backspace();
990 break;
991 case EDIT_KEY_CTRL_R:
992 search_next();
993 break;
994 default:
995 if (c >= 32 && c <= 255)
996 search_char(c);
997 break;
998 }
999
1000 return EDIT_KEY_NONE;
1001 }
1002
1003
edit_read_char(int sock,void * eloop_ctx,void * sock_ctx)1004 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
1005 {
1006 static int last_tab = 0;
1007 static int search = 0;
1008 enum edit_key_code c;
1009
1010 c = edit_read_key(sock);
1011
1012 if (search) {
1013 c = search_key(c);
1014 if (c == EDIT_KEY_NONE)
1015 return;
1016 search = 0;
1017 if (c == EDIT_KEY_EOF)
1018 return;
1019 }
1020
1021 if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
1022 last_tab = 0;
1023
1024 switch (c) {
1025 case EDIT_KEY_NONE:
1026 break;
1027 case EDIT_KEY_EOF:
1028 edit_eof_cb(edit_cb_ctx);
1029 break;
1030 case EDIT_KEY_TAB:
1031 complete(last_tab);
1032 last_tab = 1;
1033 break;
1034 case EDIT_KEY_UP:
1035 case EDIT_KEY_CTRL_P:
1036 history_prev();
1037 break;
1038 case EDIT_KEY_DOWN:
1039 case EDIT_KEY_CTRL_N:
1040 history_next();
1041 break;
1042 case EDIT_KEY_RIGHT:
1043 case EDIT_KEY_CTRL_F:
1044 move_right();
1045 break;
1046 case EDIT_KEY_LEFT:
1047 case EDIT_KEY_CTRL_B:
1048 move_left();
1049 break;
1050 case EDIT_KEY_CTRL_RIGHT:
1051 move_word_right();
1052 break;
1053 case EDIT_KEY_CTRL_LEFT:
1054 move_word_left();
1055 break;
1056 case EDIT_KEY_DELETE:
1057 delete_current();
1058 break;
1059 case EDIT_KEY_END:
1060 move_end();
1061 break;
1062 case EDIT_KEY_HOME:
1063 case EDIT_KEY_CTRL_A:
1064 move_start();
1065 break;
1066 case EDIT_KEY_F2:
1067 history_debug_dump();
1068 break;
1069 case EDIT_KEY_CTRL_D:
1070 if (cmdbuf_len > 0) {
1071 delete_current();
1072 return;
1073 }
1074 printf("\n");
1075 edit_eof_cb(edit_cb_ctx);
1076 break;
1077 case EDIT_KEY_CTRL_E:
1078 move_end();
1079 break;
1080 case EDIT_KEY_CTRL_H:
1081 case EDIT_KEY_BACKSPACE:
1082 delete_left();
1083 break;
1084 case EDIT_KEY_ENTER:
1085 case EDIT_KEY_CTRL_J:
1086 process_cmd();
1087 break;
1088 case EDIT_KEY_CTRL_K:
1089 clear_right();
1090 break;
1091 case EDIT_KEY_CTRL_L:
1092 edit_clear_line();
1093 edit_redraw();
1094 break;
1095 case EDIT_KEY_CTRL_R:
1096 search = 1;
1097 search_start();
1098 break;
1099 case EDIT_KEY_CTRL_U:
1100 clear_left();
1101 break;
1102 case EDIT_KEY_CTRL_W:
1103 delete_word();
1104 break;
1105 default:
1106 if (c >= 32 && c <= 255)
1107 insert_char(c);
1108 break;
1109 }
1110 }
1111
1112
edit_init(void (* cmd_cb)(void * ctx,char * cmd),void (* eof_cb)(void * ctx),char ** (* completion_cb)(void * ctx,const char * cmd,int pos),void * ctx,const char * history_file,const char * ps)1113 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
1114 void (*eof_cb)(void *ctx),
1115 char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
1116 void *ctx, const char *history_file, const char *ps)
1117 {
1118 currbuf[0] = '\0';
1119 dl_list_init(&history_list);
1120 history_curr = NULL;
1121 if (history_file)
1122 history_read(history_file);
1123
1124 edit_cb_ctx = ctx;
1125 edit_cmd_cb = cmd_cb;
1126 edit_eof_cb = eof_cb;
1127 edit_completion_cb = completion_cb;
1128
1129 tcgetattr(STDIN_FILENO, &prevt);
1130 newt = prevt;
1131 newt.c_lflag &= ~(ICANON | ECHO);
1132 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1133
1134 eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
1135
1136 ps2 = ps;
1137 printf("%s> ", ps2 ? ps2 : "");
1138 fflush(stdout);
1139
1140 return 0;
1141 }
1142
1143
edit_deinit(const char * history_file,int (* filter_cb)(void * ctx,const char * cmd))1144 void edit_deinit(const char *history_file,
1145 int (*filter_cb)(void *ctx, const char *cmd))
1146 {
1147 struct edit_history *h;
1148 if (history_file)
1149 history_write(history_file, filter_cb);
1150 while ((h = dl_list_first(&history_list, struct edit_history, list))) {
1151 dl_list_del(&h->list);
1152 os_free(h);
1153 }
1154 edit_clear_line();
1155 putchar('\r');
1156 fflush(stdout);
1157 eloop_unregister_read_sock(STDIN_FILENO);
1158 tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
1159 }
1160
1161
edit_redraw(void)1162 void edit_redraw(void)
1163 {
1164 char tmp;
1165 cmdbuf[cmdbuf_len] = '\0';
1166 printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
1167 if (cmdbuf_pos != cmdbuf_len) {
1168 tmp = cmdbuf[cmdbuf_pos];
1169 cmdbuf[cmdbuf_pos] = '\0';
1170 printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
1171 cmdbuf[cmdbuf_pos] = tmp;
1172 }
1173 fflush(stdout);
1174 }
1175