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