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