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