Mercurial > code
comparison inifile.c @ 72:262c053206f6
Cleaned up lot of things in inifile.c:
o now possible to have [section] option = "val" on the same line
o _register _switch function are used with a function pointer
o line may be infinite size
o user does not need to specify a line size
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 13 Nov 2011 15:43:30 +0100 |
parents | 6981419d48c1 |
children | 653d583376c9 |
comparison
equal
deleted
inserted
replaced
71:6981419d48c1 | 72:262c053206f6 |
---|---|
28 | 28 |
29 /* -------------------------------------------------------- | 29 /* -------------------------------------------------------- |
30 * structure definitions | 30 * structure definitions |
31 * -------------------------------------------------------- */ | 31 * -------------------------------------------------------- */ |
32 | 32 |
33 /* | |
34 * We use STAILQ as stack, it means if the user supplied once | |
35 * or more times the same options the last one is taken. | |
36 */ | |
37 | |
38 struct option { | 33 struct option { |
39 char *key; /* option name */ | 34 char *key; /* option name */ |
40 char *value; /* option value */ | 35 char *value; /* option value */ |
41 STAILQ_ENTRY(option) next; | 36 STAILQ_ENTRY(option) next; |
42 }; | 37 }; |
43 | 38 |
44 typedef STAILQ_HEAD(, option) OptionList; | 39 typedef STAILQ_HEAD(, option) OptionList; |
45 | 40 |
46 struct section { | 41 struct section { |
47 char *key; /* section key */ | 42 char *key; /* section key */ |
48 int length; /* number of options */ | |
49 OptionList options; /* list of options */ | 43 OptionList options; /* list of options */ |
50 STAILQ_ENTRY(section) next; | 44 STAILQ_ENTRY(section) next; |
51 }; | 45 }; |
52 | 46 |
53 typedef STAILQ_HEAD(, section) SectionList; | 47 typedef STAILQ_HEAD(, section) SectionList; |
54 | 48 |
55 struct inifile { | 49 struct inifile { |
56 const char *path; /* file path */ | 50 const char *path; /* file path */ |
57 SectionList sections; /* list of sections */ | 51 SectionList sections; /* list of sections */ |
58 struct section *current; /* current section */ | 52 struct section *current; /* current section */ |
53 int flags; /* inifile_options */ | |
54 int ignore; /* must ignore (no redefine) */ | |
55 | |
56 /* Current file line properties */ | |
57 char *line; /* line buffer */ | |
59 int lineno; /* number of line */ | 58 int lineno; /* number of line */ |
60 int linesize; /* size of line */ | 59 int linesize; /* initial line size */ |
61 int options; /* inifile_options */ | |
62 int ignore; /* must ignore (no redefine) */ | |
63 char *line; /* line buffer */ | |
64 | 60 |
65 /* For querying functions */ | 61 /* For querying functions */ |
66 struct section *q_section; /* current section query */ | 62 struct section *q_section; /* current section query */ |
67 }; | 63 }; |
68 | 64 |
71 /* -------------------------------------------------------- | 67 /* -------------------------------------------------------- |
72 * prototypes | 68 * prototypes |
73 * -------------------------------------------------------- */ | 69 * -------------------------------------------------------- */ |
74 | 70 |
75 static void *inifile_read(struct inifile *, FILE *); | 71 static void *inifile_read(struct inifile *, FILE *); |
76 static void inifile_switch(struct inifile *, char *); | 72 static int inifile_getline(struct inifile *, FILE *); |
77 static int inifile_register(struct inifile *, char *); | 73 static void *inifile_readline(struct inifile *); |
74 static int inifile_switch(struct inifile *, char **); | |
75 static int inifile_register(struct inifile *, char **); | |
78 static void *inifile_fatal(struct inifile *, FILE *, const char *, ...); | 76 static void *inifile_fatal(struct inifile *, FILE *, const char *, ...); |
79 static char *xstrndup(const char *, size_t); | 77 static char *xstrndup(const char *, size_t); |
80 | 78 static struct option *xoptiondup(const struct option *option); |
81 #define I_VERBOSE(file) ((file)->options & INIFILE_VERBOSE) | 79 |
82 #define I_NOREDEFINE(file) ((file)->options & INIFILE_NOREDEFINE) | 80 #define I_VERBOSE(file) ((file)->flags & INIFILE_VERBOSE) |
83 | 81 #define I_NOREDEFINE(file) ((file)->flags & INIFILE_NOREDEFINE) |
84 #define SKIP_SPACES(lp) while (isspace(*(lp))) ++(lp) | 82 |
83 #define SKIP_SPACES(lp) while (isspace(*lp) && *lp != '\0') ++lp | |
85 #define NFREE(p) if ((p) != NULL) free((p)) | 84 #define NFREE(p) if ((p) != NULL) free((p)) |
86 #define WARN(file, fmt, ...) \ | 85 #define WARN(file, fmt, ...) \ |
87 if (I_VERBOSE((file))) \ | 86 if (I_VERBOSE((file))) \ |
88 fprintf(stderr, fmt, __VA_ARGS__) | 87 fprintf(stderr, fmt, __VA_ARGS__) |
89 | 88 |
90 /* -------------------------------------------------------- | 89 /* -------------------------------------------------------- |
91 * public functions | 90 * public functions |
92 * -------------------------------------------------------- */ | 91 * -------------------------------------------------------- */ |
93 | 92 |
94 struct inifile * | 93 struct inifile * |
95 inifile_load(const char *path, int options, int linesize) | 94 inifile_load(const char *path, int flags) |
96 { | 95 { |
97 FILE *fp; | 96 FILE *fp; |
98 struct inifile *file; | 97 struct inifile *file; |
99 | 98 |
100 if ((file = calloc(1, sizeof (struct inifile))) == NULL) | 99 if ((file = calloc(1, sizeof (struct inifile))) == NULL) |
101 return inifile_fatal(file, fp, "%s", strerror(errno)); | 100 return inifile_fatal(file, fp, "%s", strerror(errno)); |
102 | 101 |
103 if ((fp = fopen(path, "r")) == NULL) | 102 if ((fp = fopen(path, "r")) == NULL) |
104 return inifile_fatal(file, fp, "%s: %s", path, strerror(errno)); | 103 return inifile_fatal(file, fp, "%s: %s", path, strerror(errno)); |
105 | 104 |
106 if ((file->line = calloc(linesize + 1, 1)) == NULL) | 105 if ((file->line = calloc(1024 + 1, 1)) == NULL) |
107 return inifile_fatal(file, fp, "%s", strerror(errno)); | 106 return inifile_fatal(file, fp, "%s", strerror(errno)); |
108 | 107 |
109 STAILQ_INIT(&file->sections); | 108 STAILQ_INIT(&file->sections); |
110 file->path = path; | 109 file->path = path; |
111 file->options = options; | 110 file->flags = flags; |
112 file->linesize = (linesize == 0) ? 1024 : linesize; | 111 file->ignore = 1; |
113 file->lineno = 1; | 112 file->lineno = 1; |
113 file->linesize = 1024; | |
114 | 114 |
115 return inifile_read(file, fp); | 115 return inifile_read(file, fp); |
116 } | 116 } |
117 | 117 |
118 /* | 118 /* |
259 */ | 259 */ |
260 | 260 |
261 static void * | 261 static void * |
262 inifile_read(struct inifile *file, FILE *fp) | 262 inifile_read(struct inifile *file, FILE *fp) |
263 { | 263 { |
264 char *lp; | |
265 int status; | 264 int status; |
266 | 265 |
267 for (; fgets(file->line, file->linesize, fp); ++file->lineno) { | 266 while (inifile_getline(file, fp) == 0) { |
268 /* Move the file char pointer to the next '\n' if truncated */ | 267 if (inifile_readline(file) == NULL) |
269 if ((lp = strchr(file->line, '\n')) != NULL) | 268 return inifile_fatal(file, fp, "%s", strerror(errno)); |
270 *lp = '\0'; | 269 |
271 else { | 270 ++ file->lineno; |
272 int ch; | |
273 while ((ch = fgetc(fp)) != '\n' && ch != EOF); | |
274 } | |
275 | |
276 lp = file->line; | |
277 SKIP_SPACES(lp); | |
278 | |
279 if (*lp == '\0' || *lp == '#') | |
280 continue; | |
281 | |
282 if (*lp == '[') { | |
283 inifile_switch(file, lp); | |
284 | |
285 /* Fail to create a new section? */ | |
286 if (file->current == NULL) | |
287 return inifile_fatal(file, fp, "%s", | |
288 strerror(errno)); | |
289 } else if (!file->ignore) | |
290 if (inifile_register(file, lp) < 0) | |
291 return inifile_fatal(file, fp, "%s", | |
292 strerror(errno)); | |
293 } | 271 } |
294 | 272 |
295 /* Clean up */ | 273 /* Clean up */ |
296 free(file->line); | 274 free(file->line); |
297 fclose(fp); | 275 fclose(fp); |
298 | 276 |
299 return file; | 277 return file; |
300 } | 278 } |
301 | 279 |
302 /* | 280 /* |
303 * Switch to the next section. If option NOREDEFINE is set and | 281 * Read the next line until the next '\n' character is found. Returns 0 |
304 * user wrote two or more times the same section, every options are | 282 * if the system had enough memory or -1 on allocation failure or on |
305 * simply ignore and a warning may be issued if VERBOSE is set. | 283 * end of file. |
306 * | 284 */ |
307 * This function set file->current to NULL on allocation failure. | 285 |
308 */ | 286 static int |
309 | 287 inifile_getline(struct inifile *file, FILE *fp) |
310 static void | 288 { |
311 inifile_switch(struct inifile *file, char *lp) | 289 int ch, pos; |
312 { | 290 |
313 char *endlp; | 291 memset(file->line, 0, file->linesize); |
314 int length; | 292 pos = 0; |
315 struct section *newsection; | 293 |
316 | 294 while ((ch = fgetc(fp)) != '\n') { |
317 /* Is complete? [example] */ | 295 if (feof(fp) || ferror(fp)) |
318 if ((endlp = strchr(lp, ']')) != NULL) { | 296 return -1; |
319 length = endlp - &lp[1]; /* geek */ | 297 |
320 ++ lp; | 298 /* End of buffer, realloc */ |
321 | 299 if (pos == file->linesize) { |
322 if (length == 0) { | 300 file->line = realloc(file->line, file->linesize + 513); |
323 WARN(file, "line %d: 0-length section\n", file->lineno);; | 301 if (file->line == NULL) |
324 return ; | 302 return -1; |
303 | |
304 file->linesize += 512; | |
325 } | 305 } |
326 | 306 |
327 /* | 307 file->line[pos++] = ch; |
328 * Do not redefine? If yes then the next options must | 308 } |
329 * be ignored. | 309 |
330 */ | 310 file->line[pos] = '\0'; |
331 if (file->current != NULL && I_NOREDEFINE(file) && | 311 |
332 strncmp(file->current->key, lp, length) == 0) { | 312 return 0; |
333 WARN(file, "line %d: warning redefining '%s'\n", | 313 } |
334 file->lineno, file->current->key); | 314 |
335 file->ignore = 1; | 315 /* |
336 } else { | 316 * Read the next line that have been successfully read. Returns NULL |
337 newsection = malloc(sizeof (struct section)); | 317 * on allocation failure (instanciation of new section or option) |
338 | 318 * or the file on success. |
339 /* Add a new section */ | 319 */ |
340 if (newsection != NULL) { | 320 |
341 newsection->key = xstrndup(lp, length); | 321 static void * |
342 | 322 inifile_readline(struct inifile *file) |
343 if (newsection->key == NULL) | 323 { |
344 free(newsection); | 324 char *lp; |
345 else { | 325 int (*handler)(struct inifile *, char **); |
346 STAILQ_INIT(&newsection->options); | 326 |
347 newsection->length = 0; | 327 lp = file->line; |
348 file->current = newsection; | 328 SKIP_SPACES(lp); |
349 | 329 |
350 STAILQ_INSERT_TAIL(&file->sections, | 330 /* Ignore empty line or comment */ |
351 newsection, next); | 331 if (*lp == '\0' || *lp == '#') |
352 } | 332 return file; |
353 } | 333 |
354 } | 334 while (*lp != '\0') { |
335 SKIP_SPACES(lp); | |
336 | |
337 /* Skip comments again and empty lines */ | |
338 if (*lp == '\0' || *lp == '#' || *lp == ';') | |
339 return file; | |
340 | |
341 #ifdef DEBUG | |
342 printf("-- line[%d] == [%s]\n", file->lineno, file->line); | |
343 #endif | |
344 | |
345 if (*lp == '[') | |
346 handler = &inifile_switch; | |
347 else if (!file->ignore) | |
348 handler = &inifile_register; | |
349 | |
350 /* Success or not? */ | |
351 if (handler(file, &lp) < 0) | |
352 return NULL; | |
353 | |
354 SKIP_SPACES(lp); | |
355 } | |
356 return file; | |
357 } | |
358 | |
359 /* On failure, seek next space */ | |
360 #define SEEK_NEXT(lp) \ | |
361 for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) \ | |
362 continue; | |
363 | |
364 /* On other failure seek until the next line '\0' */ | |
365 #define SEEK_NL(lp) \ | |
366 for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) | |
367 | |
368 /* | |
369 * Parse the new section (function called when '[' is found). Returns | |
370 * 0 or parse error (to avoid inifile_readling from stopping) or -1 on | |
371 * allocation failure. | |
372 */ | |
373 | |
374 static int | |
375 inifile_switch(struct inifile *file, char **lp) | |
376 { | |
377 char *endSection; | |
378 | |
379 /* Section not parsed, ignore next option to prevent breakage */ | |
380 if ((endSection = strchr(*lp, ']')) == NULL) { | |
381 WARN(file, "line %d: parse error\n", file->lineno); | |
382 SEEK_NEXT(lp); | |
383 return (file->ignore = 1) - 1; /* single line rocks */ | |
384 } | |
385 | |
386 /* Redefinition of previous and not allowed */ | |
387 ++(*lp); | |
388 if (file->current != NULL && I_NOREDEFINE(file) && | |
389 (strncmp(file->current->key, *lp, endSection - *lp)) == 0) { | |
390 WARN(file, "line %d: redefining %s\n", file->lineno, file->current->key); | |
391 SEEK_NEXT(lp); | |
392 return (file->ignore = 1) - 1; | |
393 } | |
394 | |
395 if ((file->current = malloc(sizeof (struct section))) == NULL) | |
396 return -1; | |
397 | |
398 if ((file->current->key = xstrndup(*lp, endSection - *lp)) == NULL) { | |
399 free(file->current); | |
400 return -1; | |
401 } | |
402 | |
403 *lp = endSection + 1; | |
404 | |
405 #ifdef DEBUG | |
406 printf("-- current section is now [%s]\n", file->current->key); | |
407 #endif | |
408 | |
409 /* Finally add the new section to the config */ | |
410 STAILQ_INIT(&file->current->options); | |
411 STAILQ_INSERT_TAIL(&file->sections, file->current, next); | |
412 | |
413 return (file->ignore = 0); | |
414 } | |
415 | |
416 /* | |
417 * Store the new option that have been parsed. Returns 0 on success or | |
418 * on parse error (to avoid inifile_readline from stopping) and -1 on | |
419 * allocation failure. | |
420 */ | |
421 | |
422 static int | |
423 inifile_register(struct inifile *file, char **lp) | |
424 { | |
425 char *assignKey, *endKey, *endValue, token = '\0'; | |
426 int length = 0; | |
427 struct option *option, tmp = { NULL }; | |
428 | |
429 /* No '=' found */ | |
430 if ((assignKey = strchr(*lp, '=')) == NULL) { | |
431 WARN(file, "line %d: missing '='\n", file->lineno); | |
432 SEEK_NEXT(lp); | |
433 return 0; | |
434 } | |
435 | |
436 /* Find end of option */ | |
437 for (endKey = *lp; !isspace(*endKey) && *endKey != '\0'; ++endKey) | |
438 continue; | |
439 | |
440 /* Do not register empty option name */ | |
441 if (endKey - *lp == 0) { | |
442 WARN(file, "line %d: 0-length option\n", file->lineno); | |
443 SEEK_NL(lp); | |
444 return 0; | |
445 } | |
446 | |
447 if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL) | |
448 return -1; | |
449 | |
450 endValue = &assignKey[1]; | |
451 SKIP_SPACES(endValue); | |
452 | |
453 /* Find end of option value */ | |
454 token = *endValue; | |
455 if (token == '\'' || token == '"') { | |
456 for (*lp = ++endValue; *endValue != token && *endValue != '\0'; ++endValue) | |
457 continue; | |
458 | |
459 length = endValue - *lp; | |
460 | |
461 /* Correctly closed */ | |
462 if (token != '\0' && *endValue == token) | |
463 ++ endValue; | |
464 else | |
465 WARN(file, "line %d: missing '%c'\n", file->lineno, token); | |
355 } else { | 466 } else { |
356 /* | 467 for (*lp = endValue; !isspace(*endValue) && *endValue != '\0'; ++endValue) |
357 * The section has not been parsed, next options must | 468 continue; |
358 * be ignored until a new section is found. | 469 |
359 */ | 470 length = endValue - *lp; |
360 file->ignore = 1; | 471 } |
361 } | 472 |
362 } | 473 if (length != 0) |
363 | 474 tmp.value = xstrndup(*lp, length); |
364 /* | 475 |
365 * Register a new option in the current section. The value may be emptym then | 476 if ((option = xoptiondup(&tmp)) == NULL) |
366 * the value is set to NULL. If a string is not correctly quoted a warning | |
367 * may be printed if VERBOSE is enabled, if the assigment key is missing | |
368 * a warning may be printed too. | |
369 */ | |
370 | |
371 static int | |
372 inifile_register(struct inifile *file, char *lp) | |
373 { | |
374 char *endlp, *endvalue, tok; | |
375 int length; | |
376 struct option *newoption; | |
377 | |
378 /* Parse until '=' is found to store the key */ | |
379 for (endlp = lp; !isspace(*endlp) && *endlp != '='; ++endlp) | |
380 continue; | |
381 | |
382 for (endvalue = lp; *endvalue != '=' && *endvalue != '\0'; ++ endvalue) | |
383 continue; | |
384 | |
385 /* Missing = */ | |
386 if (*endvalue != '=') { | |
387 WARN(file, "line %d: missing '='\n", file->lineno); | |
388 return 0; | |
389 } | |
390 | |
391 /* Empty option key */ | |
392 if (endlp - lp == 0) { | |
393 WARN(file, "line %d: missing keyword before '='\n", file->lineno); | |
394 return 0; | |
395 } | |
396 | |
397 if ((newoption = malloc(sizeof (struct option))) == NULL) | |
398 return -1; | 477 return -1; |
399 | 478 |
400 if ((newoption->key = xstrndup(lp, endlp - lp)) == NULL) { | 479 #ifdef DEBUG |
401 free(newoption); | 480 printf("-- next option [%s] is set to [%s]\n", tmp.key, tmp.value); |
402 return -1; | 481 #endif |
403 } | 482 |
404 | 483 /* Finally add the new option to the current section */ |
405 /* Parse until "" '' or space is found. */ | 484 *lp = endValue; |
406 ++ endvalue; | 485 STAILQ_INSERT_TAIL(&file->current->options, option, next); |
407 SKIP_SPACES(endvalue); | |
408 lp = endvalue; | |
409 | |
410 tok = *endvalue; | |
411 if (tok == '"' || tok == '\'') { | |
412 ++ endvalue; | |
413 while (*endvalue != tok && *endvalue != '\0' && *endvalue != '#') | |
414 ++ endvalue; | |
415 | |
416 /* Print a warning but store what have been parsed */ | |
417 if (*endvalue != tok) | |
418 WARN(file, "line %d: missing '%c'\n", file->lineno, tok); | |
419 | |
420 length = endvalue - lp++ - 1; | |
421 } else { | |
422 while (!isspace(*endvalue) && *endvalue != '\0') | |
423 ++ endvalue; | |
424 | |
425 length = endvalue - lp; | |
426 } | |
427 | |
428 if (length > 0) { | |
429 newoption->value = xstrndup(lp, length); | |
430 if (newoption->value == NULL) { | |
431 free(newoption->key); | |
432 free(newoption); | |
433 return -1; | |
434 } | |
435 } else | |
436 newoption->value = NULL; | |
437 | |
438 /* And finally add the option to the current section */ | |
439 file->current->length ++; | |
440 STAILQ_INSERT_TAIL(&file->current->options, newoption, next); | |
441 | 486 |
442 return 0; | 487 return 0; |
443 } | 488 } |
444 | 489 |
445 static void * | 490 static void * |
500 memcpy(res, src, length); | 545 memcpy(res, src, length); |
501 res[length] = '\0'; | 546 res[length] = '\0'; |
502 | 547 |
503 return res; | 548 return res; |
504 } | 549 } |
550 | |
551 static struct option * | |
552 xoptiondup(const struct option *option) | |
553 { | |
554 struct option *res; | |
555 | |
556 /* | |
557 * Value may be NULL but not option's key. | |
558 */ | |
559 if (option->key == NULL) { | |
560 if (option->value != NULL) | |
561 free(option->value); | |
562 | |
563 return NULL; | |
564 } | |
565 | |
566 if ((res = malloc(sizeof (struct option))) == NULL) | |
567 return NULL; | |
568 | |
569 res->key = option->key; | |
570 res->value = option->value; | |
571 | |
572 return res; | |
573 } |