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