blob: c2a5bcaa1785276b65d5f5099c08d989ce1bf0e5 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * Command line editing and history wrapper for readline
3 * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
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
18static void *edit_cb_ctx;
19static void (*edit_cmd_cb)(void *ctx, char *cmd);
20static void (*edit_eof_cb)(void *ctx);
21static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
22 NULL;
23
24static char **pending_completions = NULL;
25
26
27static 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
39static 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
63static 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
73static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
74{
75 rl_callback_read_char();
76}
77
78
79static 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
92static 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
112int 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),
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700115 void *ctx, const char *history_file, const char *ps)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700116{
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
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700130 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700141 rl_callback_handler_install("> ", readline_cmd_handler);
142
143 return 0;
144}
145
146
147void edit_deinit(const char *history_file,
148 int (*filter_cb)(void *ctx, const char *cmd))
149{
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700150 rl_set_prompt("");
151 rl_replace_line("", 0);
152 rl_redisplay();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700153 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) {
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700170 free(h->line);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700171 free(h->data);
Dmitry Shmidt44c95782013-05-17 09:51:35 -0700172 free(h);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700173 } else
174 next_history();
175 } else
176 next_history();
177 }
178 write_history(history_file);
179 }
180}
181
182
183void edit_clear_line(void)
184{
185}
186
187
188void edit_redraw(void)
189{
190 rl_on_new_line();
191 rl_redisplay();
192}