]>
git.ipfire.org Git - thirdparty/hostap.git/blob - src/utils/edit.c
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;
24 #define HISTORY_MAX 100
31 static struct dl_list history_list
;
32 static struct edit_history
*history_curr
;
34 static void *edit_cb_ctx
;
35 static void (*edit_cmd_cb
)(void *ctx
, char *cmd
);
36 static void (*edit_eof_cb
)(void *ctx
);
37 static char ** (*edit_completion_cb
)(void *ctx
, const char *cmd
, int pos
) =
40 static struct termios prevt
, newt
;
43 #define CLEAR_END_LINE "\e[K"
46 void edit_clear_line(void)
50 for (i
= 0; i
< cmdbuf_len
+ 2; i
++)
55 static void move_start(void)
62 static void move_end(void)
64 cmdbuf_pos
= cmdbuf_len
;
69 static void move_left(void)
78 static void move_right(void)
80 if (cmdbuf_pos
< cmdbuf_len
) {
87 static void move_word_left(void)
89 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] == ' ')
91 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] != ' ')
97 static void move_word_right(void)
99 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] == ' ')
101 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] != ' ')
107 static void delete_left(void)
113 os_memmove(cmdbuf
+ cmdbuf_pos
- 1, cmdbuf
+ cmdbuf_pos
,
114 cmdbuf_len
- cmdbuf_pos
);
121 static void delete_current(void)
123 if (cmdbuf_pos
== cmdbuf_len
)
127 os_memmove(cmdbuf
+ cmdbuf_pos
, cmdbuf
+ cmdbuf_pos
+ 1,
128 cmdbuf_len
- cmdbuf_pos
);
134 static void delete_word(void)
140 while (pos
> 0 && cmdbuf
[pos
- 1] == ' ')
142 while (pos
> 0 && cmdbuf
[pos
- 1] != ' ')
144 os_memmove(cmdbuf
+ pos
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
145 cmdbuf_len
-= cmdbuf_pos
- pos
;
151 static void clear_left(void)
157 os_memmove(cmdbuf
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
158 cmdbuf_len
-= cmdbuf_pos
;
164 static void clear_right(void)
166 if (cmdbuf_pos
== cmdbuf_len
)
170 cmdbuf_len
= cmdbuf_pos
;
175 static void history_add(const char *str
)
177 struct edit_history
*h
, *match
= NULL
, *last
= NULL
;
178 size_t len
, count
= 0;
183 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
184 if (os_strcmp(str
, h
->str
) == 0) {
193 dl_list_del(&h
->list
);
194 dl_list_add(&history_list
, &h
->list
);
199 if (count
>= HISTORY_MAX
&& last
) {
200 dl_list_del(&last
->list
);
204 len
= os_strlen(str
);
205 h
= os_zalloc(sizeof(*h
) + len
);
208 dl_list_add(&history_list
, &h
->list
);
209 os_strlcpy(h
->str
, str
, len
+ 1);
214 static void history_use(void)
217 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_curr
->str
);
218 os_memcpy(cmdbuf
, history_curr
->str
, cmdbuf_len
);
223 static void history_prev(void)
225 if (history_curr
== NULL
)
229 dl_list_first(&history_list
, struct edit_history
, list
)) {
230 if (!currbuf_valid
) {
231 cmdbuf
[cmdbuf_len
] = '\0';
232 os_memcpy(currbuf
, cmdbuf
, cmdbuf_len
+ 1);
240 dl_list_last(&history_list
, struct edit_history
, list
))
243 history_curr
= dl_list_entry(history_curr
->list
.next
,
244 struct edit_history
, list
);
249 static void history_next(void)
251 if (history_curr
== NULL
||
253 dl_list_first(&history_list
, struct edit_history
, list
)) {
257 cmdbuf_len
= cmdbuf_pos
= os_strlen(currbuf
);
258 os_memcpy(cmdbuf
, currbuf
, cmdbuf_len
);
264 history_curr
= dl_list_entry(history_curr
->list
.prev
,
265 struct edit_history
, list
);
270 static void history_read(const char *fname
)
273 char buf
[CMD_BUF_LEN
], *pos
;
275 f
= fopen(fname
, "r");
279 while (fgets(buf
, CMD_BUF_LEN
, f
)) {
280 for (pos
= buf
; *pos
; pos
++) {
281 if (*pos
== '\r' || *pos
== '\n') {
293 static void history_write(const char *fname
,
294 int (*filter_cb
)(void *ctx
, const char *cmd
))
297 struct edit_history
*h
;
299 f
= fopen(fname
, "w");
303 dl_list_for_each_reverse(h
, &history_list
, struct edit_history
, list
) {
304 if (filter_cb
&& filter_cb(edit_cb_ctx
, h
->str
))
306 fprintf(f
, "%s\n", h
->str
);
313 static void history_debug_dump(void)
315 struct edit_history
*h
;
318 dl_list_for_each_reverse(h
, &history_list
, struct edit_history
, list
)
319 printf("%s%s\n", h
== history_curr
? "[C]" : "", h
->str
);
321 printf("{%s}\n", currbuf
);
326 static void insert_char(int c
)
328 if (cmdbuf_len
>= (int) sizeof(cmdbuf
) - 1)
330 if (cmdbuf_len
== cmdbuf_pos
) {
331 cmdbuf
[cmdbuf_pos
++] = c
;
336 os_memmove(cmdbuf
+ cmdbuf_pos
+ 1, cmdbuf
+ cmdbuf_pos
,
337 cmdbuf_len
- cmdbuf_pos
);
338 cmdbuf
[cmdbuf_pos
++] = c
;
345 static void process_cmd(void)
348 if (cmdbuf_len
== 0) {
354 cmdbuf
[cmdbuf_len
] = '\0';
358 edit_cmd_cb(edit_cb_ctx
, cmdbuf
);
364 static void free_completions(char **c
)
369 for (i
= 0; c
[i
]; i
++)
375 static int filter_strings(char **c
, char *str
, size_t len
)
379 for (i
= 0, j
= 0; c
[j
]; j
++) {
380 if (os_strncasecmp(c
[j
], str
, len
) == 0) {
396 static int common_len(const char *a
, const char *b
)
399 while (a
[len
] && a
[len
] == b
[len
])
405 static int max_common_length(char **c
)
409 len
= os_strlen(c
[0]);
410 for (i
= 1; c
[i
]; i
++) {
411 int same
= common_len(c
[0], c
[i
]);
420 static int cmp_str(const void *a
, const void *b
)
422 return os_strcmp(* (const char **) a
, * (const char **) b
);
425 static void complete(int list
)
430 int room
, plen
, add_space
;
432 if (edit_completion_cb
== NULL
)
435 cmdbuf
[cmdbuf_len
] = '\0';
436 c
= edit_completion_cb(edit_cb_ctx
, cmdbuf
, cmdbuf_pos
);
442 while (start
> 0 && cmdbuf
[start
- 1] != ' ')
446 count
= filter_strings(c
, &cmdbuf
[start
], plen
);
452 len
= max_common_length(c
);
453 if (len
<= plen
&& count
> 1) {
455 qsort(c
, count
, sizeof(char *), cmp_str
);
458 for (i
= 0; c
[i
]; i
++)
459 printf("%s%s", i
> 0 ? " " : "", c
[i
]);
468 room
= sizeof(cmdbuf
) - 1 - cmdbuf_len
;
471 add_space
= count
== 1 && len
< room
;
473 os_memmove(cmdbuf
+ cmdbuf_pos
+ len
+ add_space
, cmdbuf
+ cmdbuf_pos
,
474 cmdbuf_len
- cmdbuf_pos
);
475 os_memcpy(&cmdbuf
[cmdbuf_pos
- plen
], c
[0], plen
+ len
);
477 cmdbuf
[cmdbuf_pos
+ len
] = ' ';
479 cmdbuf_pos
+= len
+ add_space
;
480 cmdbuf_len
+= len
+ add_space
;
543 EDIT_KEY_SHIFT_RIGHT
,
545 EDIT_KEY_ALT_SHIFT_UP
,
546 EDIT_KEY_ALT_SHIFT_DOWN
,
547 EDIT_KEY_ALT_SHIFT_RIGHT
,
548 EDIT_KEY_ALT_SHIFT_LEFT
,
552 static void show_esc_buf(const char *esc_buf
, char c
, int i
)
555 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf
, c
, i
);
560 static enum edit_key_code
esc_seq_to_key1_no(char last
)
566 return EDIT_KEY_DOWN
;
568 return EDIT_KEY_RIGHT
;
570 return EDIT_KEY_LEFT
;
572 return EDIT_KEY_NONE
;
577 static enum edit_key_code
esc_seq_to_key1_shift(char last
)
581 return EDIT_KEY_SHIFT_UP
;
583 return EDIT_KEY_SHIFT_DOWN
;
585 return EDIT_KEY_SHIFT_RIGHT
;
587 return EDIT_KEY_SHIFT_LEFT
;
589 return EDIT_KEY_NONE
;
594 static enum edit_key_code
esc_seq_to_key1_alt(char last
)
598 return EDIT_KEY_ALT_UP
;
600 return EDIT_KEY_ALT_DOWN
;
602 return EDIT_KEY_ALT_RIGHT
;
604 return EDIT_KEY_ALT_LEFT
;
606 return EDIT_KEY_NONE
;
611 static enum edit_key_code
esc_seq_to_key1_alt_shift(char last
)
615 return EDIT_KEY_ALT_SHIFT_UP
;
617 return EDIT_KEY_ALT_SHIFT_DOWN
;
619 return EDIT_KEY_ALT_SHIFT_RIGHT
;
621 return EDIT_KEY_ALT_SHIFT_LEFT
;
623 return EDIT_KEY_NONE
;
628 static enum edit_key_code
esc_seq_to_key1_ctrl(char last
)
632 return EDIT_KEY_CTRL_UP
;
634 return EDIT_KEY_CTRL_DOWN
;
636 return EDIT_KEY_CTRL_RIGHT
;
638 return EDIT_KEY_CTRL_LEFT
;
640 return EDIT_KEY_NONE
;
645 static enum edit_key_code
esc_seq_to_key1(int param1
, int param2
, char last
)
647 /* ESC-[<param1>;<param2><last> */
649 if (param1
< 0 && param2
< 0)
650 return esc_seq_to_key1_no(last
);
652 if (param1
== 1 && param2
== 2)
653 return esc_seq_to_key1_shift(last
);
655 if (param1
== 1 && param2
== 3)
656 return esc_seq_to_key1_alt(last
);
658 if (param1
== 1 && param2
== 4)
659 return esc_seq_to_key1_alt_shift(last
);
661 if (param1
== 1 && param2
== 5)
662 return esc_seq_to_key1_ctrl(last
);
666 return EDIT_KEY_NONE
;
669 return EDIT_KEY_INSERT
;
671 return EDIT_KEY_DELETE
;
673 return EDIT_KEY_PAGE_UP
;
675 return EDIT_KEY_PAGE_DOWN
;
695 return EDIT_KEY_NONE
;
699 static enum edit_key_code
esc_seq_to_key2(int param1
, int param2
, char last
)
701 /* ESC-O<param1>;<param2><last> */
703 if (param1
>= 0 || param2
>= 0)
704 return EDIT_KEY_NONE
;
710 return EDIT_KEY_HOME
;
720 return EDIT_KEY_NONE
;
725 static enum edit_key_code
esc_seq_to_key(char *seq
)
728 int param1
= -1, param2
= -1;
729 enum edit_key_code ret
= EDIT_KEY_NONE
;
732 for (pos
= seq
; *pos
; pos
++)
735 if (seq
[1] >= '0' && seq
[1] <= '9') {
736 param1
= atoi(&seq
[1]);
737 pos
= os_strchr(seq
, ';');
739 param2
= atoi(pos
+ 1);
743 ret
= esc_seq_to_key1(param1
, param2
, last
);
744 else if (seq
[0] == 'O')
745 ret
= esc_seq_to_key2(param1
, param2
, last
);
747 if (ret
!= EDIT_KEY_NONE
)
751 printf("\rUnknown escape sequence '%s'\n", seq
);
753 return EDIT_KEY_NONE
;
757 static enum edit_key_code
edit_read_key(int sock
)
760 unsigned char buf
[1];
763 static char esc_buf
[7];
765 res
= read(sock
, buf
, 1);
774 if (c
== 27 /* ESC */) {
776 return EDIT_KEY_NONE
;
780 show_esc_buf(esc_buf
, c
, 0);
789 if (esc_buf
[0] != '[' && esc_buf
[0] != 'O') {
790 show_esc_buf(esc_buf
, c
, 1);
792 return EDIT_KEY_NONE
;
794 return EDIT_KEY_NONE
; /* Escape sequence continues */
798 if ((c
>= '0' && c
<= '9') || c
== ';')
799 return EDIT_KEY_NONE
; /* Escape sequence continues */
801 if (c
== '~' || (c
>= 'A' && c
<= 'Z')) {
803 return esc_seq_to_key(esc_buf
);
806 show_esc_buf(esc_buf
, c
, 2);
808 return EDIT_KEY_NONE
;
813 return EDIT_KEY_CTRL_A
;
815 return EDIT_KEY_CTRL_B
;
817 return EDIT_KEY_CTRL_D
;
819 return EDIT_KEY_CTRL_E
;
821 return EDIT_KEY_CTRL_F
;
823 return EDIT_KEY_CTRL_G
;
825 return EDIT_KEY_CTRL_H
;
829 return EDIT_KEY_CTRL_J
;
831 return EDIT_KEY_ENTER
;
833 return EDIT_KEY_CTRL_K
;
835 return EDIT_KEY_CTRL_L
;
837 return EDIT_KEY_CTRL_N
;
839 return EDIT_KEY_CTRL_O
;
841 return EDIT_KEY_CTRL_P
;
843 return EDIT_KEY_CTRL_R
;
845 return EDIT_KEY_CTRL_T
;
847 return EDIT_KEY_CTRL_U
;
849 return EDIT_KEY_CTRL_V
;
851 return EDIT_KEY_CTRL_W
;
854 return EDIT_KEY_NONE
;
856 return EDIT_KEY_BACKSPACE
;
863 static char search_buf
[21];
864 static int search_skip
;
866 static char * search_find(void)
868 struct edit_history
*h
;
869 size_t len
= os_strlen(search_buf
);
870 int skip
= search_skip
;
875 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
876 if (os_strstr(h
->str
, search_buf
)) {
888 static void search_redraw(void)
890 char *match
= search_find();
891 printf("\rsearch '%s': %s" CLEAR_END_LINE
,
892 search_buf
, match
? match
: "");
893 printf("\rsearch '%s", search_buf
);
898 static void search_start(void)
901 search_buf
[0] = '\0';
907 static void search_clear(void)
910 printf("\r" CLEAR_END_LINE
);
914 static void search_stop(void)
916 char *match
= search_find();
917 search_buf
[0] = '\0';
920 os_strlcpy(cmdbuf
, match
, CMD_BUF_LEN
);
921 cmdbuf_len
= os_strlen(cmdbuf
);
922 cmdbuf_pos
= cmdbuf_len
;
928 static void search_cancel(void)
930 search_buf
[0] = '\0';
936 static void search_backspace(void)
939 len
= os_strlen(search_buf
);
942 search_buf
[len
- 1] = '\0';
948 static void search_next(void)
956 static void search_char(char c
)
959 len
= os_strlen(search_buf
);
960 if (len
== sizeof(search_buf
) - 1)
963 search_buf
[len
+ 1] = '\0';
969 static enum edit_key_code
search_key(enum edit_key_code c
)
973 case EDIT_KEY_CTRL_J
:
978 case EDIT_KEY_CTRL_A
:
979 case EDIT_KEY_CTRL_E
:
986 case EDIT_KEY_CTRL_H
:
987 case EDIT_KEY_BACKSPACE
:
990 case EDIT_KEY_CTRL_R
:
994 if (c
>= 32 && c
<= 255)
999 return EDIT_KEY_NONE
;
1003 static void edit_read_char(int sock
, void *eloop_ctx
, void *sock_ctx
)
1005 static int last_tab
= 0;
1006 static int search
= 0;
1007 enum edit_key_code c
;
1009 c
= edit_read_key(sock
);
1013 if (c
== EDIT_KEY_NONE
)
1016 if (c
== EDIT_KEY_EOF
)
1020 if (c
!= EDIT_KEY_TAB
&& c
!= EDIT_KEY_NONE
)
1027 edit_eof_cb(edit_cb_ctx
);
1034 case EDIT_KEY_CTRL_P
:
1038 case EDIT_KEY_CTRL_N
:
1041 case EDIT_KEY_RIGHT
:
1042 case EDIT_KEY_CTRL_F
:
1046 case EDIT_KEY_CTRL_B
:
1049 case EDIT_KEY_CTRL_RIGHT
:
1052 case EDIT_KEY_CTRL_LEFT
:
1055 case EDIT_KEY_DELETE
:
1062 case EDIT_KEY_CTRL_A
:
1066 history_debug_dump();
1068 case EDIT_KEY_CTRL_D
:
1069 if (cmdbuf_len
> 0) {
1074 edit_eof_cb(edit_cb_ctx
);
1076 case EDIT_KEY_CTRL_E
:
1079 case EDIT_KEY_CTRL_H
:
1080 case EDIT_KEY_BACKSPACE
:
1083 case EDIT_KEY_ENTER
:
1084 case EDIT_KEY_CTRL_J
:
1087 case EDIT_KEY_CTRL_K
:
1090 case EDIT_KEY_CTRL_L
:
1094 case EDIT_KEY_CTRL_R
:
1098 case EDIT_KEY_CTRL_U
:
1101 case EDIT_KEY_CTRL_W
:
1105 if (c
>= 32 && c
<= 255)
1112 int edit_init(void (*cmd_cb
)(void *ctx
, char *cmd
),
1113 void (*eof_cb
)(void *ctx
),
1114 char ** (*completion_cb
)(void *ctx
, const char *cmd
, int pos
),
1115 void *ctx
, const char *history_file
, const char *ps
)
1118 dl_list_init(&history_list
);
1119 history_curr
= NULL
;
1121 history_read(history_file
);
1124 edit_cmd_cb
= cmd_cb
;
1125 edit_eof_cb
= eof_cb
;
1126 edit_completion_cb
= completion_cb
;
1128 tcgetattr(STDIN_FILENO
, &prevt
);
1130 newt
.c_lflag
&= ~(ICANON
| ECHO
);
1131 tcsetattr(STDIN_FILENO
, TCSANOW
, &newt
);
1133 eloop_register_read_sock(STDIN_FILENO
, edit_read_char
, NULL
, NULL
);
1142 void edit_deinit(const char *history_file
,
1143 int (*filter_cb
)(void *ctx
, const char *cmd
))
1145 struct edit_history
*h
;
1147 history_write(history_file
, filter_cb
);
1148 while ((h
= dl_list_first(&history_list
, struct edit_history
, list
))) {
1149 dl_list_del(&h
->list
);
1155 eloop_unregister_read_sock(STDIN_FILENO
);
1156 tcsetattr(STDIN_FILENO
, TCSANOW
, &prevt
);
1160 void edit_redraw(void)
1163 cmdbuf
[cmdbuf_len
] = '\0';
1164 printf("\r> %s", cmdbuf
);
1165 if (cmdbuf_pos
!= cmdbuf_len
) {
1166 tmp
= cmdbuf
[cmdbuf_pos
];
1167 cmdbuf
[cmdbuf_pos
] = '\0';
1168 printf("\r> %s", cmdbuf
);
1169 cmdbuf
[cmdbuf_pos
] = tmp
;