2 * Command line editing and history
3 * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
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 static const char *ps2 = NULL;
25 #define HISTORY_MAX 100
32 static struct dl_list history_list;
33 static struct edit_history *history_curr;
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) =
41 static struct termios prevt, newt;
44 #define CLEAR_END_LINE "\e[K"
47 void edit_clear_line(void)
51 for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
56 static void move_start(void)
63 static void move_end(void)
65 cmdbuf_pos = cmdbuf_len;
70 static void move_left(void)
79 static void move_right(void)
81 if (cmdbuf_pos < cmdbuf_len) {
88 static void move_word_left(void)
90 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
92 while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
98 static void move_word_right(void)
100 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
102 while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
108 static void delete_left(void)
114 os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
115 cmdbuf_len - cmdbuf_pos);
122 static void delete_current(void)
124 if (cmdbuf_pos == cmdbuf_len)
128 os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
129 cmdbuf_len - cmdbuf_pos);
135 static void delete_word(void)
141 while (pos > 0 && cmdbuf[pos - 1] == ' ')
143 while (pos > 0 && cmdbuf[pos - 1] != ' ')
145 os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
146 cmdbuf_len -= cmdbuf_pos - pos;
152 static void clear_left(void)
158 os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
159 cmdbuf_len -= cmdbuf_pos;
165 static void clear_right(void)
167 if (cmdbuf_pos == cmdbuf_len)
171 cmdbuf_len = cmdbuf_pos;
176 static void history_add(const char *str)
178 struct edit_history *h, *match = NULL, *last = NULL;
179 size_t len, count = 0;
184 dl_list_for_each(h, &history_list, struct edit_history, list) {
185 if (os_strcmp(str, h->str) == 0) {
194 dl_list_del(&h->list);
195 dl_list_add(&history_list, &h->list);
200 if (count >= HISTORY_MAX && last) {
201 dl_list_del(&last->list);
205 len = os_strlen(str);
206 h = os_zalloc(sizeof(*h) + len);
209 dl_list_add(&history_list, &h->list);
210 os_strlcpy(h->str, str, len + 1);
215 static void history_use(void)
218 cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
219 os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
224 static void history_prev(void)
226 if (history_curr == NULL)
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);
241 dl_list_last(&history_list, struct edit_history, list))
244 history_curr = dl_list_entry(history_curr->list.next,
245 struct edit_history, list);
250 static void history_next(void)
252 if (history_curr == NULL ||
254 dl_list_first(&history_list, struct edit_history, list)) {
258 cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
259 os_memcpy(cmdbuf, currbuf, cmdbuf_len);
265 history_curr = dl_list_entry(history_curr->list.prev,
266 struct edit_history, list);
271 static void history_read(const char *fname)
274 char buf[CMD_BUF_LEN], *pos;
276 f = fopen(fname, "r");
280 while (fgets(buf, CMD_BUF_LEN, f)) {
281 for (pos = buf; *pos; pos++) {
282 if (*pos == '\r' || *pos == '\n') {
294 static void history_write(const char *fname,
295 int (*filter_cb)(void *ctx, const char *cmd))
298 struct edit_history *h;
300 f = fopen(fname, "w");
304 dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
305 if (filter_cb && filter_cb(edit_cb_ctx, h->str))
307 fprintf(f, "%s\n", h->str);
314 static void history_debug_dump(void)
316 struct edit_history *h;
319 dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
320 printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
322 printf("{%s}\n", currbuf);
327 static void insert_char(int c)
329 if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
331 if (cmdbuf_len == cmdbuf_pos) {
332 cmdbuf[cmdbuf_pos++] = c;
337 os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
338 cmdbuf_len - cmdbuf_pos);
339 cmdbuf[cmdbuf_pos++] = c;
346 static void process_cmd(void)
349 if (cmdbuf_len == 0) {
350 printf("\n%s> ", ps2 ? ps2 : "");
355 cmdbuf[cmdbuf_len] = '\0';
359 edit_cmd_cb(edit_cb_ctx, cmdbuf);
360 printf("%s> ", ps2 ? ps2 : "");
365 static void free_completions(char **c)
370 for (i = 0; c[i]; i++)
376 static int filter_strings(char **c, char *str, size_t len)
380 for (i = 0, j = 0; c[j]; j++) {
381 if (os_strncasecmp(c[j], str, len) == 0) {
397 static int common_len(const char *a, const char *b)
400 while (a[len] && a[len] == b[len])
406 static int max_common_length(char **c)
410 len = os_strlen(c[0]);
411 for (i = 1; c[i]; i++) {
412 int same = common_len(c[0], c[i]);
421 static int cmp_str(const void *a, const void *b)
423 return os_strcmp(* (const char **) a, * (const char **) b);
426 static void complete(int list)
431 int room, plen, add_space;
433 if (edit_completion_cb == NULL)
436 cmdbuf[cmdbuf_len] = '\0';
437 c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
443 while (start > 0 && cmdbuf[start - 1] != ' ')
447 count = filter_strings(c, &cmdbuf[start], plen);
453 len = max_common_length(c);
454 if (len <= plen && count > 1) {
456 qsort(c, count, sizeof(char *), cmp_str);
459 for (i = 0; c[i]; i++)
460 printf("%s%s", i > 0 ? " " : "", c[i]);
469 room = sizeof(cmdbuf) - 1 - cmdbuf_len;
472 add_space = count == 1 && len < room;
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);
478 cmdbuf[cmdbuf_pos + len] = ' ';
480 cmdbuf_pos += len + add_space;
481 cmdbuf_len += len + add_space;
544 EDIT_KEY_SHIFT_RIGHT,
546 EDIT_KEY_ALT_SHIFT_UP,
547 EDIT_KEY_ALT_SHIFT_DOWN,
548 EDIT_KEY_ALT_SHIFT_RIGHT,
549 EDIT_KEY_ALT_SHIFT_LEFT,
553 static void show_esc_buf(const char *esc_buf, char c, int i)
556 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
561 static enum edit_key_code esc_seq_to_key1_no(char last)
567 return EDIT_KEY_DOWN;
569 return EDIT_KEY_RIGHT;
571 return EDIT_KEY_LEFT;
573 return EDIT_KEY_NONE;
578 static enum edit_key_code esc_seq_to_key1_shift(char last)
582 return EDIT_KEY_SHIFT_UP;
584 return EDIT_KEY_SHIFT_DOWN;
586 return EDIT_KEY_SHIFT_RIGHT;
588 return EDIT_KEY_SHIFT_LEFT;
590 return EDIT_KEY_NONE;
595 static enum edit_key_code esc_seq_to_key1_alt(char last)
599 return EDIT_KEY_ALT_UP;
601 return EDIT_KEY_ALT_DOWN;
603 return EDIT_KEY_ALT_RIGHT;
605 return EDIT_KEY_ALT_LEFT;
607 return EDIT_KEY_NONE;
612 static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
616 return EDIT_KEY_ALT_SHIFT_UP;
618 return EDIT_KEY_ALT_SHIFT_DOWN;
620 return EDIT_KEY_ALT_SHIFT_RIGHT;
622 return EDIT_KEY_ALT_SHIFT_LEFT;
624 return EDIT_KEY_NONE;
629 static enum edit_key_code esc_seq_to_key1_ctrl(char last)
633 return EDIT_KEY_CTRL_UP;
635 return EDIT_KEY_CTRL_DOWN;
637 return EDIT_KEY_CTRL_RIGHT;
639 return EDIT_KEY_CTRL_LEFT;
641 return EDIT_KEY_NONE;
646 static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
648 /* ESC-[<param1>;<param2><last> */
650 if (param1 < 0 && param2 < 0)
651 return esc_seq_to_key1_no(last);
653 if (param1 == 1 && param2 == 2)
654 return esc_seq_to_key1_shift(last);
656 if (param1 == 1 && param2 == 3)
657 return esc_seq_to_key1_alt(last);
659 if (param1 == 1 && param2 == 4)
660 return esc_seq_to_key1_alt_shift(last);
662 if (param1 == 1 && param2 == 5)
663 return esc_seq_to_key1_ctrl(last);
667 return EDIT_KEY_NONE;
670 return EDIT_KEY_INSERT;
672 return EDIT_KEY_DELETE;
674 return EDIT_KEY_PAGE_UP;
676 return EDIT_KEY_PAGE_DOWN;
696 return EDIT_KEY_NONE;
700 static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
702 /* ESC-O<param1>;<param2><last> */
704 if (param1 >= 0 || param2 >= 0)
705 return EDIT_KEY_NONE;
711 return EDIT_KEY_HOME;
721 return EDIT_KEY_NONE;
726 static enum edit_key_code esc_seq_to_key(char *seq)
729 int param1 = -1, param2 = -1;
730 enum edit_key_code ret = EDIT_KEY_NONE;
733 for (pos = seq; *pos; pos++)
736 if (seq[1] >= '0' && seq[1] <= '9') {
737 param1 = atoi(&seq[1]);
738 pos = os_strchr(seq, ';');
740 param2 = atoi(pos + 1);
744 ret = esc_seq_to_key1(param1, param2, last);
745 else if (seq[0] == 'O')
746 ret = esc_seq_to_key2(param1, param2, last);
748 if (ret != EDIT_KEY_NONE)
752 printf("\rUnknown escape sequence '%s'\n", seq);
754 return EDIT_KEY_NONE;
758 static enum edit_key_code edit_read_key(int sock)
761 unsigned char buf[1];
764 static char esc_buf[7];
766 res = read(sock, buf, 1);
775 if (c == 27 /* ESC */) {
777 return EDIT_KEY_NONE;
781 show_esc_buf(esc_buf, c, 0);
790 if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
791 show_esc_buf(esc_buf, c, 1);
793 return EDIT_KEY_NONE;
795 return EDIT_KEY_NONE; /* Escape sequence continues */
799 if ((c >= '0' && c <= '9') || c == ';')
800 return EDIT_KEY_NONE; /* Escape sequence continues */
802 if (c == '~' || (c >= 'A' && c <= 'Z')) {
804 return esc_seq_to_key(esc_buf);
807 show_esc_buf(esc_buf, c, 2);
809 return EDIT_KEY_NONE;
814 return EDIT_KEY_CTRL_A;
816 return EDIT_KEY_CTRL_B;
818 return EDIT_KEY_CTRL_D;
820 return EDIT_KEY_CTRL_E;
822 return EDIT_KEY_CTRL_F;
824 return EDIT_KEY_CTRL_G;
826 return EDIT_KEY_CTRL_H;
830 return EDIT_KEY_CTRL_J;
832 return EDIT_KEY_ENTER;
834 return EDIT_KEY_CTRL_K;
836 return EDIT_KEY_CTRL_L;
838 return EDIT_KEY_CTRL_N;
840 return EDIT_KEY_CTRL_O;
842 return EDIT_KEY_CTRL_P;
844 return EDIT_KEY_CTRL_R;
846 return EDIT_KEY_CTRL_T;
848 return EDIT_KEY_CTRL_U;
850 return EDIT_KEY_CTRL_V;
852 return EDIT_KEY_CTRL_W;
855 return EDIT_KEY_NONE;
857 return EDIT_KEY_BACKSPACE;
864 static char search_buf[21];
865 static int search_skip;
867 static char * search_find(void)
869 struct edit_history *h;
870 size_t len = os_strlen(search_buf);
871 int skip = search_skip;
876 dl_list_for_each(h, &history_list, struct edit_history, list) {
877 if (os_strstr(h->str, search_buf)) {
889 static void search_redraw(void)
891 char *match = search_find();
892 printf("\rsearch '%s': %s" CLEAR_END_LINE,
893 search_buf, match ? match : "");
894 printf("\rsearch '%s", search_buf);
899 static void search_start(void)
902 search_buf[0] = '\0';
908 static void search_clear(void)
911 printf("\r" CLEAR_END_LINE);
915 static void search_stop(void)
917 char *match = search_find();
918 search_buf[0] = '\0';
921 os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
922 cmdbuf_len = os_strlen(cmdbuf);
923 cmdbuf_pos = cmdbuf_len;
929 static void search_cancel(void)
931 search_buf[0] = '\0';
937 static void search_backspace(void)
940 len = os_strlen(search_buf);
943 search_buf[len - 1] = '\0';
949 static void search_next(void)
957 static void search_char(char c)
960 len = os_strlen(search_buf);
961 if (len == sizeof(search_buf) - 1)
964 search_buf[len + 1] = '\0';
970 static enum edit_key_code search_key(enum edit_key_code c)
974 case EDIT_KEY_CTRL_J:
979 case EDIT_KEY_CTRL_A:
980 case EDIT_KEY_CTRL_E:
987 case EDIT_KEY_CTRL_H:
988 case EDIT_KEY_BACKSPACE:
991 case EDIT_KEY_CTRL_R:
995 if (c >= 32 && c <= 255)
1000 return EDIT_KEY_NONE;
1004 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
1006 static int last_tab = 0;
1007 static int search = 0;
1008 enum edit_key_code c;
1010 c = edit_read_key(sock);
1014 if (c == EDIT_KEY_NONE)
1017 if (c == EDIT_KEY_EOF)
1021 if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
1028 edit_eof_cb(edit_cb_ctx);
1035 case EDIT_KEY_CTRL_P:
1039 case EDIT_KEY_CTRL_N:
1042 case EDIT_KEY_RIGHT:
1043 case EDIT_KEY_CTRL_F:
1047 case EDIT_KEY_CTRL_B:
1050 case EDIT_KEY_CTRL_RIGHT:
1053 case EDIT_KEY_CTRL_LEFT:
1056 case EDIT_KEY_DELETE:
1063 case EDIT_KEY_CTRL_A:
1067 history_debug_dump();
1069 case EDIT_KEY_CTRL_D:
1070 if (cmdbuf_len > 0) {
1075 edit_eof_cb(edit_cb_ctx);
1077 case EDIT_KEY_CTRL_E:
1080 case EDIT_KEY_CTRL_H:
1081 case EDIT_KEY_BACKSPACE:
1084 case EDIT_KEY_ENTER:
1085 case EDIT_KEY_CTRL_J:
1088 case EDIT_KEY_CTRL_K:
1091 case EDIT_KEY_CTRL_L:
1095 case EDIT_KEY_CTRL_R:
1099 case EDIT_KEY_CTRL_U:
1102 case EDIT_KEY_CTRL_W:
1106 if (c >= 32 && c <= 255)
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)
1119 dl_list_init(&history_list);
1120 history_curr = NULL;
1122 history_read(history_file);
1125 edit_cmd_cb = cmd_cb;
1126 edit_eof_cb = eof_cb;
1127 edit_completion_cb = completion_cb;
1129 tcgetattr(STDIN_FILENO, &prevt);
1131 newt.c_lflag &= ~(ICANON | ECHO);
1132 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1134 eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
1137 printf("%s> ", ps2 ? ps2 : "");
1144 void edit_deinit(const char *history_file,
1145 int (*filter_cb)(void *ctx, const char *cmd))
1147 struct edit_history *h;
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);
1157 eloop_unregister_read_sock(STDIN_FILENO);
1158 tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
1162 void edit_redraw(void)
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;