read_config_file.c revision 94078ecce3a103c28457e6f90f1e5b0dacc61146
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 34static int line_no; 35static char *filename; 36static int error_count = 0; 37 38static struct arg_type_info *parse_type(char **str); 39 40Function *list_of_functions = NULL; 41 42/* Map of strings to type names. These do not need to be in any 43 * particular order */ 44static struct list_of_pt_t { 45 char *name; 46 enum arg_type pt; 47} list_of_pt[] = { 48 { 49 "void", ARGTYPE_VOID}, { 50 "int", ARGTYPE_INT}, { 51 "uint", ARGTYPE_UINT}, { 52 "long", ARGTYPE_LONG}, { 53 "ulong", ARGTYPE_ULONG}, { 54 "octal", ARGTYPE_OCTAL}, { 55 "char", ARGTYPE_CHAR}, { 56 "short", ARGTYPE_SHORT}, { 57 "ushort", ARGTYPE_USHORT}, { 58 "float", ARGTYPE_FLOAT}, { 59 "double", ARGTYPE_DOUBLE}, { 60 "addr", ARGTYPE_ADDR}, { 61 "file", ARGTYPE_FILE}, { 62 "format", ARGTYPE_FORMAT}, { 63 "string", ARGTYPE_STRING}, { 64 "array", ARGTYPE_ARRAY}, { 65 "struct", ARGTYPE_STRUCT}, { 66 "enum", ARGTYPE_ENUM}, { 67 NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */ 68}; 69 70/* Array of prototype objects for each of the types. The order in this 71 * array must exactly match the list of enumerated values in 72 * common.h */ 73static struct arg_type_info arg_type_prototypes[] = { 74 { ARGTYPE_VOID }, 75 { ARGTYPE_INT }, 76 { ARGTYPE_UINT }, 77 { ARGTYPE_LONG }, 78 { ARGTYPE_ULONG }, 79 { ARGTYPE_OCTAL }, 80 { ARGTYPE_CHAR }, 81 { ARGTYPE_SHORT }, 82 { ARGTYPE_USHORT }, 83 { ARGTYPE_FLOAT }, 84 { ARGTYPE_DOUBLE }, 85 { ARGTYPE_ADDR }, 86 { ARGTYPE_FILE }, 87 { ARGTYPE_FORMAT }, 88 { ARGTYPE_STRING }, 89 { ARGTYPE_STRING_N }, 90 { ARGTYPE_ARRAY }, 91 { ARGTYPE_ENUM }, 92 { ARGTYPE_STRUCT }, 93 { ARGTYPE_POINTER }, 94 { ARGTYPE_UNKNOWN } 95}; 96 97struct arg_type_info * 98lookup_prototype(enum arg_type at) { 99 if (at >= 0 && at <= ARGTYPE_COUNT) 100 return &arg_type_prototypes[at]; 101 else 102 return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */ 103} 104 105static struct arg_type_info * 106str2type(char **str) { 107 struct list_of_pt_t *tmp = &list_of_pt[0]; 108 109 while (tmp->name) { 110 if (!strncmp(*str, tmp->name, strlen(tmp->name)) 111 && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) { 112 *str += strlen(tmp->name); 113 return lookup_prototype(tmp->pt); 114 } 115 tmp++; 116 } 117 return lookup_prototype(ARGTYPE_UNKNOWN); 118} 119 120static void 121eat_spaces(char **str) { 122 while (**str == ' ') { 123 (*str)++; 124 } 125} 126 127static char * 128xstrndup(char *str, size_t len) { 129 char *ret = (char *) malloc(len + 1); 130 strncpy(ret, str, len); 131 ret[len] = 0; 132 return ret; 133} 134 135static char * 136parse_ident(char **str) { 137 char *ident = *str; 138 139 if (!isalnum(**str) && **str != '_') { 140 output_line(0, "Syntax error in `%s', line %d: Bad identifier", 141 filename, line_no); 142 error_count++; 143 return NULL; 144 } 145 146 while (**str && (isalnum(**str) || **str == '_')) { 147 ++(*str); 148 } 149 150 return xstrndup(ident, *str - ident); 151} 152 153/* 154 Returns position in string at the left parenthesis which starts the 155 function's argument signature. Returns NULL on error. 156*/ 157static char * 158start_of_arg_sig(char *str) { 159 char *pos; 160 int stacked = 0; 161 162 if (!strlen(str)) 163 return NULL; 164 165 pos = &str[strlen(str)]; 166 do { 167 pos--; 168 if (pos < str) 169 return NULL; 170 while ((pos > str) && (*pos != ')') && (*pos != '(')) 171 pos--; 172 173 if (*pos == ')') 174 stacked++; 175 else if (*pos == '(') 176 stacked--; 177 else 178 return NULL; 179 180 } while (stacked > 0); 181 182 return (stacked == 0) ? pos : NULL; 183} 184 185static int 186parse_int(char **str) { 187 char *end; 188 long n = strtol(*str, &end, 0); 189 if (end == *str) { 190 output_line(0, "Syntax error in `%s', line %d: Bad number (%s)", 191 filename, line_no, *str); 192 error_count++; 193 return 0; 194 } 195 196 *str = end; 197 return n; 198} 199 200/* 201 * Input: 202 * argN : The value of argument #N, counting from 1 203 * eltN : The value of element #N of the containing structure 204 * retval : The return value 205 * N : The numeric value N 206 */ 207static struct expr_node * 208parse_argnum(char **str) 209{ 210 struct expr_node *expr = malloc(sizeof(*expr)); 211 if (expr == NULL) 212 return NULL; 213 214 if (isdigit(**str)) { 215 expr_init_const_word(expr, parse_int(str), 216 type_get_simple(ARGTYPE_LONG), 0); 217 218 return expr; 219 220 } else { 221 char *name = parse_ident(str); 222 if (name == NULL) 223 goto fail; 224 225 int is_arg = strncmp(name, "arg", 3) == 0; 226 int is_elt = !is_arg && strncmp(name, "elt", 3) == 0; 227 if (is_arg || is_elt) { 228 name += 3; 229 int l = parse_int(&name); 230 if (is_arg) { 231 expr_init_argno(expr, l - 1); 232 } else { 233 struct expr_node *e_up = malloc(sizeof(*e_up)); 234 struct expr_node *e_ix = malloc(sizeof(*e_ix)); 235 if (e_up == NULL || e_ix == NULL) { 236 free(e_up); 237 free(e_ix); 238 goto fail; 239 } 240 241 expr_init_up(e_up, expr_self(), 0); 242 struct arg_type_info *ti 243 = type_get_simple(ARGTYPE_LONG); 244 expr_init_const_word(e_ix, l - 1, ti, 0); 245 expr_init_index(expr, e_up, 1, e_ix, 1); 246 } 247 248 } else if (strcmp(name, "retval") == 0) { 249 expr_init_named(expr, "retval", 0); 250 251 } else { 252 goto fail; 253 } 254 return expr; 255 } 256 257fail: 258 free(expr); 259 return NULL; 260} 261 262struct typedef_node_t { 263 char *name; 264 struct arg_type_info *info; 265 struct typedef_node_t *next; 266} *typedefs = NULL; 267 268static struct arg_type_info * 269lookup_typedef(char **str) { 270 struct typedef_node_t *node; 271 char *end = *str; 272 while (*end && (isalnum(*end) || *end == '_')) 273 ++end; 274 if (end == *str) 275 return NULL; 276 277 for (node = typedefs; node != NULL; node = node->next) { 278 if (strncmp(*str, node->name, end - *str) == 0) { 279 (*str) += strlen(node->name); 280 return node->info; 281 } 282 } 283 284 return NULL; 285} 286 287static void 288parse_typedef(char **str) { 289 char *name; 290 struct arg_type_info *info; 291 struct typedef_node_t *binding; 292 293 (*str) += strlen("typedef"); 294 eat_spaces(str); 295 296 // Grab out the name of the type 297 name = parse_ident(str); 298 299 // Skip = sign 300 eat_spaces(str); 301 if (**str != '=') { 302 output_line(0, 303 "Syntax error in `%s', line %d: expected '=', got '%c'", 304 filename, line_no, **str); 305 error_count++; 306 return; 307 } 308 (*str)++; 309 eat_spaces(str); 310 311 // Parse the type 312 info = parse_type(str); 313 314 // Insert onto beginning of linked list 315 binding = malloc(sizeof(*binding)); 316 binding->name = name; 317 binding->info = info; 318 binding->next = typedefs; 319 typedefs = binding; 320} 321 322/* Syntax: struct ( type,type,type,... ) */ 323static int 324parse_struct(char **str, struct arg_type_info *info) 325{ 326 eat_spaces(str); 327 if (**str != '(') 328 return -1; 329 ++*str; 330 331 eat_spaces(str); // Empty arg list with whitespace inside 332 333 type_init_struct(info); 334 335 while (1) { 336 eat_spaces(str); 337 if (**str == 0 || **str == ')') { 338 ++*str; 339 return 0; 340 } 341 342 /* Field delimiter. */ 343 if (type_struct_size(info) > 0) 344 ++*str; 345 346 eat_spaces(str); 347 int own = 0; 348 struct arg_type_info *field = parse_type(str); 349 if (field == NULL || type_struct_add(info, field, own)) { 350 type_destroy(info); 351 return -1; 352 } 353 } 354} 355 356static struct arg_type_info * 357parse_nonpointer_type(char **str) { 358 struct arg_type_info *simple; 359 struct arg_type_info *info; 360 361 if (strncmp(*str, "typedef", 7) == 0) { 362 parse_typedef(str); 363 return lookup_prototype(ARGTYPE_UNKNOWN); 364 } 365 366 simple = str2type(str); 367 if (simple->type == ARGTYPE_UNKNOWN) { 368 info = lookup_typedef(str); 369 if (info) 370 return info; 371 else 372 return simple; // UNKNOWN 373 } 374 375 info = malloc(sizeof(*info)); 376 info->type = simple->type; 377 378 /* Code to parse parameterized types will go into the following 379 switch statement. */ 380 381 switch (info->type) { 382 383 /* Syntax: array ( type, N|argN ) */ 384 case ARGTYPE_ARRAY: 385 (*str)++; // Get past open paren 386 eat_spaces(str); 387 if ((info->u.array_info.elt_type = parse_type(str)) == NULL) 388 return NULL; 389 (*str)++; // Get past comma 390 eat_spaces(str); 391 info->u.array_info.length = parse_argnum(str); 392 (*str)++; // Get past close paren 393 return info; 394 395 /* Syntax: enum ( keyname=value,keyname=value,... ) */ 396 case ARGTYPE_ENUM:{ 397 struct enum_opt { 398 char *key; 399 int value; 400 struct enum_opt *next; 401 }; 402 struct enum_opt *list = NULL; 403 struct enum_opt *p; 404 int entries = 0; 405 int ii; 406 407 eat_spaces(str); 408 (*str)++; // Get past open paren 409 eat_spaces(str); 410 411 while (**str && **str != ')') { 412 p = (struct enum_opt *) malloc(sizeof(*p)); 413 eat_spaces(str); 414 p->key = parse_ident(str); 415 if (error_count) { 416 free(p); 417 return NULL; 418 } 419 eat_spaces(str); 420 if (**str != '=') { 421 free(p->key); 422 free(p); 423 output_line(0, 424 "Syntax error in `%s', line %d: expected '=', got '%c'", 425 filename, line_no, **str); 426 error_count++; 427 return NULL; 428 } 429 ++(*str); 430 eat_spaces(str); 431 p->value = parse_int(str); 432 p->next = list; 433 list = p; 434 ++entries; 435 436 // Skip comma 437 eat_spaces(str); 438 if (**str == ',') { 439 (*str)++; 440 eat_spaces(str); 441 } 442 } 443 444 info->u.enum_info.entries = entries; 445 info->u.enum_info.keys = 446 (char **) malloc(entries * sizeof(char *)); 447 info->u.enum_info.values = 448 (int *) malloc(entries * sizeof(int)); 449 for (ii = 0, p = NULL; list; ++ii, list = list->next) { 450 if (p) 451 free(p); 452 info->u.enum_info.keys[ii] = list->key; 453 info->u.enum_info.values[ii] = list->value; 454 p = list; 455 } 456 if (p) 457 free(p); 458 459 return info; 460 } 461 462 case ARGTYPE_STRING: 463 if (!isdigit(**str) && **str != '[') { 464 /* Oops, was just a simple string after all */ 465 free(info); 466 return simple; 467 } 468 469 info->type = ARGTYPE_STRING_N; 470 471 /* Backwards compatibility for string0, string1, ... */ 472 if (isdigit(**str)) { 473 info->u.string_n_info.length = parse_argnum(str); 474 return info; 475 } 476 477 (*str)++; // Skip past opening [ 478 eat_spaces(str); 479 info->u.string_n_info.length = parse_argnum(str); 480 eat_spaces(str); 481 (*str)++; // Skip past closing ] 482 return info; 483 484 // Syntax: struct ( type,type,type,... ) 485 case ARGTYPE_STRUCT:{ 486 if (parse_struct(str, info) < 0) { 487 free(info); 488 output_line(0, "Parse error in `%s', line %d", 489 filename, line_no); 490 error_count++; 491 return NULL; 492 } 493 return info; 494 } 495 496 default: 497 if (info->type == ARGTYPE_UNKNOWN) { 498 output_line(0, "Syntax error in `%s', line %d: " 499 "Unknown type encountered", 500 filename, line_no); 501 free(info); 502 error_count++; 503 return NULL; 504 } else { 505 return info; 506 } 507 } 508} 509 510static struct arg_type_info * 511parse_type(char **str) { 512 struct arg_type_info *info = parse_nonpointer_type(str); 513 while (**str == '*') { 514 struct arg_type_info *outer = malloc(sizeof(*info)); 515 outer->type = ARGTYPE_POINTER; 516 outer->u.ptr_info.info = info; 517 (*str)++; 518 info = outer; 519 } 520 return info; 521} 522 523static Function * 524process_line(char *buf) { 525 Function fun; 526 Function *fun_p; 527 char *str = buf; 528 char *tmp; 529 int i; 530 int float_num = 0; 531 532 line_no++; 533 debug(3, "Reading line %d of `%s'", line_no, filename); 534 eat_spaces(&str); 535 fun.return_info = parse_type(&str); 536 if (fun.return_info == NULL) 537 return NULL; 538 if (fun.return_info->type == ARGTYPE_UNKNOWN) { 539 debug(3, " Skipping line %d", line_no); 540 return NULL; 541 } 542 debug(4, " return_type = %d", fun.return_info->type); 543 eat_spaces(&str); 544 tmp = start_of_arg_sig(str); 545 if (!tmp) { 546 output_line(0, "Syntax error in `%s', line %d", filename, 547 line_no); 548 error_count++; 549 return NULL; 550 } 551 *tmp = '\0'; 552 fun.name = strdup(str); 553 str = tmp + 1; 554 debug(3, " name = %s", fun.name); 555 fun.params_right = 0; 556 for (i = 0; i < MAX_ARGS; i++) { 557 eat_spaces(&str); 558 if (*str == ')') { 559 break; 560 } 561 if (str[0] == '+') { 562 fun.params_right++; 563 str++; 564 } else if (fun.params_right) { 565 fun.params_right++; 566 } 567 fun.arg_info[i] = parse_type(&str); 568 if (fun.arg_info[i] == NULL) { 569 output_line(0, "Syntax error in `%s', line %d" 570 ": unknown argument type", 571 filename, line_no); 572 error_count++; 573 return NULL; 574 } 575 if (fun.arg_info[i]->type == ARGTYPE_FLOAT) 576 fun.arg_info[i]->u.float_info.float_index = float_num++; 577 else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE) 578 fun.arg_info[i]->u.double_info.float_index = float_num++; 579 eat_spaces(&str); 580 if (*str == ',') { 581 str++; 582 continue; 583 } else if (*str == ')') { 584 continue; 585 } else { 586 if (str[strlen(str) - 1] == '\n') 587 str[strlen(str) - 1] = '\0'; 588 output_line(0, "Syntax error in `%s', line %d at ...\"%s\"", 589 filename, line_no, str); 590 error_count++; 591 return NULL; 592 } 593 } 594 fun.num_params = i; 595 fun_p = malloc(sizeof(Function)); 596 if (!fun_p) { 597 perror("ltrace: malloc"); 598 exit(1); 599 } 600 memcpy(fun_p, &fun, sizeof(Function)); 601 return fun_p; 602} 603 604void 605read_config_file(char *file) { 606 FILE *stream; 607 char buf[1024]; 608 609 filename = file; 610 stream = fopen(filename, "r"); 611 if (!stream) { 612 return; 613 } 614 615 debug(1, "Reading config file `%s'...", filename); 616 617 line_no = 0; 618 while (fgets(buf, 1024, stream)) { 619 Function *tmp; 620 621 error_count = 0; 622 tmp = process_line(buf); 623 624 if (tmp) { 625 debug(2, "New function: `%s'", tmp->name); 626 tmp->next = list_of_functions; 627 list_of_functions = tmp; 628 } 629 } 630 fclose(stream); 631} 632