]>
git.ipfire.org Git - thirdparty/hostap.git/blob - src/utils/edit.c
2 * Command line editing and history
3 * Copyright (c) 2010, 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;
33 static struct dl_list history_list
;
34 static struct edit_history
*history_curr
;
36 static void *edit_cb_ctx
;
37 static void (*edit_cmd_cb
)(void *ctx
, char *cmd
);
38 static void (*edit_eof_cb
)(void *ctx
);
39 static char ** (*edit_completion_cb
)(void *ctx
, const char *cmd
, int pos
) =
42 static struct termios prevt
, newt
;
45 #define CLEAR_END_LINE "\e[K"
48 void edit_clear_line(void)
52 for (i
= 0; i
< cmdbuf_len
+ 2; i
++)
57 static void move_start(void)
64 static void move_end(void)
66 cmdbuf_pos
= cmdbuf_len
;
71 static void move_left(void)
80 static void move_right(void)
82 if (cmdbuf_pos
< cmdbuf_len
) {
89 static void move_word_left(void)
91 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] == ' ')
93 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] != ' ')
99 static void move_word_right(void)
101 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] == ' ')
103 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] != ' ')
109 static void delete_left(void)
115 os_memmove(cmdbuf
+ cmdbuf_pos
- 1, cmdbuf
+ cmdbuf_pos
,
116 cmdbuf_len
- cmdbuf_pos
);
123 static void delete_current(void)
125 if (cmdbuf_pos
== cmdbuf_len
)
129 os_memmove(cmdbuf
+ cmdbuf_pos
, cmdbuf
+ cmdbuf_pos
+ 1,
130 cmdbuf_len
- cmdbuf_pos
);
136 static void delete_word(void)
142 while (pos
> 0 && cmdbuf
[pos
- 1] == ' ')
144 while (pos
> 0 && cmdbuf
[pos
- 1] != ' ')
146 os_memmove(cmdbuf
+ pos
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
147 cmdbuf_len
-= cmdbuf_pos
- pos
;
153 static void clear_left(void)
159 os_memmove(cmdbuf
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
160 cmdbuf_len
-= cmdbuf_pos
;
166 static void clear_right(void)
168 if (cmdbuf_pos
== cmdbuf_len
)
172 cmdbuf_len
= cmdbuf_pos
;
177 static void history_add(const char *str
)
179 struct edit_history
*h
, *match
= NULL
;
185 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
186 if (os_strcmp(str
, h
->str
) == 0) {
193 dl_list_del(&h
->list
);
194 dl_list_add(&history_list
, &h
->list
);
199 len
= os_strlen(str
);
200 h
= os_zalloc(sizeof(*h
) + len
);
203 dl_list_add(&history_list
, &h
->list
);
204 os_strlcpy(h
->str
, str
, len
+ 1);
209 static void history_use(void)
212 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_curr
->str
);
213 os_memcpy(cmdbuf
, history_curr
->str
, cmdbuf_len
);
218 static void history_prev(void)
220 if (history_curr
== NULL
||
222 dl_list_last(&history_list
, struct edit_history
, list
))
226 dl_list_first(&history_list
, struct edit_history
, list
)) {
227 cmdbuf
[cmdbuf_len
] = '\0';
231 history_curr
= dl_list_entry(history_curr
->list
.next
,
232 struct edit_history
, list
);
237 static void history_next(void)
239 if (history_curr
== NULL
||
241 dl_list_first(&history_list
, struct edit_history
, list
))
244 history_curr
= dl_list_entry(history_curr
->list
.prev
,
245 struct edit_history
, list
);
250 static void history_debug_dump(void)
252 struct edit_history
*h
;
255 dl_list_for_each_reverse(h
, &history_list
, struct edit_history
, list
)
256 printf("%s%s\n", h
== history_curr
? "[C]" : "", h
->str
);
261 static void insert_char(int c
)
263 if (cmdbuf_len
>= (int) sizeof(cmdbuf
) - 1)
265 if (cmdbuf_len
== cmdbuf_pos
) {
266 cmdbuf
[cmdbuf_pos
++] = c
;
271 os_memmove(cmdbuf
+ cmdbuf_pos
+ 1, cmdbuf
+ cmdbuf_pos
,
272 cmdbuf_len
- cmdbuf_pos
);
273 cmdbuf
[cmdbuf_pos
++] = c
;
280 static void process_cmd(void)
283 if (cmdbuf_len
== 0) {
289 cmdbuf
[cmdbuf_len
] = '\0';
293 edit_cmd_cb(edit_cb_ctx
, cmdbuf
);
299 static void free_completions(char **c
)
304 for (i
= 0; c
[i
]; i
++)
310 static int filter_strings(char **c
, char *str
, size_t len
)
314 for (i
= 0, j
= 0; c
[j
]; j
++) {
315 if (os_strncasecmp(c
[j
], str
, len
) == 0) {
331 static int common_len(const char *a
, const char *b
)
334 while (a
[len
] && a
[len
] == b
[len
])
340 static int max_common_length(char **c
)
344 len
= os_strlen(c
[0]);
345 for (i
= 1; c
[i
]; i
++) {
346 int same
= common_len(c
[0], c
[i
]);
355 static int cmp_str(const void *a
, const void *b
)
357 return os_strcmp(* (const char **) a
, * (const char **) b
);
360 static void complete(int list
)
365 int room
, plen
, add_space
;
367 if (edit_completion_cb
== NULL
)
370 cmdbuf
[cmdbuf_len
] = '\0';
371 c
= edit_completion_cb(edit_cb_ctx
, cmdbuf
, cmdbuf_pos
);
377 while (start
> 0 && cmdbuf
[start
- 1] != ' ')
381 count
= filter_strings(c
, &cmdbuf
[start
], plen
);
387 len
= max_common_length(c
);
388 if (len
<= plen
&& count
> 1) {
390 qsort(c
, count
, sizeof(char *), cmp_str
);
393 for (i
= 0; c
[i
]; i
++)
394 printf("%s%s", i
> 0 ? " " : "", c
[i
]);
403 room
= sizeof(cmdbuf
) - 1 - cmdbuf_len
;
406 add_space
= count
== 1 && len
< room
;
408 os_memmove(cmdbuf
+ cmdbuf_pos
+ len
+ add_space
, cmdbuf
+ cmdbuf_pos
,
409 cmdbuf_len
- cmdbuf_pos
);
410 os_memcpy(&cmdbuf
[cmdbuf_pos
- plen
], c
[0], plen
+ len
);
412 cmdbuf
[cmdbuf_pos
+ len
] = ' ';
414 cmdbuf_pos
+= len
+ add_space
;
415 cmdbuf_len
+= len
+ add_space
;
478 EDIT_KEY_SHIFT_RIGHT
,
480 EDIT_KEY_ALT_SHIFT_UP
,
481 EDIT_KEY_ALT_SHIFT_DOWN
,
482 EDIT_KEY_ALT_SHIFT_RIGHT
,
483 EDIT_KEY_ALT_SHIFT_LEFT
,
487 static void show_esc_buf(const char *esc_buf
, char c
, int i
)
490 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf
, c
, i
);
495 static enum edit_key_code
esc_seq_to_key1_no(char last
)
501 return EDIT_KEY_DOWN
;
503 return EDIT_KEY_RIGHT
;
505 return EDIT_KEY_LEFT
;
507 return EDIT_KEY_NONE
;
512 static enum edit_key_code
esc_seq_to_key1_shift(char last
)
516 return EDIT_KEY_SHIFT_UP
;
518 return EDIT_KEY_SHIFT_DOWN
;
520 return EDIT_KEY_SHIFT_RIGHT
;
522 return EDIT_KEY_SHIFT_LEFT
;
524 return EDIT_KEY_NONE
;
529 static enum edit_key_code
esc_seq_to_key1_alt(char last
)
533 return EDIT_KEY_ALT_UP
;
535 return EDIT_KEY_ALT_DOWN
;
537 return EDIT_KEY_ALT_RIGHT
;
539 return EDIT_KEY_ALT_LEFT
;
541 return EDIT_KEY_NONE
;
546 static enum edit_key_code
esc_seq_to_key1_alt_shift(char last
)
550 return EDIT_KEY_ALT_SHIFT_UP
;
552 return EDIT_KEY_ALT_SHIFT_DOWN
;
554 return EDIT_KEY_ALT_SHIFT_RIGHT
;
556 return EDIT_KEY_ALT_SHIFT_LEFT
;
558 return EDIT_KEY_NONE
;
563 static enum edit_key_code
esc_seq_to_key1_ctrl(char last
)
567 return EDIT_KEY_CTRL_UP
;
569 return EDIT_KEY_CTRL_DOWN
;
571 return EDIT_KEY_CTRL_RIGHT
;
573 return EDIT_KEY_CTRL_LEFT
;
575 return EDIT_KEY_NONE
;
580 static enum edit_key_code
esc_seq_to_key1(int param1
, int param2
, char last
)
582 /* ESC-[<param1>;<param2><last> */
584 if (param1
< 0 && param2
< 0)
585 return esc_seq_to_key1_no(last
);
587 if (param1
== 1 && param2
== 2)
588 return esc_seq_to_key1_shift(last
);
590 if (param1
== 1 && param2
== 3)
591 return esc_seq_to_key1_alt(last
);
593 if (param1
== 1 && param2
== 4)
594 return esc_seq_to_key1_alt_shift(last
);
596 if (param1
== 1 && param2
== 5)
597 return esc_seq_to_key1_ctrl(last
);
601 return EDIT_KEY_NONE
;
604 return EDIT_KEY_INSERT
;
606 return EDIT_KEY_DELETE
;
608 return EDIT_KEY_PAGE_UP
;
610 return EDIT_KEY_PAGE_DOWN
;
630 return EDIT_KEY_NONE
;
634 static enum edit_key_code
esc_seq_to_key2(int param1
, int param2
, char last
)
636 /* ESC-O<param1>;<param2><last> */
638 if (param1
>= 0 || param2
>= 0)
639 return EDIT_KEY_NONE
;
645 return EDIT_KEY_HOME
;
655 return EDIT_KEY_NONE
;
660 static enum edit_key_code
esc_seq_to_key(char *seq
)
663 int param1
= -1, param2
= -1;
664 enum edit_key_code ret
= EDIT_KEY_NONE
;
666 for (pos
= seq
; *pos
; pos
++)
669 if (seq
[1] >= '0' && seq
[1] <= '9') {
670 param1
= atoi(&seq
[1]);
671 pos
= os_strchr(seq
, ';');
673 param2
= atoi(pos
+ 1);
677 ret
= esc_seq_to_key1(param1
, param2
, last
);
678 else if (seq
[0] == 'O')
679 ret
= esc_seq_to_key2(param1
, param2
, last
);
681 if (ret
!= EDIT_KEY_NONE
)
685 printf("\rUnknown escape sequence '%s'\n", seq
);
687 return EDIT_KEY_NONE
;
691 static enum edit_key_code
edit_read_key(int sock
)
694 unsigned char buf
[1];
697 static char esc_buf
[7];
699 res
= read(sock
, buf
, 1);
708 if (c
== 27 /* ESC */) {
710 return EDIT_KEY_NONE
;
714 show_esc_buf(esc_buf
, c
, 0);
723 if (esc_buf
[0] != '[' && esc_buf
[0] != 'O') {
724 show_esc_buf(esc_buf
, c
, 1);
726 return EDIT_KEY_NONE
;
728 return EDIT_KEY_NONE
; /* Escape sequence continues */
732 if ((c
>= '0' && c
<= '9') || c
== ';')
733 return EDIT_KEY_NONE
; /* Escape sequence continues */
735 if (c
== '~' || (c
>= 'A' && c
<= 'Z')) {
737 return esc_seq_to_key(esc_buf
);
740 show_esc_buf(esc_buf
, c
, 2);
742 return EDIT_KEY_NONE
;
747 return EDIT_KEY_CTRL_A
;
749 return EDIT_KEY_CTRL_B
;
751 return EDIT_KEY_CTRL_D
;
753 return EDIT_KEY_CTRL_E
;
755 return EDIT_KEY_CTRL_F
;
757 return EDIT_KEY_CTRL_G
;
759 return EDIT_KEY_CTRL_H
;
763 return EDIT_KEY_CTRL_J
;
765 return EDIT_KEY_ENTER
;
767 return EDIT_KEY_CTRL_K
;
769 return EDIT_KEY_CTRL_L
;
771 return EDIT_KEY_CTRL_N
;
773 return EDIT_KEY_CTRL_O
;
775 return EDIT_KEY_CTRL_P
;
777 return EDIT_KEY_CTRL_R
;
779 return EDIT_KEY_CTRL_T
;
781 return EDIT_KEY_CTRL_U
;
783 return EDIT_KEY_CTRL_V
;
785 return EDIT_KEY_CTRL_W
;
788 return EDIT_KEY_NONE
;
790 return EDIT_KEY_BACKSPACE
;
797 static char search_buf
[21];
798 static int search_skip
;
800 static char * search_find(void)
802 struct edit_history
*h
;
803 size_t len
= os_strlen(search_buf
);
804 int skip
= search_skip
;
809 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
810 if (os_strstr(h
->str
, search_buf
)) {
822 static void search_redraw(void)
824 char *match
= search_find();
825 printf("\rsearch '%s': %s" CLEAR_END_LINE
,
826 search_buf
, match
? match
: "");
827 printf("\rsearch '%s", search_buf
);
832 static void search_start(void)
835 search_buf
[0] = '\0';
841 static void search_clear(void)
844 printf("\r" CLEAR_END_LINE
);
848 static void search_stop(void)
850 char *match
= search_find();
851 search_buf
[0] = '\0';
854 os_strlcpy(cmdbuf
, match
, CMD_BUF_LEN
);
855 cmdbuf_len
= os_strlen(cmdbuf
);
856 cmdbuf_pos
= cmdbuf_len
;
862 static void search_cancel(void)
864 search_buf
[0] = '\0';
870 static void search_backspace(void)
873 len
= os_strlen(search_buf
);
876 search_buf
[len
- 1] = '\0';
882 static void search_next(void)
890 static void search_char(char c
)
893 len
= os_strlen(search_buf
);
894 if (len
== sizeof(search_buf
) - 1)
897 search_buf
[len
+ 1] = '\0';
903 static enum edit_key_code
search_key(enum edit_key_code c
)
907 case EDIT_KEY_CTRL_J
:
912 case EDIT_KEY_CTRL_A
:
913 case EDIT_KEY_CTRL_E
:
920 case EDIT_KEY_CTRL_H
:
921 case EDIT_KEY_BACKSPACE
:
924 case EDIT_KEY_CTRL_R
:
928 if (c
>= 32 && c
<= 255)
933 return EDIT_KEY_NONE
;
937 static void edit_read_char(int sock
, void *eloop_ctx
, void *sock_ctx
)
939 static int last_tab
= 0;
940 static int search
= 0;
941 enum edit_key_code c
;
943 c
= edit_read_key(sock
);
947 if (c
== EDIT_KEY_NONE
)
950 if (c
== EDIT_KEY_EOF
)
954 if (c
!= EDIT_KEY_TAB
&& c
!= EDIT_KEY_NONE
)
961 edit_eof_cb(edit_cb_ctx
);
968 case EDIT_KEY_CTRL_P
:
972 case EDIT_KEY_CTRL_N
:
976 case EDIT_KEY_CTRL_F
:
980 case EDIT_KEY_CTRL_B
:
983 case EDIT_KEY_CTRL_RIGHT
:
986 case EDIT_KEY_CTRL_LEFT
:
989 case EDIT_KEY_DELETE
:
996 case EDIT_KEY_CTRL_A
:
1000 history_debug_dump();
1002 case EDIT_KEY_CTRL_D
:
1003 if (cmdbuf_len
> 0) {
1008 edit_eof_cb(edit_cb_ctx
);
1010 case EDIT_KEY_CTRL_E
:
1013 case EDIT_KEY_CTRL_H
:
1014 case EDIT_KEY_BACKSPACE
:
1017 case EDIT_KEY_ENTER
:
1018 case EDIT_KEY_CTRL_J
:
1021 case EDIT_KEY_CTRL_K
:
1024 case EDIT_KEY_CTRL_L
:
1028 case EDIT_KEY_CTRL_R
:
1032 case EDIT_KEY_CTRL_U
:
1035 case EDIT_KEY_CTRL_W
:
1039 if (c
>= 32 && c
<= 255)
1046 int edit_init(void (*cmd_cb
)(void *ctx
, char *cmd
),
1047 void (*eof_cb
)(void *ctx
),
1048 char ** (*completion_cb
)(void *ctx
, const char *cmd
, int pos
),
1049 void *ctx
, const char *history_file
)
1051 dl_list_init(&history_list
);
1052 history_curr
= NULL
;
1055 edit_cmd_cb
= cmd_cb
;
1056 edit_eof_cb
= eof_cb
;
1057 edit_completion_cb
= completion_cb
;
1059 tcgetattr(STDIN_FILENO
, &prevt
);
1061 newt
.c_lflag
&= ~(ICANON
| ECHO
);
1062 tcsetattr(STDIN_FILENO
, TCSANOW
, &newt
);
1064 eloop_register_read_sock(STDIN_FILENO
, edit_read_char
, NULL
, NULL
);
1073 void edit_deinit(const char *history_file
,
1074 int (*filter_cb
)(void *ctx
, const char *cmd
))
1076 struct edit_history
*h
;
1077 while ((h
= dl_list_first(&history_list
, struct edit_history
, list
))) {
1078 dl_list_del(&h
->list
);
1081 eloop_unregister_read_sock(STDIN_FILENO
);
1082 tcsetattr(STDIN_FILENO
, TCSANOW
, &prevt
);
1086 void edit_redraw(void)
1089 cmdbuf
[cmdbuf_len
] = '\0';
1090 printf("\r> %s", cmdbuf
);
1091 if (cmdbuf_pos
!= cmdbuf_len
) {
1092 tmp
= cmdbuf
[cmdbuf_pos
];
1093 cmdbuf
[cmdbuf_pos
] = '\0';
1094 printf("\r> %s", cmdbuf
);
1095 cmdbuf
[cmdbuf_pos
] = tmp
;