]>
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 void edit_clear_line(void)
44 for (i
= 0; i
< cmdbuf_len
+ 2; i
++)
49 static void move_start(void)
56 static void move_end(void)
58 cmdbuf_pos
= cmdbuf_len
;
63 static void move_left(void)
72 static void move_right(void)
74 if (cmdbuf_pos
< cmdbuf_len
) {
81 static void move_word_left(void)
83 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] == ' ')
85 while (cmdbuf_pos
> 0 && cmdbuf
[cmdbuf_pos
- 1] != ' ')
91 static void move_word_right(void)
93 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] == ' ')
95 while (cmdbuf_pos
< cmdbuf_len
&& cmdbuf
[cmdbuf_pos
] != ' ')
101 static void delete_left(void)
107 os_memmove(cmdbuf
+ cmdbuf_pos
- 1, cmdbuf
+ cmdbuf_pos
,
108 cmdbuf_len
- cmdbuf_pos
);
115 static void delete_current(void)
117 if (cmdbuf_pos
== cmdbuf_len
)
121 os_memmove(cmdbuf
+ cmdbuf_pos
, cmdbuf
+ cmdbuf_pos
+ 1,
122 cmdbuf_len
- cmdbuf_pos
);
128 static void delete_word(void)
131 while (cmdbuf_len
> 0 && cmdbuf
[cmdbuf_len
- 1] == ' ')
133 while (cmdbuf_len
> 0 && cmdbuf
[cmdbuf_len
- 1] != ' ')
135 if (cmdbuf_pos
> cmdbuf_len
)
136 cmdbuf_pos
= cmdbuf_len
;
141 static void clear_left(void)
147 os_memmove(cmdbuf
, cmdbuf
+ cmdbuf_pos
, cmdbuf_len
- cmdbuf_pos
);
148 cmdbuf_len
-= cmdbuf_pos
;
154 static void clear_right(void)
156 if (cmdbuf_pos
== cmdbuf_len
)
160 cmdbuf_len
= cmdbuf_pos
;
165 static void history_add(const char *str
)
172 if (history_pos
== 0)
173 prev
= CMD_HISTORY_LEN
- 1;
175 prev
= history_pos
- 1;
176 if (os_strcmp(history_buf
[prev
], str
) == 0)
179 os_strlcpy(history_buf
[history_pos
], str
, CMD_BUF_LEN
);
181 if (history_pos
== CMD_HISTORY_LEN
)
183 history_current
= history_pos
;
187 static void history_prev(void)
191 if (history_current
== (history_pos
+ 1) % CMD_HISTORY_LEN
)
194 pos
= history_current
;
196 if (history_current
== history_pos
&& cmdbuf_len
) {
197 cmdbuf
[cmdbuf_len
] = '\0';
204 pos
= CMD_HISTORY_LEN
- 1;
205 if (history_buf
[pos
][0] == '\0')
207 history_current
= pos
;
210 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_buf
[history_current
]);
211 os_memcpy(cmdbuf
, history_buf
[history_current
], cmdbuf_len
);
216 static void history_next(void)
218 if (history_current
== history_pos
)
222 if (history_current
== CMD_HISTORY_LEN
)
226 cmdbuf_len
= cmdbuf_pos
= os_strlen(history_buf
[history_current
]);
227 os_memcpy(cmdbuf
, history_buf
[history_current
], cmdbuf_len
);
232 static void history_debug_dump(void)
237 p
= (history_pos
+ 1) % CMD_HISTORY_LEN
;
239 printf("[%d%s%s] %s\n",
240 p
, p
== history_current
? "C" : "",
241 p
== history_pos
? "P" : "", history_buf
[p
]);
242 if (p
== history_pos
)
245 if (p
== CMD_HISTORY_LEN
)
252 static void insert_char(int c
)
254 if (cmdbuf_len
>= (int) sizeof(cmdbuf
) - 1)
256 if (cmdbuf_len
== cmdbuf_pos
) {
257 cmdbuf
[cmdbuf_pos
++] = c
;
262 os_memmove(cmdbuf
+ cmdbuf_pos
+ 1, cmdbuf
+ cmdbuf_pos
,
263 cmdbuf_len
- cmdbuf_pos
);
264 cmdbuf
[cmdbuf_pos
++] = c
;
271 static void process_cmd(void)
274 if (cmdbuf_len
== 0) {
280 cmdbuf
[cmdbuf_len
] = '\0';
284 edit_cmd_cb(edit_cb_ctx
, cmdbuf
);
290 static void free_completions(char **c
)
295 for (i
= 0; c
[i
]; i
++)
301 static int filter_strings(char **c
, char *str
, size_t len
)
305 for (i
= 0, j
= 0; c
[j
]; j
++) {
306 if (os_strncasecmp(c
[j
], str
, len
) == 0) {
322 static int common_len(const char *a
, const char *b
)
325 while (a
[len
] && a
[len
] == b
[len
])
331 static int max_common_length(char **c
)
335 len
= os_strlen(c
[0]);
336 for (i
= 1; c
[i
]; i
++) {
337 int same
= common_len(c
[0], c
[i
]);
346 static int cmp_str(const void *a
, const void *b
)
348 return os_strcmp(* (const char **) a
, * (const char **) b
);
351 static void complete(int list
)
356 int room
, plen
, add_space
;
358 if (edit_completion_cb
== NULL
)
361 cmdbuf
[cmdbuf_len
] = '\0';
362 c
= edit_completion_cb(edit_cb_ctx
, cmdbuf
, cmdbuf_pos
);
368 while (start
> 0 && cmdbuf
[start
- 1] != ' ')
372 count
= filter_strings(c
, &cmdbuf
[start
], plen
);
378 len
= max_common_length(c
);
379 if (len
<= plen
&& count
> 1) {
381 qsort(c
, count
, sizeof(char *), cmp_str
);
384 for (i
= 0; c
[i
]; i
++)
385 printf("%s%s", i
> 0 ? " " : "", c
[i
]);
394 room
= sizeof(cmdbuf
) - 1 - cmdbuf_len
;
397 add_space
= count
== 1 && len
< room
;
399 os_memmove(cmdbuf
+ cmdbuf_pos
+ len
+ add_space
, cmdbuf
+ cmdbuf_pos
,
400 cmdbuf_len
- cmdbuf_pos
);
401 os_memcpy(&cmdbuf
[cmdbuf_pos
- plen
], c
[0], plen
+ len
);
403 cmdbuf
[cmdbuf_pos
+ len
] = ' ';
405 cmdbuf_pos
+= len
+ add_space
;
406 cmdbuf_len
+= len
+ add_space
;
469 EDIT_KEY_SHIFT_RIGHT
,
471 EDIT_KEY_ALT_SHIFT_UP
,
472 EDIT_KEY_ALT_SHIFT_DOWN
,
473 EDIT_KEY_ALT_SHIFT_RIGHT
,
474 EDIT_KEY_ALT_SHIFT_LEFT
,
478 static void show_esc_buf(const char *esc_buf
, char c
, int i
)
481 printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf
, c
, i
);
486 static enum edit_key_code
esc_seq_to_key1_no(char last
)
492 return EDIT_KEY_DOWN
;
494 return EDIT_KEY_RIGHT
;
496 return EDIT_KEY_LEFT
;
498 return EDIT_KEY_NONE
;
503 static enum edit_key_code
esc_seq_to_key1_shift(char last
)
507 return EDIT_KEY_SHIFT_UP
;
509 return EDIT_KEY_SHIFT_DOWN
;
511 return EDIT_KEY_SHIFT_RIGHT
;
513 return EDIT_KEY_SHIFT_LEFT
;
515 return EDIT_KEY_NONE
;
520 static enum edit_key_code
esc_seq_to_key1_alt(char last
)
524 return EDIT_KEY_ALT_UP
;
526 return EDIT_KEY_ALT_DOWN
;
528 return EDIT_KEY_ALT_RIGHT
;
530 return EDIT_KEY_ALT_LEFT
;
532 return EDIT_KEY_NONE
;
537 static enum edit_key_code
esc_seq_to_key1_alt_shift(char last
)
541 return EDIT_KEY_ALT_SHIFT_UP
;
543 return EDIT_KEY_ALT_SHIFT_DOWN
;
545 return EDIT_KEY_ALT_SHIFT_RIGHT
;
547 return EDIT_KEY_ALT_SHIFT_LEFT
;
549 return EDIT_KEY_NONE
;
554 static enum edit_key_code
esc_seq_to_key1_ctrl(char last
)
558 return EDIT_KEY_CTRL_UP
;
560 return EDIT_KEY_CTRL_DOWN
;
562 return EDIT_KEY_CTRL_RIGHT
;
564 return EDIT_KEY_CTRL_LEFT
;
566 return EDIT_KEY_NONE
;
571 static enum edit_key_code
esc_seq_to_key1(int param1
, int param2
, char last
)
573 /* ESC-[<param1>;<param2><last> */
575 if (param1
< 0 && param2
< 0)
576 return esc_seq_to_key1_no(last
);
578 if (param1
== 1 && param2
== 2)
579 return esc_seq_to_key1_shift(last
);
581 if (param1
== 1 && param2
== 3)
582 return esc_seq_to_key1_alt(last
);
584 if (param1
== 1 && param2
== 4)
585 return esc_seq_to_key1_alt_shift(last
);
587 if (param1
== 1 && param2
== 5)
588 return esc_seq_to_key1_ctrl(last
);
592 return EDIT_KEY_NONE
;
595 return EDIT_KEY_INSERT
;
597 return EDIT_KEY_DELETE
;
599 return EDIT_KEY_PAGE_UP
;
601 return EDIT_KEY_PAGE_DOWN
;
621 return EDIT_KEY_NONE
;
625 static enum edit_key_code
esc_seq_to_key2(int param1
, int param2
, char last
)
627 /* ESC-O<param1>;<param2><last> */
629 if (param1
>= 0 || param2
>= 0)
630 return EDIT_KEY_NONE
;
636 return EDIT_KEY_HOME
;
646 return EDIT_KEY_NONE
;
651 static enum edit_key_code
esc_seq_to_key(char *seq
)
654 int param1
= -1, param2
= -1;
655 enum edit_key_code ret
= EDIT_KEY_NONE
;
657 for (pos
= seq
; *pos
; pos
++)
660 if (seq
[1] >= '0' && seq
[1] <= '9') {
661 param1
= atoi(&seq
[1]);
662 pos
= os_strchr(seq
, ';');
664 param2
= atoi(pos
+ 1);
668 ret
= esc_seq_to_key1(param1
, param2
, last
);
669 else if (seq
[0] == 'O')
670 ret
= esc_seq_to_key2(param1
, param2
, last
);
672 if (ret
!= EDIT_KEY_NONE
)
676 printf("\rUnknown escape sequence '%s'\n", seq
);
678 return EDIT_KEY_NONE
;
682 static enum edit_key_code
edit_read_key(int sock
)
685 unsigned char buf
[1];
688 static char esc_buf
[7];
690 res
= read(sock
, buf
, 1);
699 if (c
== 27 /* ESC */) {
701 return EDIT_KEY_NONE
;
705 show_esc_buf(esc_buf
, c
, 0);
714 if (esc_buf
[0] != '[' && esc_buf
[0] != 'O') {
715 show_esc_buf(esc_buf
, c
, 1);
717 return EDIT_KEY_NONE
;
719 return EDIT_KEY_NONE
; /* Escape sequence continues */
723 if ((c
>= '0' && c
<= '9') || c
== ';')
724 return EDIT_KEY_NONE
; /* Escape sequence continues */
726 if (c
== '~' || (c
>= 'A' && c
<= 'Z')) {
728 return esc_seq_to_key(esc_buf
);
731 show_esc_buf(esc_buf
, c
, 2);
733 return EDIT_KEY_NONE
;
738 return EDIT_KEY_CTRL_A
;
740 return EDIT_KEY_CTRL_B
;
742 return EDIT_KEY_CTRL_D
;
744 return EDIT_KEY_CTRL_E
;
746 return EDIT_KEY_CTRL_F
;
748 return EDIT_KEY_CTRL_G
;
750 return EDIT_KEY_CTRL_H
;
754 return EDIT_KEY_CTRL_J
;
756 return EDIT_KEY_ENTER
;
758 return EDIT_KEY_CTRL_K
;
760 return EDIT_KEY_CTRL_L
;
762 return EDIT_KEY_CTRL_N
;
764 return EDIT_KEY_CTRL_O
;
766 return EDIT_KEY_CTRL_P
;
768 return EDIT_KEY_CTRL_R
;
770 return EDIT_KEY_CTRL_T
;
772 return EDIT_KEY_CTRL_U
;
774 return EDIT_KEY_CTRL_V
;
776 return EDIT_KEY_CTRL_W
;
779 return EDIT_KEY_NONE
;
781 return EDIT_KEY_BACKSPACE
;
788 static void edit_read_char(int sock
, void *eloop_ctx
, void *sock_ctx
)
790 static int last_tab
= 0;
791 enum edit_key_code c
;
793 c
= edit_read_key(sock
);
795 if (c
!= EDIT_KEY_TAB
&& c
!= EDIT_KEY_NONE
)
802 edit_eof_cb(edit_cb_ctx
);
809 case EDIT_KEY_CTRL_P
:
813 case EDIT_KEY_CTRL_N
:
817 case EDIT_KEY_CTRL_F
:
821 case EDIT_KEY_CTRL_B
:
824 case EDIT_KEY_CTRL_RIGHT
:
827 case EDIT_KEY_CTRL_LEFT
:
830 case EDIT_KEY_DELETE
:
837 case EDIT_KEY_CTRL_A
:
841 history_debug_dump();
843 case EDIT_KEY_CTRL_D
:
844 if (cmdbuf_len
> 0) {
849 edit_eof_cb(edit_cb_ctx
);
851 case EDIT_KEY_CTRL_E
:
854 case EDIT_KEY_CTRL_H
:
855 case EDIT_KEY_BACKSPACE
:
859 case EDIT_KEY_CTRL_J
:
862 case EDIT_KEY_CTRL_K
:
865 case EDIT_KEY_CTRL_L
:
869 case EDIT_KEY_CTRL_R
:
870 /* TODO: search history */
872 case EDIT_KEY_CTRL_U
:
875 case EDIT_KEY_CTRL_W
:
879 if (c
>= 32 && c
<= 255)
886 int edit_init(void (*cmd_cb
)(void *ctx
, char *cmd
),
887 void (*eof_cb
)(void *ctx
),
890 os_memset(history_buf
, 0, sizeof(history_buf
));
893 edit_cmd_cb
= cmd_cb
;
894 edit_eof_cb
= eof_cb
;
896 tcgetattr(STDIN_FILENO
, &prevt
);
898 newt
.c_lflag
&= ~(ICANON
| ECHO
);
899 tcsetattr(STDIN_FILENO
, TCSANOW
, &newt
);
901 eloop_register_read_sock(STDIN_FILENO
, edit_read_char
, NULL
, NULL
);
910 void edit_deinit(void)
912 eloop_unregister_read_sock(STDIN_FILENO
);
913 tcsetattr(STDIN_FILENO
, TCSANOW
, &prevt
);
917 void edit_redraw(void)
920 cmdbuf
[cmdbuf_len
] = '\0';
921 printf("\r> %s", cmdbuf
);
922 if (cmdbuf_pos
!= cmdbuf_len
) {
923 tmp
= cmdbuf
[cmdbuf_pos
];
924 cmdbuf
[cmdbuf_pos
] = '\0';
925 printf("\r> %s", cmdbuf
);
926 cmdbuf
[cmdbuf_pos
] = tmp
;
932 void edit_set_filter_history_cb(int (*cb
)(void *ctx
, const char *cmd
))
937 void edit_set_completion_cb(char ** (*cb
)(void *ctx
, const char *cmd
, int pos
))
939 edit_completion_cb
= cb
;