Mercurial > code
comparison ini.c @ 79:fd817a7dbf2f
Switched a lot of things in ini
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 15 Nov 2011 20:12:48 +0100 |
parents | inifile.c@4c5f69f5f409 |
children | f42bcb9e7b4a |
comparison
equal
deleted
inserted
replaced
78:ad60e5ebd92c | 79:fd817a7dbf2f |
---|---|
1 /* | |
2 * ini.c -- parse .ini like files | |
3 * | |
4 * Copyright (c) 2011, David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <sys/queue.h> | |
20 #include <stdio.h> | |
21 #include <stdlib.h> | |
22 #include <stdarg.h> | |
23 #include <string.h> | |
24 #include <ctype.h> | |
25 #include <errno.h> | |
26 | |
27 #include "ini.h" | |
28 | |
29 /* -------------------------------------------------------- | |
30 * structure definitions | |
31 * -------------------------------------------------------- */ | |
32 | |
33 struct ini_option { | |
34 char *key; /* option name */ | |
35 char *value; /* option value */ | |
36 STAILQ_ENTRY(ini_option) next; | |
37 }; | |
38 | |
39 struct ini_section { | |
40 char *key; /* section key */ | |
41 STAILQ_HEAD(, ini_option) options; | |
42 STAILQ_ENTRY(ini_section) next; | |
43 }; | |
44 | |
45 struct ini_config { | |
46 const char *path; /* file path */ | |
47 int flags; /* optional flags */ | |
48 int ignore; /* must ignore (no redefine) */ | |
49 | |
50 /* Current section in file */ | |
51 struct ini_section *current; | |
52 | |
53 /* Current file line properties */ | |
54 char *line; /* line buffer */ | |
55 int lineno; /* number of line */ | |
56 int linesize; /* initial line size */ | |
57 | |
58 /* For querying functions */ | |
59 STAILQ_HEAD(, ini_section) sections; | |
60 }; | |
61 | |
62 static char iniError[1024 + 1]; | |
63 | |
64 /* -------------------------------------------------------- | |
65 * prototypes | |
66 * -------------------------------------------------------- */ | |
67 | |
68 static void *ini_read(struct ini_config *, FILE *); | |
69 static int ini_getline(struct ini_config *, FILE *); | |
70 static void *ini_readline(struct ini_config *); | |
71 static int ini_switch(struct ini_config *, char **); | |
72 static int ini_register(struct ini_config *, char **); | |
73 static void *ini_fatal(struct ini_config *, FILE *, const char *, ...); | |
74 static char *xstrndup(const char *, size_t); | |
75 static struct ini_option *xoptiondup(const struct ini_option *option); | |
76 | |
77 #define I_VERBOSE(file) ((file)->flags & INI_VERBOSE) | |
78 #define I_NOREDEFINE(file) ((file)->flags & INI_NOREDEFINE) | |
79 #define I_FAILERROR(file) ((file)->flags & INI_FAILERROR) | |
80 | |
81 #define SKIP_SPACES(lp) while (isspace(*lp) && *lp != '\0') ++lp | |
82 #define WARN(file, fmt, ...) \ | |
83 if (I_VERBOSE((file))) \ | |
84 fprintf(stderr, fmt, __VA_ARGS__) | |
85 | |
86 /* -------------------------------------------------------- | |
87 * public functions | |
88 * -------------------------------------------------------- */ | |
89 | |
90 struct ini_config * | |
91 ini_load(const char *path, int flags) | |
92 { | |
93 FILE *fp; | |
94 struct ini_config *conf; | |
95 | |
96 if ((conf = calloc(1, sizeof (struct ini_config))) == NULL) | |
97 return ini_fatal(conf, fp, "%s", strerror(errno)); | |
98 | |
99 if ((fp = fopen(path, "r")) == NULL) | |
100 return ini_fatal(conf, fp, "%s: %s", path, strerror(errno)); | |
101 | |
102 if ((conf->line = calloc(1024 + 1, 1)) == NULL) | |
103 return ini_fatal(conf, fp, "%s", strerror(errno)); | |
104 | |
105 STAILQ_INIT(&conf->sections); | |
106 conf->path = path; | |
107 conf->flags = flags; | |
108 conf->ignore = 1; | |
109 conf->lineno = 1; | |
110 conf->linesize = 1024; | |
111 | |
112 return ini_read(conf, fp); | |
113 } | |
114 | |
115 /* | |
116 * Returns a NULL terminated list of section names. If parameter | |
117 * number is not NULL, number is set to the total number of | |
118 * section correctly parsed. | |
119 */ | |
120 | |
121 char ** | |
122 ini_get_sections_names(struct ini_config *conf, int *number) | |
123 { | |
124 struct ini_section *s; | |
125 char **list; | |
126 int i; | |
127 | |
128 i = 0; | |
129 STAILQ_FOREACH(s, &conf->sections, next) | |
130 ++ i; | |
131 | |
132 if ((list = calloc(i + 1, sizeof (char *))) == NULL) | |
133 return NULL; | |
134 | |
135 if (number != NULL) | |
136 *number = i; | |
137 | |
138 i = 0; | |
139 STAILQ_FOREACH(s, &conf->sections, next) | |
140 list[i++] = s->key; | |
141 | |
142 return list; | |
143 } | |
144 | |
145 /* | |
146 * Returns a NULL terminated list of struct ini_section that could be used | |
147 * with ini_get_option(). This function makes sense only when the config | |
148 * has multiple definition enabled. | |
149 */ | |
150 | |
151 struct ini_section ** | |
152 ini_get_sections(const struct ini_config *conf, const char *key, int *nb) | |
153 { | |
154 struct ini_section **list; | |
155 struct ini_section *s; | |
156 int i; | |
157 | |
158 /* Do not use this function on simple config */ | |
159 if (I_NOREDEFINE(conf)) { | |
160 errno = EINVAL; | |
161 return NULL; | |
162 } | |
163 | |
164 i = 0; | |
165 STAILQ_FOREACH(s, &conf->sections, next) | |
166 if (strcmp(s->key, key) == 0) | |
167 ++ i; | |
168 | |
169 if ((list = calloc(i + 1, sizeof (struct ini_section *))) == NULL) | |
170 return NULL; | |
171 | |
172 if (nb != NULL) | |
173 *nb = i; | |
174 | |
175 i = 0; | |
176 STAILQ_FOREACH(s, &conf->sections, next) | |
177 if (strcmp(s->key, key) == 0) | |
178 list[i++] = s; | |
179 | |
180 return list; | |
181 } | |
182 | |
183 /* | |
184 * Select the section for further query. This improves performance as it does | |
185 * not need to seek each time the section. It can't be used when multiple | |
186 * definition is enabled because it seek from the beginning, you should use | |
187 * ini_get_sections() and then ini_get_option() for each section. | |
188 */ | |
189 | |
190 struct ini_section * | |
191 ini_select_section(struct ini_config *conf, const char *section) | |
192 { | |
193 struct ini_section *s; | |
194 | |
195 STAILQ_FOREACH(s, &conf->sections, next) | |
196 if (strcmp(s->key, section) == 0) | |
197 return s; | |
198 | |
199 return NULL; | |
200 } | |
201 | |
202 /* | |
203 * Wrapper for a simpler usage of ini_select and ini_option. This | |
204 * does not modify the selector so you can safely continue the | |
205 * call to ini_option independantly. Warning, this function returns the | |
206 * first occurence of the option so it is not advised to use it | |
207 */ | |
208 | |
209 char * | |
210 ini_option_once(struct ini_config *conf, const char *sect, const char *key) | |
211 { | |
212 struct ini_option *o; | |
213 struct ini_section *s; | |
214 | |
215 STAILQ_FOREACH(s, &conf->sections, next) | |
216 if (strcmp(s->key, sect) == 0) | |
217 break; | |
218 | |
219 STAILQ_FOREACH(o, &s->options, next) | |
220 if (strcmp(o->key, key) == 0) | |
221 return o->value; | |
222 | |
223 return NULL; | |
224 } | |
225 | |
226 /* | |
227 * Return a NULL list of all available options in the section. | |
228 */ | |
229 | |
230 char ** | |
231 ini_get_option_names(struct ini_section *section, int *nb) | |
232 { | |
233 char **list; | |
234 struct ini_option *o; | |
235 int i; | |
236 | |
237 i = 0; | |
238 STAILQ_FOREACH(o, §ion->options, next) | |
239 ++ i; | |
240 | |
241 if ((list = calloc(i + 1, sizeof (char *))) == NULL) | |
242 return NULL; | |
243 | |
244 if (nb != NULL) | |
245 *nb = i; | |
246 | |
247 i = 0; | |
248 STAILQ_FOREACH(o, §ion->options, next) | |
249 list[i++] = o->key; | |
250 | |
251 return list; | |
252 } | |
253 | |
254 /* | |
255 * Get the option in the current selected option. Note, you must call | |
256 * ini_select() before using this function. Returns the value of | |
257 * key or NULL if the option does not exists. | |
258 */ | |
259 | |
260 char * | |
261 ini_get_option(struct ini_section *section, const char *key) | |
262 { | |
263 struct ini_option *o; | |
264 | |
265 STAILQ_FOREACH(o, §ion->options, next) | |
266 if (strcmp(o->key, key) == 0) | |
267 return o->value; | |
268 | |
269 return NULL; | |
270 } | |
271 | |
272 /* | |
273 * Return the last error or "No error" if there is not error at all. | |
274 */ | |
275 | |
276 char * | |
277 ini_error(void) | |
278 { | |
279 if (iniError[0] == '\0') | |
280 return "No error"; | |
281 | |
282 return iniError; | |
283 } | |
284 | |
285 void | |
286 ini_free(struct ini_config *conf, int freeSections, int freeOptions) | |
287 { | |
288 struct ini_section *s, *stmp; | |
289 struct ini_option *o, *otmp; | |
290 | |
291 STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) { | |
292 STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) { | |
293 if (freeOptions) { | |
294 free(o->key); | |
295 free(o->value); | |
296 | |
297 STAILQ_REMOVE(&s->options, o, ini_option, next); | |
298 free(o); | |
299 } | |
300 } | |
301 | |
302 if (freeSections) | |
303 free(s->key); | |
304 | |
305 STAILQ_REMOVE(&conf->sections, s, ini_section, next); | |
306 free(s); | |
307 } | |
308 | |
309 free(conf); | |
310 } | |
311 | |
312 /* -------------------------------------------------------- | |
313 * private functions | |
314 * -------------------------------------------------------- */ | |
315 | |
316 /* | |
317 * Read file line per line and try to parse ini like file. Lines | |
318 * starting with # or empty line are ignored. Returns the file | |
319 * on success and NULL on failure. | |
320 */ | |
321 | |
322 static void * | |
323 ini_read(struct ini_config *conf, FILE *fp) | |
324 { | |
325 while (ini_getline(conf, fp) == 0) { | |
326 if (ini_readline(conf) == NULL) { | |
327 free(conf->line); | |
328 ini_free(conf, 1, 1); | |
329 | |
330 fclose(fp); | |
331 return NULL; | |
332 } | |
333 | |
334 ++ conf->lineno; | |
335 } | |
336 | |
337 /* Clean up */ | |
338 free(conf->line); | |
339 fclose(fp); | |
340 | |
341 return conf; | |
342 } | |
343 | |
344 /* | |
345 * Read the next line until the next '\n' character is found. Returns 0 | |
346 * if the system had enough memory or -1 on allocation failure or on | |
347 * end of file. | |
348 */ | |
349 | |
350 static int | |
351 ini_getline(struct ini_config *conf, FILE *fp) | |
352 { | |
353 int ch, pos; | |
354 | |
355 memset(conf->line, 0, conf->linesize); | |
356 pos = 0; | |
357 | |
358 while ((ch = fgetc(fp)) != '\n') { | |
359 if (feof(fp) || ferror(fp)) | |
360 return -1; | |
361 | |
362 /* End of buffer, realloc */ | |
363 if (pos == conf->linesize) { | |
364 conf->line = realloc(conf->line, conf->linesize + 513); | |
365 if (conf->line == NULL) | |
366 return -1; | |
367 | |
368 conf->linesize += 512; | |
369 } | |
370 | |
371 conf->line[pos++] = ch; | |
372 } | |
373 | |
374 conf->line[pos] = '\0'; | |
375 | |
376 return 0; | |
377 } | |
378 | |
379 /* | |
380 * Read the next line that have been successfully read. Returns NULL | |
381 * on allocation failure (instanciation of new section or option) | |
382 * or the file on success. | |
383 */ | |
384 | |
385 static void * | |
386 ini_readline(struct ini_config *conf) | |
387 { | |
388 char *lp; | |
389 int (*handler)(struct ini_config *, char **); | |
390 | |
391 lp = conf->line; | |
392 SKIP_SPACES(lp); | |
393 | |
394 /* Ignore empty line or comment */ | |
395 if (*lp == '\0' || *lp == '#' || *lp == ';') | |
396 return conf; | |
397 | |
398 while (*lp != '\0') { | |
399 SKIP_SPACES(lp); | |
400 | |
401 /* Skip comments again and empty lines */ | |
402 if (*lp == '\0' || *lp == '#' || *lp == ';') | |
403 return conf; | |
404 | |
405 #ifdef INI_DEBUG | |
406 printf("-- line[%d] == [%s]\n", conf->lineno, conf->line); | |
407 #endif | |
408 | |
409 if (*lp == '[') | |
410 handler = &ini_switch; | |
411 else if (!conf->ignore) | |
412 handler = &ini_register; | |
413 else | |
414 handler = NULL; | |
415 | |
416 /* Success or not? */ | |
417 if (handler != NULL && handler(conf, &lp) < 0) | |
418 return (I_FAILERROR(conf)) ? NULL : conf; | |
419 | |
420 SKIP_SPACES(lp); | |
421 } | |
422 | |
423 return conf; | |
424 } | |
425 | |
426 /* On failure, seek next space */ | |
427 #define SEEK_NEXT(lp) \ | |
428 for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) \ | |
429 continue; | |
430 | |
431 /* On other failure seek until the next line '\0' */ | |
432 #define SEEK_NL(lp) \ | |
433 for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) | |
434 | |
435 /* | |
436 * Parse the new section (function called when '[' is found). Returns | |
437 * 0 or parse error (to avoid ini_readling from stopping) or -1 on | |
438 * allocation failure. | |
439 */ | |
440 | |
441 static int | |
442 ini_switch(struct ini_config *conf, char **lp) | |
443 { | |
444 char *endSection; | |
445 | |
446 /* Section not parsed, ignore next option to prevent breakage */ | |
447 if ((endSection = strchr(*lp, ']')) == NULL) { | |
448 WARN(conf, "line %d: parse error\n", conf->lineno); | |
449 SEEK_NEXT(lp); | |
450 return (conf->ignore = 1) - 2; /* single line rocks */ | |
451 } | |
452 | |
453 /* Redefinition of previous and not allowed */ | |
454 ++(*lp); | |
455 if (conf->current != NULL && I_NOREDEFINE(conf) && | |
456 (strncmp(conf->current->key, *lp, endSection - *lp)) == 0) { | |
457 WARN(conf, "line %d: redefining %s\n", conf->lineno, conf->current->key); | |
458 SEEK_NEXT(lp); | |
459 return (conf->ignore = 1) - 2; | |
460 } | |
461 | |
462 if ((conf->current = malloc(sizeof (struct ini_section))) == NULL) | |
463 return -1; | |
464 | |
465 if ((conf->current->key = xstrndup(*lp, endSection - *lp)) == NULL) { | |
466 free(conf->current); | |
467 return -1; | |
468 } | |
469 | |
470 *lp = endSection + 1; | |
471 | |
472 #ifdef INI_DEBUG | |
473 printf("-- current section is now [%s]\n", conf->current->key); | |
474 #endif | |
475 | |
476 /* Finally add the new section to the config */ | |
477 STAILQ_INIT(&conf->current->options); | |
478 STAILQ_INSERT_TAIL(&conf->sections, conf->current, next); | |
479 | |
480 return (conf->ignore = 0); | |
481 } | |
482 | |
483 /* | |
484 * Store the new option that have been parsed. Returns 0 on success or | |
485 * on parse error (to avoid ini_readline from stopping) and -1 on | |
486 * allocation failure. | |
487 */ | |
488 | |
489 static int | |
490 ini_register(struct ini_config *conf, char **lp) | |
491 { | |
492 char *assignKey, *endKey, *endValue, token = '\0'; | |
493 int length = 0; | |
494 struct ini_option *option, tmp; | |
495 | |
496 /* No '=' found */ | |
497 if ((assignKey = strchr(*lp, '=')) == NULL) { | |
498 WARN(conf, "line %d: missing '='\n", conf->lineno); | |
499 SEEK_NEXT(lp); | |
500 return -1; | |
501 } | |
502 | |
503 /* Find end of option */ | |
504 endKey = *lp; | |
505 for (; !isspace(*endKey) && *endKey != '\0' && *endKey != '='; ++endKey) | |
506 continue; | |
507 | |
508 /* Do not register empty option name */ | |
509 if (endKey - *lp <= 0) { | |
510 WARN(conf, "line %d: 0-length option\n", conf->lineno); | |
511 SEEK_NL(lp); | |
512 return -1; | |
513 } | |
514 | |
515 memset(&tmp, 0, sizeof (struct ini_option)); | |
516 if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL) | |
517 return -1; | |
518 | |
519 endValue = &assignKey[1]; | |
520 SKIP_SPACES(endValue); | |
521 | |
522 /* Find end of option value */ | |
523 token = *endValue; | |
524 if (token == '\'' || token == '"') { | |
525 for (*lp = ++endValue; *endValue != token && *endValue != '\0'; ++endValue) | |
526 continue; | |
527 | |
528 length = endValue - *lp; | |
529 | |
530 /* Correctly closed */ | |
531 if (token != '\0' && *endValue == token) | |
532 ++ endValue; | |
533 else | |
534 WARN(conf, "line %d: missing '%c'\n", conf->lineno, token); | |
535 } else { | |
536 for (*lp = endValue; !isspace(*endValue) && *endValue != '\0'; ++endValue) | |
537 continue; | |
538 | |
539 length = endValue - *lp; | |
540 } | |
541 | |
542 if (length != 0) | |
543 tmp.value = xstrndup(*lp, length); | |
544 | |
545 if ((option = xoptiondup(&tmp)) == NULL) | |
546 return -1; | |
547 | |
548 #ifdef INI_DEBUG | |
549 printf("-- next option [%s] is set to [%s]\n", tmp.key, tmp.value); | |
550 #endif | |
551 | |
552 /* Finally add the new option to the current section */ | |
553 *lp = endValue; | |
554 STAILQ_INSERT_TAIL(&conf->current->options, option, next); | |
555 | |
556 return 0; | |
557 } | |
558 | |
559 static void * | |
560 ini_fatal(struct ini_config *conf, FILE *fp, const char *fmt, ...) | |
561 { | |
562 va_list ap; | |
563 | |
564 if (conf != NULL) { | |
565 struct ini_section *s, *stmp; | |
566 struct ini_option *o, *otmp; | |
567 | |
568 if (conf->line != NULL) | |
569 free(conf->line); | |
570 | |
571 STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) { | |
572 STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) { | |
573 free(o->key); | |
574 free(o->value); | |
575 | |
576 STAILQ_REMOVE(&s->options, o, ini_option, next); | |
577 | |
578 free(o); | |
579 } | |
580 | |
581 free(s->key); | |
582 free(s); | |
583 } | |
584 | |
585 } | |
586 | |
587 if (fp != NULL) | |
588 fclose(fp); | |
589 | |
590 va_start(ap, fmt); | |
591 vsnprintf(iniError, 1024, fmt, ap); | |
592 va_end(ap); | |
593 | |
594 /* Directly print error if VERBOSE is enabled */ | |
595 WARN(conf, "%s\n", iniError); | |
596 | |
597 free(conf); | |
598 | |
599 return NULL; | |
600 } | |
601 | |
602 static char * | |
603 xstrndup(const char *src, size_t max) | |
604 { | |
605 char *res; | |
606 size_t length; | |
607 | |
608 for (length = 0; length < max && src[length] != '\0'; ++length) | |
609 continue; | |
610 | |
611 if ((res = malloc(length + 1)) == NULL) | |
612 return NULL; | |
613 | |
614 memcpy(res, src, length); | |
615 res[length] = '\0'; | |
616 | |
617 return res; | |
618 } | |
619 | |
620 static struct ini_option * | |
621 xoptiondup(const struct ini_option *option) | |
622 { | |
623 struct ini_option *res; | |
624 | |
625 /* | |
626 * Value may be NULL but not option's key. | |
627 */ | |
628 if (option->key == NULL) { | |
629 if (option->value != NULL) | |
630 free(option->value); | |
631 | |
632 return NULL; | |
633 } | |
634 | |
635 if ((res = malloc(sizeof (struct ini_option))) == NULL) | |
636 return NULL; | |
637 | |
638 res->key = option->key; | |
639 res->value = option->value; | |
640 | |
641 return res; | |
642 } |