1*3ff40c12SJohn Marino /*
2*3ff40c12SJohn Marino  * Command line editing and history wrapper for readline
3*3ff40c12SJohn Marino  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4*3ff40c12SJohn Marino  *
5*3ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
6*3ff40c12SJohn Marino  * See README for more details.
7*3ff40c12SJohn Marino  */
8*3ff40c12SJohn Marino 
9*3ff40c12SJohn Marino #include "includes.h"
10*3ff40c12SJohn Marino #include <readline/readline.h>
11*3ff40c12SJohn Marino #include <readline/history.h>
12*3ff40c12SJohn Marino 
13*3ff40c12SJohn Marino #include "common.h"
14*3ff40c12SJohn Marino #include "eloop.h"
15*3ff40c12SJohn Marino #include "edit.h"
16*3ff40c12SJohn Marino 
17*3ff40c12SJohn Marino 
18*3ff40c12SJohn Marino static void *edit_cb_ctx;
19*3ff40c12SJohn Marino static void (*edit_cmd_cb)(void *ctx, char *cmd);
20*3ff40c12SJohn Marino static void (*edit_eof_cb)(void *ctx);
21*3ff40c12SJohn Marino static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
22*3ff40c12SJohn Marino 	NULL;
23*3ff40c12SJohn Marino 
24*3ff40c12SJohn Marino static char **pending_completions = NULL;
25*3ff40c12SJohn Marino 
26*3ff40c12SJohn Marino 
readline_free_completions(void)27*3ff40c12SJohn Marino static void readline_free_completions(void)
28*3ff40c12SJohn Marino {
29*3ff40c12SJohn Marino 	int i;
30*3ff40c12SJohn Marino 	if (pending_completions == NULL)
31*3ff40c12SJohn Marino 		return;
32*3ff40c12SJohn Marino 	for (i = 0; pending_completions[i]; i++)
33*3ff40c12SJohn Marino 		os_free(pending_completions[i]);
34*3ff40c12SJohn Marino 	os_free(pending_completions);
35*3ff40c12SJohn Marino 	pending_completions = NULL;
36*3ff40c12SJohn Marino }
37*3ff40c12SJohn Marino 
38*3ff40c12SJohn Marino 
readline_completion_func(const char * text,int state)39*3ff40c12SJohn Marino static char * readline_completion_func(const char *text, int state)
40*3ff40c12SJohn Marino {
41*3ff40c12SJohn Marino 	static int pos = 0;
42*3ff40c12SJohn Marino 	static size_t len = 0;
43*3ff40c12SJohn Marino 
44*3ff40c12SJohn Marino 	if (pending_completions == NULL) {
45*3ff40c12SJohn Marino 		rl_attempted_completion_over = 1;
46*3ff40c12SJohn Marino 		return NULL;
47*3ff40c12SJohn Marino 	}
48*3ff40c12SJohn Marino 
49*3ff40c12SJohn Marino 	if (state == 0) {
50*3ff40c12SJohn Marino 		pos = 0;
51*3ff40c12SJohn Marino 		len = os_strlen(text);
52*3ff40c12SJohn Marino 	}
53*3ff40c12SJohn Marino 	for (; pending_completions[pos]; pos++) {
54*3ff40c12SJohn Marino 		if (strncmp(pending_completions[pos], text, len) == 0)
55*3ff40c12SJohn Marino 			return strdup(pending_completions[pos++]);
56*3ff40c12SJohn Marino 	}
57*3ff40c12SJohn Marino 
58*3ff40c12SJohn Marino 	rl_attempted_completion_over = 1;
59*3ff40c12SJohn Marino 	return NULL;
60*3ff40c12SJohn Marino }
61*3ff40c12SJohn Marino 
62*3ff40c12SJohn Marino 
readline_completion(const char * text,int start,int end)63*3ff40c12SJohn Marino static char ** readline_completion(const char *text, int start, int end)
64*3ff40c12SJohn Marino {
65*3ff40c12SJohn Marino 	readline_free_completions();
66*3ff40c12SJohn Marino 	if (edit_completion_cb)
67*3ff40c12SJohn Marino 		pending_completions = edit_completion_cb(edit_cb_ctx,
68*3ff40c12SJohn Marino 							 rl_line_buffer, end);
69*3ff40c12SJohn Marino 	return rl_completion_matches(text, readline_completion_func);
70*3ff40c12SJohn Marino }
71*3ff40c12SJohn Marino 
72*3ff40c12SJohn Marino 
edit_read_char(int sock,void * eloop_ctx,void * sock_ctx)73*3ff40c12SJohn Marino static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
74*3ff40c12SJohn Marino {
75*3ff40c12SJohn Marino 	rl_callback_read_char();
76*3ff40c12SJohn Marino }
77*3ff40c12SJohn Marino 
78*3ff40c12SJohn Marino 
trunc_nl(char * str)79*3ff40c12SJohn Marino static void trunc_nl(char *str)
80*3ff40c12SJohn Marino {
81*3ff40c12SJohn Marino 	char *pos = str;
82*3ff40c12SJohn Marino 	while (*pos != '\0') {
83*3ff40c12SJohn Marino 		if (*pos == '\n') {
84*3ff40c12SJohn Marino 			*pos = '\0';
85*3ff40c12SJohn Marino 			break;
86*3ff40c12SJohn Marino 		}
87*3ff40c12SJohn Marino 		pos++;
88*3ff40c12SJohn Marino 	}
89*3ff40c12SJohn Marino }
90*3ff40c12SJohn Marino 
91*3ff40c12SJohn Marino 
readline_cmd_handler(char * cmd)92*3ff40c12SJohn Marino static void readline_cmd_handler(char *cmd)
93*3ff40c12SJohn Marino {
94*3ff40c12SJohn Marino 	if (cmd && *cmd) {
95*3ff40c12SJohn Marino 		HIST_ENTRY *h;
96*3ff40c12SJohn Marino 		while (next_history())
97*3ff40c12SJohn Marino 			;
98*3ff40c12SJohn Marino 		h = previous_history();
99*3ff40c12SJohn Marino 		if (h == NULL || os_strcmp(cmd, h->line) != 0)
100*3ff40c12SJohn Marino 			add_history(cmd);
101*3ff40c12SJohn Marino 		next_history();
102*3ff40c12SJohn Marino 	}
103*3ff40c12SJohn Marino 	if (cmd == NULL) {
104*3ff40c12SJohn Marino 		edit_eof_cb(edit_cb_ctx);
105*3ff40c12SJohn Marino 		return;
106*3ff40c12SJohn Marino 	}
107*3ff40c12SJohn Marino 	trunc_nl(cmd);
108*3ff40c12SJohn Marino 	edit_cmd_cb(edit_cb_ctx, cmd);
109*3ff40c12SJohn Marino }
110*3ff40c12SJohn Marino 
111*3ff40c12SJohn Marino 
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)112*3ff40c12SJohn Marino int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
113*3ff40c12SJohn Marino 	      void (*eof_cb)(void *ctx),
114*3ff40c12SJohn Marino 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
115*3ff40c12SJohn Marino 	      void *ctx, const char *history_file, const char *ps)
116*3ff40c12SJohn Marino {
117*3ff40c12SJohn Marino 	edit_cb_ctx = ctx;
118*3ff40c12SJohn Marino 	edit_cmd_cb = cmd_cb;
119*3ff40c12SJohn Marino 	edit_eof_cb = eof_cb;
120*3ff40c12SJohn Marino 	edit_completion_cb = completion_cb;
121*3ff40c12SJohn Marino 
122*3ff40c12SJohn Marino 	rl_attempted_completion_function = readline_completion;
123*3ff40c12SJohn Marino 	if (history_file) {
124*3ff40c12SJohn Marino 		read_history(history_file);
125*3ff40c12SJohn Marino 		stifle_history(100);
126*3ff40c12SJohn Marino 	}
127*3ff40c12SJohn Marino 
128*3ff40c12SJohn Marino 	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
129*3ff40c12SJohn Marino 
130*3ff40c12SJohn Marino 	if (ps) {
131*3ff40c12SJohn Marino 		size_t blen = os_strlen(ps) + 3;
132*3ff40c12SJohn Marino 		char *ps2 = os_malloc(blen);
133*3ff40c12SJohn Marino 		if (ps2) {
134*3ff40c12SJohn Marino 			os_snprintf(ps2, blen, "%s> ", ps);
135*3ff40c12SJohn Marino 			rl_callback_handler_install(ps2, readline_cmd_handler);
136*3ff40c12SJohn Marino 			os_free(ps2);
137*3ff40c12SJohn Marino 			return 0;
138*3ff40c12SJohn Marino 		}
139*3ff40c12SJohn Marino 	}
140*3ff40c12SJohn Marino 
141*3ff40c12SJohn Marino 	rl_callback_handler_install("> ", readline_cmd_handler);
142*3ff40c12SJohn Marino 
143*3ff40c12SJohn Marino 	return 0;
144*3ff40c12SJohn Marino }
145*3ff40c12SJohn Marino 
146*3ff40c12SJohn Marino 
edit_deinit(const char * history_file,int (* filter_cb)(void * ctx,const char * cmd))147*3ff40c12SJohn Marino void edit_deinit(const char *history_file,
148*3ff40c12SJohn Marino 		 int (*filter_cb)(void *ctx, const char *cmd))
149*3ff40c12SJohn Marino {
150*3ff40c12SJohn Marino 	rl_set_prompt("");
151*3ff40c12SJohn Marino 	rl_replace_line("", 0);
152*3ff40c12SJohn Marino 	rl_redisplay();
153*3ff40c12SJohn Marino 	rl_callback_handler_remove();
154*3ff40c12SJohn Marino 	readline_free_completions();
155*3ff40c12SJohn Marino 
156*3ff40c12SJohn Marino 	eloop_unregister_read_sock(STDIN_FILENO);
157*3ff40c12SJohn Marino 
158*3ff40c12SJohn Marino 	if (history_file) {
159*3ff40c12SJohn Marino 		/* Save command history, excluding lines that may contain
160*3ff40c12SJohn Marino 		 * passwords. */
161*3ff40c12SJohn Marino 		HIST_ENTRY *h;
162*3ff40c12SJohn Marino 		history_set_pos(0);
163*3ff40c12SJohn Marino 		while ((h = current_history())) {
164*3ff40c12SJohn Marino 			char *p = h->line;
165*3ff40c12SJohn Marino 			while (*p == ' ' || *p == '\t')
166*3ff40c12SJohn Marino 				p++;
167*3ff40c12SJohn Marino 			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
168*3ff40c12SJohn Marino 				h = remove_history(where_history());
169*3ff40c12SJohn Marino 				if (h) {
170*3ff40c12SJohn Marino 					free(h->line);
171*3ff40c12SJohn Marino 					free(h->data);
172*3ff40c12SJohn Marino 					free(h);
173*3ff40c12SJohn Marino 				} else
174*3ff40c12SJohn Marino 					next_history();
175*3ff40c12SJohn Marino 			} else
176*3ff40c12SJohn Marino 				next_history();
177*3ff40c12SJohn Marino 		}
178*3ff40c12SJohn Marino 		write_history(history_file);
179*3ff40c12SJohn Marino 	}
180*3ff40c12SJohn Marino }
181*3ff40c12SJohn Marino 
182*3ff40c12SJohn Marino 
edit_clear_line(void)183*3ff40c12SJohn Marino void edit_clear_line(void)
184*3ff40c12SJohn Marino {
185*3ff40c12SJohn Marino }
186*3ff40c12SJohn Marino 
187*3ff40c12SJohn Marino 
edit_redraw(void)188*3ff40c12SJohn Marino void edit_redraw(void)
189*3ff40c12SJohn Marino {
190*3ff40c12SJohn Marino 	rl_on_new_line();
191*3ff40c12SJohn Marino 	rl_redisplay();
192*3ff40c12SJohn Marino }
193