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