Disconnect hostapd from building in base
[dragonfly.git] / contrib / hostapd / src / utils / edit.c
CommitLineData
4781064b
JM
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
18static char cmdbuf[CMD_BUF_LEN];
19static int cmdbuf_pos = 0;
20static int cmdbuf_len = 0;
21static char currbuf[CMD_BUF_LEN];
22static int currbuf_valid = 0;
23static const char *ps2 = NULL;
24
25#define HISTORY_MAX 100
26
27struct edit_history {
28 struct dl_list list;
29 char str[1];
30};
31
32static struct dl_list history_list;
33static struct edit_history *history_curr;
34
35static void *edit_cb_ctx;
36static void (*edit_cmd_cb)(void *ctx, char *cmd);
37static void (*edit_eof_cb)(void *ctx);
38static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
39 NULL;
40
41static struct termios prevt, newt;
42
43
44#define CLEAR_END_LINE "\e[K"
45
46
47void 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
56static void move_start(void)
57{
58 cmdbuf_pos = 0;
59 edit_redraw();
60}
61
62
63static void move_end(void)
64{
65 cmdbuf_pos = cmdbuf_len;
66 edit_redraw();
67}
68
69
70static void move_left(void)
71{
72 if (cmdbuf_pos > 0) {
73 cmdbuf_pos--;
74 edit_redraw();
75 }
76}
77
78
79static void move_right(void)
80{
81 if (cmdbuf_pos < cmdbuf_len) {
82 cmdbuf_pos++;
83 edit_redraw();
84 }
85}
86
87
88static 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
98static 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
108static 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
122static 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
135static 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
152static 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
165static 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
176static 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
215static 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
224static 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
250static 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
271static 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
294static 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
314static 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
327static 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
346static 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
365static 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
376static 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
397static 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
406static 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
421static int cmp_str(const void *a, const void *b)
422{
423 return os_strcmp(* (const char **) a, * (const char **) b);
424}
425
426static 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
489enum 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
553static 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
561static 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
578static 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
595static 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
612static 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
629static 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
646static 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
700static 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
726static 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
758static 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
864static char search_buf[21];
865static int search_skip;
866
867static 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
889static 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
899static void search_start(void)
900{
901 edit_clear_line();
902 search_buf[0] = '\0';
903 search_skip = 0;
904 search_redraw();
905}
906
907
908static void search_clear(void)
909{
910 search_redraw();
911 printf("\r" CLEAR_END_LINE);
912}
913
914
915static 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
929static void search_cancel(void)
930{
931 search_buf[0] = '\0';
932 search_clear();
933 edit_redraw();
934}
935
936
937static 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
949static void search_next(void)
950{
951 search_skip++;
952 search_find();
953 search_redraw();
954}
955
956
957static 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
970static 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
1004static 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
1113int 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
1144void 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
1162void 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}