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