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