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