1 /* This file is part of GNU Radius.
2 Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
3
4 Written by Sergey Poznyakoff
5
6 GNU Radius is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Radius is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Radius; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #if defined(HAVE_CONFIG_H)
21 # include <config.h>
22 #endif
23
24 #ifdef WITH_READLINE
25
26 #include <stdio.h>
27 #ifdef HAVE_READLINE_READLINE_H
28 # include <readline/readline.h>
29 #endif
30 #include <common.h>
31 #include <radius/radius.h>
32 #include <radtest.h>
33 #include "gram.h"
34
35 struct key_tab {
36 char *name;
37 int len;
38 int tok;
39 int initial;
40 };
41
42 static struct key_tab key_tab[] = {
43 { "auth", 4, AUTH, 0 },
44 { "print", 5, PRINT, 1 },
45 { "send", 4, SEND, 1 },
46 { "exit", 4, EXIT, 1 },
47 { "expect",6, EXPECT, 1 },
48 { "acct", 4, ACCT, 0 },
49 { "begin", 5, T_BEGIN, 0 },
50 { "end", 3, T_END, 0 },
51 { "while", 5, WHILE, 1 },
52 { "do", 2, DO, 1 },
53 { "break", 5, BREAK, 1 },
54 { "continue", 8, CONTINUE, 1 },
55 { "if", 2, IF, 1 },
56 { "else", 4, ELSE, 1 },
57 { "set", 3, SET, 1 },
58 { "getopt",6, GETOPT, 0 },
59 { "input", 5, INPUT, 1 },
60 { "shift", 5, SHIFT, 1 },
61 { "return",6, T_RETURN,1 },
62 { "case", 4, CASE, 1 },
63 { "in", 2, IN, 0 },
64 { NULL }
65 };
66
67 static char *
gen_state0_list(const char * text,int state)68 gen_state0_list(const char *text, int state)
69 {
70 static int len;
71 static struct key_tab *cursor;
72 struct key_tab *kp;
73
74 if (!state) {
75 len = strlen(text);
76 cursor = key_tab;
77 }
78
79 while ((kp = cursor++)->name)
80 if (kp->initial
81 && (len == 0
82 || (len <= strlen(kp->name)
83 && strncmp(kp->name, text, len) == 0)))
84 return strdup(kp->name);
85 return NULL;
86 }
87
88 static char *
gen_match_list(char * list[],const char * text,int state)89 gen_match_list(char *list[], const char *text, int state)
90 {
91 static char **cursor;
92 static int len;
93 char *str;
94
95 if (!state) {
96 len = strlen(text);
97 cursor = list;
98 }
99
100 while ((str = *cursor++))
101 if (strlen (str) >= len && strncmp (str, text, len) == 0)
102 return strdup (str);
103
104 return NULL;
105 }
106
107 static char *
gen_number_list(const char * text,int state)108 gen_number_list(const char *text, int state)
109 {
110 static void *itr_data = NULL;
111 const char *str;
112
113 if (!state)
114 str = grad_first_matching_code_name(text, &itr_data);
115 else
116 str = grad_next_matching_code_name(itr_data);
117 if (!str) {
118 grad_free(itr_data);
119 return NULL;
120 }
121 return strdup(str);
122 }
123
124 static char *
gen_port_list(const char * text,int state)125 gen_port_list(const char *text, int state)
126 {
127 static char *names[] = { "auth", "acct", NULL };
128 return gen_match_list(names, text, state);
129 }
130
131 struct dict_match {
132 const char *text;
133 int len;
134
135 struct obstack stk;
136 char *curp;
137 };
138
139 int
select_matching_attr(void * data,char const * name,grad_dict_attr_t const * dict_entry ARG_UNUSED)140 select_matching_attr(void *data, char const *name,
141 grad_dict_attr_t const *dict_entry ARG_UNUSED)
142 {
143 struct dict_match *dm = data;
144 if (strlen(name) >= dm->len && strncmp(name, dm->text, dm->len) == 0)
145 obstack_grow(&dm->stk, name, strlen(name)+1);
146 return 0;
147 }
148
149 static char *
gen_attribute_name(const char * text,int state)150 gen_attribute_name(const char *text, int state)
151 {
152 static struct dict_match dict_match;
153 if (!state) {
154 obstack_init(&dict_match.stk);
155 dict_match.text = text;
156 dict_match.len = strlen(text);
157 grad_dictionary_iterate(select_matching_attr, &dict_match);
158 obstack_1grow(&dict_match.stk, 0);
159 dict_match.curp = obstack_finish(&dict_match.stk);
160 }
161 if (*dict_match.curp) {
162 char *ret = strdup(dict_match.curp);
163 dict_match.curp += strlen(dict_match.curp) + 1;
164 return ret;
165 }
166 obstack_free(&dict_match.stk, NULL);
167 return NULL;
168 }
169
170 static int attribute_number;
171
172 int
select_matching_value(void * data,grad_dict_value_t * val)173 select_matching_value(void *data, grad_dict_value_t *val)
174 {
175 struct dict_match *dm = data;
176 if (val->attr->value == attribute_number
177 && strlen(val->name) >= dm->len
178 && strncmp(val->name, dm->text, dm->len) == 0)
179 obstack_grow(&dm->stk, val->name, strlen(val->name)+1);
180 return 0;
181 }
182
183 static char *
gen_attribute_value(const char * text,int state)184 gen_attribute_value(const char *text, int state)
185 {
186 static struct dict_match dict_match;
187 if (!state) {
188 obstack_init(&dict_match.stk);
189 dict_match.text = text;
190 dict_match.len = strlen(text);
191 grad_dictionary_value_iterate(select_matching_value,
192 &dict_match);
193 obstack_1grow(&dict_match.stk, 0);
194 dict_match.curp = obstack_finish(&dict_match.stk);
195 }
196 if (*dict_match.curp) {
197 char *ret = strdup(dict_match.curp);
198 dict_match.curp += strlen(dict_match.curp) + 1;
199 return ret;
200 }
201 obstack_free(&dict_match.stk, NULL);
202 return NULL;
203 }
204
205 static int
is_cmp_op(char * str)206 is_cmp_op(char *str)
207 {
208 switch (str[0]) {
209 case '=':
210 return str[1] == 0;
211
212 case '<':
213 case '>':
214 return str[1] == 0 || str[1] == '=';
215 }
216 return 0;
217 }
218
219
220 static struct obstack var_stk;
221 static char **namelist;
222
223 static char *
gen_variable_name(const char * text,int state)224 gen_variable_name(const char *text, int state)
225 {
226 if (*namelist)
227 return strdup(*(namelist++));
228 return NULL;
229 }
230
231 struct varname_buf {
232 char *ptr;
233 size_t len;
234 };
235
236 int
variable_selector(void * data,grad_symbol_t * sym)237 variable_selector(void *data, grad_symbol_t *sym)
238 {
239 struct varname_buf *vb = data;
240 if (strlen(sym->name) >= vb->len
241 && strncmp(sym->name, vb->ptr, vb->len) == 0)
242 obstack_grow(&var_stk, sym->name, strlen(sym->name)+1);
243 return 0;
244 }
245
246 char **
complete_variable(int start,int end)247 complete_variable(int start, int end)
248 {
249 char **retval;
250 char *p;
251 struct varname_buf d;
252
253 d.ptr = rl_line_buffer+start;
254 d.len = end - start;
255
256 obstack_init(&var_stk);
257 grad_symtab_iterate(vartab, variable_selector, &d);
258 obstack_1grow(&var_stk, 0);
259 for (p = obstack_finish(&var_stk); *p; p += strlen(p)+1)
260 obstack_grow(&var_stk, &p, sizeof(char**));
261 p = NULL;
262 obstack_grow(&var_stk, &p, sizeof(char**));
263 namelist = obstack_finish(&var_stk);
264 retval = rl_completion_matches(rl_line_buffer, gen_variable_name);
265 obstack_free(&var_stk, NULL);
266 return retval;
267 }
268
269 char **
radtest_command_completion(char * text,int start,int end)270 radtest_command_completion(char *text, int start, int end)
271 {
272 if (start == 0)
273 return rl_completion_matches(text, gen_state0_list);
274 else {
275 int rc;
276 int argc;
277 char **argv;
278 char *buf = grad_emalloc (start);
279 memcpy(buf, rl_line_buffer, start);
280 buf[start-1] = 0;
281
282 rc = grad_argcv_get(buf, "=", "#", &argc, &argv);
283
284 grad_free(buf);
285
286 if (rc)
287 return NULL;
288
289 if (start > 1
290 && rl_line_buffer[start-1] == '$'
291 && !isspace(rl_line_buffer[end]))
292 return complete_variable(start, end);
293
294 if (strcmp (argv[argc-1], "send") == 0)
295 return rl_completion_matches(text, gen_port_list);
296 else if (strcmp (argv[argc-1], "auth") == 0
297 || strcmp (argv[argc-1], "acct") == 0
298 || (argc == 1 && strcmp (argv[0], "expect") == 0))
299 return rl_completion_matches(text, gen_number_list);
300 else if (argc == 2 && strcmp (argv[0], "expect") == 0)
301 return rl_completion_matches(text,
302 gen_attribute_name);
303
304 else if (argc > 2) {
305
306 if (strcmp (argv[argc-2], "auth") == 0
307 || strcmp (argv[argc-2], "acct") == 0
308 || is_cmp_op(argv[argc-2]))
309 return rl_completion_matches(text,
310 gen_attribute_name);
311 else if (is_cmp_op(argv[argc-1])) {
312 grad_dict_attr_t *dict =
313 grad_attr_name_to_dict(argv[argc-2]);
314 if (!dict)
315 return NULL;
316 attribute_number = dict->value;
317 return rl_completion_matches(text,
318 gen_attribute_value);
319 } else if (strcmp (argv[0], "expect") == 0)
320 return rl_completion_matches(text,
321 gen_attribute_name);
322 }
323 }
324
325
326 return NULL;
327 }
328
329 #else /* !WITH_READLINE */
330
331 char **
radtest_command_completion(char * text ARG_UNUSED,int start ARG_UNUSED,int end ARG_UNUSED)332 radtest_command_completion(char *text ARG_UNUSED, int start ARG_UNUSED,
333 int end ARG_UNUSED)
334 {
335 return 0;
336 }
337
338 #endif /* WITH_READLINE */
339