xref: /freebsd/contrib/wpa/src/utils/edit.c (revision 5b9c547c)
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