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