18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Command line editing and history wrapper for readline
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * See README for more details.
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <readline/readline.h>
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <readline/history.h>
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h"
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eloop.h"
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "edit.h"
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void *edit_cb_ctx;
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void (*edit_cmd_cb)(void *ctx, char *cmd);
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void (*edit_eof_cb)(void *ctx);
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	NULL;
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char **pending_completions = NULL;
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void readline_free_completions(void)
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int i;
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (pending_completions == NULL)
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (i = 0; pending_completions[i]; i++)
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		os_free(pending_completions[i]);
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(pending_completions);
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	pending_completions = NULL;
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char * readline_completion_func(const char *text, int state)
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	static int pos = 0;
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	static size_t len = 0;
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (pending_completions == NULL) {
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rl_attempted_completion_over = 1;
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (state == 0) {
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pos = 0;
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		len = os_strlen(text);
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (; pending_completions[pos]; pos++) {
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (strncmp(pending_completions[pos], text, len) == 0)
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return strdup(pending_completions[pos++]);
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_attempted_completion_over = 1;
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic char ** readline_completion(const char *text, int start, int end)
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	readline_free_completions();
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (edit_completion_cb)
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pending_completions = edit_completion_cb(edit_cb_ctx,
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt							 rl_line_buffer, end);
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return rl_completion_matches(text, readline_completion_func);
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_callback_read_char();
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void trunc_nl(char *str)
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *pos = str;
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (*pos != '\0') {
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (*pos == '\n') {
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			*pos = '\0';
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		pos++;
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void readline_cmd_handler(char *cmd)
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (cmd && *cmd) {
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		HIST_ENTRY *h;
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (next_history())
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			;
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h = previous_history();
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h == NULL || os_strcmp(cmd, h->line) != 0)
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			add_history(cmd);
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		next_history();
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (cmd == NULL) {
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		edit_eof_cb(edit_cb_ctx);
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	trunc_nl(cmd);
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	edit_cmd_cb(edit_cb_ctx, cmd);
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint edit_init(void (*cmd_cb)(void *ctx, char *cmd),
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	      void (*eof_cb)(void *ctx),
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
11561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	      void *ctx, const char *history_file, const char *ps)
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	edit_cb_ctx = ctx;
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	edit_cmd_cb = cmd_cb;
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	edit_eof_cb = eof_cb;
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	edit_completion_cb = completion_cb;
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_attempted_completion_function = readline_completion;
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (history_file) {
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		read_history(history_file);
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		stifle_history(100);
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	if (ps) {
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		size_t blen = os_strlen(ps) + 3;
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		char *ps2 = os_malloc(blen);
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (ps2) {
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			os_snprintf(ps2, blen, "%s> ", ps);
13561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			rl_callback_handler_install(ps2, readline_cmd_handler);
13661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			os_free(ps2);
13761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return 0;
13861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
13961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	}
14061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_callback_handler_install("> ", readline_cmd_handler);
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid edit_deinit(const char *history_file,
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 int (*filter_cb)(void *ctx, const char *cmd))
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
15061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rl_set_prompt("");
15161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rl_replace_line("", 0);
15261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt	rl_redisplay();
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_callback_handler_remove();
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	readline_free_completions();
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	eloop_unregister_read_sock(STDIN_FILENO);
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (history_file) {
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* Save command history, excluding lines that may contain
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * passwords. */
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		HIST_ENTRY *h;
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		history_set_pos(0);
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while ((h = current_history())) {
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			char *p = h->line;
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			while (*p == ' ' || *p == '\t')
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				p++;
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h = remove_history(where_history());
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (h) {
17044c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					free(h->line);
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					free(h->data);
17244c957860ca714a86357591f39aff0bfa904c743Dmitry Shmidt					free(h);
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				} else
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					next_history();
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				next_history();
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		write_history(history_file);
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid edit_clear_line(void)
1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid edit_redraw(void)
1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_on_new_line();
1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rl_redisplay();
1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
193