wpa_supplicant vendor branch: Update version 0.6.10 => 2.1
[dragonfly.git] / contrib / wpa_supplicant / src / utils / edit.c
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 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;
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
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
56 static void move_start(void)
57 {
58         cmdbuf_pos = 0;
59         edit_redraw();
60 }
61
62
63 static void move_end(void)
64 {
65         cmdbuf_pos = cmdbuf_len;
66         edit_redraw();
67 }
68
69
70 static void move_left(void)
71 {
72         if (cmdbuf_pos > 0) {
73                 cmdbuf_pos--;
74                 edit_redraw();
75         }
76 }
77
78
79 static void move_right(void)
80 {
81         if (cmdbuf_pos < cmdbuf_len) {
82                 cmdbuf_pos++;
83                 edit_redraw();
84         }
85 }
86
87
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
421 static int cmp_str(const void *a, const void *b)
422 {
423         return os_strcmp(* (const char **) a, * (const char **) b);
424 }
425
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
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
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
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
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
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
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
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
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
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
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
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
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
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
908 static void search_clear(void)
909 {
910         search_redraw();
911         printf("\r" CLEAR_END_LINE);
912 }
913
914
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
929 static void search_cancel(void)
930 {
931         search_buf[0] = '\0';
932         search_clear();
933         edit_redraw();
934 }
935
936
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
949 static void search_next(void)
950 {
951         search_skip++;
952         search_find();
953         search_redraw();
954 }
955
956
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
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
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
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
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
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 }