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