]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/wpa/src/utils/edit_readline.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / wpa / src / utils / edit_readline.c
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
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
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
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
73 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
74 {
75         rl_callback_read_char();
76 }
77
78
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
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
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
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                                         os_free(h->line);
171                                         free(h->data);
172                                         os_free(h);
173                                 } else
174                                         next_history();
175                         } else
176                                 next_history();
177                 }
178                 write_history(history_file);
179         }
180 }
181
182
183 void edit_clear_line(void)
184 {
185 }
186
187
188 void edit_redraw(void)
189 {
190         rl_on_new_line();
191         rl_redisplay();
192 }