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 }