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