read_config_file.c revision 7bafff09cc66e23519512a54e2d1ebd3664a1a70
1#if HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <string.h>
6#include <stdlib.h>
7#include <ctype.h>
8
9#include "ltrace.h"
10#include "read_config_file.h"
11#include "output.h"
12#include "debug.h"
13
14struct function *list_of_functions = NULL;
15
16static struct list_of_pt_t {
17	char *name;
18	enum arg_type pt;
19} list_of_pt[] = {
20	{
21	"void", ARGTYPE_VOID}, {
22	"int", ARGTYPE_INT}, {
23	"uint", ARGTYPE_UINT}, {
24	"long", ARGTYPE_LONG}, {
25	"ulong", ARGTYPE_ULONG}, {
26	"octal", ARGTYPE_OCTAL}, {
27	"char", ARGTYPE_CHAR}, {
28	"addr", ARGTYPE_ADDR}, {
29	"file", ARGTYPE_FILE}, {
30	"format", ARGTYPE_FORMAT}, {
31	"string", ARGTYPE_STRING}, {
32	"ignore", ARGTYPE_IGNORE}, {
33	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
34};
35
36static arg_type_info arg_type_singletons[] = {
37	{ ARGTYPE_VOID },
38	{ ARGTYPE_INT },
39	{ ARGTYPE_UINT },
40	{ ARGTYPE_LONG },
41	{ ARGTYPE_ULONG },
42	{ ARGTYPE_OCTAL },
43	{ ARGTYPE_CHAR },
44	{ ARGTYPE_ADDR },
45	{ ARGTYPE_FILE },
46	{ ARGTYPE_FORMAT },
47	{ ARGTYPE_STRING },
48	{ ARGTYPE_STRING_N },
49	{ ARGTYPE_IGNORE },
50	{ ARGTYPE_POINTER },
51	{ ARGTYPE_UNKNOWN }
52};
53
54arg_type_info *lookup_singleton(enum arg_type at)
55{
56	if (at >= 0 && at <= ARGTYPE_COUNT)
57		return &arg_type_singletons[at];
58	else
59		return &arg_type_singletons[ARGTYPE_COUNT]; /* UNKNOWN */
60}
61
62static arg_type_info *str2type(char **str)
63{
64	struct list_of_pt_t *tmp = &list_of_pt[0];
65
66	while (tmp->name) {
67		if (!strncmp(*str, tmp->name, strlen(tmp->name))
68		    && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
69			*str += strlen(tmp->name);
70			return lookup_singleton(tmp->pt);
71		}
72		tmp++;
73	}
74	return lookup_singleton(ARGTYPE_UNKNOWN);
75}
76
77static void eat_spaces(char **str)
78{
79	while (**str == ' ') {
80		(*str)++;
81	}
82}
83
84/*
85  Returns position in string at the left parenthesis which starts the
86  function's argument signature. Returns NULL on error.
87*/
88static char *start_of_arg_sig(char *str)
89{
90	char *pos;
91	int stacked = 0;
92
93	if (!strlen(str))
94		return NULL;
95
96	pos = &str[strlen(str)];
97	do {
98		pos--;
99		if (pos < str)
100			return NULL;
101		while ((pos > str) && (*pos != ')') && (*pos != '('))
102			pos--;
103
104		if (*pos == ')')
105			stacked++;
106		else if (*pos == '(')
107			stacked--;
108		else
109			return NULL;
110
111	} while (stacked > 0);
112
113	return (stacked == 0) ? pos : NULL;
114}
115
116/*
117  Decide whether a type needs any additional parameters.
118  For now, we do not parse any nontrivial argument types.
119*/
120static int simple_type(enum arg_type at)
121{
122	return 1;
123}
124
125static int line_no;
126static char *filename;
127
128static int parse_int(char **str)
129{
130    char *end;
131    long n = strtol(*str, &end, 0);
132    if (end == *str) {
133	output_line(0, "Syntax error in `%s', line %d: Bad number",
134		    filename, line_no);
135	return 0;
136    }
137
138    *str = end;
139    return n;
140}
141
142/*
143 * Input:
144 *  argN   : The value of argument #N, counting from 1 (arg0 = retval)
145 *  eltN   : The value of element #N of the containing structure
146 *  retval : The return value
147 *  0      : Error
148 *  N      : The numeric value N, if N > 0
149 *
150 * Output:
151 * > 0   actual numeric value
152 * = 0   return value
153 * < 0   (arg -n), counting from one
154 */
155static int parse_argnum(char **str)
156{
157    int multiplier = 1;
158    int n = 0;
159
160    if (strncmp(*str, "arg", 3) == 0) {
161	(*str) += 3;
162	multiplier = -1;
163    } else if (strncmp(*str, "retval", 6) == 0) {
164	(*str) += 6;
165	return 0;
166    }
167
168    n = parse_int(str);
169
170    return n * multiplier;
171}
172
173static arg_type_info *parse_nonpointer_type(char **str)
174{
175	arg_type_info *simple;
176	arg_type_info *info;
177
178	simple = str2type(str);
179	if (simple->type == ARGTYPE_UNKNOWN) {
180		return simple;		// UNKNOWN
181	}
182
183	if (simple_type(simple->type) && simple->type != ARGTYPE_STRING)
184		return simple;
185
186	info = malloc(sizeof(*info));
187	info->type = simple->type;
188
189	/* Code to parse parameterized types will go into the following
190	   switch statement. */
191
192	switch (info->type) {
193
194	case ARGTYPE_STRING:
195	    if (!isdigit(**str) && **str != '[') {
196		/* Oops, was just a simple string after all */
197		free(info);
198		return simple;
199	    }
200
201	    info->type = ARGTYPE_STRING_N;
202
203	    /* Backwards compatibility for string0, string1, ... */
204	    if (isdigit(**str)) {
205		info->u.string_n_info.size_spec = -parse_int(str);
206		return info;
207	    }
208
209	    (*str)++;		// Skip past opening [
210	    eat_spaces(str);
211	    info->u.string_n_info.size_spec = parse_argnum(str);
212	    eat_spaces(str);
213	    (*str)++;		// Skip past closing ]
214	    return info;
215
216	default:
217		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
218			    filename, line_no);
219		free(info);
220		return NULL;
221	}
222}
223
224static arg_type_info *parse_type(char **str)
225{
226	arg_type_info *info = parse_nonpointer_type(str);
227	while (**str == '*') {
228		arg_type_info *outer = malloc(sizeof(*info));
229		outer->type = ARGTYPE_POINTER;
230		outer->u.ptr_info.info = info;
231		(*str)++;
232		info = outer;
233	}
234	return info;
235}
236
237static struct function *process_line(char *buf)
238{
239	struct function fun;
240	struct function *fun_p;
241	char *str = buf;
242	char *tmp;
243	int i;
244
245	line_no++;
246	debug(3, "Reading line %d of `%s'", line_no, filename);
247	eat_spaces(&str);
248	fun.return_info = parse_type(&str);
249	if (fun.return_info == NULL)
250        	return NULL;
251	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
252		debug(3, " Skipping line %d", line_no);
253		return NULL;
254	}
255	debug(4, " return_type = %d", fun.return_info->type);
256	eat_spaces(&str);
257	tmp = start_of_arg_sig(str);
258	if (!tmp) {
259		output_line(0, "Syntax error in `%s', line %d", filename,
260			    line_no);
261		return NULL;
262	}
263	*tmp = '\0';
264	fun.name = strdup(str);
265	str = tmp + 1;
266	debug(3, " name = %s", fun.name);
267	fun.params_right = 0;
268	for (i = 0; i < MAX_ARGS; i++) {
269		eat_spaces(&str);
270		if (*str == ')') {
271			break;
272		}
273		if (str[0] == '+') {
274			fun.params_right++;
275			str++;
276		} else if (fun.params_right) {
277			fun.params_right++;
278		}
279		fun.arg_info[i] = parse_type(&str);
280		if (fun.arg_info[i] == NULL) {
281			output_line(0, "Syntax error in `%s', line %d"
282                                    ": unknown argument type",
283				    filename, line_no);
284			return NULL;
285		}
286		eat_spaces(&str);
287		if (*str == ',') {
288			str++;
289			continue;
290		} else if (*str == ')') {
291			continue;
292		} else {
293			if (str[strlen(str) - 1] == '\n')
294				str[strlen(str) - 1] = '\0';
295			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
296				    filename, line_no, str);
297			return NULL;
298		}
299	}
300	fun.num_params = i;
301	fun_p = malloc(sizeof(struct function));
302	memcpy(fun_p, &fun, sizeof(struct function));
303	return fun_p;
304}
305
306void read_config_file(char *file)
307{
308	FILE *stream;
309	char buf[1024];
310
311	filename = file;
312
313	debug(1, "Reading config file `%s'...", filename);
314
315	stream = fopen(filename, "r");
316	if (!stream) {
317		return;
318	}
319	line_no = 0;
320	while (fgets(buf, 1024, stream)) {
321		struct function *tmp = process_line(buf);
322
323		if (tmp) {
324			debug(2, "New function: `%s'", tmp->name);
325			tmp->next = list_of_functions;
326			list_of_functions = tmp;
327		}
328	}
329	fclose(stream);
330}
331