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