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