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