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