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