blob: c9ff0e6dc24ed9274e1f227bf7beb0da841a00e4 [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),
115 void *ctx, const char *history_file)
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 rl_callback_handler_install("> ", readline_cmd_handler);
131
132 return 0;
133}
134
135
136void edit_deinit(const char *history_file,
137 int (*filter_cb)(void *ctx, const char *cmd))
138{
139 rl_callback_handler_remove();
140 readline_free_completions();
141
142 eloop_unregister_read_sock(STDIN_FILENO);
143
144 if (history_file) {
145 /* Save command history, excluding lines that may contain
146 * passwords. */
147 HIST_ENTRY *h;
148 history_set_pos(0);
149 while ((h = current_history())) {
150 char *p = h->line;
151 while (*p == ' ' || *p == '\t')
152 p++;
153 if (filter_cb && filter_cb(edit_cb_ctx, p)) {
154 h = remove_history(where_history());
155 if (h) {
156 os_free(h->line);
157 free(h->data);
158 os_free(h);
159 } else
160 next_history();
161 } else
162 next_history();
163 }
164 write_history(history_file);
165 }
166}
167
168
169void edit_clear_line(void)
170{
171}
172
173
174void edit_redraw(void)
175{
176 rl_on_new_line();
177 rl_redisplay();
178}