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