read_config_file.c revision 3df476b28e4a9cdb43cf29fff8e89481310eb30d
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 "main.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
18static arg_type_info *parse_type(char **str);
19
20Function *list_of_functions = NULL;
21
22/* Map of strings to type names. These do not need to be in any
23 * particular order */
24static struct list_of_pt_t {
25	char *name;
26	enum arg_type pt;
27} list_of_pt[] = {
28	{
29	"void", ARGTYPE_VOID}, {
30	"int", ARGTYPE_INT}, {
31	"uint", ARGTYPE_UINT}, {
32	"long", ARGTYPE_LONG}, {
33	"ulong", ARGTYPE_ULONG}, {
34	"octal", ARGTYPE_OCTAL}, {
35	"char", ARGTYPE_CHAR}, {
36	"short", ARGTYPE_SHORT}, {
37	"ushort", ARGTYPE_USHORT}, {
38	"float", ARGTYPE_FLOAT}, {
39	"double", ARGTYPE_DOUBLE}, {
40	"addr", ARGTYPE_ADDR}, {
41	"file", ARGTYPE_FILE}, {
42	"format", ARGTYPE_FORMAT}, {
43	"string", ARGTYPE_STRING}, {
44	"array", ARGTYPE_ARRAY}, {
45	"struct", ARGTYPE_STRUCT}, {
46	"enum", ARGTYPE_ENUM}, {
47	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
48};
49
50/* Array of prototype objects for each of the types. The order in this
51 * array must exactly match the list of enumerated values in
52 * main.h */
53static arg_type_info arg_type_prototypes[] = {
54	{ ARGTYPE_VOID },
55	{ ARGTYPE_INT },
56	{ ARGTYPE_UINT },
57	{ ARGTYPE_LONG },
58	{ ARGTYPE_ULONG },
59	{ ARGTYPE_OCTAL },
60	{ ARGTYPE_CHAR },
61	{ ARGTYPE_SHORT },
62	{ ARGTYPE_USHORT },
63	{ ARGTYPE_FLOAT },
64	{ ARGTYPE_DOUBLE },
65	{ ARGTYPE_ADDR },
66	{ ARGTYPE_FILE },
67	{ ARGTYPE_FORMAT },
68	{ ARGTYPE_STRING },
69	{ ARGTYPE_STRING_N },
70	{ ARGTYPE_ARRAY },
71	{ ARGTYPE_ENUM },
72	{ ARGTYPE_STRUCT },
73	{ ARGTYPE_POINTER },
74	{ ARGTYPE_UNKNOWN }
75};
76
77arg_type_info *
78lookup_prototype(enum arg_type at) {
79	if (at >= 0 && at <= ARGTYPE_COUNT)
80		return &arg_type_prototypes[at];
81	else
82		return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
83}
84
85static arg_type_info *
86str2type(char **str) {
87	struct list_of_pt_t *tmp = &list_of_pt[0];
88
89	while (tmp->name) {
90		if (!strncmp(*str, tmp->name, strlen(tmp->name))
91				&& index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
92			*str += strlen(tmp->name);
93			return lookup_prototype(tmp->pt);
94		}
95		tmp++;
96	}
97	return lookup_prototype(ARGTYPE_UNKNOWN);
98}
99
100static void
101eat_spaces(char **str) {
102	while (**str == ' ') {
103		(*str)++;
104	}
105}
106
107static char *
108xstrndup(char *str, size_t len) {
109	char *ret = (char *) malloc(len + 1);
110	strncpy(ret, str, len);
111	ret[len] = 0;
112	return ret;
113}
114
115static char *
116parse_ident(char **str) {
117	char *ident = *str;
118
119	if (!isalnum(**str) && **str != '_') {
120		output_line(0, "Syntax error in `%s', line %d: Bad identifier",
121				filename, line_no);
122		error_count++;
123		return NULL;
124	}
125
126	while (**str && (isalnum(**str) || **str == '_')) {
127		++(*str);
128	}
129
130	return xstrndup(ident, *str - ident);
131}
132
133/*
134  Returns position in string at the left parenthesis which starts the
135  function's argument signature. Returns NULL on error.
136*/
137static char *
138start_of_arg_sig(char *str) {
139	char *pos;
140	int stacked = 0;
141
142	if (!strlen(str))
143		return NULL;
144
145	pos = &str[strlen(str)];
146	do {
147		pos--;
148		if (pos < str)
149			return NULL;
150		while ((pos > str) && (*pos != ')') && (*pos != '('))
151			pos--;
152
153		if (*pos == ')')
154			stacked++;
155		else if (*pos == '(')
156			stacked--;
157		else
158			return NULL;
159
160	} while (stacked > 0);
161
162	return (stacked == 0) ? pos : NULL;
163}
164
165static int
166parse_int(char **str) {
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 (%s)",
171				filename, line_no, *str);
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
194parse_argnum(char **str) {
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, "elt", 3) == 0) {
202		(*str) += 3;
203		multiplier = -1;
204	} else if (strncmp(*str, "retval", 6) == 0) {
205		(*str) += 6;
206		return 0;
207	}
208
209	n = parse_int(str);
210
211	return n * multiplier;
212}
213
214struct typedef_node_t {
215	char *name;
216	arg_type_info *info;
217	struct typedef_node_t *next;
218} *typedefs = NULL;
219
220static arg_type_info *
221lookup_typedef(char **str) {
222	struct typedef_node_t *node;
223	char *end = *str;
224	while (*end && (isalnum(*end) || *end == '_'))
225		++end;
226	if (end == *str)
227		return NULL;
228
229	for (node = typedefs; node != NULL; node = node->next) {
230		if (strncmp(*str, node->name, end - *str) == 0) {
231			(*str) += strlen(node->name);
232			return node->info;
233		}
234	}
235
236	return NULL;
237}
238
239static void
240parse_typedef(char **str) {
241	char *name;
242	arg_type_info *info;
243	struct typedef_node_t *binding;
244
245	(*str) += strlen("typedef");
246	eat_spaces(str);
247
248	// Grab out the name of the type
249	name = parse_ident(str);
250
251	// Skip = sign
252	eat_spaces(str);
253	if (**str != '=') {
254		output_line(0,
255				"Syntax error in `%s', line %d: expected '=', got '%c'",
256				filename, line_no, **str);
257		error_count++;
258		return;
259	}
260	(*str)++;
261	eat_spaces(str);
262
263	// Parse the type
264	info = parse_type(str);
265
266	// Insert onto beginning of linked list
267	binding = malloc(sizeof(*binding));
268	binding->name = name;
269	binding->info = info;
270	binding->next = typedefs;
271	typedefs = binding;
272}
273
274static size_t
275arg_sizeof(arg_type_info * arg) {
276	if (arg->type == ARGTYPE_CHAR) {
277		return sizeof(char);
278	} else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) {
279		return sizeof(short);
280	} else if (arg->type == ARGTYPE_FLOAT) {
281		return sizeof(float);
282	} else if (arg->type == ARGTYPE_DOUBLE) {
283		return sizeof(double);
284	} else if (arg->type == ARGTYPE_ENUM) {
285		return sizeof(int);
286	} else if (arg->type == ARGTYPE_STRUCT) {
287		return arg->u.struct_info.size;
288	} else if (arg->type == ARGTYPE_POINTER) {
289		return sizeof(void*);
290	} else if (arg->type == ARGTYPE_ARRAY) {
291		if (arg->u.array_info.len_spec > 0)
292			return arg->u.array_info.len_spec * arg->u.array_info.elt_size;
293		else
294			return sizeof(void *);
295	} else {
296		return sizeof(int);
297	}
298}
299
300#undef alignof
301#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
302static size_t
303arg_align(arg_type_info * arg) {
304	struct { char c; char C; } cC;
305	struct { char c; short s; } cs;
306	struct { char c; int i; } ci;
307	struct { char c; long l; } cl;
308	struct { char c; void* p; } cp;
309	struct { char c; float f; } cf;
310	struct { char c; double d; } cd;
311
312	static size_t char_alignment = alignof(C, cC);
313	static size_t short_alignment = alignof(s, cs);
314	static size_t int_alignment = alignof(i, ci);
315	static size_t long_alignment = alignof(l, cl);
316	static size_t ptr_alignment = alignof(p, cp);
317	static size_t float_alignment = alignof(f, cf);
318	static size_t double_alignment = alignof(d, cd);
319
320	switch (arg->type) {
321		case ARGTYPE_LONG:
322		case ARGTYPE_ULONG:
323			return long_alignment;
324		case ARGTYPE_CHAR:
325			return char_alignment;
326		case ARGTYPE_SHORT:
327		case ARGTYPE_USHORT:
328			return short_alignment;
329		case ARGTYPE_FLOAT:
330			return float_alignment;
331		case ARGTYPE_DOUBLE:
332			return double_alignment;
333		case ARGTYPE_ADDR:
334		case ARGTYPE_FILE:
335		case ARGTYPE_FORMAT:
336		case ARGTYPE_STRING:
337		case ARGTYPE_STRING_N:
338		case ARGTYPE_POINTER:
339			return ptr_alignment;
340
341		case ARGTYPE_ARRAY:
342			return arg_align(&arg->u.array_info.elt_type[0]);
343
344		case ARGTYPE_STRUCT:
345			return arg_align(arg->u.struct_info.fields[0]);
346
347		default:
348			return int_alignment;
349	}
350}
351
352static size_t
353align_skip(size_t alignment, size_t offset) {
354	if (offset % alignment)
355		return alignment - (offset % alignment);
356	else
357		return 0;
358}
359
360/* I'm sure this isn't completely correct, but just try to get most of
361 * them right for now. */
362static void
363align_struct(arg_type_info* info) {
364	size_t offset;
365	int i;
366
367	if (info->u.struct_info.size != 0)
368		return;			// Already done
369
370	// Compute internal padding due to alignment constraints for
371	// various types.
372	offset = 0;
373	for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
374		arg_type_info *field = info->u.struct_info.fields[i];
375		offset += align_skip(arg_align(field), offset);
376		info->u.struct_info.offset[i] = offset;
377		offset += arg_sizeof(field);
378	}
379
380	info->u.struct_info.size = offset;
381}
382
383static arg_type_info *
384parse_nonpointer_type(char **str) {
385	arg_type_info *simple;
386	arg_type_info *info;
387
388	if (strncmp(*str, "typedef", 7) == 0) {
389		parse_typedef(str);
390		return lookup_prototype(ARGTYPE_UNKNOWN);
391	}
392
393	simple = str2type(str);
394	if (simple->type == ARGTYPE_UNKNOWN) {
395		info = lookup_typedef(str);
396		if (info)
397			return info;
398		else
399			return simple;		// UNKNOWN
400	}
401
402	info = malloc(sizeof(*info));
403	info->type = simple->type;
404
405	/* Code to parse parameterized types will go into the following
406	   switch statement. */
407
408	switch (info->type) {
409
410	/* Syntax: array ( type, N|argN ) */
411	case ARGTYPE_ARRAY:
412		(*str)++;		// Get past open paren
413		eat_spaces(str);
414		if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
415			return NULL;
416		info->u.array_info.elt_size =
417			arg_sizeof(info->u.array_info.elt_type);
418		(*str)++;		// Get past comma
419		eat_spaces(str);
420		info->u.array_info.len_spec = parse_argnum(str);
421		(*str)++;		// Get past close paren
422		return info;
423
424	/* Syntax: enum ( keyname=value,keyname=value,... ) */
425	case ARGTYPE_ENUM:{
426		struct enum_opt {
427			char *key;
428			int value;
429			struct enum_opt *next;
430		};
431		struct enum_opt *list = NULL;
432		struct enum_opt *p;
433		int entries = 0;
434		int ii;
435
436		eat_spaces(str);
437		(*str)++;		// Get past open paren
438		eat_spaces(str);
439
440		while (**str && **str != ')') {
441			p = (struct enum_opt *) malloc(sizeof(*p));
442			eat_spaces(str);
443			p->key = parse_ident(str);
444			if (error_count) {
445				free(p);
446				return NULL;
447			}
448			eat_spaces(str);
449			if (**str != '=') {
450				free(p->key);
451				free(p);
452				output_line(0,
453						"Syntax error in `%s', line %d: expected '=', got '%c'",
454						filename, line_no, **str);
455				error_count++;
456				return NULL;
457			}
458			++(*str);
459			eat_spaces(str);
460			p->value = parse_int(str);
461			p->next = list;
462			list = p;
463			++entries;
464
465			// Skip comma
466			eat_spaces(str);
467			if (**str == ',') {
468				(*str)++;
469				eat_spaces(str);
470			}
471		}
472
473		info->u.enum_info.entries = entries;
474		info->u.enum_info.keys =
475			(char **) malloc(entries * sizeof(char *));
476		info->u.enum_info.values =
477			(int *) malloc(entries * sizeof(int));
478		for (ii = 0, p = NULL; list; ++ii, list = list->next) {
479			if (p)
480				free(p);
481			info->u.enum_info.keys[ii] = list->key;
482			info->u.enum_info.values[ii] = list->value;
483			p = list;
484		}
485		if (p)
486			free(p);
487
488		return info;
489	}
490
491	case ARGTYPE_STRING:
492		if (!isdigit(**str) && **str != '[') {
493			/* Oops, was just a simple string after all */
494			free(info);
495			return simple;
496		}
497
498		info->type = ARGTYPE_STRING_N;
499
500		/* Backwards compatibility for string0, string1, ... */
501		if (isdigit(**str)) {
502			info->u.string_n_info.size_spec = -parse_int(str);
503			return info;
504		}
505
506		(*str)++;		// Skip past opening [
507		eat_spaces(str);
508		info->u.string_n_info.size_spec = parse_argnum(str);
509		eat_spaces(str);
510		(*str)++;		// Skip past closing ]
511		return info;
512
513	// Syntax: struct ( type,type,type,... )
514	case ARGTYPE_STRUCT:{
515		int field_num = 0;
516		(*str)++;		// Get past open paren
517		info->u.struct_info.fields =
518			malloc((MAX_ARGS + 1) * sizeof(void *));
519		info->u.struct_info.offset =
520			malloc((MAX_ARGS + 1) * sizeof(size_t));
521		info->u.struct_info.size = 0;
522		eat_spaces(str); // Empty arg list with whitespace inside
523		while (**str && **str != ')') {
524			if (field_num == MAX_ARGS) {
525				output_line(0,
526						"Error in `%s', line %d: Too many structure elements",
527						filename, line_no);
528				error_count++;
529				return NULL;
530			}
531			eat_spaces(str);
532			if (field_num != 0) {
533				(*str)++;	// Get past comma
534				eat_spaces(str);
535			}
536			if ((info->u.struct_info.fields[field_num++] =
537						parse_type(str)) == NULL)
538				return NULL;
539
540			// Must trim trailing spaces so the check for
541			// the closing paren is simple
542			eat_spaces(str);
543		}
544		(*str)++;		// Get past closing paren
545		info->u.struct_info.fields[field_num] = NULL;
546		align_struct(info);
547		return info;
548	}
549
550	default:
551		if (info->type == ARGTYPE_UNKNOWN) {
552			output_line(0, "Syntax error in `%s', line %d: "
553					"Unknown type encountered",
554					filename, line_no);
555			free(info);
556			error_count++;
557			return NULL;
558		} else {
559			return info;
560		}
561	}
562}
563
564static arg_type_info *
565parse_type(char **str) {
566	arg_type_info *info = parse_nonpointer_type(str);
567	while (**str == '*') {
568		arg_type_info *outer = malloc(sizeof(*info));
569		outer->type = ARGTYPE_POINTER;
570		outer->u.ptr_info.info = info;
571		(*str)++;
572		info = outer;
573	}
574	return info;
575}
576
577static Function *
578process_line(char *buf) {
579	Function fun;
580	Function *fun_p;
581	char *str = buf;
582	char *tmp;
583	int i;
584	int float_num = 0;
585
586	line_no++;
587	debug(3, "Reading line %d of `%s'", line_no, filename);
588	eat_spaces(&str);
589	fun.return_info = parse_type(&str);
590	if (fun.return_info == NULL)
591		return NULL;
592	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
593		debug(3, " Skipping line %d", line_no);
594		return NULL;
595	}
596	debug(4, " return_type = %d", fun.return_info->type);
597	eat_spaces(&str);
598	tmp = start_of_arg_sig(str);
599	if (!tmp) {
600		output_line(0, "Syntax error in `%s', line %d", filename,
601				line_no);
602		error_count++;
603		return NULL;
604	}
605	*tmp = '\0';
606	fun.name = strdup(str);
607	str = tmp + 1;
608	debug(3, " name = %s", fun.name);
609	fun.params_right = 0;
610	for (i = 0; i < MAX_ARGS; i++) {
611		eat_spaces(&str);
612		if (*str == ')') {
613			break;
614		}
615		if (str[0] == '+') {
616			fun.params_right++;
617			str++;
618		} else if (fun.params_right) {
619			fun.params_right++;
620		}
621		fun.arg_info[i] = parse_type(&str);
622		if (fun.arg_info[i] == NULL) {
623			output_line(0, "Syntax error in `%s', line %d"
624					": unknown argument type",
625					filename, line_no);
626			error_count++;
627			return NULL;
628		}
629		if (fun.arg_info[i]->type == ARGTYPE_FLOAT)
630			fun.arg_info[i]->u.float_info.float_index = float_num++;
631		else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE)
632			fun.arg_info[i]->u.double_info.float_index = float_num++;
633		eat_spaces(&str);
634		if (*str == ',') {
635			str++;
636			continue;
637		} else if (*str == ')') {
638			continue;
639		} else {
640			if (str[strlen(str) - 1] == '\n')
641				str[strlen(str) - 1] = '\0';
642			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
643					filename, line_no, str);
644			error_count++;
645			return NULL;
646		}
647	}
648	fun.num_params = i;
649	fun_p = malloc(sizeof(Function));
650	if (!fun_p) {
651		perror("ltrace: malloc");
652		exit(1);
653	}
654	memcpy(fun_p, &fun, sizeof(Function));
655	return fun_p;
656}
657
658void
659read_config_file(char *file) {
660	FILE *stream;
661	char buf[1024];
662
663	filename = file;
664	stream = fopen(filename, "r");
665	if (!stream) {
666		return;
667	}
668
669	debug(1, "Reading config file `%s'...", filename);
670
671	line_no = 0;
672	while (fgets(buf, 1024, stream)) {
673		Function *tmp;
674
675		error_count = 0;
676		tmp = process_line(buf);
677
678		if (tmp) {
679			debug(2, "New function: `%s'", tmp->name);
680			tmp->next = list_of_functions;
681			list_of_functions = tmp;
682		}
683	}
684	fclose(stream);
685}
686