read_config_file.c revision 6a3e24dc1709530e59dd6ae3e91ced1c221fe1b9
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
14static int line_no;
15static char *filename;
16static int error_count = 0;
17
18struct function *list_of_functions = NULL;
19
20static struct list_of_pt_t {
21	char *name;
22	enum arg_type pt;
23} list_of_pt[] = {
24	{
25	"void", ARGTYPE_VOID}, {
26	"int", ARGTYPE_INT}, {
27	"uint", ARGTYPE_UINT}, {
28	"long", ARGTYPE_LONG}, {
29	"ulong", ARGTYPE_ULONG}, {
30	"octal", ARGTYPE_OCTAL}, {
31	"char", ARGTYPE_CHAR}, {
32	"addr", ARGTYPE_ADDR}, {
33	"file", ARGTYPE_FILE}, {
34	"format", ARGTYPE_FORMAT}, {
35	"string", ARGTYPE_STRING}, {
36	"enum", ARGTYPE_ENUM}, {
37	"ignore", ARGTYPE_IGNORE}, {
38	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
39};
40
41static arg_type_info arg_type_singletons[] = {
42	{ ARGTYPE_VOID },
43	{ ARGTYPE_INT },
44	{ ARGTYPE_UINT },
45	{ ARGTYPE_LONG },
46	{ ARGTYPE_ULONG },
47	{ ARGTYPE_OCTAL },
48	{ ARGTYPE_CHAR },
49	{ ARGTYPE_ADDR },
50	{ ARGTYPE_FILE },
51	{ ARGTYPE_FORMAT },
52	{ ARGTYPE_STRING },
53	{ ARGTYPE_STRING_N },
54	{ ARGTYPE_ENUM },
55	{ ARGTYPE_IGNORE },
56	{ ARGTYPE_POINTER },
57	{ ARGTYPE_UNKNOWN }
58};
59
60arg_type_info *lookup_singleton(enum arg_type at)
61{
62	if (at >= 0 && at <= ARGTYPE_COUNT)
63		return &arg_type_singletons[at];
64	else
65		return &arg_type_singletons[ARGTYPE_COUNT]; /* UNKNOWN */
66}
67
68static arg_type_info *str2type(char **str)
69{
70	struct list_of_pt_t *tmp = &list_of_pt[0];
71
72	while (tmp->name) {
73		if (!strncmp(*str, tmp->name, strlen(tmp->name))
74		    && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
75			*str += strlen(tmp->name);
76			return lookup_singleton(tmp->pt);
77		}
78		tmp++;
79	}
80	return lookup_singleton(ARGTYPE_UNKNOWN);
81}
82
83static void eat_spaces(char **str)
84{
85	while (**str == ' ') {
86		(*str)++;
87	}
88}
89
90static char *xstrndup(char *str, size_t len)
91{
92    char *ret = (char *) malloc(len + 1);
93    strncpy(ret, str, len);
94    ret[len] = 0;
95    return ret;
96}
97
98static char *parse_ident(char **str)
99{
100    char *ident = *str;
101
102    if (!isalnum(**str) && **str != '_') {
103	output_line(0, "Syntax error in `%s', line %d: Bad identifier",
104		    filename, line_no);
105	error_count++;
106	return NULL;
107    }
108
109    while (**str && (isalnum(**str) || **str == '_')) {
110	++(*str);
111    }
112
113    return xstrndup(ident, *str - ident);
114}
115
116/*
117  Returns position in string at the left parenthesis which starts the
118  function's argument signature. Returns NULL on error.
119*/
120static char *start_of_arg_sig(char *str)
121{
122	char *pos;
123	int stacked = 0;
124
125	if (!strlen(str))
126		return NULL;
127
128	pos = &str[strlen(str)];
129	do {
130		pos--;
131		if (pos < str)
132			return NULL;
133		while ((pos > str) && (*pos != ')') && (*pos != '('))
134			pos--;
135
136		if (*pos == ')')
137			stacked++;
138		else if (*pos == '(')
139			stacked--;
140		else
141			return NULL;
142
143	} while (stacked > 0);
144
145	return (stacked == 0) ? pos : NULL;
146}
147
148/*
149  Decide whether a type needs any additional parameters.
150  For now, we do not parse any nontrivial argument types.
151*/
152static int simple_type(enum arg_type at)
153{
154    switch (at) {
155    case ARGTYPE_STRING:
156    case ARGTYPE_STRING_N:
157    case ARGTYPE_ENUM:
158	return 0;
159
160    default:
161	return 1;
162    }
163}
164
165static int parse_int(char **str)
166{
167    char *end;
168    long n = strtol(*str, &end, 0);
169    if (end == *str) {
170	output_line(0, "Syntax error in `%s', line %d: Bad number",
171		    filename, line_no);
172	error_count++;
173	return 0;
174    }
175
176    *str = end;
177    return n;
178}
179
180/*
181 * Input:
182 *  argN   : The value of argument #N, counting from 1 (arg0 = retval)
183 *  eltN   : The value of element #N of the containing structure
184 *  retval : The return value
185 *  0      : Error
186 *  N      : The numeric value N, if N > 0
187 *
188 * Output:
189 * > 0   actual numeric value
190 * = 0   return value
191 * < 0   (arg -n), counting from one
192 */
193static int parse_argnum(char **str)
194{
195    int multiplier = 1;
196    int n = 0;
197
198    if (strncmp(*str, "arg", 3) == 0) {
199	(*str) += 3;
200	multiplier = -1;
201    } else if (strncmp(*str, "retval", 6) == 0) {
202	(*str) += 6;
203	return 0;
204    }
205
206    n = parse_int(str);
207
208    return n * multiplier;
209}
210
211static arg_type_info *parse_nonpointer_type(char **str)
212{
213	arg_type_info *simple;
214	arg_type_info *info;
215
216	simple = str2type(str);
217	if (simple->type == ARGTYPE_UNKNOWN) {
218		return simple;		// UNKNOWN
219	}
220
221	if (simple_type(simple->type) && simple->type != ARGTYPE_STRING)
222		return simple;
223
224	info = malloc(sizeof(*info));
225	info->type = simple->type;
226
227	/* Code to parse parameterized types will go into the following
228	   switch statement. */
229
230	switch (info->type) {
231
232	// Syntax: enum ( keyname=value,keyname=value,... )
233	case ARGTYPE_ENUM:{
234	    struct enum_opt {
235		char *key;
236		int value;
237		struct enum_opt *next;
238	    };
239	    struct enum_opt *list = NULL;
240	    struct enum_opt *p;
241	    int entries = 0;
242	    int ii;
243
244	    eat_spaces(str);
245	    (*str)++;		// Get past open paren
246	    eat_spaces(str);
247
248	    while (**str && **str != ')') {
249		p = (struct enum_opt *) malloc(sizeof(*p));
250		eat_spaces(str);
251		p->key = parse_ident(str);
252		if (error_count) {
253		    free(p);
254		    return NULL;
255		}
256		eat_spaces(str);
257		if (**str != '=') {
258		    free(p->key);
259		    free(p);
260		    output_line(0,
261				"Syntax error in `%s', line %d: expected '=', got '%c'",
262				filename, line_no, **str);
263		    error_count++;
264		    return NULL;
265		}
266		++(*str);
267		eat_spaces(str);
268		p->value = parse_int(str);
269		p->next = list;
270		list = p;
271		++entries;
272
273		// Skip comma
274		eat_spaces(str);
275		if (**str == ',') {
276		    (*str)++;
277		    eat_spaces(str);
278		}
279	    }
280
281	    info->u.enum_info.entries = entries;
282	    info->u.enum_info.keys =
283		(char **) malloc(entries * sizeof(char *));
284	    info->u.enum_info.values =
285		(int *) malloc(entries * sizeof(int));
286	    for (ii = 0, p = NULL; list; ++ii, list = list->next) {
287		if (p)
288		    free(p);
289		info->u.enum_info.keys[ii] = list->key;
290		info->u.enum_info.values[ii] = list->value;
291		p = list;
292	    }
293	    if (p)
294		free(p);
295
296	    return info;
297	}
298
299	case ARGTYPE_STRING:
300	    if (!isdigit(**str) && **str != '[') {
301		/* Oops, was just a simple string after all */
302		free(info);
303		return simple;
304	    }
305
306	    info->type = ARGTYPE_STRING_N;
307
308	    /* Backwards compatibility for string0, string1, ... */
309	    if (isdigit(**str)) {
310		info->u.string_n_info.size_spec = -parse_int(str);
311		return info;
312	    }
313
314	    (*str)++;		// Skip past opening [
315	    eat_spaces(str);
316	    info->u.string_n_info.size_spec = parse_argnum(str);
317	    eat_spaces(str);
318	    (*str)++;		// Skip past closing ]
319	    return info;
320
321	default:
322		output_line(0, "Syntax error in `%s', line %d: Unknown type encountered",
323			    filename, line_no);
324		free(info);
325		error_count++;
326		return NULL;
327	}
328}
329
330static arg_type_info *parse_type(char **str)
331{
332	arg_type_info *info = parse_nonpointer_type(str);
333	while (**str == '*') {
334		arg_type_info *outer = malloc(sizeof(*info));
335		outer->type = ARGTYPE_POINTER;
336		outer->u.ptr_info.info = info;
337		(*str)++;
338		info = outer;
339	}
340	return info;
341}
342
343static struct function *process_line(char *buf)
344{
345	struct function fun;
346	struct function *fun_p;
347	char *str = buf;
348	char *tmp;
349	int i;
350
351	line_no++;
352	debug(3, "Reading line %d of `%s'", line_no, filename);
353	eat_spaces(&str);
354	fun.return_info = parse_type(&str);
355	if (fun.return_info == NULL)
356        	return NULL;
357	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
358		debug(3, " Skipping line %d", line_no);
359		return NULL;
360	}
361	debug(4, " return_type = %d", fun.return_info->type);
362	eat_spaces(&str);
363	tmp = start_of_arg_sig(str);
364	if (!tmp) {
365		output_line(0, "Syntax error in `%s', line %d", filename,
366			    line_no);
367		error_count++;
368		return NULL;
369	}
370	*tmp = '\0';
371	fun.name = strdup(str);
372	str = tmp + 1;
373	debug(3, " name = %s", fun.name);
374	fun.params_right = 0;
375	for (i = 0; i < MAX_ARGS; i++) {
376		eat_spaces(&str);
377		if (*str == ')') {
378			break;
379		}
380		if (str[0] == '+') {
381			fun.params_right++;
382			str++;
383		} else if (fun.params_right) {
384			fun.params_right++;
385		}
386		fun.arg_info[i] = parse_type(&str);
387		if (fun.arg_info[i] == NULL) {
388			output_line(0, "Syntax error in `%s', line %d"
389                                    ": unknown argument type",
390				    filename, line_no);
391			error_count++;
392			return NULL;
393		}
394		eat_spaces(&str);
395		if (*str == ',') {
396			str++;
397			continue;
398		} else if (*str == ')') {
399			continue;
400		} else {
401			if (str[strlen(str) - 1] == '\n')
402				str[strlen(str) - 1] = '\0';
403			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
404				    filename, line_no, str);
405			error_count++;
406			return NULL;
407		}
408	}
409	fun.num_params = i;
410	fun_p = malloc(sizeof(struct function));
411	memcpy(fun_p, &fun, sizeof(struct function));
412	return fun_p;
413}
414
415void read_config_file(char *file)
416{
417	FILE *stream;
418	char buf[1024];
419
420	filename = file;
421
422	debug(1, "Reading config file `%s'...", filename);
423
424	stream = fopen(filename, "r");
425	if (!stream) {
426		return;
427	}
428	line_no = 0;
429	while (fgets(buf, 1024, stream)) {
430		struct function *tmp;
431
432		error_count = 0;
433		tmp = process_line(buf);
434
435		if (tmp) {
436			debug(2, "New function: `%s'", tmp->name);
437			tmp->next = list_of_functions;
438			list_of_functions = tmp;
439		}
440	}
441	fclose(stream);
442}
443