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