read_config_file.c revision 77d2137b1f1db9c3a0c7090dee882e0e17984350
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
227/*
228 * Input:
229 *  argN   : The value of argument #N, counting from 1
230 *  eltN   : The value of element #N of the containing structure
231 *  retval : The return value
232 *  N      : The numeric value N
233 */
234static struct expr_node *
235parse_argnum(char **str)
236{
237	struct expr_node *expr = malloc(sizeof(*expr));
238	if (expr == NULL)
239		return NULL;
240
241	if (isdigit(**str)) {
242		long l;
243		if (parse_int(str, &l) < 0
244		    || check_nonnegative(l) < 0
245		    || check_int(l) < 0)
246			goto fail;
247
248		expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0);
249
250		return expr;
251
252	} else {
253		char *name = parse_ident(str);
254		if (name == NULL)
255			goto fail;
256
257		int is_arg = strncmp(name, "arg", 3) == 0;
258		int is_elt = !is_arg && strncmp(name, "elt", 3) == 0;
259		if (is_arg || is_elt) {
260			long l;
261			name += 3;
262			if (parse_int(&name, &l) < 0
263			    || check_int(l) < 0)
264				goto fail;
265
266			if (is_arg) {
267				expr_init_argno(expr, l - 1);
268			} else {
269				struct expr_node *e_up = malloc(sizeof(*e_up));
270				struct expr_node *e_ix = malloc(sizeof(*e_ix));
271				if (e_up == NULL || e_ix == NULL) {
272					free(e_up);
273					free(e_ix);
274					goto fail;
275				}
276
277				expr_init_up(e_up, expr_self(), 0);
278				struct arg_type_info *ti
279					= type_get_simple(ARGTYPE_LONG);
280				expr_init_const_word(e_ix, l - 1, ti, 0);
281				expr_init_index(expr, e_up, 1, e_ix, 1);
282			}
283
284		} else if (strcmp(name, "retval") == 0) {
285			expr_init_named(expr, "retval", 0);
286
287		} else {
288			report_error(filename, line_no,
289				     "Unknown length specifier: '%s'", name);
290			goto fail;
291		}
292		return expr;
293	}
294
295fail:
296	free(expr);
297	return NULL;
298}
299
300struct typedef_node_t {
301	char *name;
302	struct arg_type_info *info;
303	struct typedef_node_t *next;
304} *typedefs = NULL;
305
306static struct arg_type_info *
307lookup_typedef(char **str) {
308	struct typedef_node_t *node;
309	char *end = *str;
310	while (*end && (isalnum(*end) || *end == '_'))
311		++end;
312	if (end == *str)
313		return NULL;
314
315	for (node = typedefs; node != NULL; node = node->next) {
316		if (strncmp(*str, node->name, end - *str) == 0) {
317			(*str) += strlen(node->name);
318			return node->info;
319		}
320	}
321
322	return NULL;
323}
324
325static void
326parse_typedef(char **str) {
327	char *name;
328	struct arg_type_info *info;
329	struct typedef_node_t *binding;
330
331	(*str) += strlen("typedef");
332	eat_spaces(str);
333
334	// Grab out the name of the type
335	name = parse_ident(str);
336
337	// Skip = sign
338	eat_spaces(str);
339	if (**str != '=') {
340		output_line(0,
341				"Syntax error in `%s', line %d: expected '=', got '%c'",
342				filename, line_no, **str);
343		error_count++;
344		return;
345	}
346	(*str)++;
347	eat_spaces(str);
348
349	// Parse the type
350	info = parse_type(str);
351
352	// Insert onto beginning of linked list
353	binding = malloc(sizeof(*binding));
354	binding->name = name;
355	binding->info = info;
356	binding->next = typedefs;
357	typedefs = binding;
358}
359
360/* Syntax: struct ( type,type,type,... ) */
361static int
362parse_struct(char **str, struct arg_type_info *info)
363{
364	eat_spaces(str);
365	if (**str != '(')
366		return -1;
367	++*str;
368
369	eat_spaces(str); // Empty arg list with whitespace inside
370
371	type_init_struct(info);
372
373	while (1) {
374		eat_spaces(str);
375		if (**str == 0 || **str == ')') {
376			++*str;
377			return 0;
378		}
379
380		/* Field delimiter.  */
381		if (type_struct_size(info) > 0)
382			++*str;
383
384		eat_spaces(str);
385		int own = 0;
386		struct arg_type_info *field = parse_type(str);
387		if (field == NULL || type_struct_add(info, field, own)) {
388			type_destroy(info);
389			return -1;
390		}
391	}
392}
393
394static struct arg_type_info *
395parse_nonpointer_type(char **str) {
396	struct arg_type_info *simple;
397	struct arg_type_info *info;
398
399	if (strncmp(*str, "typedef", 7) == 0) {
400		parse_typedef(str);
401		return lookup_prototype(ARGTYPE_UNKNOWN);
402	}
403
404	simple = str2type(str);
405	if (simple->type == ARGTYPE_UNKNOWN) {
406		info = lookup_typedef(str);
407		if (info)
408			return info;
409		else
410			return simple;		// UNKNOWN
411	}
412
413	info = malloc(sizeof(*info));
414	info->type = simple->type;
415
416	/* Code to parse parameterized types will go into the following
417	   switch statement. */
418
419	switch (info->type) {
420
421	/* Syntax: array ( type, N|argN ) */
422	case ARGTYPE_ARRAY:
423		(*str)++;		// Get past open paren
424		eat_spaces(str);
425		if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
426			return NULL;
427		(*str)++;		// Get past comma
428		eat_spaces(str);
429		info->u.array_info.length = parse_argnum(str);
430		(*str)++;		// Get past close paren
431		return info;
432
433	/* Syntax: enum ( keyname=value,keyname=value,... ) */
434	case ARGTYPE_ENUM:{
435		struct enum_opt {
436			char *key;
437			int value;
438			struct enum_opt *next;
439		};
440		struct enum_opt *list = NULL;
441		struct enum_opt *p;
442		int entries = 0;
443		int ii;
444
445		eat_spaces(str);
446		(*str)++;		// Get past open paren
447		eat_spaces(str);
448
449		while (**str && **str != ')') {
450			p = (struct enum_opt *) malloc(sizeof(*p));
451			eat_spaces(str);
452			p->key = parse_ident(str);
453			if (error_count) {
454				free(p);
455				return NULL;
456			}
457			eat_spaces(str);
458			if (**str != '=') {
459			fail:
460				free(p->key);
461				free(p);
462				output_line(0,
463						"Syntax error in `%s', line %d: expected '=', got '%c'",
464						filename, line_no, **str);
465				error_count++;
466				return NULL;
467			}
468			++(*str);
469			eat_spaces(str);
470			long l;
471			if (parse_int(str, &l) < 0 || check_int(l) < 0)
472				goto fail;
473			p->value = l;
474			p->next = list;
475			list = p;
476			++entries;
477
478			// Skip comma
479			eat_spaces(str);
480			if (**str == ',') {
481				(*str)++;
482				eat_spaces(str);
483			}
484		}
485
486		info->u.enum_info.entries = entries;
487		info->u.enum_info.keys =
488			(char **) malloc(entries * sizeof(char *));
489		info->u.enum_info.values =
490			(int *) malloc(entries * sizeof(int));
491		for (ii = 0, p = NULL; list; ++ii, list = list->next) {
492			if (p)
493				free(p);
494			info->u.enum_info.keys[ii] = list->key;
495			info->u.enum_info.values[ii] = list->value;
496			p = list;
497		}
498		if (p)
499			free(p);
500
501		return info;
502	}
503
504	case ARGTYPE_STRING:
505		if (!isdigit(**str) && **str != '[') {
506			/* Oops, was just a simple string after all */
507			free(info);
508			return simple;
509		}
510
511		info->type = ARGTYPE_STRING_N;
512
513		/* Backwards compatibility for string0, string1, ... */
514		if (isdigit(**str)) {
515			info->u.string_n_info.length = parse_argnum(str);
516			return info;
517		}
518
519		(*str)++;		// Skip past opening [
520		eat_spaces(str);
521		info->u.string_n_info.length = parse_argnum(str);
522		eat_spaces(str);
523		(*str)++;		// Skip past closing ]
524		return info;
525
526	// Syntax: struct ( type,type,type,... )
527	case ARGTYPE_STRUCT:{
528		if (parse_struct(str, info) < 0) {
529			free(info);
530			output_line(0, "Parse error in `%s', line %d",
531				    filename, line_no);
532			error_count++;
533			return NULL;
534		}
535		return info;
536	}
537
538	default:
539		if (info->type == ARGTYPE_UNKNOWN) {
540			output_line(0, "Syntax error in `%s', line %d: "
541					"Unknown type encountered",
542					filename, line_no);
543			free(info);
544			error_count++;
545			return NULL;
546		} else {
547			return info;
548		}
549	}
550}
551
552static struct arg_type_info *
553parse_type(char **str) {
554	struct arg_type_info *info = parse_nonpointer_type(str);
555	while (**str == '*') {
556		struct arg_type_info *outer = malloc(sizeof(*info));
557		outer->type = ARGTYPE_POINTER;
558		outer->u.ptr_info.info = info;
559		(*str)++;
560		info = outer;
561	}
562	return info;
563}
564
565static Function *
566process_line(char *buf) {
567	Function fun;
568	Function *fun_p;
569	char *str = buf;
570	char *tmp;
571	int i;
572	int float_num = 0;
573
574	line_no++;
575	debug(3, "Reading line %d of `%s'", line_no, filename);
576	eat_spaces(&str);
577	fun.return_info = parse_type(&str);
578	if (fun.return_info == NULL)
579		return NULL;
580	if (fun.return_info->type == ARGTYPE_UNKNOWN) {
581	err:
582		debug(3, " Skipping line %d", line_no);
583		return NULL;
584	}
585	debug(4, " return_type = %d", fun.return_info->type);
586	eat_spaces(&str);
587	tmp = start_of_arg_sig(str);
588	if (tmp == NULL) {
589		report_error(filename, line_no, "syntax error");
590		goto err;
591	}
592	*tmp = '\0';
593	fun.name = strdup(str);
594	str = tmp + 1;
595	debug(3, " name = %s", fun.name);
596	fun.params_right = 0;
597	for (i = 0; i < MAX_ARGS; i++) {
598		eat_spaces(&str);
599		if (*str == ')') {
600			break;
601		}
602		if (str[0] == '+') {
603			fun.params_right++;
604			str++;
605		} else if (fun.params_right) {
606			fun.params_right++;
607		}
608		fun.arg_info[i] = parse_type(&str);
609		if (fun.arg_info[i] == NULL) {
610			output_line(0, "Syntax error in `%s', line %d"
611					": unknown argument type",
612					filename, line_no);
613			error_count++;
614			return NULL;
615		}
616		if (fun.arg_info[i]->type == ARGTYPE_FLOAT)
617			fun.arg_info[i]->u.float_info.float_index = float_num++;
618		else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE)
619			fun.arg_info[i]->u.double_info.float_index = float_num++;
620		eat_spaces(&str);
621		if (*str == ',') {
622			str++;
623			continue;
624		} else if (*str == ')') {
625			continue;
626		} else {
627			if (str[strlen(str) - 1] == '\n')
628				str[strlen(str) - 1] = '\0';
629			report_error(filename, line_no,
630				     "syntax error around \"%s\"", str);
631			goto err;
632		}
633	}
634	fun.num_params = i;
635	fun_p = malloc(sizeof(Function));
636	if (!fun_p) {
637		perror("ltrace: malloc");
638		exit(1);
639	}
640	memcpy(fun_p, &fun, sizeof(Function));
641	return fun_p;
642}
643
644void
645read_config_file(char *file) {
646	FILE *stream;
647	char buf[1024];
648
649	filename = file;
650	stream = fopen(filename, "r");
651	if (!stream) {
652		return;
653	}
654
655	debug(1, "Reading config file `%s'...", filename);
656
657	line_no = 0;
658	while (fgets(buf, 1024, stream)) {
659		Function *tmp;
660
661		error_count = 0;
662		tmp = process_line(buf);
663
664		if (tmp) {
665			debug(2, "New function: `%s'", tmp->name);
666			tmp->next = list_of_functions;
667			list_of_functions = tmp;
668		}
669	}
670	fclose(stream);
671}
672