xref: /netbsd-src/external/bsd/wpa/dist/src/utils/edit_readline.c (revision 3c260e60464a854d43a997892d1e6c468d47919a)
1111b9fd8Schristos /*
2111b9fd8Schristos  * Command line editing and history wrapper for readline
3111b9fd8Schristos  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4111b9fd8Schristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
7111b9fd8Schristos  */
8111b9fd8Schristos 
9111b9fd8Schristos #include "includes.h"
10111b9fd8Schristos #include <readline/readline.h>
11111b9fd8Schristos #include <readline/history.h>
12111b9fd8Schristos 
13111b9fd8Schristos #include "common.h"
14111b9fd8Schristos #include "eloop.h"
15111b9fd8Schristos #include "edit.h"
16111b9fd8Schristos 
17111b9fd8Schristos 
18111b9fd8Schristos static void *edit_cb_ctx;
19111b9fd8Schristos static void (*edit_cmd_cb)(void *ctx, char *cmd);
20111b9fd8Schristos static void (*edit_eof_cb)(void *ctx);
21111b9fd8Schristos static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
22111b9fd8Schristos 	NULL;
23111b9fd8Schristos 
24111b9fd8Schristos static char **pending_completions = NULL;
25111b9fd8Schristos 
26111b9fd8Schristos 
readline_free_completions(void)27111b9fd8Schristos static void readline_free_completions(void)
28111b9fd8Schristos {
29111b9fd8Schristos 	int i;
30111b9fd8Schristos 	if (pending_completions == NULL)
31111b9fd8Schristos 		return;
32111b9fd8Schristos 	for (i = 0; pending_completions[i]; i++)
33111b9fd8Schristos 		os_free(pending_completions[i]);
34111b9fd8Schristos 	os_free(pending_completions);
35111b9fd8Schristos 	pending_completions = NULL;
36111b9fd8Schristos }
37111b9fd8Schristos 
38111b9fd8Schristos 
readline_completion_func(const char * text,int state)39111b9fd8Schristos static char * readline_completion_func(const char *text, int state)
40111b9fd8Schristos {
41111b9fd8Schristos 	static int pos = 0;
42111b9fd8Schristos 	static size_t len = 0;
43111b9fd8Schristos 
44111b9fd8Schristos 	if (pending_completions == NULL) {
45111b9fd8Schristos 		rl_attempted_completion_over = 1;
46111b9fd8Schristos 		return NULL;
47111b9fd8Schristos 	}
48111b9fd8Schristos 
49111b9fd8Schristos 	if (state == 0) {
50111b9fd8Schristos 		pos = 0;
51111b9fd8Schristos 		len = os_strlen(text);
52111b9fd8Schristos 	}
53111b9fd8Schristos 	for (; pending_completions[pos]; pos++) {
54111b9fd8Schristos 		if (strncmp(pending_completions[pos], text, len) == 0)
55111b9fd8Schristos 			return strdup(pending_completions[pos++]);
56111b9fd8Schristos 	}
57111b9fd8Schristos 
58111b9fd8Schristos 	rl_attempted_completion_over = 1;
59111b9fd8Schristos 	return NULL;
60111b9fd8Schristos }
61111b9fd8Schristos 
62111b9fd8Schristos 
readline_completion(const char * text,int start,int end)63111b9fd8Schristos static char ** readline_completion(const char *text, int start, int end)
64111b9fd8Schristos {
65111b9fd8Schristos 	readline_free_completions();
66111b9fd8Schristos 	if (edit_completion_cb)
67111b9fd8Schristos 		pending_completions = edit_completion_cb(edit_cb_ctx,
68111b9fd8Schristos 							 rl_line_buffer, end);
69111b9fd8Schristos 	return rl_completion_matches(text, readline_completion_func);
70111b9fd8Schristos }
71111b9fd8Schristos 
72111b9fd8Schristos 
edit_read_char(int sock,void * eloop_ctx,void * sock_ctx)73111b9fd8Schristos static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
74111b9fd8Schristos {
75111b9fd8Schristos 	rl_callback_read_char();
76111b9fd8Schristos }
77111b9fd8Schristos 
78111b9fd8Schristos 
trunc_nl(char * str)79111b9fd8Schristos static void trunc_nl(char *str)
80111b9fd8Schristos {
81111b9fd8Schristos 	char *pos = str;
82111b9fd8Schristos 	while (*pos != '\0') {
83111b9fd8Schristos 		if (*pos == '\n') {
84111b9fd8Schristos 			*pos = '\0';
85111b9fd8Schristos 			break;
86111b9fd8Schristos 		}
87111b9fd8Schristos 		pos++;
88111b9fd8Schristos 	}
89111b9fd8Schristos }
90111b9fd8Schristos 
91111b9fd8Schristos 
readline_cmd_handler(char * cmd)92111b9fd8Schristos static void readline_cmd_handler(char *cmd)
93111b9fd8Schristos {
94111b9fd8Schristos 	if (cmd && *cmd) {
95111b9fd8Schristos 		HIST_ENTRY *h;
96111b9fd8Schristos 		while (next_history())
97111b9fd8Schristos 			;
98111b9fd8Schristos 		h = previous_history();
99111b9fd8Schristos 		if (h == NULL || os_strcmp(cmd, h->line) != 0)
100111b9fd8Schristos 			add_history(cmd);
101111b9fd8Schristos 		next_history();
102111b9fd8Schristos 	}
103111b9fd8Schristos 	if (cmd == NULL) {
104111b9fd8Schristos 		edit_eof_cb(edit_cb_ctx);
105111b9fd8Schristos 		return;
106111b9fd8Schristos 	}
107111b9fd8Schristos 	trunc_nl(cmd);
108111b9fd8Schristos 	edit_cmd_cb(edit_cb_ctx, cmd);
109111b9fd8Schristos }
110111b9fd8Schristos 
111111b9fd8Schristos 
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)112111b9fd8Schristos int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
113111b9fd8Schristos 	      void (*eof_cb)(void *ctx),
114111b9fd8Schristos 	      char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
115e604d861Schristos 	      void *ctx, const char *history_file, const char *ps)
116111b9fd8Schristos {
117111b9fd8Schristos 	edit_cb_ctx = ctx;
118111b9fd8Schristos 	edit_cmd_cb = cmd_cb;
119111b9fd8Schristos 	edit_eof_cb = eof_cb;
120111b9fd8Schristos 	edit_completion_cb = completion_cb;
121111b9fd8Schristos 
122111b9fd8Schristos 	rl_attempted_completion_function = readline_completion;
123111b9fd8Schristos 	if (history_file) {
124111b9fd8Schristos 		read_history(history_file);
125111b9fd8Schristos 		stifle_history(100);
126111b9fd8Schristos 	}
127111b9fd8Schristos 
128111b9fd8Schristos 	eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
129111b9fd8Schristos 
130e604d861Schristos 	if (ps) {
131e604d861Schristos 		size_t blen = os_strlen(ps) + 3;
132e604d861Schristos 		char *ps2 = os_malloc(blen);
133e604d861Schristos 		if (ps2) {
134e604d861Schristos 			os_snprintf(ps2, blen, "%s> ", ps);
135e604d861Schristos 			rl_callback_handler_install(ps2, readline_cmd_handler);
136e604d861Schristos 			os_free(ps2);
137e604d861Schristos 			return 0;
138e604d861Schristos 		}
139e604d861Schristos 	}
140e604d861Schristos 
141111b9fd8Schristos 	rl_callback_handler_install("> ", readline_cmd_handler);
142111b9fd8Schristos 
143111b9fd8Schristos 	return 0;
144111b9fd8Schristos }
145111b9fd8Schristos 
146111b9fd8Schristos 
edit_deinit(const char * history_file,int (* filter_cb)(void * ctx,const char * cmd))147111b9fd8Schristos void edit_deinit(const char *history_file,
148111b9fd8Schristos 		 int (*filter_cb)(void *ctx, const char *cmd))
149111b9fd8Schristos {
150e604d861Schristos 	rl_set_prompt("");
151e604d861Schristos 	rl_replace_line("", 0);
152e604d861Schristos 	rl_redisplay();
153111b9fd8Schristos 	rl_callback_handler_remove();
154111b9fd8Schristos 	readline_free_completions();
155111b9fd8Schristos 
156111b9fd8Schristos 	eloop_unregister_read_sock(STDIN_FILENO);
157111b9fd8Schristos 
158111b9fd8Schristos 	if (history_file) {
159111b9fd8Schristos 		/* Save command history, excluding lines that may contain
160111b9fd8Schristos 		 * passwords. */
161111b9fd8Schristos 		HIST_ENTRY *h;
162111b9fd8Schristos 		history_set_pos(0);
163111b9fd8Schristos 		while ((h = current_history())) {
164111b9fd8Schristos 			char *p = h->line;
165111b9fd8Schristos 			while (*p == ' ' || *p == '\t')
166111b9fd8Schristos 				p++;
167111b9fd8Schristos 			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
168111b9fd8Schristos 				h = remove_history(where_history());
169111b9fd8Schristos 				if (h) {
170*3c260e60Schristos 					free(h->line);
171111b9fd8Schristos 					free(h->data);
172*3c260e60Schristos 					free(h);
173111b9fd8Schristos 				} else
174111b9fd8Schristos 					next_history();
175111b9fd8Schristos 			} else
176111b9fd8Schristos 				next_history();
177111b9fd8Schristos 		}
178111b9fd8Schristos 		write_history(history_file);
179111b9fd8Schristos 	}
180111b9fd8Schristos }
181111b9fd8Schristos 
182111b9fd8Schristos 
edit_clear_line(void)183111b9fd8Schristos void edit_clear_line(void)
184111b9fd8Schristos {
185111b9fd8Schristos }
186111b9fd8Schristos 
187111b9fd8Schristos 
edit_redraw(void)188111b9fd8Schristos void edit_redraw(void)
189111b9fd8Schristos {
190111b9fd8Schristos 	rl_on_new_line();
191111b9fd8Schristos 	rl_redisplay();
192111b9fd8Schristos }
193