read_config_file.c revision 94078ecce3a103c28457e6f90f1e5b0dacc61146
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
4 * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes
5 * Copyright (C) 2006 Ian Wienand
6 * Copyright (C) 2006 Steve Fink
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include "config.h"
25
26#include <string.h>
27#include <stdlib.h>
28#include <ctype.h>
29
30#include "common.h"
31#include "type.h"
32#include "expr.h"
33
34static int line_no;
35static char *filename;
36static int error_count = 0;
37
38static struct arg_type_info *parse_type(char **str);
39
40Function *list_of_functions = NULL;
41
42/* Map of strings to type names. These do not need to be in any
43 * particular order */
44static struct list_of_pt_t {
45	char *name;
46	enum arg_type pt;
47} list_of_pt[] = {
48	{
49	"void", ARGTYPE_VOID}, {
50	"int", ARGTYPE_INT}, {
51	"uint", ARGTYPE_UINT}, {
52	"long", ARGTYPE_LONG}, {
53	"ulong", ARGTYPE_ULONG}, {
54	"octal", ARGTYPE_OCTAL}, {
55	"char", ARGTYPE_CHAR}, {
56	"short", ARGTYPE_SHORT}, {
57	"ushort", ARGTYPE_USHORT}, {
58	"float", ARGTYPE_FLOAT}, {
59	"double", ARGTYPE_DOUBLE}, {
60	"addr", ARGTYPE_ADDR}, {
61	"file", ARGTYPE_FILE}, {
62	"format", ARGTYPE_FORMAT}, {
63	"string", ARGTYPE_STRING}, {
64	"array", ARGTYPE_ARRAY}, {
65	"struct", ARGTYPE_STRUCT}, {
66	"enum", ARGTYPE_ENUM}, {
67	NULL, ARGTYPE_UNKNOWN}	/* Must finish with NULL */
68};
69
70/* Array of prototype objects for each of the types. The order in this
71 * array must exactly match the list of enumerated values in
72 * common.h */
73static struct arg_type_info arg_type_prototypes[] = {
74	{ ARGTYPE_VOID },
75	{ ARGTYPE_INT },
76	{ ARGTYPE_UINT },
77	{ ARGTYPE_LONG },
78	{ ARGTYPE_ULONG },
79	{ ARGTYPE_OCTAL },
80	{ ARGTYPE_CHAR },
81	{ ARGTYPE_SHORT },
82	{ ARGTYPE_USHORT },
83	{ ARGTYPE_FLOAT },
84	{ ARGTYPE_DOUBLE },
85	{ ARGTYPE_ADDR },
86	{ ARGTYPE_FILE },
87	{ ARGTYPE_FORMAT },
88	{ ARGTYPE_STRING },
89	{ ARGTYPE_STRING_N },
90	{ ARGTYPE_ARRAY },
91	{ ARGTYPE_ENUM },
92	{ ARGTYPE_STRUCT },
93	{ ARGTYPE_POINTER },
94	{ ARGTYPE_UNKNOWN }
95};
96
97struct arg_type_info *
98lookup_prototype(enum arg_type at) {
99	if (at >= 0 && at <= ARGTYPE_COUNT)
100		return &arg_type_prototypes[at];
101	else
102		return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
103}
104
105static struct arg_type_info *
106str2type(char **str) {
107	struct list_of_pt_t *tmp = &list_of_pt[0];
108
109	while (tmp->name) {
110		if (!strncmp(*str, tmp->name, strlen(tmp->name))
111				&& index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
112			*str += strlen(tmp->name);
113			return lookup_prototype(tmp->pt);
114		}
115		tmp++;
116	}
117	return lookup_prototype(ARGTYPE_UNKNOWN);
118}
119
120static void
121eat_spaces(char **str) {
122	while (**str == ' ') {
123		(*str)++;
124	}
125}
126
127static char *
128xstrndup(char *str, size_t len) {
129	char *ret = (char *) malloc(len + 1);
130	strncpy(ret, str, len);
131	ret[len] = 0;
132	return ret;
133}
134
135static char *
136parse_ident(char **str) {
137	char *ident = *str;
138
139	if (!isalnum(**str) && **str != '_') {
140		output_line(0, "Syntax error in `%s', line %d: Bad identifier",
141				filename, line_no);
142		error_count++;
143		return NULL;
144	}
145
146	while (**str && (isalnum(**str) || **str == '_')) {
147		++(*str);
148	}
149
150	return xstrndup(ident, *str - ident);
151}
152
153/*
154  Returns position in string at the left parenthesis which starts the
155  function's argument signature. Returns NULL on error.
156*/
157static char *
158start_of_arg_sig(char *str) {
159	char *pos;
160	int stacked = 0;
161
162	if (!strlen(str))
163		return NULL;
164
165	pos = &str[strlen(str)];
166	do {
167		pos--;
168		if (pos < str)
169			return NULL;
170		while ((pos > str) && (*pos != ')') && (*pos != '('))
171			pos--;
172
173		if (*pos == ')')
174			stacked++;
175		else if (*pos == '(')
176			stacked--;
177		else
178			return NULL;
179
180	} while (stacked > 0);
181
182	return (stacked == 0) ? pos : NULL;
183}
184
185static int
186parse_int(char **str) {
187	char *end;
188	long n = strtol(*str, &end, 0);
189	if (end == *str) {
190		output_line(0, "Syntax error in `%s', line %d: Bad number (%s)",
191				filename, line_no, *str);
192		error_count++;
193		return 0;
194	}
195
196	*str = end;
197	return n;
198}
199
200/*
201 * Input:
202 *  argN   : The value of argument #N, counting from 1
203 *  eltN   : The value of element #N of the containing structure
204 *  retval : The return value
205 *  N      : The numeric value N
206 */
207static struct expr_node *
208parse_argnum(char **str)
209{
210	struct expr_node *expr = malloc(sizeof(*expr));
211	if (expr == NULL)
212		return NULL;
213
214	if (isdigit(**str)) {
215		expr_init_const_word(expr, parse_int(str),
216				     type_get_simple(ARGTYPE_LONG), 0);
217
218		return expr;
219
220	} else {
221		char *name = parse_ident(str);
222		if (name == NULL)
223			goto fail;
224
225		int is_arg = strncmp(name, "arg", 3) == 0;
226		int is_elt = !is_arg && strncmp(name, "elt", 3) == 0;
227		if (is_arg || is_elt) {
228			name += 3;
229			int l = parse_int(&name);
230			if (is_arg) {
231				expr_init_argno(expr, l - 1);
232			} else {
233				struct expr_node *e_up = malloc(sizeof(*e_up));
234				struct expr_node *e_ix = malloc(sizeof(*e_ix));
235				if (e_up == NULL || e_ix == NULL) {
236					free(e_up);
237					free(e_ix);
238					goto fail;
239				}
240
241				expr_init_up(e_up, expr_self(), 0);
242				struct arg_type_info *ti
243					= type_get_simple(ARGTYPE_LONG);
244				expr_init_const_word(e_ix, l - 1, ti, 0);
245				expr_init_index(expr, e_up, 1, e_ix, 1);
246			}
247
248		} else if (strcmp(name, "retval") == 0) {
249			expr_init_named(expr, "retval", 0);
250
251		} else {
252			goto fail;
253		}
254		return expr;
255	}
256
257fail:
258	free(expr);
259	return NULL;
260}
261
262struct typedef_node_t {
263	char *name;
264	struct arg_type_info *info;
265	struct typedef_node_t *next;
266} *typedefs = NULL;
267
268static struct arg_type_info *
269lookup_typedef(char **str) {
270	struct typedef_node_t *node;
271	char *end = *str;
272	while (*end && (isalnum(*end) || *end == '_'))
273		++end;
274	if (end == *str)
275		return NULL;
276
277	for (node = typedefs; node != NULL; node = node->next) {
278		if (strncmp(*str, node->name, end - *str) == 0) {
279			(*str) += strlen(node->name);
280			return node->info;
281		}
282	}
283
284	return NULL;
285}
286
287static void
288parse_typedef(char **str) {
289	char *name;
290	struct arg_type_info *info;
291	struct typedef_node_t *binding;
292
293	(*str) += strlen("typedef");
294	eat_spaces(str);
295
296	// Grab out the name of the type
297	name = parse_ident(str);
298
299	// Skip = sign
300	eat_spaces(str);
301	if (**str != '=') {
302		output_line(0,
303				"Syntax error in `%s', line %d: expected '=', got '%c'",
304				filename, line_no, **str);
305		error_count++;
306		return;
307	}
308	(*str)++;
309	eat_spaces(str);
310
311	// Parse the type
312	info = parse_type(str);
313
314	// Insert onto beginning of linked list
315	binding = malloc(sizeof(*binding));
316	binding->name = name;
317	binding->info = info;
318	binding->next = typedefs;
319	typedefs = binding;
320}
321
322/* Syntax: struct ( type,type,type,... ) */
323static int
324parse_struct(char **str, struct arg_type_info *info)
325{
326	eat_spaces(str);
327	if (**str != '(')
328		return -1;
329	++*str;
330
331	eat_spaces(str); // Empty arg list with whitespace inside
332
333	type_init_struct(info);
334
335	while (1) {
336		eat_spaces(str);
337		if (**str == 0 || **str == ')') {
338			++*str;
339			return 0;
340		}
341
342		/* Field delimiter.  */
343		if (type_struct_size(info) > 0)
344			++*str;
345
346		eat_spaces(str);
347		int own = 0;
348		struct arg_type_info *field = parse_type(str);
349		if (field == NULL || type_struct_add(info, field, own)) {
350			type_destroy(info);
351			return -1;
352		}
353	}
354}
355
356static struct arg_type_info *
357parse_nonpointer_type(char **str) {
358	struct arg_type_info *simple;
359	struct arg_type_info *info;
360
361	if (strncmp(*str, "typedef", 7) == 0) {
362		parse_typedef(str);
363		return lookup_prototype(ARGTYPE_UNKNOWN);
364	}
365
366	simple = str2type(str);
367	if (simple->type == ARGTYPE_UNKNOWN) {
368		info = lookup_typedef(str);
369		if (info)
370			return info;
371		else
372			return simple;		// UNKNOWN
373	}
374
375	info = malloc(sizeof(*info));
376	info->type = simple->type;
377
378	/* Code to parse parameterized types will go into the following
379	   switch statement. */
380
381	switch (info->type) {
382
383	/* Syntax: array ( type, N|argN ) */
384	case ARGTYPE_ARRAY:
385		(*str)++;		// Get past open paren
386		eat_spaces(str);
387		if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
388			return NULL;
389		(*str)++;		// Get past comma
390		eat_spaces(str);
391		info->u.array_info.length = parse_argnum(str);
392		(*str)++;		// Get past close paren
393		return info;
394
395	/* Syntax: enum ( keyname=value,keyname=value,... ) */
396	case ARGTYPE_ENUM:{
397		struct enum_opt {
398			char *key;
399			int value;
400			struct enum_opt *next;
401		};
402		struct enum_opt *list = NULL;
403		struct enum_opt *p;
404		int entries = 0;
405		int ii;
406
407		eat_spaces(str);
408		(*str)++;		// Get past open paren
409		eat_spaces(str);
410
411		while (**str && **str != ')') {
412			p = (struct enum_opt *) malloc(sizeof(*p));
413			eat_spaces(str);
414			p->key = parse_ident(str);
415			if (error_count) {
416				free(p);
417				return NULL;
418			}
419			eat_spaces(str);
420			if (**str != '=') {
421				free(p->key);
422				free(p);
423				output_line(0,
424						"Syntax error in `%s', line %d: expected '=', got '%c'",
425						filename, line_no, **str);
426				error_count++;
427				return NULL;
428			}
429			++(*str);
430			eat_spaces(str);
431			p->value = parse_int(str);
432			p->next = list;
433			list = p;
434			++entries;
435
436			// Skip comma
437			eat_spaces(str);
438			if (**str == ',') {
439				(*str)++;
440				eat_spaces(str);
441			}
442		}
443
444		info->u.enum_info.entries = entries;
445		info->u.enum_info.keys =
446			(char **) malloc(entries * sizeof(char *));
447		info->u.enum_info.values =
448			(int *) malloc(entries * sizeof(int));
449		for (ii = 0, p = NULL; list; ++ii, list = list->next) {
450			if (p)
451				free(p);
452			info->u.enum_info.keys[ii] = list->key;
453			info->u.enum_info.values[ii] = list->value;
454			p = list;
455		}
456		if (p)
457			free(p);
458
459		return info;
460	}
461
462	case ARGTYPE_STRING:
463		if (!isdigit(**str) && **str != '[') {
464			/* Oops, was just a simple string after all */
465			free(info);
466			return simple;
467		}
468
469		info->type = ARGTYPE_STRING_N;
470
471		/* Backwards compatibility for string0, string1, ... */
472		if (isdigit(**str)) {
473			info->u.string_n_info.length = parse_argnum(str);
474			return info;
475		}
476
477		(*str)++;		// Skip past opening [
478		eat_spaces(str);
479		info->u.string_n_info.length = parse_argnum(str);
480		eat_spaces(str);
481		(*str)++;		// Skip past closing ]
482		return info;
483
484	// Syntax: struct ( type,type,type,... )
485	case ARGTYPE_STRUCT:{
486		if (parse_struct(str, info) < 0) {
487			free(info);
488			output_line(0, "Parse error in `%s', line %d",
489				    filename, line_no);
490			error_count++;
491			return NULL;
492		}
493		return info;
494	}
495
496	default:
497		if (info->type == ARGTYPE_UNKNOWN) {
498			output_line(0, "Syntax error in `%s', line %d: "
499					"Unknown type encountered",
500					filename, line_no);
501			free(info);
502			error_count++;
503			return NULL;
504		} else {
505			return info;
506		}
507	}
508}
509
510static struct arg_type_info *
511parse_type(char **str) {
512	struct arg_type_info *info = parse_nonpointer_type(str);
513	while (**str == '*') {
514		struct arg_type_info *outer = malloc(sizeof(*info));
515		outer->type = ARGTYPE_POINTER;
516		outer->u.ptr_info.info = info;
517		(*str)++;
518		info = outer;
519	}
520	return info;
521}
522
523static Function *
524process_line(char *buf) {
525	Function fun;
526	Function *fun_p;
527	char *str = buf;
528	char *tmp;
529	int i;
530	int float_num = 0;
531
532	line_no++;
533	debug(3, "Reading line %d of `%s'", line_no, filename);
534	eat_spaces(&str);
535	fun.return_info = parse_type(&str);
536	if (fun.return_info == NULL)
537		return NULL;
538	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
539		debug(3, " Skipping line %d", line_no);
540		return NULL;
541	}
542	debug(4, " return_type = %d", fun.return_info->type);
543	eat_spaces(&str);
544	tmp = start_of_arg_sig(str);
545	if (!tmp) {
546		output_line(0, "Syntax error in `%s', line %d", filename,
547				line_no);
548		error_count++;
549		return NULL;
550	}
551	*tmp = '\0';
552	fun.name = strdup(str);
553	str = tmp + 1;
554	debug(3, " name = %s", fun.name);
555	fun.params_right = 0;
556	for (i = 0; i < MAX_ARGS; i++) {
557		eat_spaces(&str);
558		if (*str == ')') {
559			break;
560		}
561		if (str[0] == '+') {
562			fun.params_right++;
563			str++;
564		} else if (fun.params_right) {
565			fun.params_right++;
566		}
567		fun.arg_info[i] = parse_type(&str);
568		if (fun.arg_info[i] == NULL) {
569			output_line(0, "Syntax error in `%s', line %d"
570					": unknown argument type",
571					filename, line_no);
572			error_count++;
573			return NULL;
574		}
575		if (fun.arg_info[i]->type == ARGTYPE_FLOAT)
576			fun.arg_info[i]->u.float_info.float_index = float_num++;
577		else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE)
578			fun.arg_info[i]->u.double_info.float_index = float_num++;
579		eat_spaces(&str);
580		if (*str == ',') {
581			str++;
582			continue;
583		} else if (*str == ')') {
584			continue;
585		} else {
586			if (str[strlen(str) - 1] == '\n')
587				str[strlen(str) - 1] = '\0';
588			output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
589					filename, line_no, str);
590			error_count++;
591			return NULL;
592		}
593	}
594	fun.num_params = i;
595	fun_p = malloc(sizeof(Function));
596	if (!fun_p) {
597		perror("ltrace: malloc");
598		exit(1);
599	}
600	memcpy(fun_p, &fun, sizeof(Function));
601	return fun_p;
602}
603
604void
605read_config_file(char *file) {
606	FILE *stream;
607	char buf[1024];
608
609	filename = file;
610	stream = fopen(filename, "r");
611	if (!stream) {
612		return;
613	}
614
615	debug(1, "Reading config file `%s'...", filename);
616
617	line_no = 0;
618	while (fgets(buf, 1024, stream)) {
619		Function *tmp;
620
621		error_count = 0;
622		tmp = process_line(buf);
623
624		if (tmp) {
625			debug(2, "New function: `%s'", tmp->name);
626			tmp->next = list_of_functions;
627			list_of_functions = tmp;
628		}
629	}
630	fclose(stream);
631}
632