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