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