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