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