1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo * Command line editing and history
3f05cddf9SRui Paulo * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
4f05cddf9SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7f05cddf9SRui Paulo */
8f05cddf9SRui Paulo
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo #include <termios.h>
11f05cddf9SRui Paulo
12f05cddf9SRui Paulo #include "common.h"
13f05cddf9SRui Paulo #include "eloop.h"
14f05cddf9SRui Paulo #include "list.h"
15f05cddf9SRui Paulo #include "edit.h"
16f05cddf9SRui Paulo
17*5b9c547cSRui Paulo #define CMD_BUF_LEN 4096
18f05cddf9SRui Paulo static char cmdbuf[CMD_BUF_LEN];
19f05cddf9SRui Paulo static int cmdbuf_pos = 0;
20f05cddf9SRui Paulo static int cmdbuf_len = 0;
21f05cddf9SRui Paulo static char currbuf[CMD_BUF_LEN];
22f05cddf9SRui Paulo static int currbuf_valid = 0;
23f05cddf9SRui Paulo static const char *ps2 = NULL;
24f05cddf9SRui Paulo
25f05cddf9SRui Paulo #define HISTORY_MAX 100
26f05cddf9SRui Paulo
27f05cddf9SRui Paulo struct edit_history {
28f05cddf9SRui Paulo struct dl_list list;
29f05cddf9SRui Paulo char str[1];
30f05cddf9SRui Paulo };
31f05cddf9SRui Paulo
32f05cddf9SRui Paulo static struct dl_list history_list;
33f05cddf9SRui Paulo static struct edit_history *history_curr;
34f05cddf9SRui Paulo
35f05cddf9SRui Paulo static void *edit_cb_ctx;
36f05cddf9SRui Paulo static void (*edit_cmd_cb)(void *ctx, char *cmd);
37f05cddf9SRui Paulo static void (*edit_eof_cb)(void *ctx);
38f05cddf9SRui Paulo static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
39f05cddf9SRui Paulo NULL;
40f05cddf9SRui Paulo
41f05cddf9SRui Paulo static struct termios prevt, newt;
42f05cddf9SRui Paulo
43f05cddf9SRui Paulo
44f05cddf9SRui Paulo #define CLEAR_END_LINE "\e[K"
45f05cddf9SRui Paulo
46f05cddf9SRui Paulo
edit_clear_line(void)47f05cddf9SRui Paulo void edit_clear_line(void)
48f05cddf9SRui Paulo {
49f05cddf9SRui Paulo int i;
50f05cddf9SRui Paulo putchar('\r');
51f05cddf9SRui Paulo for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
52f05cddf9SRui Paulo putchar(' ');
53f05cddf9SRui Paulo }
54f05cddf9SRui Paulo
55f05cddf9SRui Paulo
move_start(void)56f05cddf9SRui Paulo static void move_start(void)
57f05cddf9SRui Paulo {
58f05cddf9SRui Paulo cmdbuf_pos = 0;
59f05cddf9SRui Paulo edit_redraw();
60f05cddf9SRui Paulo }
61f05cddf9SRui Paulo
62f05cddf9SRui Paulo
move_end(void)63f05cddf9SRui Paulo static void move_end(void)
64f05cddf9SRui Paulo {
65f05cddf9SRui Paulo cmdbuf_pos = cmdbuf_len;
66f05cddf9SRui Paulo edit_redraw();
67f05cddf9SRui Paulo }
68f05cddf9SRui Paulo
69f05cddf9SRui Paulo
move_left(void)70f05cddf9SRui Paulo static void move_left(void)
71f05cddf9SRui Paulo {
72f05cddf9SRui Paulo if (cmdbuf_pos > 0) {
73f05cddf9SRui Paulo cmdbuf_pos--;
74f05cddf9SRui Paulo edit_redraw();
75f05cddf9SRui Paulo }
76f05cddf9SRui Paulo }
77f05cddf9SRui Paulo
78f05cddf9SRui Paulo
move_right(void)79f05cddf9SRui Paulo static void move_right(void)
80f05cddf9SRui Paulo {
81f05cddf9SRui Paulo if (cmdbuf_pos < cmdbuf_len) {
82f05cddf9SRui Paulo cmdbuf_pos++;
83f05cddf9SRui Paulo edit_redraw();
84f05cddf9SRui Paulo }
85f05cddf9SRui Paulo }
86f05cddf9SRui Paulo
87f05cddf9SRui Paulo
move_word_left(void)88f05cddf9SRui Paulo static void move_word_left(void)
89f05cddf9SRui Paulo {
90f05cddf9SRui Paulo while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
91f05cddf9SRui Paulo cmdbuf_pos--;
92f05cddf9SRui Paulo while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
93f05cddf9SRui Paulo cmdbuf_pos--;
94f05cddf9SRui Paulo edit_redraw();
95f05cddf9SRui Paulo }
96f05cddf9SRui Paulo
97f05cddf9SRui Paulo
move_word_right(void)98f05cddf9SRui Paulo static void move_word_right(void)
99f05cddf9SRui Paulo {
100f05cddf9SRui Paulo while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
101f05cddf9SRui Paulo cmdbuf_pos++;
102f05cddf9SRui Paulo while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
103f05cddf9SRui Paulo cmdbuf_pos++;
104f05cddf9SRui Paulo edit_redraw();
105f05cddf9SRui Paulo }
106f05cddf9SRui Paulo
107f05cddf9SRui Paulo
delete_left(void)108f05cddf9SRui Paulo static void delete_left(void)
109f05cddf9SRui Paulo {
110f05cddf9SRui Paulo if (cmdbuf_pos == 0)
111f05cddf9SRui Paulo return;
112f05cddf9SRui Paulo
113f05cddf9SRui Paulo edit_clear_line();
114f05cddf9SRui Paulo os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
115f05cddf9SRui Paulo cmdbuf_len - cmdbuf_pos);
116f05cddf9SRui Paulo cmdbuf_pos--;
117f05cddf9SRui Paulo cmdbuf_len--;
118f05cddf9SRui Paulo edit_redraw();
119f05cddf9SRui Paulo }
120f05cddf9SRui Paulo
121f05cddf9SRui Paulo
delete_current(void)122f05cddf9SRui Paulo static void delete_current(void)
123f05cddf9SRui Paulo {
124f05cddf9SRui Paulo if (cmdbuf_pos == cmdbuf_len)
125f05cddf9SRui Paulo return;
126f05cddf9SRui Paulo
127f05cddf9SRui Paulo edit_clear_line();
128f05cddf9SRui Paulo os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
129f05cddf9SRui Paulo cmdbuf_len - cmdbuf_pos);
130f05cddf9SRui Paulo cmdbuf_len--;
131f05cddf9SRui Paulo edit_redraw();
132f05cddf9SRui Paulo }
133f05cddf9SRui Paulo
134f05cddf9SRui Paulo
delete_word(void)135f05cddf9SRui Paulo static void delete_word(void)
136f05cddf9SRui Paulo {
137f05cddf9SRui Paulo int pos;
138f05cddf9SRui Paulo
139f05cddf9SRui Paulo edit_clear_line();
140f05cddf9SRui Paulo pos = cmdbuf_pos;
141f05cddf9SRui Paulo while (pos > 0 && cmdbuf[pos - 1] == ' ')
142f05cddf9SRui Paulo pos--;
143f05cddf9SRui Paulo while (pos > 0 && cmdbuf[pos - 1] != ' ')
144f05cddf9SRui Paulo pos--;
145f05cddf9SRui Paulo os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
146f05cddf9SRui Paulo cmdbuf_len -= cmdbuf_pos - pos;
147f05cddf9SRui Paulo cmdbuf_pos = pos;
148f05cddf9SRui Paulo edit_redraw();
149f05cddf9SRui Paulo }
150f05cddf9SRui Paulo
151f05cddf9SRui Paulo
clear_left(void)152f05cddf9SRui Paulo static void clear_left(void)
153f05cddf9SRui Paulo {
154f05cddf9SRui Paulo if (cmdbuf_pos == 0)
155f05cddf9SRui Paulo return;
156f05cddf9SRui Paulo
157f05cddf9SRui Paulo edit_clear_line();
158f05cddf9SRui Paulo os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
159f05cddf9SRui Paulo cmdbuf_len -= cmdbuf_pos;
160f05cddf9SRui Paulo cmdbuf_pos = 0;
161f05cddf9SRui Paulo edit_redraw();
162f05cddf9SRui Paulo }
163f05cddf9SRui Paulo
164f05cddf9SRui Paulo
clear_right(void)165f05cddf9SRui Paulo static void clear_right(void)
166f05cddf9SRui Paulo {
167f05cddf9SRui Paulo if (cmdbuf_pos == cmdbuf_len)
168f05cddf9SRui Paulo return;
169f05cddf9SRui Paulo
170f05cddf9SRui Paulo edit_clear_line();
171f05cddf9SRui Paulo cmdbuf_len = cmdbuf_pos;
172f05cddf9SRui Paulo edit_redraw();
173f05cddf9SRui Paulo }
174f05cddf9SRui Paulo
175f05cddf9SRui Paulo
history_add(const char * str)176f05cddf9SRui Paulo static void history_add(const char *str)
177f05cddf9SRui Paulo {
178f05cddf9SRui Paulo struct edit_history *h, *match = NULL, *last = NULL;
179f05cddf9SRui Paulo size_t len, count = 0;
180f05cddf9SRui Paulo
181f05cddf9SRui Paulo if (str[0] == '\0')
182f05cddf9SRui Paulo return;
183f05cddf9SRui Paulo
184f05cddf9SRui Paulo dl_list_for_each(h, &history_list, struct edit_history, list) {
185f05cddf9SRui Paulo if (os_strcmp(str, h->str) == 0) {
186f05cddf9SRui Paulo match = h;
187f05cddf9SRui Paulo break;
188f05cddf9SRui Paulo }
189f05cddf9SRui Paulo last = h;
190f05cddf9SRui Paulo count++;
191f05cddf9SRui Paulo }
192f05cddf9SRui Paulo
193f05cddf9SRui Paulo if (match) {
194f05cddf9SRui Paulo dl_list_del(&h->list);
195f05cddf9SRui Paulo dl_list_add(&history_list, &h->list);
196f05cddf9SRui Paulo history_curr = h;
197f05cddf9SRui Paulo return;
198f05cddf9SRui Paulo }
199f05cddf9SRui Paulo
200f05cddf9SRui Paulo if (count >= HISTORY_MAX && last) {
201f05cddf9SRui Paulo dl_list_del(&last->list);
202f05cddf9SRui Paulo os_free(last);
203f05cddf9SRui Paulo }
204f05cddf9SRui Paulo
205f05cddf9SRui Paulo len = os_strlen(str);
206f05cddf9SRui Paulo h = os_zalloc(sizeof(*h) + len);
207f05cddf9SRui Paulo if (h == NULL)
208f05cddf9SRui Paulo return;
209f05cddf9SRui Paulo dl_list_add(&history_list, &h->list);
210f05cddf9SRui Paulo os_strlcpy(h->str, str, len + 1);
211f05cddf9SRui Paulo history_curr = h;
212f05cddf9SRui Paulo }
213f05cddf9SRui Paulo
214f05cddf9SRui Paulo
history_use(void)215f05cddf9SRui Paulo static void history_use(void)
216f05cddf9SRui Paulo {
217f05cddf9SRui Paulo edit_clear_line();
218f05cddf9SRui Paulo cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
219f05cddf9SRui Paulo os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
220f05cddf9SRui Paulo edit_redraw();
221f05cddf9SRui Paulo }
222f05cddf9SRui Paulo
223f05cddf9SRui Paulo
history_prev(void)224f05cddf9SRui Paulo static void history_prev(void)
225f05cddf9SRui Paulo {
226f05cddf9SRui Paulo if (history_curr == NULL)
227f05cddf9SRui Paulo return;
228f05cddf9SRui Paulo
229f05cddf9SRui Paulo if (history_curr ==
230f05cddf9SRui Paulo dl_list_first(&history_list, struct edit_history, list)) {
231f05cddf9SRui Paulo if (!currbuf_valid) {
232f05cddf9SRui Paulo cmdbuf[cmdbuf_len] = '\0';
233f05cddf9SRui Paulo os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
234f05cddf9SRui Paulo currbuf_valid = 1;
235f05cddf9SRui Paulo history_use();
236f05cddf9SRui Paulo return;
237f05cddf9SRui Paulo }
238f05cddf9SRui Paulo }
239f05cddf9SRui Paulo
240f05cddf9SRui Paulo if (history_curr ==
241f05cddf9SRui Paulo dl_list_last(&history_list, struct edit_history, list))
242f05cddf9SRui Paulo return;
243f05cddf9SRui Paulo
244f05cddf9SRui Paulo history_curr = dl_list_entry(history_curr->list.next,
245f05cddf9SRui Paulo struct edit_history, list);
246f05cddf9SRui Paulo history_use();
247f05cddf9SRui Paulo }
248f05cddf9SRui Paulo
249f05cddf9SRui Paulo
history_next(void)250f05cddf9SRui Paulo static void history_next(void)
251f05cddf9SRui Paulo {
252f05cddf9SRui Paulo if (history_curr == NULL ||
253f05cddf9SRui Paulo history_curr ==
254f05cddf9SRui Paulo dl_list_first(&history_list, struct edit_history, list)) {
255f05cddf9SRui Paulo if (currbuf_valid) {
256f05cddf9SRui Paulo currbuf_valid = 0;
257f05cddf9SRui Paulo edit_clear_line();
258f05cddf9SRui Paulo cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
259f05cddf9SRui Paulo os_memcpy(cmdbuf, currbuf, cmdbuf_len);
260f05cddf9SRui Paulo edit_redraw();
261f05cddf9SRui Paulo }
262f05cddf9SRui Paulo return;
263f05cddf9SRui Paulo }
264f05cddf9SRui Paulo
265f05cddf9SRui Paulo history_curr = dl_list_entry(history_curr->list.prev,
266f05cddf9SRui Paulo struct edit_history, list);
267f05cddf9SRui Paulo history_use();
268f05cddf9SRui Paulo }
269f05cddf9SRui Paulo
270f05cddf9SRui Paulo
history_read(const char * fname)271f05cddf9SRui Paulo static void history_read(const char *fname)
272f05cddf9SRui Paulo {
273f05cddf9SRui Paulo FILE *f;
274f05cddf9SRui Paulo char buf[CMD_BUF_LEN], *pos;
275f05cddf9SRui Paulo
276f05cddf9SRui Paulo f = fopen(fname, "r");
277f05cddf9SRui Paulo if (f == NULL)
278f05cddf9SRui Paulo return;
279f05cddf9SRui Paulo
280f05cddf9SRui Paulo while (fgets(buf, CMD_BUF_LEN, f)) {
281f05cddf9SRui Paulo for (pos = buf; *pos; pos++) {
282f05cddf9SRui Paulo if (*pos == '\r' || *pos == '\n') {
283f05cddf9SRui Paulo *pos = '\0';
284f05cddf9SRui Paulo break;
285f05cddf9SRui Paulo }
286f05cddf9SRui Paulo }
287f05cddf9SRui Paulo history_add(buf);
288f05cddf9SRui Paulo }
289f05cddf9SRui Paulo
290f05cddf9SRui Paulo fclose(f);
291f05cddf9SRui Paulo }
292f05cddf9SRui Paulo
293f05cddf9SRui Paulo
history_write(const char * fname,int (* filter_cb)(void * ctx,const char * cmd))294f05cddf9SRui Paulo static void history_write(const char *fname,
295f05cddf9SRui Paulo int (*filter_cb)(void *ctx, const char *cmd))
296f05cddf9SRui Paulo {
297f05cddf9SRui Paulo FILE *f;
298f05cddf9SRui Paulo struct edit_history *h;
299f05cddf9SRui Paulo
300f05cddf9SRui Paulo f = fopen(fname, "w");
301f05cddf9SRui Paulo if (f == NULL)
302f05cddf9SRui Paulo return;
303f05cddf9SRui Paulo
304f05cddf9SRui Paulo dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
305f05cddf9SRui Paulo if (filter_cb && filter_cb(edit_cb_ctx, h->str))
306f05cddf9SRui Paulo continue;
307f05cddf9SRui Paulo fprintf(f, "%s\n", h->str);
308f05cddf9SRui Paulo }
309f05cddf9SRui Paulo
310f05cddf9SRui Paulo fclose(f);
311f05cddf9SRui Paulo }
312f05cddf9SRui Paulo
313f05cddf9SRui Paulo
history_debug_dump(void)314f05cddf9SRui Paulo static void history_debug_dump(void)
315f05cddf9SRui Paulo {
316f05cddf9SRui Paulo struct edit_history *h;
317f05cddf9SRui Paulo edit_clear_line();
318f05cddf9SRui Paulo printf("\r");
319f05cddf9SRui Paulo dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
320f05cddf9SRui Paulo printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
321f05cddf9SRui Paulo if (currbuf_valid)
322f05cddf9SRui Paulo printf("{%s}\n", currbuf);
323f05cddf9SRui Paulo edit_redraw();
324f05cddf9SRui Paulo }
325f05cddf9SRui Paulo
326f05cddf9SRui Paulo
insert_char(int c)327f05cddf9SRui Paulo static void insert_char(int c)
328f05cddf9SRui Paulo {
329f05cddf9SRui Paulo if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
330f05cddf9SRui Paulo return;
331f05cddf9SRui Paulo if (cmdbuf_len == cmdbuf_pos) {
332f05cddf9SRui Paulo cmdbuf[cmdbuf_pos++] = c;
333f05cddf9SRui Paulo cmdbuf_len++;
334f05cddf9SRui Paulo putchar(c);
335f05cddf9SRui Paulo fflush(stdout);
336f05cddf9SRui Paulo } else {
337f05cddf9SRui Paulo os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
338f05cddf9SRui Paulo cmdbuf_len - cmdbuf_pos);
339f05cddf9SRui Paulo cmdbuf[cmdbuf_pos++] = c;
340f05cddf9SRui Paulo cmdbuf_len++;
341f05cddf9SRui Paulo edit_redraw();
342f05cddf9SRui Paulo }
343f05cddf9SRui Paulo }
344f05cddf9SRui Paulo
345f05cddf9SRui Paulo
process_cmd(void)346f05cddf9SRui Paulo static void process_cmd(void)
347f05cddf9SRui Paulo {
348*5b9c547cSRui Paulo currbuf_valid = 0;
349f05cddf9SRui Paulo if (cmdbuf_len == 0) {
350f05cddf9SRui Paulo printf("\n%s> ", ps2 ? ps2 : "");
351f05cddf9SRui Paulo fflush(stdout);
352f05cddf9SRui Paulo return;
353f05cddf9SRui Paulo }
354f05cddf9SRui Paulo printf("\n");
355f05cddf9SRui Paulo cmdbuf[cmdbuf_len] = '\0';
356f05cddf9SRui Paulo history_add(cmdbuf);
357f05cddf9SRui Paulo cmdbuf_pos = 0;
358f05cddf9SRui Paulo cmdbuf_len = 0;
359f05cddf9SRui Paulo edit_cmd_cb(edit_cb_ctx, cmdbuf);
360f05cddf9SRui Paulo printf("%s> ", ps2 ? ps2 : "");
361f05cddf9SRui Paulo fflush(stdout);
362f05cddf9SRui Paulo }
363f05cddf9SRui Paulo
364f05cddf9SRui Paulo
free_completions(char ** c)365f05cddf9SRui Paulo static void free_completions(char **c)
366f05cddf9SRui Paulo {
367f05cddf9SRui Paulo int i;
368f05cddf9SRui Paulo if (c == NULL)
369f05cddf9SRui Paulo return;
370f05cddf9SRui Paulo for (i = 0; c[i]; i++)
371f05cddf9SRui Paulo os_free(c[i]);
372f05cddf9SRui Paulo os_free(c);
373f05cddf9SRui Paulo }
374f05cddf9SRui Paulo
375f05cddf9SRui Paulo
filter_strings(char ** c,char * str,size_t len)376f05cddf9SRui Paulo static int filter_strings(char **c, char *str, size_t len)
377f05cddf9SRui Paulo {
378f05cddf9SRui Paulo int i, j;
379f05cddf9SRui Paulo
380f05cddf9SRui Paulo for (i = 0, j = 0; c[j]; j++) {
381f05cddf9SRui Paulo if (os_strncasecmp(c[j], str, len) == 0) {
382f05cddf9SRui Paulo if (i != j) {
383f05cddf9SRui Paulo c[i] = c[j];
384f05cddf9SRui Paulo c[j] = NULL;
385f05cddf9SRui Paulo }
386f05cddf9SRui Paulo i++;
387f05cddf9SRui Paulo } else {
388f05cddf9SRui Paulo os_free(c[j]);
389f05cddf9SRui Paulo c[j] = NULL;
390f05cddf9SRui Paulo }
391f05cddf9SRui Paulo }
392f05cddf9SRui Paulo c[i] = NULL;
393f05cddf9SRui Paulo return i;
394f05cddf9SRui Paulo }
395f05cddf9SRui Paulo
396f05cddf9SRui Paulo
common_len(const char * a,const char * b)397f05cddf9SRui Paulo static int common_len(const char *a, const char *b)
398f05cddf9SRui Paulo {
399f05cddf9SRui Paulo int len = 0;
400f05cddf9SRui Paulo while (a[len] && a[len] == b[len])
401f05cddf9SRui Paulo len++;
402f05cddf9SRui Paulo return len;
403f05cddf9SRui Paulo }
404f05cddf9SRui Paulo
405f05cddf9SRui Paulo
max_common_length(char ** c)406f05cddf9SRui Paulo static int max_common_length(char **c)
407f05cddf9SRui Paulo {
408f05cddf9SRui Paulo int len, i;
409f05cddf9SRui Paulo
410f05cddf9SRui Paulo len = os_strlen(c[0]);
411f05cddf9SRui Paulo for (i = 1; c[i]; i++) {
412f05cddf9SRui Paulo int same = common_len(c[0], c[i]);
413f05cddf9SRui Paulo if (same < len)
414f05cddf9SRui Paulo len = same;
415f05cddf9SRui Paulo }
416f05cddf9SRui Paulo
417f05cddf9SRui Paulo return len;
418f05cddf9SRui Paulo }
419f05cddf9SRui Paulo
420f05cddf9SRui Paulo
cmp_str(const void * a,const void * b)421f05cddf9SRui Paulo static int cmp_str(const void *a, const void *b)
422f05cddf9SRui Paulo {
423f05cddf9SRui Paulo return os_strcmp(* (const char **) a, * (const char **) b);
424f05cddf9SRui Paulo }
425f05cddf9SRui Paulo
complete(int list)426f05cddf9SRui Paulo static void complete(int list)
427f05cddf9SRui Paulo {
428f05cddf9SRui Paulo char **c;
429f05cddf9SRui Paulo int i, len, count;
430f05cddf9SRui Paulo int start, end;
431f05cddf9SRui Paulo int room, plen, add_space;
432f05cddf9SRui Paulo
433f05cddf9SRui Paulo if (edit_completion_cb == NULL)
434f05cddf9SRui Paulo return;
435f05cddf9SRui Paulo
436f05cddf9SRui Paulo cmdbuf[cmdbuf_len] = '\0';
437f05cddf9SRui Paulo c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
438f05cddf9SRui Paulo if (c == NULL)
439f05cddf9SRui Paulo return;
440f05cddf9SRui Paulo
441f05cddf9SRui Paulo end = cmdbuf_pos;
442f05cddf9SRui Paulo start = end;
443f05cddf9SRui Paulo while (start > 0 && cmdbuf[start - 1] != ' ')
444f05cddf9SRui Paulo start--;
445f05cddf9SRui Paulo plen = end - start;
446f05cddf9SRui Paulo
447f05cddf9SRui Paulo count = filter_strings(c, &cmdbuf[start], plen);
448f05cddf9SRui Paulo if (count == 0) {
449f05cddf9SRui Paulo free_completions(c);
450f05cddf9SRui Paulo return;
451f05cddf9SRui Paulo }
452f05cddf9SRui Paulo
453f05cddf9SRui Paulo len = max_common_length(c);
454f05cddf9SRui Paulo if (len <= plen && count > 1) {
455f05cddf9SRui Paulo if (list) {
456f05cddf9SRui Paulo qsort(c, count, sizeof(char *), cmp_str);
457f05cddf9SRui Paulo edit_clear_line();
458f05cddf9SRui Paulo printf("\r");
459f05cddf9SRui Paulo for (i = 0; c[i]; i++)
460f05cddf9SRui Paulo printf("%s%s", i > 0 ? " " : "", c[i]);
461f05cddf9SRui Paulo printf("\n");
462f05cddf9SRui Paulo edit_redraw();
463f05cddf9SRui Paulo }
464f05cddf9SRui Paulo free_completions(c);
465f05cddf9SRui Paulo return;
466f05cddf9SRui Paulo }
467f05cddf9SRui Paulo len -= plen;
468f05cddf9SRui Paulo
469f05cddf9SRui Paulo room = sizeof(cmdbuf) - 1 - cmdbuf_len;
470f05cddf9SRui Paulo if (room < len)
471f05cddf9SRui Paulo len = room;
472f05cddf9SRui Paulo add_space = count == 1 && len < room;
473f05cddf9SRui Paulo
474f05cddf9SRui Paulo os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
475f05cddf9SRui Paulo cmdbuf_len - cmdbuf_pos);
476f05cddf9SRui Paulo os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
477f05cddf9SRui Paulo if (add_space)
478f05cddf9SRui Paulo cmdbuf[cmdbuf_pos + len] = ' ';
479f05cddf9SRui Paulo
480f05cddf9SRui Paulo cmdbuf_pos += len + add_space;
481f05cddf9SRui Paulo cmdbuf_len += len + add_space;
482f05cddf9SRui Paulo
483f05cddf9SRui Paulo edit_redraw();
484f05cddf9SRui Paulo
485f05cddf9SRui Paulo free_completions(c);
486f05cddf9SRui Paulo }
487f05cddf9SRui Paulo
488f05cddf9SRui Paulo
489f05cddf9SRui Paulo enum edit_key_code {
490f05cddf9SRui Paulo EDIT_KEY_NONE = 256,
491f05cddf9SRui Paulo EDIT_KEY_TAB,
492f05cddf9SRui Paulo EDIT_KEY_UP,
493f05cddf9SRui Paulo EDIT_KEY_DOWN,
494f05cddf9SRui Paulo EDIT_KEY_RIGHT,
495f05cddf9SRui Paulo EDIT_KEY_LEFT,
496f05cddf9SRui Paulo EDIT_KEY_ENTER,
497f05cddf9SRui Paulo EDIT_KEY_BACKSPACE,
498f05cddf9SRui Paulo EDIT_KEY_INSERT,
499f05cddf9SRui Paulo EDIT_KEY_DELETE,
500f05cddf9SRui Paulo EDIT_KEY_HOME,
501f05cddf9SRui Paulo EDIT_KEY_END,
502f05cddf9SRui Paulo EDIT_KEY_PAGE_UP,
503f05cddf9SRui Paulo EDIT_KEY_PAGE_DOWN,
504f05cddf9SRui Paulo EDIT_KEY_F1,
505f05cddf9SRui Paulo EDIT_KEY_F2,
506f05cddf9SRui Paulo EDIT_KEY_F3,
507f05cddf9SRui Paulo EDIT_KEY_F4,
508f05cddf9SRui Paulo EDIT_KEY_F5,
509f05cddf9SRui Paulo EDIT_KEY_F6,
510f05cddf9SRui Paulo EDIT_KEY_F7,
511f05cddf9SRui Paulo EDIT_KEY_F8,
512f05cddf9SRui Paulo EDIT_KEY_F9,
513f05cddf9SRui Paulo EDIT_KEY_F10,
514f05cddf9SRui Paulo EDIT_KEY_F11,
515f05cddf9SRui Paulo EDIT_KEY_F12,
516f05cddf9SRui Paulo EDIT_KEY_CTRL_UP,
517f05cddf9SRui Paulo EDIT_KEY_CTRL_DOWN,
518f05cddf9SRui Paulo EDIT_KEY_CTRL_RIGHT,
519f05cddf9SRui Paulo EDIT_KEY_CTRL_LEFT,
520f05cddf9SRui Paulo EDIT_KEY_CTRL_A,
521f05cddf9SRui Paulo EDIT_KEY_CTRL_B,
522f05cddf9SRui Paulo EDIT_KEY_CTRL_D,
523f05cddf9SRui Paulo EDIT_KEY_CTRL_E,
524f05cddf9SRui Paulo EDIT_KEY_CTRL_F,
525f05cddf9SRui Paulo EDIT_KEY_CTRL_G,
526f05cddf9SRui Paulo EDIT_KEY_CTRL_H,
527f05cddf9SRui Paulo EDIT_KEY_CTRL_J,
528f05cddf9SRui Paulo EDIT_KEY_CTRL_K,
529f05cddf9SRui Paulo EDIT_KEY_CTRL_L,
530f05cddf9SRui Paulo EDIT_KEY_CTRL_N,
531f05cddf9SRui Paulo EDIT_KEY_CTRL_O,
532f05cddf9SRui Paulo EDIT_KEY_CTRL_P,
533f05cddf9SRui Paulo EDIT_KEY_CTRL_R,
534f05cddf9SRui Paulo EDIT_KEY_CTRL_T,
535f05cddf9SRui Paulo EDIT_KEY_CTRL_U,
536f05cddf9SRui Paulo EDIT_KEY_CTRL_V,
537f05cddf9SRui Paulo EDIT_KEY_CTRL_W,
538f05cddf9SRui Paulo EDIT_KEY_ALT_UP,
539f05cddf9SRui Paulo EDIT_KEY_ALT_DOWN,
540f05cddf9SRui Paulo EDIT_KEY_ALT_RIGHT,
541f05cddf9SRui Paulo EDIT_KEY_ALT_LEFT,
542f05cddf9SRui Paulo EDIT_KEY_SHIFT_UP,
543f05cddf9SRui Paulo EDIT_KEY_SHIFT_DOWN,
544f05cddf9SRui Paulo EDIT_KEY_SHIFT_RIGHT,
545f05cddf9SRui Paulo EDIT_KEY_SHIFT_LEFT,
546f05cddf9SRui Paulo EDIT_KEY_ALT_SHIFT_UP,
547f05cddf9SRui Paulo EDIT_KEY_ALT_SHIFT_DOWN,
548f05cddf9SRui Paulo EDIT_KEY_ALT_SHIFT_RIGHT,
549f05cddf9SRui Paulo EDIT_KEY_ALT_SHIFT_LEFT,
550f05cddf9SRui Paulo EDIT_KEY_EOF
551f05cddf9SRui Paulo };
552f05cddf9SRui Paulo
show_esc_buf(const char * esc_buf,char c,int i)553f05cddf9SRui Paulo static void show_esc_buf(const char *esc_buf, char c, int i)
554f05cddf9SRui Paulo {
555f05cddf9SRui Paulo edit_clear_line();
556f05cddf9SRui Paulo printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
557f05cddf9SRui Paulo edit_redraw();
558f05cddf9SRui Paulo }
559f05cddf9SRui Paulo
560f05cddf9SRui Paulo
esc_seq_to_key1_no(char last)561f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1_no(char last)
562f05cddf9SRui Paulo {
563f05cddf9SRui Paulo switch (last) {
564f05cddf9SRui Paulo case 'A':
565f05cddf9SRui Paulo return EDIT_KEY_UP;
566f05cddf9SRui Paulo case 'B':
567f05cddf9SRui Paulo return EDIT_KEY_DOWN;
568f05cddf9SRui Paulo case 'C':
569f05cddf9SRui Paulo return EDIT_KEY_RIGHT;
570f05cddf9SRui Paulo case 'D':
571f05cddf9SRui Paulo return EDIT_KEY_LEFT;
572f05cddf9SRui Paulo default:
573f05cddf9SRui Paulo return EDIT_KEY_NONE;
574f05cddf9SRui Paulo }
575f05cddf9SRui Paulo }
576f05cddf9SRui Paulo
577f05cddf9SRui Paulo
esc_seq_to_key1_shift(char last)578f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1_shift(char last)
579f05cddf9SRui Paulo {
580f05cddf9SRui Paulo switch (last) {
581f05cddf9SRui Paulo case 'A':
582f05cddf9SRui Paulo return EDIT_KEY_SHIFT_UP;
583f05cddf9SRui Paulo case 'B':
584f05cddf9SRui Paulo return EDIT_KEY_SHIFT_DOWN;
585f05cddf9SRui Paulo case 'C':
586f05cddf9SRui Paulo return EDIT_KEY_SHIFT_RIGHT;
587f05cddf9SRui Paulo case 'D':
588f05cddf9SRui Paulo return EDIT_KEY_SHIFT_LEFT;
589f05cddf9SRui Paulo default:
590f05cddf9SRui Paulo return EDIT_KEY_NONE;
591f05cddf9SRui Paulo }
592f05cddf9SRui Paulo }
593f05cddf9SRui Paulo
594f05cddf9SRui Paulo
esc_seq_to_key1_alt(char last)595f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1_alt(char last)
596f05cddf9SRui Paulo {
597f05cddf9SRui Paulo switch (last) {
598f05cddf9SRui Paulo case 'A':
599f05cddf9SRui Paulo return EDIT_KEY_ALT_UP;
600f05cddf9SRui Paulo case 'B':
601f05cddf9SRui Paulo return EDIT_KEY_ALT_DOWN;
602f05cddf9SRui Paulo case 'C':
603f05cddf9SRui Paulo return EDIT_KEY_ALT_RIGHT;
604f05cddf9SRui Paulo case 'D':
605f05cddf9SRui Paulo return EDIT_KEY_ALT_LEFT;
606f05cddf9SRui Paulo default:
607f05cddf9SRui Paulo return EDIT_KEY_NONE;
608f05cddf9SRui Paulo }
609f05cddf9SRui Paulo }
610f05cddf9SRui Paulo
611f05cddf9SRui Paulo
esc_seq_to_key1_alt_shift(char last)612f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
613f05cddf9SRui Paulo {
614f05cddf9SRui Paulo switch (last) {
615f05cddf9SRui Paulo case 'A':
616f05cddf9SRui Paulo return EDIT_KEY_ALT_SHIFT_UP;
617f05cddf9SRui Paulo case 'B':
618f05cddf9SRui Paulo return EDIT_KEY_ALT_SHIFT_DOWN;
619f05cddf9SRui Paulo case 'C':
620f05cddf9SRui Paulo return EDIT_KEY_ALT_SHIFT_RIGHT;
621f05cddf9SRui Paulo case 'D':
622f05cddf9SRui Paulo return EDIT_KEY_ALT_SHIFT_LEFT;
623f05cddf9SRui Paulo default:
624f05cddf9SRui Paulo return EDIT_KEY_NONE;
625f05cddf9SRui Paulo }
626f05cddf9SRui Paulo }
627f05cddf9SRui Paulo
628f05cddf9SRui Paulo
esc_seq_to_key1_ctrl(char last)629f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1_ctrl(char last)
630f05cddf9SRui Paulo {
631f05cddf9SRui Paulo switch (last) {
632f05cddf9SRui Paulo case 'A':
633f05cddf9SRui Paulo return EDIT_KEY_CTRL_UP;
634f05cddf9SRui Paulo case 'B':
635f05cddf9SRui Paulo return EDIT_KEY_CTRL_DOWN;
636f05cddf9SRui Paulo case 'C':
637f05cddf9SRui Paulo return EDIT_KEY_CTRL_RIGHT;
638f05cddf9SRui Paulo case 'D':
639f05cddf9SRui Paulo return EDIT_KEY_CTRL_LEFT;
640f05cddf9SRui Paulo default:
641f05cddf9SRui Paulo return EDIT_KEY_NONE;
642f05cddf9SRui Paulo }
643f05cddf9SRui Paulo }
644f05cddf9SRui Paulo
645f05cddf9SRui Paulo
esc_seq_to_key1(int param1,int param2,char last)646f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
647f05cddf9SRui Paulo {
648f05cddf9SRui Paulo /* ESC-[<param1>;<param2><last> */
649f05cddf9SRui Paulo
650f05cddf9SRui Paulo if (param1 < 0 && param2 < 0)
651f05cddf9SRui Paulo return esc_seq_to_key1_no(last);
652f05cddf9SRui Paulo
653f05cddf9SRui Paulo if (param1 == 1 && param2 == 2)
654f05cddf9SRui Paulo return esc_seq_to_key1_shift(last);
655f05cddf9SRui Paulo
656f05cddf9SRui Paulo if (param1 == 1 && param2 == 3)
657f05cddf9SRui Paulo return esc_seq_to_key1_alt(last);
658f05cddf9SRui Paulo
659f05cddf9SRui Paulo if (param1 == 1 && param2 == 4)
660f05cddf9SRui Paulo return esc_seq_to_key1_alt_shift(last);
661f05cddf9SRui Paulo
662f05cddf9SRui Paulo if (param1 == 1 && param2 == 5)
663f05cddf9SRui Paulo return esc_seq_to_key1_ctrl(last);
664f05cddf9SRui Paulo
665f05cddf9SRui Paulo if (param2 < 0) {
666f05cddf9SRui Paulo if (last != '~')
667f05cddf9SRui Paulo return EDIT_KEY_NONE;
668f05cddf9SRui Paulo switch (param1) {
669f05cddf9SRui Paulo case 2:
670f05cddf9SRui Paulo return EDIT_KEY_INSERT;
671f05cddf9SRui Paulo case 3:
672f05cddf9SRui Paulo return EDIT_KEY_DELETE;
673f05cddf9SRui Paulo case 5:
674f05cddf9SRui Paulo return EDIT_KEY_PAGE_UP;
675f05cddf9SRui Paulo case 6:
676f05cddf9SRui Paulo return EDIT_KEY_PAGE_DOWN;
677f05cddf9SRui Paulo case 15:
678f05cddf9SRui Paulo return EDIT_KEY_F5;
679f05cddf9SRui Paulo case 17:
680f05cddf9SRui Paulo return EDIT_KEY_F6;
681f05cddf9SRui Paulo case 18:
682f05cddf9SRui Paulo return EDIT_KEY_F7;
683f05cddf9SRui Paulo case 19:
684f05cddf9SRui Paulo return EDIT_KEY_F8;
685f05cddf9SRui Paulo case 20:
686f05cddf9SRui Paulo return EDIT_KEY_F9;
687f05cddf9SRui Paulo case 21:
688f05cddf9SRui Paulo return EDIT_KEY_F10;
689f05cddf9SRui Paulo case 23:
690f05cddf9SRui Paulo return EDIT_KEY_F11;
691f05cddf9SRui Paulo case 24:
692f05cddf9SRui Paulo return EDIT_KEY_F12;
693f05cddf9SRui Paulo }
694f05cddf9SRui Paulo }
695f05cddf9SRui Paulo
696f05cddf9SRui Paulo return EDIT_KEY_NONE;
697f05cddf9SRui Paulo }
698f05cddf9SRui Paulo
699f05cddf9SRui Paulo
esc_seq_to_key2(int param1,int param2,char last)700f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
701f05cddf9SRui Paulo {
702f05cddf9SRui Paulo /* ESC-O<param1>;<param2><last> */
703f05cddf9SRui Paulo
704f05cddf9SRui Paulo if (param1 >= 0 || param2 >= 0)
705f05cddf9SRui Paulo return EDIT_KEY_NONE;
706f05cddf9SRui Paulo
707f05cddf9SRui Paulo switch (last) {
708f05cddf9SRui Paulo case 'F':
709f05cddf9SRui Paulo return EDIT_KEY_END;
710f05cddf9SRui Paulo case 'H':
711f05cddf9SRui Paulo return EDIT_KEY_HOME;
712f05cddf9SRui Paulo case 'P':
713f05cddf9SRui Paulo return EDIT_KEY_F1;
714f05cddf9SRui Paulo case 'Q':
715f05cddf9SRui Paulo return EDIT_KEY_F2;
716f05cddf9SRui Paulo case 'R':
717f05cddf9SRui Paulo return EDIT_KEY_F3;
718f05cddf9SRui Paulo case 'S':
719f05cddf9SRui Paulo return EDIT_KEY_F4;
720f05cddf9SRui Paulo default:
721f05cddf9SRui Paulo return EDIT_KEY_NONE;
722f05cddf9SRui Paulo }
723f05cddf9SRui Paulo }
724f05cddf9SRui Paulo
725f05cddf9SRui Paulo
esc_seq_to_key(char * seq)726f05cddf9SRui Paulo static enum edit_key_code esc_seq_to_key(char *seq)
727f05cddf9SRui Paulo {
728f05cddf9SRui Paulo char last, *pos;
729f05cddf9SRui Paulo int param1 = -1, param2 = -1;
730f05cddf9SRui Paulo enum edit_key_code ret = EDIT_KEY_NONE;
731f05cddf9SRui Paulo
732f05cddf9SRui Paulo last = '\0';
733f05cddf9SRui Paulo for (pos = seq; *pos; pos++)
734f05cddf9SRui Paulo last = *pos;
735f05cddf9SRui Paulo
736f05cddf9SRui Paulo if (seq[1] >= '0' && seq[1] <= '9') {
737f05cddf9SRui Paulo param1 = atoi(&seq[1]);
738f05cddf9SRui Paulo pos = os_strchr(seq, ';');
739f05cddf9SRui Paulo if (pos)
740f05cddf9SRui Paulo param2 = atoi(pos + 1);
741f05cddf9SRui Paulo }
742f05cddf9SRui Paulo
743f05cddf9SRui Paulo if (seq[0] == '[')
744f05cddf9SRui Paulo ret = esc_seq_to_key1(param1, param2, last);
745f05cddf9SRui Paulo else if (seq[0] == 'O')
746f05cddf9SRui Paulo ret = esc_seq_to_key2(param1, param2, last);
747f05cddf9SRui Paulo
748f05cddf9SRui Paulo if (ret != EDIT_KEY_NONE)
749f05cddf9SRui Paulo return ret;
750f05cddf9SRui Paulo
751f05cddf9SRui Paulo edit_clear_line();
752f05cddf9SRui Paulo printf("\rUnknown escape sequence '%s'\n", seq);
753f05cddf9SRui Paulo edit_redraw();
754f05cddf9SRui Paulo return EDIT_KEY_NONE;
755f05cddf9SRui Paulo }
756f05cddf9SRui Paulo
757f05cddf9SRui Paulo
edit_read_key(int sock)758f05cddf9SRui Paulo static enum edit_key_code edit_read_key(int sock)
759f05cddf9SRui Paulo {
760f05cddf9SRui Paulo int c;
761f05cddf9SRui Paulo unsigned char buf[1];
762f05cddf9SRui Paulo int res;
763f05cddf9SRui Paulo static int esc = -1;
764f05cddf9SRui Paulo static char esc_buf[7];
765f05cddf9SRui Paulo
766f05cddf9SRui Paulo res = read(sock, buf, 1);
767f05cddf9SRui Paulo if (res < 0)
768f05cddf9SRui Paulo perror("read");
769f05cddf9SRui Paulo if (res <= 0)
770f05cddf9SRui Paulo return EDIT_KEY_EOF;
771f05cddf9SRui Paulo
772f05cddf9SRui Paulo c = buf[0];
773f05cddf9SRui Paulo
774f05cddf9SRui Paulo if (esc >= 0) {
775f05cddf9SRui Paulo if (c == 27 /* ESC */) {
776f05cddf9SRui Paulo esc = 0;
777f05cddf9SRui Paulo return EDIT_KEY_NONE;
778f05cddf9SRui Paulo }
779f05cddf9SRui Paulo
780f05cddf9SRui Paulo if (esc == 6) {
781f05cddf9SRui Paulo show_esc_buf(esc_buf, c, 0);
782f05cddf9SRui Paulo esc = -1;
783f05cddf9SRui Paulo } else {
784f05cddf9SRui Paulo esc_buf[esc++] = c;
785f05cddf9SRui Paulo esc_buf[esc] = '\0';
786f05cddf9SRui Paulo }
787f05cddf9SRui Paulo }
788f05cddf9SRui Paulo
789f05cddf9SRui Paulo if (esc == 1) {
790f05cddf9SRui Paulo if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
791f05cddf9SRui Paulo show_esc_buf(esc_buf, c, 1);
792f05cddf9SRui Paulo esc = -1;
793f05cddf9SRui Paulo return EDIT_KEY_NONE;
794f05cddf9SRui Paulo } else
795f05cddf9SRui Paulo return EDIT_KEY_NONE; /* Escape sequence continues */
796f05cddf9SRui Paulo }
797f05cddf9SRui Paulo
798f05cddf9SRui Paulo if (esc > 1) {
799f05cddf9SRui Paulo if ((c >= '0' && c <= '9') || c == ';')
800f05cddf9SRui Paulo return EDIT_KEY_NONE; /* Escape sequence continues */
801f05cddf9SRui Paulo
802f05cddf9SRui Paulo if (c == '~' || (c >= 'A' && c <= 'Z')) {
803f05cddf9SRui Paulo esc = -1;
804f05cddf9SRui Paulo return esc_seq_to_key(esc_buf);
805f05cddf9SRui Paulo }
806f05cddf9SRui Paulo
807f05cddf9SRui Paulo show_esc_buf(esc_buf, c, 2);
808f05cddf9SRui Paulo esc = -1;
809f05cddf9SRui Paulo return EDIT_KEY_NONE;
810f05cddf9SRui Paulo }
811f05cddf9SRui Paulo
812f05cddf9SRui Paulo switch (c) {
813f05cddf9SRui Paulo case 1:
814f05cddf9SRui Paulo return EDIT_KEY_CTRL_A;
815f05cddf9SRui Paulo case 2:
816f05cddf9SRui Paulo return EDIT_KEY_CTRL_B;
817f05cddf9SRui Paulo case 4:
818f05cddf9SRui Paulo return EDIT_KEY_CTRL_D;
819f05cddf9SRui Paulo case 5:
820f05cddf9SRui Paulo return EDIT_KEY_CTRL_E;
821f05cddf9SRui Paulo case 6:
822f05cddf9SRui Paulo return EDIT_KEY_CTRL_F;
823f05cddf9SRui Paulo case 7:
824f05cddf9SRui Paulo return EDIT_KEY_CTRL_G;
825f05cddf9SRui Paulo case 8:
826f05cddf9SRui Paulo return EDIT_KEY_CTRL_H;
827f05cddf9SRui Paulo case 9:
828f05cddf9SRui Paulo return EDIT_KEY_TAB;
829f05cddf9SRui Paulo case 10:
830f05cddf9SRui Paulo return EDIT_KEY_CTRL_J;
831f05cddf9SRui Paulo case 13: /* CR */
832f05cddf9SRui Paulo return EDIT_KEY_ENTER;
833f05cddf9SRui Paulo case 11:
834f05cddf9SRui Paulo return EDIT_KEY_CTRL_K;
835f05cddf9SRui Paulo case 12:
836f05cddf9SRui Paulo return EDIT_KEY_CTRL_L;
837f05cddf9SRui Paulo case 14:
838f05cddf9SRui Paulo return EDIT_KEY_CTRL_N;
839f05cddf9SRui Paulo case 15:
840f05cddf9SRui Paulo return EDIT_KEY_CTRL_O;
841f05cddf9SRui Paulo case 16:
842f05cddf9SRui Paulo return EDIT_KEY_CTRL_P;
843f05cddf9SRui Paulo case 18:
844f05cddf9SRui Paulo return EDIT_KEY_CTRL_R;
845f05cddf9SRui Paulo case 20:
846f05cddf9SRui Paulo return EDIT_KEY_CTRL_T;
847f05cddf9SRui Paulo case 21:
848f05cddf9SRui Paulo return EDIT_KEY_CTRL_U;
849f05cddf9SRui Paulo case 22:
850f05cddf9SRui Paulo return EDIT_KEY_CTRL_V;
851f05cddf9SRui Paulo case 23:
852f05cddf9SRui Paulo return EDIT_KEY_CTRL_W;
853f05cddf9SRui Paulo case 27: /* ESC */
854f05cddf9SRui Paulo esc = 0;
855f05cddf9SRui Paulo return EDIT_KEY_NONE;
856f05cddf9SRui Paulo case 127:
857f05cddf9SRui Paulo return EDIT_KEY_BACKSPACE;
858f05cddf9SRui Paulo default:
859f05cddf9SRui Paulo return c;
860f05cddf9SRui Paulo }
861f05cddf9SRui Paulo }
862f05cddf9SRui Paulo
863f05cddf9SRui Paulo
864f05cddf9SRui Paulo static char search_buf[21];
865f05cddf9SRui Paulo static int search_skip;
866f05cddf9SRui Paulo
search_find(void)867f05cddf9SRui Paulo static char * search_find(void)
868f05cddf9SRui Paulo {
869f05cddf9SRui Paulo struct edit_history *h;
870f05cddf9SRui Paulo size_t len = os_strlen(search_buf);
871f05cddf9SRui Paulo int skip = search_skip;
872f05cddf9SRui Paulo
873f05cddf9SRui Paulo if (len == 0)
874f05cddf9SRui Paulo return NULL;
875f05cddf9SRui Paulo
876f05cddf9SRui Paulo dl_list_for_each(h, &history_list, struct edit_history, list) {
877f05cddf9SRui Paulo if (os_strstr(h->str, search_buf)) {
878f05cddf9SRui Paulo if (skip == 0)
879f05cddf9SRui Paulo return h->str;
880f05cddf9SRui Paulo skip--;
881f05cddf9SRui Paulo }
882f05cddf9SRui Paulo }
883f05cddf9SRui Paulo
884f05cddf9SRui Paulo search_skip = 0;
885f05cddf9SRui Paulo return NULL;
886f05cddf9SRui Paulo }
887f05cddf9SRui Paulo
888f05cddf9SRui Paulo
search_redraw(void)889f05cddf9SRui Paulo static void search_redraw(void)
890f05cddf9SRui Paulo {
891f05cddf9SRui Paulo char *match = search_find();
892f05cddf9SRui Paulo printf("\rsearch '%s': %s" CLEAR_END_LINE,
893f05cddf9SRui Paulo search_buf, match ? match : "");
894f05cddf9SRui Paulo printf("\rsearch '%s", search_buf);
895f05cddf9SRui Paulo fflush(stdout);
896f05cddf9SRui Paulo }
897f05cddf9SRui Paulo
898f05cddf9SRui Paulo
search_start(void)899f05cddf9SRui Paulo static void search_start(void)
900f05cddf9SRui Paulo {
901f05cddf9SRui Paulo edit_clear_line();
902f05cddf9SRui Paulo search_buf[0] = '\0';
903f05cddf9SRui Paulo search_skip = 0;
904f05cddf9SRui Paulo search_redraw();
905f05cddf9SRui Paulo }
906f05cddf9SRui Paulo
907f05cddf9SRui Paulo
search_clear(void)908f05cddf9SRui Paulo static void search_clear(void)
909f05cddf9SRui Paulo {
910f05cddf9SRui Paulo search_redraw();
911f05cddf9SRui Paulo printf("\r" CLEAR_END_LINE);
912f05cddf9SRui Paulo }
913f05cddf9SRui Paulo
914f05cddf9SRui Paulo
search_stop(void)915f05cddf9SRui Paulo static void search_stop(void)
916f05cddf9SRui Paulo {
917f05cddf9SRui Paulo char *match = search_find();
918f05cddf9SRui Paulo search_buf[0] = '\0';
919f05cddf9SRui Paulo search_clear();
920f05cddf9SRui Paulo if (match) {
921f05cddf9SRui Paulo os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
922f05cddf9SRui Paulo cmdbuf_len = os_strlen(cmdbuf);
923f05cddf9SRui Paulo cmdbuf_pos = cmdbuf_len;
924f05cddf9SRui Paulo }
925f05cddf9SRui Paulo edit_redraw();
926f05cddf9SRui Paulo }
927f05cddf9SRui Paulo
928f05cddf9SRui Paulo
search_cancel(void)929f05cddf9SRui Paulo static void search_cancel(void)
930f05cddf9SRui Paulo {
931f05cddf9SRui Paulo search_buf[0] = '\0';
932f05cddf9SRui Paulo search_clear();
933f05cddf9SRui Paulo edit_redraw();
934f05cddf9SRui Paulo }
935f05cddf9SRui Paulo
936f05cddf9SRui Paulo
search_backspace(void)937f05cddf9SRui Paulo static void search_backspace(void)
938f05cddf9SRui Paulo {
939f05cddf9SRui Paulo size_t len;
940f05cddf9SRui Paulo len = os_strlen(search_buf);
941f05cddf9SRui Paulo if (len == 0)
942f05cddf9SRui Paulo return;
943f05cddf9SRui Paulo search_buf[len - 1] = '\0';
944f05cddf9SRui Paulo search_skip = 0;
945f05cddf9SRui Paulo search_redraw();
946f05cddf9SRui Paulo }
947f05cddf9SRui Paulo
948f05cddf9SRui Paulo
search_next(void)949f05cddf9SRui Paulo static void search_next(void)
950f05cddf9SRui Paulo {
951f05cddf9SRui Paulo search_skip++;
952f05cddf9SRui Paulo search_find();
953f05cddf9SRui Paulo search_redraw();
954f05cddf9SRui Paulo }
955f05cddf9SRui Paulo
956f05cddf9SRui Paulo
search_char(char c)957f05cddf9SRui Paulo static void search_char(char c)
958f05cddf9SRui Paulo {
959f05cddf9SRui Paulo size_t len;
960f05cddf9SRui Paulo len = os_strlen(search_buf);
961f05cddf9SRui Paulo if (len == sizeof(search_buf) - 1)
962f05cddf9SRui Paulo return;
963f05cddf9SRui Paulo search_buf[len] = c;
964f05cddf9SRui Paulo search_buf[len + 1] = '\0';
965f05cddf9SRui Paulo search_skip = 0;
966f05cddf9SRui Paulo search_redraw();
967f05cddf9SRui Paulo }
968f05cddf9SRui Paulo
969f05cddf9SRui Paulo
search_key(enum edit_key_code c)970f05cddf9SRui Paulo static enum edit_key_code search_key(enum edit_key_code c)
971f05cddf9SRui Paulo {
972f05cddf9SRui Paulo switch (c) {
973f05cddf9SRui Paulo case EDIT_KEY_ENTER:
974f05cddf9SRui Paulo case EDIT_KEY_CTRL_J:
975f05cddf9SRui Paulo case EDIT_KEY_LEFT:
976f05cddf9SRui Paulo case EDIT_KEY_RIGHT:
977f05cddf9SRui Paulo case EDIT_KEY_HOME:
978f05cddf9SRui Paulo case EDIT_KEY_END:
979f05cddf9SRui Paulo case EDIT_KEY_CTRL_A:
980f05cddf9SRui Paulo case EDIT_KEY_CTRL_E:
981f05cddf9SRui Paulo search_stop();
982f05cddf9SRui Paulo return c;
983f05cddf9SRui Paulo case EDIT_KEY_DOWN:
984f05cddf9SRui Paulo case EDIT_KEY_UP:
985f05cddf9SRui Paulo search_cancel();
986f05cddf9SRui Paulo return EDIT_KEY_EOF;
987f05cddf9SRui Paulo case EDIT_KEY_CTRL_H:
988f05cddf9SRui Paulo case EDIT_KEY_BACKSPACE:
989f05cddf9SRui Paulo search_backspace();
990f05cddf9SRui Paulo break;
991f05cddf9SRui Paulo case EDIT_KEY_CTRL_R:
992f05cddf9SRui Paulo search_next();
993f05cddf9SRui Paulo break;
994f05cddf9SRui Paulo default:
995f05cddf9SRui Paulo if (c >= 32 && c <= 255)
996f05cddf9SRui Paulo search_char(c);
997f05cddf9SRui Paulo break;
998f05cddf9SRui Paulo }
999f05cddf9SRui Paulo
1000f05cddf9SRui Paulo return EDIT_KEY_NONE;
1001f05cddf9SRui Paulo }
1002f05cddf9SRui Paulo
1003f05cddf9SRui Paulo
edit_read_char(int sock,void * eloop_ctx,void * sock_ctx)1004f05cddf9SRui Paulo static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
1005f05cddf9SRui Paulo {
1006f05cddf9SRui Paulo static int last_tab = 0;
1007f05cddf9SRui Paulo static int search = 0;
1008f05cddf9SRui Paulo enum edit_key_code c;
1009f05cddf9SRui Paulo
1010f05cddf9SRui Paulo c = edit_read_key(sock);
1011f05cddf9SRui Paulo
1012f05cddf9SRui Paulo if (search) {
1013f05cddf9SRui Paulo c = search_key(c);
1014f05cddf9SRui Paulo if (c == EDIT_KEY_NONE)
1015f05cddf9SRui Paulo return;
1016f05cddf9SRui Paulo search = 0;
1017f05cddf9SRui Paulo if (c == EDIT_KEY_EOF)
1018f05cddf9SRui Paulo return;
1019f05cddf9SRui Paulo }
1020f05cddf9SRui Paulo
1021f05cddf9SRui Paulo if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
1022f05cddf9SRui Paulo last_tab = 0;
1023f05cddf9SRui Paulo
1024f05cddf9SRui Paulo switch (c) {
1025f05cddf9SRui Paulo case EDIT_KEY_NONE:
1026f05cddf9SRui Paulo break;
1027f05cddf9SRui Paulo case EDIT_KEY_EOF:
1028f05cddf9SRui Paulo edit_eof_cb(edit_cb_ctx);
1029f05cddf9SRui Paulo break;
1030f05cddf9SRui Paulo case EDIT_KEY_TAB:
1031f05cddf9SRui Paulo complete(last_tab);
1032f05cddf9SRui Paulo last_tab = 1;
1033f05cddf9SRui Paulo break;
1034f05cddf9SRui Paulo case EDIT_KEY_UP:
1035f05cddf9SRui Paulo case EDIT_KEY_CTRL_P:
1036f05cddf9SRui Paulo history_prev();
1037f05cddf9SRui Paulo break;
1038f05cddf9SRui Paulo case EDIT_KEY_DOWN:
1039f05cddf9SRui Paulo case EDIT_KEY_CTRL_N:
1040f05cddf9SRui Paulo history_next();
1041f05cddf9SRui Paulo break;
1042f05cddf9SRui Paulo case EDIT_KEY_RIGHT:
1043f05cddf9SRui Paulo case EDIT_KEY_CTRL_F:
1044f05cddf9SRui Paulo move_right();
1045f05cddf9SRui Paulo break;
1046f05cddf9SRui Paulo case EDIT_KEY_LEFT:
1047f05cddf9SRui Paulo case EDIT_KEY_CTRL_B:
1048f05cddf9SRui Paulo move_left();
1049f05cddf9SRui Paulo break;
1050f05cddf9SRui Paulo case EDIT_KEY_CTRL_RIGHT:
1051f05cddf9SRui Paulo move_word_right();
1052f05cddf9SRui Paulo break;
1053f05cddf9SRui Paulo case EDIT_KEY_CTRL_LEFT:
1054f05cddf9SRui Paulo move_word_left();
1055f05cddf9SRui Paulo break;
1056f05cddf9SRui Paulo case EDIT_KEY_DELETE:
1057f05cddf9SRui Paulo delete_current();
1058f05cddf9SRui Paulo break;
1059f05cddf9SRui Paulo case EDIT_KEY_END:
1060f05cddf9SRui Paulo move_end();
1061f05cddf9SRui Paulo break;
1062f05cddf9SRui Paulo case EDIT_KEY_HOME:
1063f05cddf9SRui Paulo case EDIT_KEY_CTRL_A:
1064f05cddf9SRui Paulo move_start();
1065f05cddf9SRui Paulo break;
1066f05cddf9SRui Paulo case EDIT_KEY_F2:
1067f05cddf9SRui Paulo history_debug_dump();
1068f05cddf9SRui Paulo break;
1069f05cddf9SRui Paulo case EDIT_KEY_CTRL_D:
1070f05cddf9SRui Paulo if (cmdbuf_len > 0) {
1071f05cddf9SRui Paulo delete_current();
1072f05cddf9SRui Paulo return;
1073f05cddf9SRui Paulo }
1074f05cddf9SRui Paulo printf("\n");
1075f05cddf9SRui Paulo edit_eof_cb(edit_cb_ctx);
1076f05cddf9SRui Paulo break;
1077f05cddf9SRui Paulo case EDIT_KEY_CTRL_E:
1078f05cddf9SRui Paulo move_end();
1079f05cddf9SRui Paulo break;
1080f05cddf9SRui Paulo case EDIT_KEY_CTRL_H:
1081f05cddf9SRui Paulo case EDIT_KEY_BACKSPACE:
1082f05cddf9SRui Paulo delete_left();
1083f05cddf9SRui Paulo break;
1084f05cddf9SRui Paulo case EDIT_KEY_ENTER:
1085f05cddf9SRui Paulo case EDIT_KEY_CTRL_J:
1086f05cddf9SRui Paulo process_cmd();
1087f05cddf9SRui Paulo break;
1088f05cddf9SRui Paulo case EDIT_KEY_CTRL_K:
1089f05cddf9SRui Paulo clear_right();
1090f05cddf9SRui Paulo break;
1091f05cddf9SRui Paulo case EDIT_KEY_CTRL_L:
1092f05cddf9SRui Paulo edit_clear_line();
1093f05cddf9SRui Paulo edit_redraw();
1094f05cddf9SRui Paulo break;
1095f05cddf9SRui Paulo case EDIT_KEY_CTRL_R:
1096f05cddf9SRui Paulo search = 1;
1097f05cddf9SRui Paulo search_start();
1098f05cddf9SRui Paulo break;
1099f05cddf9SRui Paulo case EDIT_KEY_CTRL_U:
1100f05cddf9SRui Paulo clear_left();
1101f05cddf9SRui Paulo break;
1102f05cddf9SRui Paulo case EDIT_KEY_CTRL_W:
1103f05cddf9SRui Paulo delete_word();
1104f05cddf9SRui Paulo break;
1105f05cddf9SRui Paulo default:
1106f05cddf9SRui Paulo if (c >= 32 && c <= 255)
1107f05cddf9SRui Paulo insert_char(c);
1108f05cddf9SRui Paulo break;
1109f05cddf9SRui Paulo }
1110f05cddf9SRui Paulo }
1111f05cddf9SRui Paulo
1112f05cddf9SRui Paulo
edit_init(void (* cmd_cb)(void * ctx,char * cmd),void (* eof_cb)(void * ctx),char ** (* completion_cb)(void * ctx,const char * cmd,int pos),void * ctx,const char * history_file,const char * ps)1113f05cddf9SRui Paulo int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
1114f05cddf9SRui Paulo void (*eof_cb)(void *ctx),
1115f05cddf9SRui Paulo char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
1116f05cddf9SRui Paulo void *ctx, const char *history_file, const char *ps)
1117f05cddf9SRui Paulo {
1118f05cddf9SRui Paulo currbuf[0] = '\0';
1119f05cddf9SRui Paulo dl_list_init(&history_list);
1120f05cddf9SRui Paulo history_curr = NULL;
1121f05cddf9SRui Paulo if (history_file)
1122f05cddf9SRui Paulo history_read(history_file);
1123f05cddf9SRui Paulo
1124f05cddf9SRui Paulo edit_cb_ctx = ctx;
1125f05cddf9SRui Paulo edit_cmd_cb = cmd_cb;
1126f05cddf9SRui Paulo edit_eof_cb = eof_cb;
1127f05cddf9SRui Paulo edit_completion_cb = completion_cb;
1128f05cddf9SRui Paulo
1129f05cddf9SRui Paulo tcgetattr(STDIN_FILENO, &prevt);
1130f05cddf9SRui Paulo newt = prevt;
1131f05cddf9SRui Paulo newt.c_lflag &= ~(ICANON | ECHO);
1132f05cddf9SRui Paulo tcsetattr(STDIN_FILENO, TCSANOW, &newt);
1133f05cddf9SRui Paulo
1134f05cddf9SRui Paulo eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
1135f05cddf9SRui Paulo
1136f05cddf9SRui Paulo ps2 = ps;
1137f05cddf9SRui Paulo printf("%s> ", ps2 ? ps2 : "");
1138f05cddf9SRui Paulo fflush(stdout);
1139f05cddf9SRui Paulo
1140f05cddf9SRui Paulo return 0;
1141f05cddf9SRui Paulo }
1142f05cddf9SRui Paulo
1143f05cddf9SRui Paulo
edit_deinit(const char * history_file,int (* filter_cb)(void * ctx,const char * cmd))1144f05cddf9SRui Paulo void edit_deinit(const char *history_file,
1145f05cddf9SRui Paulo int (*filter_cb)(void *ctx, const char *cmd))
1146f05cddf9SRui Paulo {
1147f05cddf9SRui Paulo struct edit_history *h;
1148f05cddf9SRui Paulo if (history_file)
1149f05cddf9SRui Paulo history_write(history_file, filter_cb);
1150f05cddf9SRui Paulo while ((h = dl_list_first(&history_list, struct edit_history, list))) {
1151f05cddf9SRui Paulo dl_list_del(&h->list);
1152f05cddf9SRui Paulo os_free(h);
1153f05cddf9SRui Paulo }
1154f05cddf9SRui Paulo edit_clear_line();
1155f05cddf9SRui Paulo putchar('\r');
1156f05cddf9SRui Paulo fflush(stdout);
1157f05cddf9SRui Paulo eloop_unregister_read_sock(STDIN_FILENO);
1158f05cddf9SRui Paulo tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
1159f05cddf9SRui Paulo }
1160f05cddf9SRui Paulo
1161f05cddf9SRui Paulo
edit_redraw(void)1162f05cddf9SRui Paulo void edit_redraw(void)
1163f05cddf9SRui Paulo {
1164f05cddf9SRui Paulo char tmp;
1165f05cddf9SRui Paulo cmdbuf[cmdbuf_len] = '\0';
1166f05cddf9SRui Paulo printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
1167f05cddf9SRui Paulo if (cmdbuf_pos != cmdbuf_len) {
1168f05cddf9SRui Paulo tmp = cmdbuf[cmdbuf_pos];
1169f05cddf9SRui Paulo cmdbuf[cmdbuf_pos] = '\0';
1170f05cddf9SRui Paulo printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
1171f05cddf9SRui Paulo cmdbuf[cmdbuf_pos] = tmp;
1172f05cddf9SRui Paulo }
1173f05cddf9SRui Paulo fflush(stdout);
1174f05cddf9SRui Paulo }
1175