2 * Command line editing and history
3 * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
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 static char currbuf[CMD_BUF_LEN];
28 static int currbuf_valid = 0;
30 #define HISTORY_MAX 100
37 static struct dl_list history_list;
38 static struct edit_history *history_curr;
40 static void *edit_cb_ctx;
41 static void (*edit_cmd_cb)(void *ctx, char *cmd);
42 static void (*edit_eof_cb)(void *ctx);
43 static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
46 static struct termios prevt, newt;
49 #define CLEAR_END_LINE "\e[K"
52 void edit_clear_line(void)
56 for (i = 0; i < cmdbuf_len + 2; i++)
61 static void move_start(void)
68 static void move_end(void)
70 cmdbuf_pos = cmdbuf_len;
75 static void move_left(void)
84 static void move_right(void)
86 if (cmdbuf_pos < cmdbuf_len) {
93 static void move_word_left(void)
95 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
97 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
103 static void move_word_right(void)
105 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
107 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
113 static void delete_left(void)
119 os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
120 cmdbuf_len - cmdbuf_pos);
127 static void delete_current(void)
129 if (cmdbuf_pos == cmdbuf_len)
133 os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
134 cmdbuf_len - cmdbuf_pos);
140 static void delete_word(void)
146 while (pos > 0 && cmdbuf[pos - 1] == ' ')
148 while (pos > 0 && cmdbuf[pos - 1] != ' ')
150 os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
151 cmdbuf_len -= cmdbuf_pos - pos;
157 static void clear_left(void)
163 os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
164 cmdbuf_len -= cmdbuf_pos;
170 static void clear_right(void)
172 if (cmdbuf_pos == cmdbuf_len)
176 cmdbuf_len = cmdbuf_pos;
181 static void history_add(const char *str)
183 struct edit_history *h, *match = NULL, *last = NULL;
184 size_t len, count = 0;
189 dl_list_for_each(h, &history_list, struct edit_history, list) {
190 if (os_strcmp(str, h->str) == 0) {
199 dl_list_del(&h->list);
200 dl_list_add(&history_list, &h->list);
205 if (count >= HISTORY_MAX && last) {
206 dl_list_del(&last->list);
210 len = os_strlen(str);
211 h = os_zalloc(sizeof(*h) + len);
214 dl_list_add(&history_list, &h->list);
215 os_strlcpy(h->str, str, len + 1);
220 static void history_use(void)
223 cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
224 os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
229 static void history_prev(void)
231 if (history_curr == NULL)
235 dl_list_first(&history_list, struct edit_history, list)) {
236 if (!currbuf_valid) {
237 cmdbuf[cmdbuf_len] = '\0';
238 os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
246 dl_list_last(&history_list, struct edit_history, list))
249 history_curr = dl_list_entry(history_curr->list.next,
250 struct edit_history, list);
255 static void history_next(void)
257 if (history_curr == NULL ||
259 dl_list_first(&history_list, struct edit_history, list)) {
263 cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
264 os_memcpy(cmdbuf, currbuf, cmdbuf_len);
270 history_curr = dl_list_entry(history_curr->list.prev,
271 struct edit_history, list);
276 static void history_read(const char *fname)
279 char buf[CMD_BUF_LEN], *pos;
281 f = fopen(fname, "r");
285 while (fgets(buf, CMD_BUF_LEN, f)) {
286 for (pos = buf; *pos; pos++) {
287 if (*pos == '\r' || *pos == '\n') {
299 static void history_write(const char *fname,
300 int (*filter_cb)(void *ctx, const char *cmd))
303 struct edit_history *h;
305 f = fopen(fname, "w");
309 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
310 if (filter_cb && filter_cb(edit_cb_ctx, h->str))
312 fprintf(f, "%s\n", h->str);
319 static void history_debug_dump(void)
321 struct edit_history *h;
324 dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
325 printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
327 printf("{%s}\n", currbuf);
332 static void insert_char(int c)
334 if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
336 if (cmdbuf_len == cmdbuf_pos) {
337 cmdbuf[cmdbuf_pos++] = c;
342 os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
343 cmdbuf_len - cmdbuf_pos);
344 cmdbuf[cmdbuf_pos++] = c;
351 static void process_cmd(void)
354 if (cmdbuf_len == 0) {
360 cmdbuf[cmdbuf_len] = '\0';
364 edit_cmd_cb(edit_cb_ctx, cmdbuf);
370 static void free_completions(char **c)
375 for (i = 0; c[i]; i++)
381 static int filter_strings(char **c, char *str, size_t len)
385 for (i = 0, j = 0; c[j]; j++) {
386 if (os_strncasecmp(c[j], str, len) == 0) {
402 static int common_len(const char *a, const char *b)
405 while (a[len] && a[len] == b[len])
411 static int max_common_length(char **c)
415 len = os_strlen(c[0]);
416 for (i = 1; c[i]; i++) {
417 int same = common_len(c[0], c[i]);
426 static int cmp_str(const void *a, const void *b)
428 return os_strcmp(* (const char **) a, * (const char **) b);
431 static void complete(int list)
436 int room, plen, add_space;
438 if (edit_completion_cb == NULL)
441 cmdbuf[cmdbuf_len] = '\0';
442 c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
448 while (start > 0 && cmdbuf[start - 1] != ' ')
452 count = filter_strings(c, &cmdbuf[start], plen);
458 len = max_common_length(c);
459 if (len <= plen && count > 1) {
461 qsort(c, count, sizeof(char *), cmp_str);
464 for (i = 0; c[i]; i++)
465 printf("%s%s", i > 0 ? " " : "", c[i]);
474 room = sizeof(cmdbuf) - 1 - cmdbuf_len;
477 add_space = count == 1 && len < room;
479 os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
480 cmdbuf_len - cmdbuf_pos);
481 os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
483 cmdbuf[cmdbuf_pos + len] = ' ';
485 cmdbuf_pos += len + add_space;
486 cmdbuf_len += len + add_space;
549 EDIT_KEY_SHIFT_RIGHT,
551 EDIT_KEY_ALT_SHIFT_UP,
552 EDIT_KEY_ALT_SHIFT_DOWN,
553 EDIT_KEY_ALT_SHIFT_RIGHT,
554 EDIT_KEY_ALT_SHIFT_LEFT,
558 static void show_esc_buf(const char *esc_buf, char c, int i)
561 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
566 static enum edit_key_code esc_seq_to_key1_no(char last)
572 return EDIT_KEY_DOWN;
574 return EDIT_KEY_RIGHT;
576 return EDIT_KEY_LEFT;
578 return EDIT_KEY_NONE;
583 static enum edit_key_code esc_seq_to_key1_shift(char last)
587 return EDIT_KEY_SHIFT_UP;
589 return EDIT_KEY_SHIFT_DOWN;
591 return EDIT_KEY_SHIFT_RIGHT;
593 return EDIT_KEY_SHIFT_LEFT;
595 return EDIT_KEY_NONE;
600 static enum edit_key_code esc_seq_to_key1_alt(char last)
604 return EDIT_KEY_ALT_UP;
606 return EDIT_KEY_ALT_DOWN;
608 return EDIT_KEY_ALT_RIGHT;
610 return EDIT_KEY_ALT_LEFT;
612 return EDIT_KEY_NONE;
617 static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
621 return EDIT_KEY_ALT_SHIFT_UP;
623 return EDIT_KEY_ALT_SHIFT_DOWN;
625 return EDIT_KEY_ALT_SHIFT_RIGHT;
627 return EDIT_KEY_ALT_SHIFT_LEFT;
629 return EDIT_KEY_NONE;
634 static enum edit_key_code esc_seq_to_key1_ctrl(char last)
638 return EDIT_KEY_CTRL_UP;
640 return EDIT_KEY_CTRL_DOWN;
642 return EDIT_KEY_CTRL_RIGHT;
644 return EDIT_KEY_CTRL_LEFT;
646 return EDIT_KEY_NONE;
651 static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
653 /* ESC-[<param1>;<param2><last> */
655 if (param1 < 0 && param2 < 0)
656 return esc_seq_to_key1_no(last);
658 if (param1 == 1 && param2 == 2)
659 return esc_seq_to_key1_shift(last);
661 if (param1 == 1 && param2 == 3)
662 return esc_seq_to_key1_alt(last);
664 if (param1 == 1 && param2 == 4)
665 return esc_seq_to_key1_alt_shift(last);
667 if (param1 == 1 && param2 == 5)
668 return esc_seq_to_key1_ctrl(last);
672 return EDIT_KEY_NONE;
675 return EDIT_KEY_INSERT;
677 return EDIT_KEY_DELETE;
679 return EDIT_KEY_PAGE_UP;
681 return EDIT_KEY_PAGE_DOWN;
701 return EDIT_KEY_NONE;
705 static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
707 /* ESC-O<param1>;<param2><last> */
709 if (param1 >= 0 || param2 >= 0)
710 return EDIT_KEY_NONE;
716 return EDIT_KEY_HOME;
726 return EDIT_KEY_NONE;
731 static enum edit_key_code esc_seq_to_key(char *seq)
734 int param1 = -1, param2 = -1;
735 enum edit_key_code ret = EDIT_KEY_NONE;
738 for (pos = seq; *pos; pos++)
741 if (seq[1] >= '0' && seq[1] <= '9') {
742 param1 = atoi(&seq[1]);
743 pos = os_strchr(seq, ';');
745 param2 = atoi(pos + 1);
749 ret = esc_seq_to_key1(param1, param2, last);
750 else if (seq[0] == 'O')
751 ret = esc_seq_to_key2(param1, param2, last);
753 if (ret != EDIT_KEY_NONE)
757 printf("\rUnknown escape sequence '%s'\n", seq);
759 return EDIT_KEY_NONE;
763 static enum edit_key_code edit_read_key(int sock)
766 unsigned char buf[1];
769 static char esc_buf[7];
771 res = read(sock, buf, 1);
780 if (c == 27 /* ESC */) {
782 return EDIT_KEY_NONE;
786 show_esc_buf(esc_buf, c, 0);
795 if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
796 show_esc_buf(esc_buf, c, 1);
798 return EDIT_KEY_NONE;
800 return EDIT_KEY_NONE; /* Escape sequence continues */
804 if ((c >= '0' && c <= '9') || c == ';')
805 return EDIT_KEY_NONE; /* Escape sequence continues */
807 if (c == '~' || (c >= 'A' && c <= 'Z')) {
809 return esc_seq_to_key(esc_buf);
812 show_esc_buf(esc_buf, c, 2);
814 return EDIT_KEY_NONE;
819 return EDIT_KEY_CTRL_A;
821 return EDIT_KEY_CTRL_B;
823 return EDIT_KEY_CTRL_D;
825 return EDIT_KEY_CTRL_E;
827 return EDIT_KEY_CTRL_F;
829 return EDIT_KEY_CTRL_G;
831 return EDIT_KEY_CTRL_H;
835 return EDIT_KEY_CTRL_J;
837 return EDIT_KEY_ENTER;
839 return EDIT_KEY_CTRL_K;
841 return EDIT_KEY_CTRL_L;
843 return EDIT_KEY_CTRL_N;
845 return EDIT_KEY_CTRL_O;
847 return EDIT_KEY_CTRL_P;
849 return EDIT_KEY_CTRL_R;
851 return EDIT_KEY_CTRL_T;
853 return EDIT_KEY_CTRL_U;
855 return EDIT_KEY_CTRL_V;
857 return EDIT_KEY_CTRL_W;
860 return EDIT_KEY_NONE;
862 return EDIT_KEY_BACKSPACE;
869 static char search_buf[21];
870 static int search_skip;
872 static char * search_find(void)
874 struct edit_history *h;
875 size_t len = os_strlen(search_buf);
876 int skip = search_skip;
881 dl_list_for_each(h, &history_list, struct edit_history, list) {
882 if (os_strstr(h->str, search_buf)) {
894 static void search_redraw(void)
896 char *match = search_find();
897 printf("\rsearch '%s': %s" CLEAR_END_LINE,
898 search_buf, match ? match : "");
899 printf("\rsearch '%s", search_buf);
904 static void search_start(void)
907 search_buf[0] = '\0';
913 static void search_clear(void)
916 printf("\r" CLEAR_END_LINE);
920 static void search_stop(void)
922 char *match = search_find();
923 search_buf[0] = '\0';
926 os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
927 cmdbuf_len = os_strlen(cmdbuf);
928 cmdbuf_pos = cmdbuf_len;
934 static void search_cancel(void)
936 search_buf[0] = '\0';
942 static void search_backspace(void)
945 len = os_strlen(search_buf);
948 search_buf[len - 1] = '\0';
954 static void search_next(void)
962 static void search_char(char c)
965 len = os_strlen(search_buf);
966 if (len == sizeof(search_buf) - 1)
969 search_buf[len + 1] = '\0';
975 static enum edit_key_code search_key(enum edit_key_code c)
979 case EDIT_KEY_CTRL_J:
984 case EDIT_KEY_CTRL_A:
985 case EDIT_KEY_CTRL_E:
992 case EDIT_KEY_CTRL_H:
993 case EDIT_KEY_BACKSPACE:
996 case EDIT_KEY_CTRL_R:
1000 if (c >= 32 && c <= 255)
1005 return EDIT_KEY_NONE;
1009 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
1011 static int last_tab = 0;
1012 static int search = 0;
1013 enum edit_key_code c;
1015 c = edit_read_key(sock);
1019 if (c == EDIT_KEY_NONE)
1022 if (c == EDIT_KEY_EOF)
1026 if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
1033 edit_eof_cb(edit_cb_ctx);
1040 case EDIT_KEY_CTRL_P:
1044 case EDIT_KEY_CTRL_N:
1047 case EDIT_KEY_RIGHT:
1048 case EDIT_KEY_CTRL_F:
1052 case EDIT_KEY_CTRL_B:
1055 case EDIT_KEY_CTRL_RIGHT:
1058 case EDIT_KEY_CTRL_LEFT:
1061 case EDIT_KEY_DELETE:
1068 case EDIT_KEY_CTRL_A:
1072 history_debug_dump();
1074 case EDIT_KEY_CTRL_D:
1075 if (cmdbuf_len > 0) {
1080 edit_eof_cb(edit_cb_ctx);
1082 case EDIT_KEY_CTRL_E:
1085 case EDIT_KEY_CTRL_H:
1086 case EDIT_KEY_BACKSPACE:
1089 case EDIT_KEY_ENTER:
1090 case EDIT_KEY_CTRL_J:
1093 case EDIT_KEY_CTRL_K:
1096 case EDIT_KEY_CTRL_L:
1100 case EDIT_KEY_CTRL_R:
1104 case EDIT_KEY_CTRL_U:
1107 case EDIT_KEY_CTRL_W:
1111 if (c >= 32 && c <= 255)
1118 int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
1119 void (*eof_cb)(void *ctx),
1120 char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
1121 void *ctx, const char *history_file)
1124 dl_list_init(&history_list);
1125 history_curr = NULL;
1127 history_read(history_file);
1130 edit_cmd_cb = cmd_cb;
1131 edit_eof_cb = eof_cb;
1132 edit_completion_cb = completion_cb;
1134 tcgetattr(STDIN_FILENO, &prevt);
1136 newt.c_lflag &= ~(ICANON | ECHO);
1137 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1139 eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
1148 void edit_deinit(const char *history_file,
1149 int (*filter_cb)(void *ctx, const char *cmd))
1151 struct edit_history *h;
1153 history_write(history_file, filter_cb);
1154 while ((h = dl_list_first(&history_list, struct edit_history, list))) {
1155 dl_list_del(&h->list);
1161 eloop_unregister_read_sock(STDIN_FILENO);
1162 tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
1166 void edit_redraw(void)
1169 cmdbuf[cmdbuf_len] = '\0';
1170 printf("\r> %s", cmdbuf);
1171 if (cmdbuf_pos != cmdbuf_len) {
1172 tmp = cmdbuf[cmdbuf_pos];
1173 cmdbuf[cmdbuf_pos] = '\0';
1174 printf("\r> %s", cmdbuf);
1175 cmdbuf[cmdbuf_pos] = tmp;