]>
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;
28 #define HISTORY_MAX 100
35 static struct dl_list history_list
;
36 static struct edit_history
*history_curr
;
38 static void *edit_cb_ctx
;
39 static void (*edit_cmd_cb
)(void *ctx
, char *cmd
);
40 static void (*edit_eof_cb
)(void *ctx
);
41 static char ** (*edit_completion_cb
)(void *ctx
, const char *cmd
, int pos
) =
44 static struct termios prevt
, newt
;
47 #define CLEAR_END_LINE "\e[K"
50 void edit_clear_line(void)
54 for (i
= 0; i
< cmdbuf_len
+ 2; i
++)
59 static void move_start(void)
66 static void move_end(void)
68 cmdbuf_pos
= cmdbuf_len
;
73 static void move_left(void)
82 static void move_right(void)
84 if (cmdbuf_pos
< cmdbuf_len
) {
91 static void move_word_left(void)
93 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] == ' ')
95 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] != ' ')
101 static void move_word_right(void)
103 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] == ' ')
105 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] != ' ')
111 static void delete_left(void)
117 os_memmove(cmdbuf
+ cmdbuf_pos
- 1, cmdbuf
+ cmdbuf_pos
,
118 cmdbuf_len
- cmdbuf_pos
);
125 static void delete_current(void)
127 if (cmdbuf_pos
== cmdbuf_len
)
131 os_memmove(cmdbuf
+ cmdbuf_pos
, cmdbuf
+ cmdbuf_pos
+ 1,
132 cmdbuf_len
- cmdbuf_pos
);
138 static void delete_word(void)
144 while (pos
> 0 && cmdbuf
[pos
- 1] == ' ')
146 while (pos
> 0 && cmdbuf
[pos
- 1] != ' ')
148 os_memmove(cmdbuf
+ pos
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
149 cmdbuf_len
-= cmdbuf_pos
- pos
;
155 static void clear_left(void)
161 os_memmove(cmdbuf
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
162 cmdbuf_len
-= cmdbuf_pos
;
168 static void clear_right(void)
170 if (cmdbuf_pos
== cmdbuf_len
)
174 cmdbuf_len
= cmdbuf_pos
;
179 static void history_add(const char *str
)
181 struct edit_history
*h
, *match
= NULL
, *last
= NULL
;
182 size_t len
, count
= 0;
187 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
188 if (os_strcmp(str
, h
->str
) == 0) {
197 dl_list_del(&h
->list
);
198 dl_list_add(&history_list
, &h
->list
);
203 if (count
>= HISTORY_MAX
&& last
) {
204 dl_list_del(&last
->list
);
208 len
= os_strlen(str
);
209 h
= os_zalloc(sizeof(*h
) + len
);
212 dl_list_add(&history_list
, &h
->list
);
213 os_strlcpy(h
->str
, str
, len
+ 1);
218 static void history_use(void)
221 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_curr
->str
);
222 os_memcpy(cmdbuf
, history_curr
->str
, cmdbuf_len
);
227 static void history_prev(void)
229 if (history_curr
== NULL
)
233 dl_list_first(&history_list
, struct edit_history
, list
)) {
234 cmdbuf
[cmdbuf_len
] = '\0';
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
);
249 static void history_next(void)
251 if (history_curr
== NULL
||
253 dl_list_first(&history_list
, struct edit_history
, list
))
256 history_curr
= dl_list_entry(history_curr
->list
.prev
,
257 struct edit_history
, list
);
262 static void history_read(const char *fname
)
265 char buf
[CMD_BUF_LEN
], *pos
;
267 f
= fopen(fname
, "r");
271 while (fgets(buf
, CMD_BUF_LEN
, f
)) {
272 for (pos
= buf
; *pos
; pos
++) {
273 if (*pos
== '\r' || *pos
== '\n') {
285 static void history_write(const char *fname
,
286 int (*filter_cb
)(void *ctx
, const char *cmd
))
289 struct edit_history
*h
;
291 f
= fopen(fname
, "w");
295 dl_list_for_each_reverse(h
, &history_list
, struct edit_history
, list
) {
296 if (filter_cb
&& filter_cb(edit_cb_ctx
, h
->str
))
298 fprintf(f
, "%s\n", h
->str
);
305 static void history_debug_dump(void)
307 struct edit_history
*h
;
310 dl_list_for_each_reverse(h
, &history_list
, struct edit_history
, list
)
311 printf("%s%s\n", h
== history_curr
? "[C]" : "", h
->str
);
316 static void insert_char(int c
)
318 if (cmdbuf_len
>= (int) sizeof(cmdbuf
) - 1)
320 if (cmdbuf_len
== cmdbuf_pos
) {
321 cmdbuf
[cmdbuf_pos
++] = c
;
326 os_memmove(cmdbuf
+ cmdbuf_pos
+ 1, cmdbuf
+ cmdbuf_pos
,
327 cmdbuf_len
- cmdbuf_pos
);
328 cmdbuf
[cmdbuf_pos
++] = c
;
335 static void process_cmd(void)
338 if (cmdbuf_len
== 0) {
344 cmdbuf
[cmdbuf_len
] = '\0';
348 edit_cmd_cb(edit_cb_ctx
, cmdbuf
);
354 static void free_completions(char **c
)
359 for (i
= 0; c
[i
]; i
++)
365 static int filter_strings(char **c
, char *str
, size_t len
)
369 for (i
= 0, j
= 0; c
[j
]; j
++) {
370 if (os_strncasecmp(c
[j
], str
, len
) == 0) {
386 static int common_len(const char *a
, const char *b
)
389 while (a
[len
] && a
[len
] == b
[len
])
395 static int max_common_length(char **c
)
399 len
= os_strlen(c
[0]);
400 for (i
= 1; c
[i
]; i
++) {
401 int same
= common_len(c
[0], c
[i
]);
410 static int cmp_str(const void *a
, const void *b
)
412 return os_strcmp(* (const char **) a
, * (const char **) b
);
415 static void complete(int list
)
420 int room
, plen
, add_space
;
422 if (edit_completion_cb
== NULL
)
425 cmdbuf
[cmdbuf_len
] = '\0';
426 c
= edit_completion_cb(edit_cb_ctx
, cmdbuf
, cmdbuf_pos
);
432 while (start
> 0 && cmdbuf
[start
- 1] != ' ')
436 count
= filter_strings(c
, &cmdbuf
[start
], plen
);
442 len
= max_common_length(c
);
443 if (len
<= plen
&& count
> 1) {
445 qsort(c
, count
, sizeof(char *), cmp_str
);
448 for (i
= 0; c
[i
]; i
++)
449 printf("%s%s", i
> 0 ? " " : "", c
[i
]);
458 room
= sizeof(cmdbuf
) - 1 - cmdbuf_len
;
461 add_space
= count
== 1 && len
< room
;
463 os_memmove(cmdbuf
+ cmdbuf_pos
+ len
+ add_space
, cmdbuf
+ cmdbuf_pos
,
464 cmdbuf_len
- cmdbuf_pos
);
465 os_memcpy(&cmdbuf
[cmdbuf_pos
- plen
], c
[0], plen
+ len
);
467 cmdbuf
[cmdbuf_pos
+ len
] = ' ';
469 cmdbuf_pos
+= len
+ add_space
;
470 cmdbuf_len
+= len
+ add_space
;
533 EDIT_KEY_SHIFT_RIGHT
,
535 EDIT_KEY_ALT_SHIFT_UP
,
536 EDIT_KEY_ALT_SHIFT_DOWN
,
537 EDIT_KEY_ALT_SHIFT_RIGHT
,
538 EDIT_KEY_ALT_SHIFT_LEFT
,
542 static void show_esc_buf(const char *esc_buf
, char c
, int i
)
545 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf
, c
, i
);
550 static enum edit_key_code
esc_seq_to_key1_no(char last
)
556 return EDIT_KEY_DOWN
;
558 return EDIT_KEY_RIGHT
;
560 return EDIT_KEY_LEFT
;
562 return EDIT_KEY_NONE
;
567 static enum edit_key_code
esc_seq_to_key1_shift(char last
)
571 return EDIT_KEY_SHIFT_UP
;
573 return EDIT_KEY_SHIFT_DOWN
;
575 return EDIT_KEY_SHIFT_RIGHT
;
577 return EDIT_KEY_SHIFT_LEFT
;
579 return EDIT_KEY_NONE
;
584 static enum edit_key_code
esc_seq_to_key1_alt(char last
)
588 return EDIT_KEY_ALT_UP
;
590 return EDIT_KEY_ALT_DOWN
;
592 return EDIT_KEY_ALT_RIGHT
;
594 return EDIT_KEY_ALT_LEFT
;
596 return EDIT_KEY_NONE
;
601 static enum edit_key_code
esc_seq_to_key1_alt_shift(char last
)
605 return EDIT_KEY_ALT_SHIFT_UP
;
607 return EDIT_KEY_ALT_SHIFT_DOWN
;
609 return EDIT_KEY_ALT_SHIFT_RIGHT
;
611 return EDIT_KEY_ALT_SHIFT_LEFT
;
613 return EDIT_KEY_NONE
;
618 static enum edit_key_code
esc_seq_to_key1_ctrl(char last
)
622 return EDIT_KEY_CTRL_UP
;
624 return EDIT_KEY_CTRL_DOWN
;
626 return EDIT_KEY_CTRL_RIGHT
;
628 return EDIT_KEY_CTRL_LEFT
;
630 return EDIT_KEY_NONE
;
635 static enum edit_key_code
esc_seq_to_key1(int param1
, int param2
, char last
)
637 /* ESC-[<param1>;<param2><last> */
639 if (param1
< 0 && param2
< 0)
640 return esc_seq_to_key1_no(last
);
642 if (param1
== 1 && param2
== 2)
643 return esc_seq_to_key1_shift(last
);
645 if (param1
== 1 && param2
== 3)
646 return esc_seq_to_key1_alt(last
);
648 if (param1
== 1 && param2
== 4)
649 return esc_seq_to_key1_alt_shift(last
);
651 if (param1
== 1 && param2
== 5)
652 return esc_seq_to_key1_ctrl(last
);
656 return EDIT_KEY_NONE
;
659 return EDIT_KEY_INSERT
;
661 return EDIT_KEY_DELETE
;
663 return EDIT_KEY_PAGE_UP
;
665 return EDIT_KEY_PAGE_DOWN
;
685 return EDIT_KEY_NONE
;
689 static enum edit_key_code
esc_seq_to_key2(int param1
, int param2
, char last
)
691 /* ESC-O<param1>;<param2><last> */
693 if (param1
>= 0 || param2
>= 0)
694 return EDIT_KEY_NONE
;
700 return EDIT_KEY_HOME
;
710 return EDIT_KEY_NONE
;
715 static enum edit_key_code
esc_seq_to_key(char *seq
)
718 int param1
= -1, param2
= -1;
719 enum edit_key_code ret
= EDIT_KEY_NONE
;
721 for (pos
= seq
; *pos
; pos
++)
724 if (seq
[1] >= '0' && seq
[1] <= '9') {
725 param1
= atoi(&seq
[1]);
726 pos
= os_strchr(seq
, ';');
728 param2
= atoi(pos
+ 1);
732 ret
= esc_seq_to_key1(param1
, param2
, last
);
733 else if (seq
[0] == 'O')
734 ret
= esc_seq_to_key2(param1
, param2
, last
);
736 if (ret
!= EDIT_KEY_NONE
)
740 printf("\rUnknown escape sequence '%s'\n", seq
);
742 return EDIT_KEY_NONE
;
746 static enum edit_key_code
edit_read_key(int sock
)
749 unsigned char buf
[1];
752 static char esc_buf
[7];
754 res
= read(sock
, buf
, 1);
763 if (c
== 27 /* ESC */) {
765 return EDIT_KEY_NONE
;
769 show_esc_buf(esc_buf
, c
, 0);
778 if (esc_buf
[0] != '[' && esc_buf
[0] != 'O') {
779 show_esc_buf(esc_buf
, c
, 1);
781 return EDIT_KEY_NONE
;
783 return EDIT_KEY_NONE
; /* Escape sequence continues */
787 if ((c
>= '0' && c
<= '9') || c
== ';')
788 return EDIT_KEY_NONE
; /* Escape sequence continues */
790 if (c
== '~' || (c
>= 'A' && c
<= 'Z')) {
792 return esc_seq_to_key(esc_buf
);
795 show_esc_buf(esc_buf
, c
, 2);
797 return EDIT_KEY_NONE
;
802 return EDIT_KEY_CTRL_A
;
804 return EDIT_KEY_CTRL_B
;
806 return EDIT_KEY_CTRL_D
;
808 return EDIT_KEY_CTRL_E
;
810 return EDIT_KEY_CTRL_F
;
812 return EDIT_KEY_CTRL_G
;
814 return EDIT_KEY_CTRL_H
;
818 return EDIT_KEY_CTRL_J
;
820 return EDIT_KEY_ENTER
;
822 return EDIT_KEY_CTRL_K
;
824 return EDIT_KEY_CTRL_L
;
826 return EDIT_KEY_CTRL_N
;
828 return EDIT_KEY_CTRL_O
;
830 return EDIT_KEY_CTRL_P
;
832 return EDIT_KEY_CTRL_R
;
834 return EDIT_KEY_CTRL_T
;
836 return EDIT_KEY_CTRL_U
;
838 return EDIT_KEY_CTRL_V
;
840 return EDIT_KEY_CTRL_W
;
843 return EDIT_KEY_NONE
;
845 return EDIT_KEY_BACKSPACE
;
852 static char search_buf
[21];
853 static int search_skip
;
855 static char * search_find(void)
857 struct edit_history
*h
;
858 size_t len
= os_strlen(search_buf
);
859 int skip
= search_skip
;
864 dl_list_for_each(h
, &history_list
, struct edit_history
, list
) {
865 if (os_strstr(h
->str
, search_buf
)) {
877 static void search_redraw(void)
879 char *match
= search_find();
880 printf("\rsearch '%s': %s" CLEAR_END_LINE
,
881 search_buf
, match
? match
: "");
882 printf("\rsearch '%s", search_buf
);
887 static void search_start(void)
890 search_buf
[0] = '\0';
896 static void search_clear(void)
899 printf("\r" CLEAR_END_LINE
);
903 static void search_stop(void)
905 char *match
= search_find();
906 search_buf
[0] = '\0';
909 os_strlcpy(cmdbuf
, match
, CMD_BUF_LEN
);
910 cmdbuf_len
= os_strlen(cmdbuf
);
911 cmdbuf_pos
= cmdbuf_len
;
917 static void search_cancel(void)
919 search_buf
[0] = '\0';
925 static void search_backspace(void)
928 len
= os_strlen(search_buf
);
931 search_buf
[len
- 1] = '\0';
937 static void search_next(void)
945 static void search_char(char c
)
948 len
= os_strlen(search_buf
);
949 if (len
== sizeof(search_buf
) - 1)
952 search_buf
[len
+ 1] = '\0';
958 static enum edit_key_code
search_key(enum edit_key_code c
)
962 case EDIT_KEY_CTRL_J
:
967 case EDIT_KEY_CTRL_A
:
968 case EDIT_KEY_CTRL_E
:
975 case EDIT_KEY_CTRL_H
:
976 case EDIT_KEY_BACKSPACE
:
979 case EDIT_KEY_CTRL_R
:
983 if (c
>= 32 && c
<= 255)
988 return EDIT_KEY_NONE
;
992 static void edit_read_char(int sock
, void *eloop_ctx
, void *sock_ctx
)
994 static int last_tab
= 0;
995 static int search
= 0;
996 enum edit_key_code c
;
998 c
= edit_read_key(sock
);
1002 if (c
== EDIT_KEY_NONE
)
1005 if (c
== EDIT_KEY_EOF
)
1009 if (c
!= EDIT_KEY_TAB
&& c
!= EDIT_KEY_NONE
)
1016 edit_eof_cb(edit_cb_ctx
);
1023 case EDIT_KEY_CTRL_P
:
1027 case EDIT_KEY_CTRL_N
:
1030 case EDIT_KEY_RIGHT
:
1031 case EDIT_KEY_CTRL_F
:
1035 case EDIT_KEY_CTRL_B
:
1038 case EDIT_KEY_CTRL_RIGHT
:
1041 case EDIT_KEY_CTRL_LEFT
:
1044 case EDIT_KEY_DELETE
:
1051 case EDIT_KEY_CTRL_A
:
1055 history_debug_dump();
1057 case EDIT_KEY_CTRL_D
:
1058 if (cmdbuf_len
> 0) {
1063 edit_eof_cb(edit_cb_ctx
);
1065 case EDIT_KEY_CTRL_E
:
1068 case EDIT_KEY_CTRL_H
:
1069 case EDIT_KEY_BACKSPACE
:
1072 case EDIT_KEY_ENTER
:
1073 case EDIT_KEY_CTRL_J
:
1076 case EDIT_KEY_CTRL_K
:
1079 case EDIT_KEY_CTRL_L
:
1083 case EDIT_KEY_CTRL_R
:
1087 case EDIT_KEY_CTRL_U
:
1090 case EDIT_KEY_CTRL_W
:
1094 if (c
>= 32 && c
<= 255)
1101 int edit_init(void (*cmd_cb
)(void *ctx
, char *cmd
),
1102 void (*eof_cb
)(void *ctx
),
1103 char ** (*completion_cb
)(void *ctx
, const char *cmd
, int pos
),
1104 void *ctx
, const char *history_file
)
1106 dl_list_init(&history_list
);
1107 history_curr
= NULL
;
1109 history_read(history_file
);
1112 edit_cmd_cb
= cmd_cb
;
1113 edit_eof_cb
= eof_cb
;
1114 edit_completion_cb
= completion_cb
;
1116 tcgetattr(STDIN_FILENO
, &prevt
);
1118 newt
.c_lflag
&= ~(ICANON
| ECHO
);
1119 tcsetattr(STDIN_FILENO
, TCSANOW
, &newt
);
1121 eloop_register_read_sock(STDIN_FILENO
, edit_read_char
, NULL
, NULL
);
1130 void edit_deinit(const char *history_file
,
1131 int (*filter_cb
)(void *ctx
, const char *cmd
))
1133 struct edit_history
*h
;
1135 history_write(history_file
, filter_cb
);
1136 while ((h
= dl_list_first(&history_list
, struct edit_history
, list
))) {
1137 dl_list_del(&h
->list
);
1140 eloop_unregister_read_sock(STDIN_FILENO
);
1141 tcsetattr(STDIN_FILENO
, TCSANOW
, &prevt
);
1145 void edit_redraw(void)
1148 cmdbuf
[cmdbuf_len
] = '\0';
1149 printf("\r> %s", cmdbuf
);
1150 if (cmdbuf_pos
!= cmdbuf_len
) {
1151 tmp
= cmdbuf
[cmdbuf_pos
];
1152 cmdbuf
[cmdbuf_pos
] = '\0';
1153 printf("\r> %s", cmdbuf
);
1154 cmdbuf
[cmdbuf_pos
] = tmp
;