• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define HISTORY_MAX 100
29 
30 struct edit_history {
31 	struct dl_list list;
32 	char str[1];
33 };
34 
35 static struct dl_list history_list;
36 static struct edit_history *history_curr;
37 
38 static void *edit_cb_ctx;
39 static void (*edit_cmd_cb)(void *ctx, char *cmd);
40 static void (*edit_eof_cb)(void *ctx);
41 static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
42 	NULL;
43 
44 static struct termios prevt, newt;
45 
46 
47 #define CLEAR_END_LINE "\e[K"
48 
49 
edit_clear_line(void)50 void 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 
move_start(void)59 static void move_start(void)
60 {
61 	cmdbuf_pos = 0;
62 	edit_redraw();
63 }
64 
65 
move_end(void)66 static void move_end(void)
67 {
68 	cmdbuf_pos = cmdbuf_len;
69 	edit_redraw();
70 }
71 
72 
move_left(void)73 static void move_left(void)
74 {
75 	if (cmdbuf_pos > 0) {
76 		cmdbuf_pos--;
77 		edit_redraw();
78 	}
79 }
80 
81 
move_right(void)82 static void move_right(void)
83 {
84 	if (cmdbuf_pos < cmdbuf_len) {
85 		cmdbuf_pos++;
86 		edit_redraw();
87 	}
88 }
89 
90 
move_word_left(void)91 static 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 
move_word_right(void)101 static 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 
delete_left(void)111 static 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 
delete_current(void)125 static 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 
delete_word(void)138 static 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 
clear_left(void)155 static 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 
clear_right(void)168 static 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 
history_add(const char * str)179 static 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 
history_use(void)218 static 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 
history_prev(void)227 static 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 
history_next(void)249 static void history_next(void)
250 {
251 	if (history_curr == NULL ||
252 	    history_curr ==
253 	    dl_list_first(&history_list, struct edit_history, list))
254 		return;
255 
256 	history_curr = dl_list_entry(history_curr->list.prev,
257 				     struct edit_history, list);
258 	history_use();
259 }
260 
261 
history_read(const char * fname)262 static 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 
history_write(const char * fname,int (* filter_cb)(void * ctx,const char * cmd))285 static 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 
history_debug_dump(void)305 static 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 
insert_char(int c)316 static 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 
process_cmd(void)335 static 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 
free_completions(char ** c)354 static 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 
filter_strings(char ** c,char * str,size_t len)365 static 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 
common_len(const char * a,const char * b)386 static 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 
max_common_length(char ** c)395 static 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 
cmp_str(const void * a,const void * b)410 static int cmp_str(const void *a, const void *b)
411 {
412 	return os_strcmp(* (const char **) a, * (const char **) b);
413 }
414 
complete(int list)415 static 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 
478 enum 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 
show_esc_buf(const char * esc_buf,char c,int i)542 static 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 
esc_seq_to_key1_no(char last)550 static 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 
esc_seq_to_key1_shift(char last)567 static 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 
esc_seq_to_key1_alt(char last)584 static 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 
esc_seq_to_key1_alt_shift(char last)601 static 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 
esc_seq_to_key1_ctrl(char last)618 static 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 
esc_seq_to_key1(int param1,int param2,char last)635 static 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 
esc_seq_to_key2(int param1,int param2,char last)689 static 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 
esc_seq_to_key(char * seq)715 static 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 
edit_read_key(int sock)747 static 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 
853 static char search_buf[21];
854 static int search_skip;
855 
search_find(void)856 static 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 
search_redraw(void)878 static 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 
search_start(void)888 static void search_start(void)
889 {
890 	edit_clear_line();
891 	search_buf[0] = '\0';
892 	search_skip = 0;
893 	search_redraw();
894 }
895 
896 
search_clear(void)897 static void search_clear(void)
898 {
899 	search_redraw();
900 	printf("\r" CLEAR_END_LINE);
901 }
902 
903 
search_stop(void)904 static 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 
search_cancel(void)918 static void search_cancel(void)
919 {
920 	search_buf[0] = '\0';
921 	search_clear();
922 	edit_redraw();
923 }
924 
925 
search_backspace(void)926 static 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 
search_next(void)938 static void search_next(void)
939 {
940 	search_skip++;
941 	search_find();
942 	search_redraw();
943 }
944 
945 
search_char(char c)946 static 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 
search_key(enum edit_key_code c)959 static 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 
edit_read_char(int sock,void * eloop_ctx,void * sock_ctx)993 static 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 
edit_init(void (* cmd_cb)(void * ctx,char * cmd),void (* eof_cb)(void * ctx),char ** (* completion_cb)(void * ctx,const char * cmd,int pos),void * ctx,const char * history_file)1102 int 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 
edit_deinit(const char * history_file,int (* filter_cb)(void * ctx,const char * cmd))1131 void 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 
edit_redraw(void)1149 void 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 }
1162