read_config_file.c revision 6a3e24dc1709530e59dd6ae3e91ced1c221fe1b9
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 18struct function *list_of_functions = NULL; 19 20static struct list_of_pt_t { 21 char *name; 22 enum arg_type pt; 23} list_of_pt[] = { 24 { 25 "void", ARGTYPE_VOID}, { 26 "int", ARGTYPE_INT}, { 27 "uint", ARGTYPE_UINT}, { 28 "long", ARGTYPE_LONG}, { 29 "ulong", ARGTYPE_ULONG}, { 30 "octal", ARGTYPE_OCTAL}, { 31 "char", ARGTYPE_CHAR}, { 32 "addr", ARGTYPE_ADDR}, { 33 "file", ARGTYPE_FILE}, { 34 "format", ARGTYPE_FORMAT}, { 35 "string", ARGTYPE_STRING}, { 36 "enum", ARGTYPE_ENUM}, { 37 "ignore", ARGTYPE_IGNORE}, { 38 NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */ 39}; 40 41static arg_type_info arg_type_singletons[] = { 42 { ARGTYPE_VOID }, 43 { ARGTYPE_INT }, 44 { ARGTYPE_UINT }, 45 { ARGTYPE_LONG }, 46 { ARGTYPE_ULONG }, 47 { ARGTYPE_OCTAL }, 48 { ARGTYPE_CHAR }, 49 { ARGTYPE_ADDR }, 50 { ARGTYPE_FILE }, 51 { ARGTYPE_FORMAT }, 52 { ARGTYPE_STRING }, 53 { ARGTYPE_STRING_N }, 54 { ARGTYPE_ENUM }, 55 { ARGTYPE_IGNORE }, 56 { ARGTYPE_POINTER }, 57 { ARGTYPE_UNKNOWN } 58}; 59 60arg_type_info *lookup_singleton(enum arg_type at) 61{ 62 if (at >= 0 && at <= ARGTYPE_COUNT) 63 return &arg_type_singletons[at]; 64 else 65 return &arg_type_singletons[ARGTYPE_COUNT]; /* UNKNOWN */ 66} 67 68static arg_type_info *str2type(char **str) 69{ 70 struct list_of_pt_t *tmp = &list_of_pt[0]; 71 72 while (tmp->name) { 73 if (!strncmp(*str, tmp->name, strlen(tmp->name)) 74 && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) { 75 *str += strlen(tmp->name); 76 return lookup_singleton(tmp->pt); 77 } 78 tmp++; 79 } 80 return lookup_singleton(ARGTYPE_UNKNOWN); 81} 82 83static void eat_spaces(char **str) 84{ 85 while (**str == ' ') { 86 (*str)++; 87 } 88} 89 90static char *xstrndup(char *str, size_t len) 91{ 92 char *ret = (char *) malloc(len + 1); 93 strncpy(ret, str, len); 94 ret[len] = 0; 95 return ret; 96} 97 98static char *parse_ident(char **str) 99{ 100 char *ident = *str; 101 102 if (!isalnum(**str) && **str != '_') { 103 output_line(0, "Syntax error in `%s', line %d: Bad identifier", 104 filename, line_no); 105 error_count++; 106 return NULL; 107 } 108 109 while (**str && (isalnum(**str) || **str == '_')) { 110 ++(*str); 111 } 112 113 return xstrndup(ident, *str - ident); 114} 115 116/* 117 Returns position in string at the left parenthesis which starts the 118 function's argument signature. Returns NULL on error. 119*/ 120static char *start_of_arg_sig(char *str) 121{ 122 char *pos; 123 int stacked = 0; 124 125 if (!strlen(str)) 126 return NULL; 127 128 pos = &str[strlen(str)]; 129 do { 130 pos--; 131 if (pos < str) 132 return NULL; 133 while ((pos > str) && (*pos != ')') && (*pos != '(')) 134 pos--; 135 136 if (*pos == ')') 137 stacked++; 138 else if (*pos == '(') 139 stacked--; 140 else 141 return NULL; 142 143 } while (stacked > 0); 144 145 return (stacked == 0) ? pos : NULL; 146} 147 148/* 149 Decide whether a type needs any additional parameters. 150 For now, we do not parse any nontrivial argument types. 151*/ 152static int simple_type(enum arg_type at) 153{ 154 switch (at) { 155 case ARGTYPE_STRING: 156 case ARGTYPE_STRING_N: 157 case ARGTYPE_ENUM: 158 return 0; 159 160 default: 161 return 1; 162 } 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", 171 filename, line_no); 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, "retval", 6) == 0) { 202 (*str) += 6; 203 return 0; 204 } 205 206 n = parse_int(str); 207 208 return n * multiplier; 209} 210 211static arg_type_info *parse_nonpointer_type(char **str) 212{ 213 arg_type_info *simple; 214 arg_type_info *info; 215 216 simple = str2type(str); 217 if (simple->type == ARGTYPE_UNKNOWN) { 218 return simple; // UNKNOWN 219 } 220 221 if (simple_type(simple->type) && simple->type != ARGTYPE_STRING) 222 return simple; 223 224 info = malloc(sizeof(*info)); 225 info->type = simple->type; 226 227 /* Code to parse parameterized types will go into the following 228 switch statement. */ 229 230 switch (info->type) { 231 232 // Syntax: enum ( keyname=value,keyname=value,... ) 233 case ARGTYPE_ENUM:{ 234 struct enum_opt { 235 char *key; 236 int value; 237 struct enum_opt *next; 238 }; 239 struct enum_opt *list = NULL; 240 struct enum_opt *p; 241 int entries = 0; 242 int ii; 243 244 eat_spaces(str); 245 (*str)++; // Get past open paren 246 eat_spaces(str); 247 248 while (**str && **str != ')') { 249 p = (struct enum_opt *) malloc(sizeof(*p)); 250 eat_spaces(str); 251 p->key = parse_ident(str); 252 if (error_count) { 253 free(p); 254 return NULL; 255 } 256 eat_spaces(str); 257 if (**str != '=') { 258 free(p->key); 259 free(p); 260 output_line(0, 261 "Syntax error in `%s', line %d: expected '=', got '%c'", 262 filename, line_no, **str); 263 error_count++; 264 return NULL; 265 } 266 ++(*str); 267 eat_spaces(str); 268 p->value = parse_int(str); 269 p->next = list; 270 list = p; 271 ++entries; 272 273 // Skip comma 274 eat_spaces(str); 275 if (**str == ',') { 276 (*str)++; 277 eat_spaces(str); 278 } 279 } 280 281 info->u.enum_info.entries = entries; 282 info->u.enum_info.keys = 283 (char **) malloc(entries * sizeof(char *)); 284 info->u.enum_info.values = 285 (int *) malloc(entries * sizeof(int)); 286 for (ii = 0, p = NULL; list; ++ii, list = list->next) { 287 if (p) 288 free(p); 289 info->u.enum_info.keys[ii] = list->key; 290 info->u.enum_info.values[ii] = list->value; 291 p = list; 292 } 293 if (p) 294 free(p); 295 296 return info; 297 } 298 299 case ARGTYPE_STRING: 300 if (!isdigit(**str) && **str != '[') { 301 /* Oops, was just a simple string after all */ 302 free(info); 303 return simple; 304 } 305 306 info->type = ARGTYPE_STRING_N; 307 308 /* Backwards compatibility for string0, string1, ... */ 309 if (isdigit(**str)) { 310 info->u.string_n_info.size_spec = -parse_int(str); 311 return info; 312 } 313 314 (*str)++; // Skip past opening [ 315 eat_spaces(str); 316 info->u.string_n_info.size_spec = parse_argnum(str); 317 eat_spaces(str); 318 (*str)++; // Skip past closing ] 319 return info; 320 321 default: 322 output_line(0, "Syntax error in `%s', line %d: Unknown type encountered", 323 filename, line_no); 324 free(info); 325 error_count++; 326 return NULL; 327 } 328} 329 330static arg_type_info *parse_type(char **str) 331{ 332 arg_type_info *info = parse_nonpointer_type(str); 333 while (**str == '*') { 334 arg_type_info *outer = malloc(sizeof(*info)); 335 outer->type = ARGTYPE_POINTER; 336 outer->u.ptr_info.info = info; 337 (*str)++; 338 info = outer; 339 } 340 return info; 341} 342 343static struct function *process_line(char *buf) 344{ 345 struct function fun; 346 struct function *fun_p; 347 char *str = buf; 348 char *tmp; 349 int i; 350 351 line_no++; 352 debug(3, "Reading line %d of `%s'", line_no, filename); 353 eat_spaces(&str); 354 fun.return_info = parse_type(&str); 355 if (fun.return_info == NULL) 356 return NULL; 357 if (fun.return_info->type == ARGTYPE_UNKNOWN) { 358 debug(3, " Skipping line %d", line_no); 359 return NULL; 360 } 361 debug(4, " return_type = %d", fun.return_info->type); 362 eat_spaces(&str); 363 tmp = start_of_arg_sig(str); 364 if (!tmp) { 365 output_line(0, "Syntax error in `%s', line %d", filename, 366 line_no); 367 error_count++; 368 return NULL; 369 } 370 *tmp = '\0'; 371 fun.name = strdup(str); 372 str = tmp + 1; 373 debug(3, " name = %s", fun.name); 374 fun.params_right = 0; 375 for (i = 0; i < MAX_ARGS; i++) { 376 eat_spaces(&str); 377 if (*str == ')') { 378 break; 379 } 380 if (str[0] == '+') { 381 fun.params_right++; 382 str++; 383 } else if (fun.params_right) { 384 fun.params_right++; 385 } 386 fun.arg_info[i] = parse_type(&str); 387 if (fun.arg_info[i] == NULL) { 388 output_line(0, "Syntax error in `%s', line %d" 389 ": unknown argument type", 390 filename, line_no); 391 error_count++; 392 return NULL; 393 } 394 eat_spaces(&str); 395 if (*str == ',') { 396 str++; 397 continue; 398 } else if (*str == ')') { 399 continue; 400 } else { 401 if (str[strlen(str) - 1] == '\n') 402 str[strlen(str) - 1] = '\0'; 403 output_line(0, "Syntax error in `%s', line %d at ...\"%s\"", 404 filename, line_no, str); 405 error_count++; 406 return NULL; 407 } 408 } 409 fun.num_params = i; 410 fun_p = malloc(sizeof(struct function)); 411 memcpy(fun_p, &fun, sizeof(struct function)); 412 return fun_p; 413} 414 415void read_config_file(char *file) 416{ 417 FILE *stream; 418 char buf[1024]; 419 420 filename = file; 421 422 debug(1, "Reading config file `%s'...", filename); 423 424 stream = fopen(filename, "r"); 425 if (!stream) { 426 return; 427 } 428 line_no = 0; 429 while (fgets(buf, 1024, stream)) { 430 struct function *tmp; 431 432 error_count = 0; 433 tmp = process_line(buf); 434 435 if (tmp) { 436 debug(2, "New function: `%s'", tmp->name); 437 tmp->next = list_of_functions; 438 list_of_functions = tmp; 439 } 440 } 441 fclose(stream); 442} 443