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