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