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