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