]>
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.
22 #define CMD_BUF_LEN 256
23 static char cmdbuf
[CMD_BUF_LEN
];
24 static int cmdbuf_pos
= 0;
25 static int cmdbuf_len
= 0;
26 #define CMD_HISTORY_LEN 20
27 static char history_buf
[CMD_HISTORY_LEN
][CMD_BUF_LEN
];
28 static int history_pos
= 0;
29 static int history_current
= 0;
31 static void *edit_cb_ctx
;
32 static void (*edit_cmd_cb
)(void *ctx
, char *cmd
);
33 static void (*edit_eof_cb
)(void *ctx
);
34 static char ** (*edit_completion_cb
)(void *ctx
, const char *cmd
, int pos
) =
37 static struct termios prevt
, newt
;
40 #define CLEAR_END_LINE "\e[K"
43 void edit_clear_line(void)
47 for (i
= 0; i
< cmdbuf_len
+ 2; i
++)
52 static void move_start(void)
59 static void move_end(void)
61 cmdbuf_pos
= cmdbuf_len
;
66 static void move_left(void)
75 static void move_right(void)
77 if (cmdbuf_pos
< cmdbuf_len
) {
84 static void move_word_left(void)
86 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] == ' ')
88 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] != ' ')
94 static void move_word_right(void)
96 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] == ' ')
98 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] != ' ')
104 static void delete_left(void)
110 os_memmove(cmdbuf
+ cmdbuf_pos
- 1, cmdbuf
+ cmdbuf_pos
,
111 cmdbuf_len
- cmdbuf_pos
);
118 static void delete_current(void)
120 if (cmdbuf_pos
== cmdbuf_len
)
124 os_memmove(cmdbuf
+ cmdbuf_pos
, cmdbuf
+ cmdbuf_pos
+ 1,
125 cmdbuf_len
- cmdbuf_pos
);
131 static void delete_word(void)
137 while (pos
> 0 && cmdbuf
[pos
- 1] == ' ')
139 while (pos
> 0 && cmdbuf
[pos
- 1] != ' ')
141 os_memmove(cmdbuf
+ pos
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
142 cmdbuf_len
-= cmdbuf_pos
- pos
;
148 static void clear_left(void)
154 os_memmove(cmdbuf
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
155 cmdbuf_len
-= cmdbuf_pos
;
161 static void clear_right(void)
163 if (cmdbuf_pos
== cmdbuf_len
)
167 cmdbuf_len
= cmdbuf_pos
;
172 static void history_add(const char *str
)
179 if (history_pos
== 0)
180 prev
= CMD_HISTORY_LEN
- 1;
182 prev
= history_pos
- 1;
183 if (os_strcmp(history_buf
[prev
], str
) == 0)
186 os_strlcpy(history_buf
[history_pos
], str
, CMD_BUF_LEN
);
188 if (history_pos
== CMD_HISTORY_LEN
)
190 history_current
= history_pos
;
194 static void history_prev(void)
198 if (history_current
== (history_pos
+ 1) % CMD_HISTORY_LEN
)
201 pos
= history_current
;
203 if (history_current
== history_pos
&& cmdbuf_len
) {
204 cmdbuf
[cmdbuf_len
] = '\0';
211 pos
= CMD_HISTORY_LEN
- 1;
212 if (history_buf
[pos
][0] == '\0')
214 history_current
= pos
;
217 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_buf
[history_current
]);
218 os_memcpy(cmdbuf
, history_buf
[history_current
], cmdbuf_len
);
223 static void history_next(void)
225 if (history_current
== history_pos
)
229 if (history_current
== CMD_HISTORY_LEN
)
233 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_buf
[history_current
]);
234 os_memcpy(cmdbuf
, history_buf
[history_current
], cmdbuf_len
);
239 static void history_debug_dump(void)
244 p
= (history_pos
+ 1) % CMD_HISTORY_LEN
;
246 printf("[%d%s%s] %s\n",
247 p
, p
== history_current
? "C" : "",
248 p
== history_pos
? "P" : "", history_buf
[p
]);
249 if (p
== history_pos
)
252 if (p
== CMD_HISTORY_LEN
)
259 static void insert_char(int c
)
261 if (cmdbuf_len
>= (int) sizeof(cmdbuf
) - 1)
263 if (cmdbuf_len
== cmdbuf_pos
) {
264 cmdbuf
[cmdbuf_pos
++] = c
;
269 os_memmove(cmdbuf
+ cmdbuf_pos
+ 1, cmdbuf
+ cmdbuf_pos
,
270 cmdbuf_len
- cmdbuf_pos
);
271 cmdbuf
[cmdbuf_pos
++] = c
;
278 static void process_cmd(void)
281 if (cmdbuf_len
== 0) {
287 cmdbuf
[cmdbuf_len
] = '\0';
291 edit_cmd_cb(edit_cb_ctx
, cmdbuf
);
297 static void free_completions(char **c
)
302 for (i
= 0; c
[i
]; i
++)
308 static int filter_strings(char **c
, char *str
, size_t len
)
312 for (i
= 0, j
= 0; c
[j
]; j
++) {
313 if (os_strncasecmp(c
[j
], str
, len
) == 0) {
329 static int common_len(const char *a
, const char *b
)
332 while (a
[len
] && a
[len
] == b
[len
])
338 static int max_common_length(char **c
)
342 len
= os_strlen(c
[0]);
343 for (i
= 1; c
[i
]; i
++) {
344 int same
= common_len(c
[0], c
[i
]);
353 static int cmp_str(const void *a
, const void *b
)
355 return os_strcmp(* (const char **) a
, * (const char **) b
);
358 static void complete(int list
)
363 int room
, plen
, add_space
;
365 if (edit_completion_cb
== NULL
)
368 cmdbuf
[cmdbuf_len
] = '\0';
369 c
= edit_completion_cb(edit_cb_ctx
, cmdbuf
, cmdbuf_pos
);
375 while (start
> 0 && cmdbuf
[start
- 1] != ' ')
379 count
= filter_strings(c
, &cmdbuf
[start
], plen
);
385 len
= max_common_length(c
);
386 if (len
<= plen
&& count
> 1) {
388 qsort(c
, count
, sizeof(char *), cmp_str
);
391 for (i
= 0; c
[i
]; i
++)
392 printf("%s%s", i
> 0 ? " " : "", c
[i
]);
401 room
= sizeof(cmdbuf
) - 1 - cmdbuf_len
;
404 add_space
= count
== 1 && len
< room
;
406 os_memmove(cmdbuf
+ cmdbuf_pos
+ len
+ add_space
, cmdbuf
+ cmdbuf_pos
,
407 cmdbuf_len
- cmdbuf_pos
);
408 os_memcpy(&cmdbuf
[cmdbuf_pos
- plen
], c
[0], plen
+ len
);
410 cmdbuf
[cmdbuf_pos
+ len
] = ' ';
412 cmdbuf_pos
+= len
+ add_space
;
413 cmdbuf_len
+= len
+ add_space
;
476 EDIT_KEY_SHIFT_RIGHT
,
478 EDIT_KEY_ALT_SHIFT_UP
,
479 EDIT_KEY_ALT_SHIFT_DOWN
,
480 EDIT_KEY_ALT_SHIFT_RIGHT
,
481 EDIT_KEY_ALT_SHIFT_LEFT
,
485 static void show_esc_buf(const char *esc_buf
, char c
, int i
)
488 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf
, c
, i
);
493 static enum edit_key_code
esc_seq_to_key1_no(char last
)
499 return EDIT_KEY_DOWN
;
501 return EDIT_KEY_RIGHT
;
503 return EDIT_KEY_LEFT
;
505 return EDIT_KEY_NONE
;
510 static enum edit_key_code
esc_seq_to_key1_shift(char last
)
514 return EDIT_KEY_SHIFT_UP
;
516 return EDIT_KEY_SHIFT_DOWN
;
518 return EDIT_KEY_SHIFT_RIGHT
;
520 return EDIT_KEY_SHIFT_LEFT
;
522 return EDIT_KEY_NONE
;
527 static enum edit_key_code
esc_seq_to_key1_alt(char last
)
531 return EDIT_KEY_ALT_UP
;
533 return EDIT_KEY_ALT_DOWN
;
535 return EDIT_KEY_ALT_RIGHT
;
537 return EDIT_KEY_ALT_LEFT
;
539 return EDIT_KEY_NONE
;
544 static enum edit_key_code
esc_seq_to_key1_alt_shift(char last
)
548 return EDIT_KEY_ALT_SHIFT_UP
;
550 return EDIT_KEY_ALT_SHIFT_DOWN
;
552 return EDIT_KEY_ALT_SHIFT_RIGHT
;
554 return EDIT_KEY_ALT_SHIFT_LEFT
;
556 return EDIT_KEY_NONE
;
561 static enum edit_key_code
esc_seq_to_key1_ctrl(char last
)
565 return EDIT_KEY_CTRL_UP
;
567 return EDIT_KEY_CTRL_DOWN
;
569 return EDIT_KEY_CTRL_RIGHT
;
571 return EDIT_KEY_CTRL_LEFT
;
573 return EDIT_KEY_NONE
;
578 static enum edit_key_code
esc_seq_to_key1(int param1
, int param2
, char last
)
580 /* ESC-[<param1>;<param2><last> */
582 if (param1
< 0 && param2
< 0)
583 return esc_seq_to_key1_no(last
);
585 if (param1
== 1 && param2
== 2)
586 return esc_seq_to_key1_shift(last
);
588 if (param1
== 1 && param2
== 3)
589 return esc_seq_to_key1_alt(last
);
591 if (param1
== 1 && param2
== 4)
592 return esc_seq_to_key1_alt_shift(last
);
594 if (param1
== 1 && param2
== 5)
595 return esc_seq_to_key1_ctrl(last
);
599 return EDIT_KEY_NONE
;
602 return EDIT_KEY_INSERT
;
604 return EDIT_KEY_DELETE
;
606 return EDIT_KEY_PAGE_UP
;
608 return EDIT_KEY_PAGE_DOWN
;
628 return EDIT_KEY_NONE
;
632 static enum edit_key_code
esc_seq_to_key2(int param1
, int param2
, char last
)
634 /* ESC-O<param1>;<param2><last> */
636 if (param1
>= 0 || param2
>= 0)
637 return EDIT_KEY_NONE
;
643 return EDIT_KEY_HOME
;
653 return EDIT_KEY_NONE
;
658 static enum edit_key_code
esc_seq_to_key(char *seq
)
661 int param1
= -1, param2
= -1;
662 enum edit_key_code ret
= EDIT_KEY_NONE
;
664 for (pos
= seq
; *pos
; pos
++)
667 if (seq
[1] >= '0' && seq
[1] <= '9') {
668 param1
= atoi(&seq
[1]);
669 pos
= os_strchr(seq
, ';');
671 param2
= atoi(pos
+ 1);
675 ret
= esc_seq_to_key1(param1
, param2
, last
);
676 else if (seq
[0] == 'O')
677 ret
= esc_seq_to_key2(param1
, param2
, last
);
679 if (ret
!= EDIT_KEY_NONE
)
683 printf("\rUnknown escape sequence '%s'\n", seq
);
685 return EDIT_KEY_NONE
;
689 static enum edit_key_code
edit_read_key(int sock
)
692 unsigned char buf
[1];
695 static char esc_buf
[7];
697 res
= read(sock
, buf
, 1);
706 if (c
== 27 /* ESC */) {
708 return EDIT_KEY_NONE
;
712 show_esc_buf(esc_buf
, c
, 0);
721 if (esc_buf
[0] != '[' && esc_buf
[0] != 'O') {
722 show_esc_buf(esc_buf
, c
, 1);
724 return EDIT_KEY_NONE
;
726 return EDIT_KEY_NONE
; /* Escape sequence continues */
730 if ((c
>= '0' && c
<= '9') || c
== ';')
731 return EDIT_KEY_NONE
; /* Escape sequence continues */
733 if (c
== '~' || (c
>= 'A' && c
<= 'Z')) {
735 return esc_seq_to_key(esc_buf
);
738 show_esc_buf(esc_buf
, c
, 2);
740 return EDIT_KEY_NONE
;
745 return EDIT_KEY_CTRL_A
;
747 return EDIT_KEY_CTRL_B
;
749 return EDIT_KEY_CTRL_D
;
751 return EDIT_KEY_CTRL_E
;
753 return EDIT_KEY_CTRL_F
;
755 return EDIT_KEY_CTRL_G
;
757 return EDIT_KEY_CTRL_H
;
761 return EDIT_KEY_CTRL_J
;
763 return EDIT_KEY_ENTER
;
765 return EDIT_KEY_CTRL_K
;
767 return EDIT_KEY_CTRL_L
;
769 return EDIT_KEY_CTRL_N
;
771 return EDIT_KEY_CTRL_O
;
773 return EDIT_KEY_CTRL_P
;
775 return EDIT_KEY_CTRL_R
;
777 return EDIT_KEY_CTRL_T
;
779 return EDIT_KEY_CTRL_U
;
781 return EDIT_KEY_CTRL_V
;
783 return EDIT_KEY_CTRL_W
;
786 return EDIT_KEY_NONE
;
788 return EDIT_KEY_BACKSPACE
;
795 static char search_buf
[21];
796 static int search_skip
;
798 static char * search_find(void)
800 int pos
= history_pos
;
801 size_t len
= os_strlen(search_buf
);
802 int skip
= search_skip
;
809 pos
= CMD_HISTORY_LEN
- 1;
812 if (pos
== history_pos
) {
817 if (os_strstr(history_buf
[pos
], search_buf
)) {
819 return history_buf
[pos
];
826 static void search_redraw(void)
828 char *match
= search_find();
829 printf("\rsearch '%s': %s" CLEAR_END_LINE
,
830 search_buf
, match
? match
: "");
831 printf("\rsearch '%s", search_buf
);
836 static void search_start(void)
839 search_buf
[0] = '\0';
845 static void search_clear(void)
848 printf("\r" CLEAR_END_LINE
);
852 static void search_stop(void)
854 char *match
= search_find();
855 search_buf
[0] = '\0';
858 os_strlcpy(cmdbuf
, match
, CMD_BUF_LEN
);
859 cmdbuf_len
= os_strlen(cmdbuf
);
860 cmdbuf_pos
= cmdbuf_len
;
866 static void search_cancel(void)
868 search_buf
[0] = '\0';
874 static void search_backspace(void)
877 len
= os_strlen(search_buf
);
880 search_buf
[len
- 1] = '\0';
886 static void search_next(void)
894 static void search_char(char c
)
897 len
= os_strlen(search_buf
);
898 if (len
== sizeof(search_buf
) - 1)
901 search_buf
[len
+ 1] = '\0';
907 static enum edit_key_code
search_key(enum edit_key_code c
)
911 case EDIT_KEY_CTRL_J
:
916 case EDIT_KEY_CTRL_A
:
917 case EDIT_KEY_CTRL_E
:
924 case EDIT_KEY_CTRL_H
:
925 case EDIT_KEY_BACKSPACE
:
928 case EDIT_KEY_CTRL_R
:
932 if (c
>= 32 && c
<= 255)
937 return EDIT_KEY_NONE
;
941 static void edit_read_char(int sock
, void *eloop_ctx
, void *sock_ctx
)
943 static int last_tab
= 0;
944 static int search
= 0;
945 enum edit_key_code c
;
947 c
= edit_read_key(sock
);
951 if (c
== EDIT_KEY_NONE
)
954 if (c
== EDIT_KEY_EOF
)
958 if (c
!= EDIT_KEY_TAB
&& c
!= EDIT_KEY_NONE
)
965 edit_eof_cb(edit_cb_ctx
);
972 case EDIT_KEY_CTRL_P
:
976 case EDIT_KEY_CTRL_N
:
980 case EDIT_KEY_CTRL_F
:
984 case EDIT_KEY_CTRL_B
:
987 case EDIT_KEY_CTRL_RIGHT
:
990 case EDIT_KEY_CTRL_LEFT
:
993 case EDIT_KEY_DELETE
:
1000 case EDIT_KEY_CTRL_A
:
1004 history_debug_dump();
1006 case EDIT_KEY_CTRL_D
:
1007 if (cmdbuf_len
> 0) {
1012 edit_eof_cb(edit_cb_ctx
);
1014 case EDIT_KEY_CTRL_E
:
1017 case EDIT_KEY_CTRL_H
:
1018 case EDIT_KEY_BACKSPACE
:
1021 case EDIT_KEY_ENTER
:
1022 case EDIT_KEY_CTRL_J
:
1025 case EDIT_KEY_CTRL_K
:
1028 case EDIT_KEY_CTRL_L
:
1032 case EDIT_KEY_CTRL_R
:
1036 case EDIT_KEY_CTRL_U
:
1039 case EDIT_KEY_CTRL_W
:
1043 if (c
>= 32 && c
<= 255)
1050 int edit_init(void (*cmd_cb
)(void *ctx
, char *cmd
),
1051 void (*eof_cb
)(void *ctx
),
1054 os_memset(history_buf
, 0, sizeof(history_buf
));
1057 edit_cmd_cb
= cmd_cb
;
1058 edit_eof_cb
= eof_cb
;
1060 tcgetattr(STDIN_FILENO
, &prevt
);
1062 newt
.c_lflag
&= ~(ICANON
| ECHO
);
1063 tcsetattr(STDIN_FILENO
, TCSANOW
, &newt
);
1065 eloop_register_read_sock(STDIN_FILENO
, edit_read_char
, NULL
, NULL
);
1074 void edit_deinit(void)
1076 eloop_unregister_read_sock(STDIN_FILENO
);
1077 tcsetattr(STDIN_FILENO
, TCSANOW
, &prevt
);
1081 void edit_redraw(void)
1084 cmdbuf
[cmdbuf_len
] = '\0';
1085 printf("\r> %s", cmdbuf
);
1086 if (cmdbuf_pos
!= cmdbuf_len
) {
1087 tmp
= cmdbuf
[cmdbuf_pos
];
1088 cmdbuf
[cmdbuf_pos
] = '\0';
1089 printf("\r> %s", cmdbuf
);
1090 cmdbuf
[cmdbuf_pos
] = tmp
;
1096 void edit_set_filter_history_cb(int (*cb
)(void *ctx
, const char *cmd
))
1101 void edit_set_completion_cb(char ** (*cb
)(void *ctx
, const char *cmd
, int pos
))
1103 edit_completion_cb
= cb
;