2 * This file is part of ltrace.
3 * Copyright (C) 2011,2012,2013 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
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.
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.
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
24 /* getline is POSIX.1-2008. It was originally a GNU extension, and
25 * chances are uClibc still needs _GNU_SOURCE, but for now try it this
27 #define _POSIX_C_SOURCE 200809L
42 #include "prototype.h"
46 #include "lens_default.h"
47 #include "lens_enum.h"
49 /* Lifted from GCC: The ctype functions are often implemented as
50 * macros which do lookups in arrays using the parameter as the
51 * offset. If the ctype function parameter is a char, then gcc will
52 * (appropriately) warn that a "subscript has type char". Using a
53 * (signed) char as a subscript is bad because you may get negative
54 * offsets and thus it is not 8-bit safe. The CTYPE_CONV macro
55 * ensures that the parameter is cast to an unsigned char when a char
56 * is passed in. When an int is passed in, the parameter is left
57 * alone so we don't lose EOF. */
59 #define CTYPE_CONV(CH) \
60 (sizeof(CH) == sizeof(unsigned char) ? (int)(unsigned char)(CH) : (int)(CH))
68 static struct arg_type_info *parse_nonpointer_type(struct protolib *plib,
71 struct param **extra_param,
73 int *ownp, int *forwardp);
74 static struct arg_type_info *parse_type(struct protolib *plib,
76 char **str, struct param **extra_param,
77 size_t param_num, int *ownp,
79 static struct arg_type_info *parse_lens(struct protolib *plib,
81 char **str, struct param **extra_param,
82 size_t param_num, int *ownp,
84 static int parse_enum(struct protolib *plib, struct locus *loc,
85 char **str, struct arg_type_info **retp, int *ownp);
87 struct prototype *list_of_functions = NULL;
90 parse_arg_type(char **name, enum arg_type *ret)
93 enum arg_type candidate;
95 #define KEYWORD(KWD, TYPE) \
97 if (strncmp(*name, KWD, sizeof(KWD) - 1) == 0) { \
98 rest = *name + sizeof(KWD) - 1; \
104 KEYWORD("void", ARGTYPE_VOID);
105 KEYWORD("int", ARGTYPE_INT);
106 KEYWORD("uint", ARGTYPE_UINT);
107 KEYWORD("long", ARGTYPE_LONG);
108 KEYWORD("ulong", ARGTYPE_ULONG);
109 KEYWORD("char", ARGTYPE_CHAR);
110 KEYWORD("short", ARGTYPE_SHORT);
111 KEYWORD("ushort", ARGTYPE_USHORT);
112 KEYWORD("float", ARGTYPE_FLOAT);
113 KEYWORD("double", ARGTYPE_DOUBLE);
114 KEYWORD("array", ARGTYPE_ARRAY);
115 KEYWORD("struct", ARGTYPE_STRUCT);
117 /* Misspelling of int used in ltrace.conf that we used to
119 KEYWORD("itn", ARGTYPE_INT);
121 assert(rest == NULL);
127 if (isalnum(CTYPE_CONV(*rest)) || *rest == '_')
136 eat_spaces(char **str) {
137 while (**str == ' ') {
143 xstrndup(char *str, size_t len) {
144 char *ret = (char *) malloc(len + 1);
146 report_global_error("malloc: %s", strerror(errno));
149 strncpy(ret, str, len);
155 parse_ident(struct locus *loc, char **str)
159 if (!isalpha(CTYPE_CONV(**str)) && **str != '_') {
160 report_error(loc->filename, loc->line_no, "bad identifier");
164 while (**str && (isalnum(CTYPE_CONV(**str)) || **str == '_')) {
168 return xstrndup(ident, *str - ident);
172 Returns position in string at the left parenthesis which starts the
173 function's argument signature. Returns NULL on error.
176 start_of_arg_sig(char *str) {
183 pos = &str[strlen(str)];
188 while ((pos > str) && (*pos != ')') && (*pos != '('))
193 else if (*pos == '(')
198 } while (stacked > 0);
200 return (stacked == 0) ? pos : NULL;
204 parse_int(struct locus *loc, char **str, long *ret)
207 long n = strtol(*str, &end, 0);
209 report_error(loc->filename, loc->line_no, "bad number");
220 check_nonnegative(struct locus *loc, long l)
223 report_error(loc->filename, loc->line_no,
224 "expected non-negative value, got %ld", l);
231 check_int(struct locus *loc, long l)
235 report_error(loc->filename, loc->line_no,
236 "Number too large: %ld", l);
243 parse_char(struct locus *loc, char **str, char expected)
245 if (**str != expected) {
246 report_error(loc->filename, loc->line_no,
247 "expected '%c', got '%c'", expected, **str);
255 static struct expr_node *parse_argnum(struct locus *loc,
256 char **str, int *ownp, int zero);
258 static struct expr_node *
259 parse_zero(struct locus *loc, char **str, int *ownp)
265 struct expr_node *arg = parse_argnum(loc, str, &own, 0);
268 if (parse_char(loc, str, ')') < 0) {
275 struct expr_node *ret = build_zero_w_arg(arg, own);
283 return expr_node_zero();
288 wrap_in_zero(struct expr_node **nodep)
290 struct expr_node *n = build_zero_w_arg(*nodep, 1);
299 * argN : The value of argument #N, counting from 1
300 * eltN : The value of element #N of the containing structure
301 * retval : The return value
302 * N : The numeric value N
304 static struct expr_node *
305 parse_argnum(struct locus *loc, char **str, int *ownp, int zero)
307 struct expr_node *expr = malloc(sizeof(*expr));
311 if (isdigit(CTYPE_CONV(**str))) {
313 if (parse_int(loc, str, &l) < 0
314 || check_nonnegative(loc, l) < 0
315 || check_int(loc, l) < 0)
318 expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0);
320 if (zero && wrap_in_zero(&expr) < 0)
327 char *const name = parse_ident(loc, str);
334 int is_arg = strncmp(name, "arg", 3) == 0;
335 if (is_arg || strncmp(name, "elt", 3) == 0) {
337 char *num = name + 3;
338 if (parse_int(loc, &num, &l) < 0
339 || check_int(loc, l) < 0)
344 expr_init_named(expr, "retval", 0);
346 expr_init_argno(expr, l - 1);
348 struct expr_node *e_up = malloc(sizeof(*e_up));
349 struct expr_node *e_ix = malloc(sizeof(*e_ix));
350 if (e_up == NULL || e_ix == NULL) {
356 expr_init_up(e_up, expr_self(), 0);
357 struct arg_type_info *ti
358 = type_get_simple(ARGTYPE_LONG);
359 expr_init_const_word(e_ix, l - 1, ti, 0);
360 expr_init_index(expr, e_up, 1, e_ix, 1);
363 } else if (strcmp(name, "retval") == 0) {
364 expr_init_named(expr, "retval", 0);
366 } else if (strcmp(name, "zero") == 0) {
367 struct expr_node *ret
368 = parse_zero(loc, str, ownp);
376 report_error(loc->filename, loc->line_no,
377 "Unknown length specifier: '%s'", name);
381 if (zero && wrap_in_zero(&expr) < 0)
394 static struct arg_type_info *
395 parse_typedef_name(struct protolib *plib, char **str)
398 while (*end && (isalnum(CTYPE_CONV(*end)) || *end == '_'))
403 size_t len = end - *str;
405 memcpy(buf, *str, len);
409 struct named_type *nt = protolib_lookup_type(plib, buf, true);
416 parse_typedef(struct protolib *plib, struct locus *loc, char **str)
418 (*str) += strlen("typedef");
420 char *name = parse_ident(loc, str);
422 /* Look through the typedef list whether we already have a
423 * forward of this type. If we do, it must be forward
425 struct named_type *forward = protolib_lookup_type(plib, name, true);
427 && (forward->info->type != ARGTYPE_STRUCT
428 || !forward->forward)) {
429 report_error(loc->filename, loc->line_no,
430 "Redefinition of typedef '%s'", name);
438 if (parse_char(loc, str, '=') < 0)
444 struct arg_type_info *info
445 = parse_lens(plib, loc, str, NULL, 0, &own, &fwd);
449 struct named_type this_nt;
450 named_type_init(&this_nt, info, own);
451 this_nt.forward = fwd;
453 if (forward == NULL) {
454 if (protolib_add_named_type(plib, name, 1, &this_nt) < 0) {
455 named_type_destroy(&this_nt);
461 /* If we are defining a forward, make sure the definition is a
462 * structure as well. */
463 if (this_nt.info->type != ARGTYPE_STRUCT) {
464 report_error(loc->filename, loc->line_no,
465 "Definition of forward '%s' must be a structure.",
467 named_type_destroy(&this_nt);
471 /* Now move guts of the actual type over to the forward type.
472 * We can't just move pointers around, because references to
473 * forward must stay intact. */
474 assert(this_nt.own_type);
475 type_destroy(forward->info);
476 *forward->info = *this_nt.info;
477 forward->forward = 0;
483 /* Syntax: struct ( type,type,type,... ) */
485 parse_struct(struct protolib *plib, struct locus *loc,
486 char **str, struct arg_type_info *info,
492 if (forwardp == NULL) {
493 report_error(loc->filename, loc->line_no,
494 "Forward struct can be declared only "
495 "directly after a typedef.");
499 /* Forward declaration is currently handled as an
501 type_init_struct(info);
506 if (parse_char(loc, str, '(') < 0)
509 eat_spaces(str); // Empty arg list with whitespace inside
511 type_init_struct(info);
515 if (**str == 0 || **str == ')') {
516 parse_char(loc, str, ')');
520 /* Field delimiter. */
521 if (type_struct_size(info) > 0)
522 parse_char(loc, str, ',');
526 struct arg_type_info *field
527 = parse_lens(plib, loc, str, NULL, 0, &own, NULL);
528 if (field == NULL || type_struct_add(info, field, own)) {
535 /* Make a copy of INFO and set the *OWN bit if it's not already
538 unshare_type_info(struct locus *loc, struct arg_type_info **infop, int *ownp)
543 struct arg_type_info *ninfo = malloc(sizeof(*ninfo));
544 if (ninfo == NULL || type_clone(ninfo, *infop) < 0) {
545 report_error(loc->filename, loc->line_no,
546 "malloc: %s", strerror(errno));
556 parse_string(struct protolib *plib, struct locus *loc,
557 char **str, struct arg_type_info **retp, int *ownp)
559 struct arg_type_info *info = NULL;
560 struct expr_node *length;
563 if (isdigit(CTYPE_CONV(**str))) {
564 /* string0 is string[retval], length is zero(retval)
565 * stringN is string[argN], length is zero(argN) */
567 if (parse_int(loc, str, &l) < 0
568 || check_int(loc, l) < 0)
571 struct expr_node *length_arg = malloc(sizeof(*length_arg));
572 if (length_arg == NULL)
576 expr_init_named(length_arg, "retval", 0);
578 expr_init_argno(length_arg, l - 1);
580 length = build_zero_w_arg(length_arg, 1);
581 if (length == NULL) {
582 expr_destroy(length_arg);
594 length = parse_argnum(loc, str, &own_length, 1);
599 parse_char(loc, str, ']');
601 } else if (**str == '(') {
602 /* Usage of "string" as lens. */
606 info = parse_type(plib, loc, str, NULL, 0, ownp, NULL);
614 parse_char(loc, str, ')');
617 /* It was just a simple string after all. */
618 length = expr_node_zero();
623 /* String is a pointer to array of chars. */
625 struct arg_type_info *info1 = malloc(sizeof(*info1));
626 struct arg_type_info *info2 = malloc(sizeof(*info2));
627 if (info1 == NULL || info2 == NULL) {
632 assert(length != NULL);
633 expr_destroy(length);
638 type_init_array(info2, type_get_simple(ARGTYPE_CHAR), 0,
640 type_init_pointer(info1, info2, 1);
646 /* We'll need to set the lens, so unshare. */
647 if (unshare_type_info(loc, &info, ownp) < 0)
648 /* If unshare_type_info failed, it must have been as a
649 * result of cloning attempt because *OWNP was 0.
650 * Thus we don't need to destroy INFO. */
653 info->lens = &string_lens;
661 build_printf_pack(struct locus *loc, struct param **packp, size_t param_num)
664 report_error(loc->filename, loc->line_no,
665 "'format' type in unexpected context");
668 if (*packp != NULL) {
669 report_error(loc->filename, loc->line_no,
670 "only one 'format' type per function supported");
674 *packp = malloc(sizeof(**packp));
678 struct expr_node *node = malloc(sizeof(*node));
685 expr_init_argno(node, param_num);
687 param_pack_init_printf(*packp, node, 1);
692 /* Match and consume KWD if it's next in stream, and return 0.
693 * Otherwise return negative number. */
695 try_parse_kwd(char **str, const char *kwd)
697 size_t len = strlen(kwd);
698 if (strncmp(*str, kwd, len) == 0
699 && !isalnum(CTYPE_CONV((*str)[len]))
700 && (*str)[len] != '_') {
707 /* XXX EXTRA_PARAM and PARAM_NUM are a kludge to get in
708 * backward-compatible support for "format" parameter type. The
709 * latter is only valid if the former is non-NULL, which is only in
710 * top-level context. */
712 parse_alias(struct protolib *plib, struct locus *loc,
713 char **str, struct arg_type_info **retp, int *ownp,
714 struct param **extra_param, size_t param_num)
716 /* For backward compatibility, we need to support things like
717 * stringN (which is like string[argN], string[N], and also
718 * bare string. We might, in theory, replace this by
719 * preprocessing configure file sources with M4, but for now,
720 * "string" is syntax. */
721 if (strncmp(*str, "string", 6) == 0) {
723 return parse_string(plib, loc, str, retp, ownp);
725 } else if (try_parse_kwd(str, "format") >= 0
726 && extra_param != NULL) {
727 /* For backward compatibility, format is parsed as
728 * "string", but it smuggles to the parameter list of
729 * a function a "printf" argument pack with this
730 * parameter as argument. */
731 if (parse_string(plib, loc, str, retp, ownp) < 0)
734 return build_printf_pack(loc, extra_param, param_num);
736 } else if (try_parse_kwd(str, "enum") >=0) {
738 return parse_enum(plib, loc, str, retp, ownp);
746 /* Syntax: array ( type, N|argN ) */
748 parse_array(struct protolib *plib, struct locus *loc,
749 char **str, struct arg_type_info *info)
752 if (parse_char(loc, str, '(') < 0)
757 struct arg_type_info *elt_info
758 = parse_lens(plib, loc, str, NULL, 0, &own, NULL);
759 if (elt_info == NULL)
763 parse_char(loc, str, ',');
767 struct expr_node *length = parse_argnum(loc, str, &own_length, 0);
768 if (length == NULL) {
770 type_destroy(elt_info);
776 type_init_array(info, elt_info, own, length, own_length);
779 parse_char(loc, str, ')');
784 * enum (keyname[=value],keyname[=value],... )
785 * enum<type> (keyname[=value],keyname[=value],... )
788 parse_enum(struct protolib *plib, struct locus *loc, char **str,
789 struct arg_type_info **retp, int *ownp)
791 /* Optional type argument. */
794 parse_char(loc, str, '[');
796 *retp = parse_nonpointer_type(plib, loc, str, NULL, 0, ownp, 0);
800 if (!type_is_integral((*retp)->type)) {
801 report_error(loc->filename, loc->line_no,
802 "integral type required as enum argument");
805 /* This also releases associated lens
806 * if any was set so far. */
814 if (parse_char(loc, str, ']') < 0)
818 *retp = type_get_simple(ARGTYPE_INT);
822 /* We'll need to set the lens, so unshare. */
823 if (unshare_type_info(loc, retp, ownp) < 0)
827 if (parse_char(loc, str, '(') < 0)
830 struct enum_lens *lens = malloc(sizeof(*lens));
832 report_error(loc->filename, loc->line_no,
833 "malloc enum lens: %s", strerror(errno));
837 lens_init_enum(lens);
838 (*retp)->lens = &lens->super;
839 (*retp)->own_lens = 1;
844 if (**str == 0 || **str == ')') {
845 parse_char(loc, str, ')');
849 /* Field delimiter. XXX should we support the C
850 * syntax, where the enumeration can end in pending
852 if (lens_enum_size(lens) > 0)
853 parse_char(loc, str, ',');
856 char *key = parse_ident(loc, str);
866 if (parse_int(loc, str, &last_val) < 0)
870 struct value *value = malloc(sizeof(*value));
873 value_init_detached(value, NULL, *retp, 0);
874 value_set_word(value, last_val);
876 if (lens_enum_add(lens, key, 1, value, 1) < 0)
885 static struct arg_type_info *
886 parse_nonpointer_type(struct protolib *plib, struct locus *loc,
887 char **str, struct param **extra_param, size_t param_num,
888 int *ownp, int *forwardp)
890 const char *orig_str = *str;
892 if (parse_arg_type(str, &type) < 0) {
893 struct arg_type_info *type;
894 if (parse_alias(plib, loc, str, &type,
895 ownp, extra_param, param_num) < 0)
897 else if (type != NULL)
901 if ((type = parse_typedef_name(plib, str)) == NULL)
902 report_error(loc->filename, loc->line_no,
903 "unknown type around '%s'", orig_str);
907 /* For some types that's all we need. */
920 return type_get_simple(type);
926 case ARGTYPE_POINTER:
927 /* Pointer syntax is not based on keyword, so we
928 * should never get this type. */
929 assert(type != ARGTYPE_POINTER);
933 struct arg_type_info *info = malloc(sizeof(*info));
935 report_error(loc->filename, loc->line_no,
936 "malloc: %s", strerror(errno));
941 if (type == ARGTYPE_ARRAY) {
942 if (parse_array(plib, loc, str, info) < 0) {
948 assert(type == ARGTYPE_STRUCT);
949 if (parse_struct(plib, loc, str, info, forwardp) < 0)
956 static struct named_lens {
960 { "hide", &blind_lens },
961 { "octal", &octal_lens },
962 { "oct", &octal_lens },
963 { "bitvec", &bitvect_lens },
964 { "hex", &hex_lens },
965 { "bool", &bool_lens },
966 { "guess", &guess_lens },
970 name2lens(char **str, int *own_lensp)
973 for (i = 0; i < sizeof(lenses)/sizeof(*lenses); ++i)
974 if (try_parse_kwd(str, lenses[i].name) == 0) {
976 return lenses[i].lens;
982 static struct arg_type_info *
983 parse_type(struct protolib *plib, struct locus *loc, char **str,
984 struct param **extra_param, size_t param_num,
985 int *ownp, int *forwardp)
987 struct arg_type_info *info
988 = parse_nonpointer_type(plib, loc, str, extra_param,
989 param_num, ownp, forwardp);
996 struct arg_type_info *outer = malloc(sizeof(*outer));
1002 report_error(loc->filename, loc->line_no,
1003 "malloc: %s", strerror(errno));
1006 type_init_pointer(outer, info, *ownp);
1016 static struct arg_type_info *
1017 parse_lens(struct protolib *plib, struct locus *loc,
1018 char **str, struct param **extra_param,
1019 size_t param_num, int *ownp, int *forwardp)
1022 struct lens *lens = name2lens(str, &own_lens);
1024 struct arg_type_info *info;
1028 /* Octal lens gets special treatment, because of
1029 * backward compatibility. */
1030 if (lens == &octal_lens && **str != '(') {
1032 info = type_get_simple(ARGTYPE_INT);
1034 } else if (parse_char(loc, str, '(') < 0) {
1035 report_error(loc->filename, loc->line_no,
1036 "expected type argument after the lens");
1043 info = parse_type(plib, loc, str, extra_param, param_num,
1047 if (own_lens && lens != NULL)
1053 if (lens != NULL && has_args) {
1055 parse_char(loc, str, ')');
1058 /* We can't modify shared types. Make a copy if we have a
1060 if (lens != NULL && unshare_type_info(loc, &info, ownp) < 0)
1065 info->own_lens = own_lens;
1072 param_is_void(struct param *param)
1074 return param->flavor == PARAM_FLAVOR_TYPE
1075 && param->u.type.type->type == ARGTYPE_VOID;
1078 static struct arg_type_info *
1079 get_hidden_int(void)
1081 static struct arg_type_info info, *pinfo = NULL;
1085 info = *type_get_simple(ARGTYPE_INT);
1086 info.lens = &blind_lens;
1092 static enum callback_status
1093 void_to_hidden_int(struct prototype *proto, struct param *param, void *data)
1095 struct locus *loc = data;
1096 if (param_is_void(param)) {
1097 report_warning(loc->filename, loc->line_no,
1098 "void parameter assumed to be 'hide(int)'");
1100 static struct arg_type_info *type = NULL;
1102 type = get_hidden_int();
1103 param_destroy(param);
1104 param_init_type(param, type, 0);
1110 process_line(struct protolib *plib, struct locus *loc, char *buf)
1115 debug(3, "Reading line %d of `%s'", loc->line_no, loc->filename);
1118 /* A comment or empty line. */
1119 if (*str == ';' || *str == 0 || *str == '\n' || *str == '#')
1122 if (strncmp(str, "typedef", 7) == 0) {
1123 parse_typedef(plib, loc, &str);
1127 struct prototype fun;
1128 prototype_init(&fun);
1130 struct param *extra_param = NULL;
1131 char *proto_name = NULL;
1133 fun.return_info = parse_lens(plib, loc, &str, NULL, 0, &own, NULL);
1134 if (fun.return_info == NULL) {
1136 debug(3, " Skipping line %d", loc->line_no);
1138 if (extra_param != NULL) {
1139 param_destroy(extra_param);
1143 prototype_destroy(&fun);
1147 fun.own_return_info = own;
1148 debug(4, " return_type = %d", fun.return_info->type);
1151 tmp = start_of_arg_sig(str);
1153 report_error(loc->filename, loc->line_no, "syntax error");
1158 proto_name = strdup(str);
1159 if (proto_name == NULL) {
1161 report_error(loc->filename, loc->line_no,
1162 "%s", strerror(errno));
1167 debug(3, " name = %s", proto_name);
1176 if (str[0] == '+') {
1177 if (have_stop == 0) {
1179 param_init_stop(¶m);
1180 if (prototype_push_param(&fun, ¶m) < 0)
1188 size_t param_num = prototype_num_params(&fun) - have_stop;
1189 struct arg_type_info *type
1190 = parse_lens(plib, loc, &str, &extra_param,
1191 param_num, &own, NULL);
1193 report_error(loc->filename, loc->line_no,
1194 "unknown argument type");
1199 param_init_type(¶m, type, own);
1200 if (prototype_push_param(&fun, ¶m) < 0)
1207 } else if (*str == ')') {
1210 if (str[strlen(str) - 1] == '\n')
1211 str[strlen(str) - 1] = '\0';
1212 report_error(loc->filename, loc->line_no,
1213 "syntax error around \"%s\"", str);
1218 /* We used to allow void parameter as a synonym to an argument
1219 * that shouldn't be displayed. But backends really need to
1220 * know the exact type that they are dealing with. The proper
1221 * way to do this these days is to use the hide lens.
1223 * So if there are any voids in the parameter list, show a
1224 * warning and assume that they are ints. If there's a sole
1225 * void, assume the function doesn't take any arguments. The
1226 * latter is conservative, we can drop the argument
1227 * altogether, instead of fetching and then not showing it,
1228 * without breaking any observable behavior. */
1229 if (prototype_num_params(&fun) == 1
1230 && param_is_void(prototype_get_nth_param(&fun, 0))) {
1232 /* Don't show this warning. Pre-0.7.0
1233 * ltrace.conf often used this idiom. This
1234 * should be postponed until much later, when
1235 * extant uses are likely gone. */
1236 report_warning(loc->filename, loc->line_no,
1237 "sole void parameter ignored");
1238 prototype_destroy_nth_param(&fun, 0);
1240 prototype_each_param(&fun, NULL, void_to_hidden_int, loc);
1243 if (extra_param != NULL) {
1244 prototype_push_param(&fun, extra_param);
1249 if (protolib_add_prototype(plib, proto_name, 1, &fun) < 0) {
1250 report_error(loc->filename, loc->line_no,
1251 "couldn't add prototype: %s",
1260 read_config_file(FILE *stream, const char *path, struct protolib *plib)
1262 debug(DEBUG_FUNCTION, "Reading config file `%s'...", path);
1264 struct locus loc = { path, 0 };
1267 while (getline(&line, &len, stream) >= 0) {
1269 process_line(plib, &loc, line);