Mercurial > code
changeset 96:dcaf2c61c902
Big change in ini, much cleaner and simpler
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 13 Jan 2012 19:05:16 +0100 |
parents | cb5d3d66ea04 |
children | 0321fc3c0972 |
files | ini.c ini.h |
diffstat | 2 files changed, 179 insertions(+), 350 deletions(-) [+] |
line wrap: on
line diff
--- a/ini.c Thu Jan 12 20:12:48 2012 +0100 +++ b/ini.c Fri Jan 13 19:05:16 2012 +0100 @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/queue.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -26,53 +25,20 @@ #include "ini.h" -/* -------------------------------------------------------- */ -/* structure definitions */ -/* -------------------------------------------------------- */ - -struct ini_option { - char *key; /* option name */ - char *value; /* option value */ - STAILQ_ENTRY(ini_option) next; -}; - -struct ini_section { - char *key; /* section key */ - STAILQ_HEAD(, ini_option) options; - STAILQ_ENTRY(ini_section) next; -}; - -struct ini_config { - const char *path; /* file path */ - int flags; /* optional flags */ - int ignore; /* must ignore (no redefine) */ - - /* Current section in file */ - struct ini_section *current; - - /* Current file line properties */ - char *line; /* line buffer */ - int lineno; /* number of line */ - int linesize; /* initial line size */ - - /* For querying functions */ - STAILQ_HEAD(, ini_section) sections; -}; - static char ini_error[1024 + 1]; -/* -------------------------------------------------------- */ -/* prototypes */ -/* -------------------------------------------------------- */ +/* -------------------------------------------------------- + * prototypes + * -------------------------------------------------------- */ -static void *ini_read(struct ini_config *, FILE *); -static int ini_getline(struct ini_config *, FILE *); -static void *ini_readline(struct ini_config *); -static int ini_switch(struct ini_config *, char **); -static int ini_register(struct ini_config *, char **); -static void *ini_fatal(struct ini_config *, FILE *, const char *, ...); -static char *xstrndup(const char *, size_t); -static struct ini_option *xoptiondup(const struct ini_option *option); +static void *ini_read(INI_Config *, FILE *); +static int ini_getline(INI_Config *, FILE *); +static void *ini_readline(INI_Config *); +static int ini_switch(INI_Config *, char **); +static int ini_register(INI_Config *, char **); +static void *ini_fatal(INI_Config *, FILE *, const char *, ...); +static char *xstrndup(const char *, size_t); +static INI_Option *xoptiondup(const INI_Option *option); #define I_VERBOSE(file) ((file)->flags & INI_VERBOSE) #define I_NOREDEFINE(file) ((file)->flags & INI_NOREDEFINE) @@ -87,193 +53,104 @@ * public functions * -------------------------------------------------------- */ -struct ini_config * +INI_Config * ini_load(const char *path, int flags) { + INI_Config *conf; FILE *fp; - struct ini_config *conf; - if ((conf = calloc(1, sizeof (struct ini_config))) == NULL) + if ((conf = calloc(1, sizeof (INI_Config))) == NULL) return ini_fatal(conf, fp, "%s", strerror(errno)); if ((fp = fopen(path, "r")) == NULL) return ini_fatal(conf, fp, "%s: %s", path, strerror(errno)); - if ((conf->line = calloc(1024 + 1, 1)) == NULL) + if ((conf->_line = calloc(1024 + 1, 1)) == NULL) return ini_fatal(conf, fp, "%s", strerror(errno)); - STAILQ_INIT(&conf->sections); conf->path = path; conf->flags = flags; - conf->ignore = 1; - conf->lineno = 1; - conf->linesize = 1024; + conf->_ignore = 1; + conf->_lineno = 1; + conf->_linesize = 1024; return ini_read(conf, fp); } /* - * Returns a NULL terminated list of section names. If parameter - * number is not NULL, number is set to the total number of - * section correctly parsed. - */ - -char ** -ini_get_sections_names(const struct ini_config *conf, int *number) -{ - struct ini_section *s; - char **list; - int i; - - i = 0; - STAILQ_FOREACH(s, &conf->sections, next) - ++ i; - - /* For safety */ - if (number != NULL) - *number = 0; - - if ((list = calloc(i + 1, sizeof (char *))) == NULL) - return NULL; - - if (number != NULL) - *number = i; - - i = 0; - STAILQ_FOREACH(s, &conf->sections, next) - list[i++] = s->key; - - return list; -} - -/* - * Returns a NULL terminated list of struct ini_section that could be used - * with ini_get_option(). This function makes sense only when the config - * has multiple definition enabled. + * Search the section `sectname' in the config structure. If the section + * is not found, this functions returns NULL. */ -struct ini_section ** -ini_get_sections(const struct ini_config *conf, const char *key, int *nb) +INI_Section * +ini_select_section(const INI_Config *conf, const char *sectname) { - struct ini_section **list; - struct ini_section *s; - int i; - - /* Do not use this function on simple config */ - if (I_NOREDEFINE(conf)) { - errno = EINVAL; - return NULL; - } - - i = 0; - STAILQ_FOREACH(s, &conf->sections, next) - if (strcmp(s->key, key) == 0) - ++ i; - - if ((list = calloc(i + 1, sizeof (struct ini_section *))) == NULL) - return NULL; + INI_Section *s; - if (nb != NULL) - *nb = i; - - i = 0; - STAILQ_FOREACH(s, &conf->sections, next) - if (strcmp(s->key, key) == 0) - list[i++] = s; - - return list; -} - -/* - * Select the section for further query. This improves performance as it does - * not need to seek each time the section. It can't be used when multiple - * definition is enabled because it seek from the beginning, you should use - * ini_get_sections() and then ini_get_option() for each section. - */ - -struct ini_section * -ini_select_section(const struct ini_config *conf, const char *section) -{ - struct ini_section *s; - - STAILQ_FOREACH(s, &conf->sections, next) - if (strcmp(s->key, section) == 0) + for (s = conf->sections; s != NULL; s = s->next) + if (strcmp(s->key, sectname) == 0) return s; return NULL; } /* - * Wrapper for a simpler usage of ini_select and ini_option. This - * does not modify the selector so you can safely continue the - * call to ini_option independantly. Warning, this function returns the - * first occurence of the option so it is not advised to use it + * Search the option `optname' in the section structure, argument + * section musn't be NULL. If the option is not found, this function + * returns NULL. */ -char * -ini_get_option_once(const struct ini_config *conf, const char *sect, const char *key) +INI_Option * +ini_select_option(const INI_Section *section, const char *optname) { - struct ini_option *o; - struct ini_section *s; + INI_Option *o; - STAILQ_FOREACH(s, &conf->sections, next) - if (strcmp(s->key, sect) == 0) - break; - - STAILQ_FOREACH(o, &s->options, next) - if (strcmp(o->key, key) == 0) - return o->value; + for (o = section->options; o != NULL; o = o->next) + if (strcmp(o->key, optname) == 0) + return o; return NULL; } /* - * Return a NULL list of all available options in the section. - */ - -char ** -ini_get_option_names(const struct ini_section *section, int *nb) -{ - char **list; - struct ini_option *o; - int i; - - i = 0; - STAILQ_FOREACH(o, §ion->options, next) - ++ i; - - if (nb != NULL) - *nb = i; - - if ((list = calloc(i + 1, sizeof (char *))) == NULL) - return NULL; - - if (nb != NULL) - *nb = i; - - i = 0; - STAILQ_FOREACH(o, §ion->options, next) - list[i++] = o->key; - - return list; -} - -/* - * Get the option in the current selected option. Note, you must call - * ini_select() before using this function. Returns the value of - * key or NULL if the option does not exists. + * Return the last error or "No error" if there is not error at all. */ char * -ini_get_option(const struct ini_section *section, const char *key) +ini_get_error(void) { - struct ini_option *o; + if (ini_error[0] == '\0') + return "No error"; + + return ini_error; +} + +void +ini_free(INI_Config *conf, int freeSections, int freeOptions) +{ + INI_Section *s, *snext; + INI_Option *o, *onext; + + for (s = snext = conf->sections; snext != NULL; s = snext) { + snext = s->next; - STAILQ_FOREACH(o, §ion->options, next) - if (strcmp(o->key, key) == 0) - return o->value; + for (o = onext = s->options; o != NULL; o = onext) { + onext = o->next; - return NULL; + if (freeOptions) { + free(o->key); + free(o->value); + free(o); + } + } + + if (freeSections) + free(s->key); + + free(s); + } + + free(conf); } /* @@ -283,27 +160,17 @@ */ void -ini_value_dispatch(struct ini_config *config, struct ini_handler *hdrs, int length) +ini_dispatch(INI_Section *section, INI_Handler *hdrs, int length) { - const char *sectionName; + INI_Option *option; int i; - char *value; - struct ini_section *section; - sectionName = NULL; for (i = 0; i < length; ++i) { - /* Do not select the same section for performance. */ - if (sectionName == NULL || strcmp(hdrs[i].section, sectionName) != 0) { - sectionName = hdrs[i].section; - section = ini_select_section(config, hdrs[i].section); - } + option = ini_select_option(section, hdrs[i].key); - /* Skip the section if does not exists in the config */ - if (section == NULL) - continue; - - value = ini_get_option(section, hdrs[i].option); - hdrs[i].handler(hdrs[i].dst, value, hdrs[i].userdata); + if (option != NULL) + hdrs[i].handler(hdrs[i].dst, option->value, + hdrs[i].userdata); } } @@ -318,9 +185,9 @@ if (strcmp(value, "yes") == 0 || strcmp(value, "true") == 0 || strcmp(value, "1") == 0) - *p = 1; + *p = (char)1; else - *p = 0; + *p = (char)0; (void)dummy; } @@ -361,46 +228,6 @@ (void)dummy; } -/* - * Return the last error or "No error" if there is not error at all. - */ - -char * -ini_get_error(void) -{ - if (ini_error[0] == '\0') - return "No error"; - - return ini_error; -} - -void -ini_free(struct ini_config *conf, int freeSections, int freeOptions) -{ - struct ini_section *s, *stmp; - struct ini_option *o, *otmp; - - STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) { - STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) { - if (freeOptions) { - free(o->key); - free(o->value); - - STAILQ_REMOVE(&s->options, o, ini_option, next); - free(o); - } - } - - if (freeSections) - free(s->key); - - STAILQ_REMOVE(&conf->sections, s, ini_section, next); - free(s); - } - - free(conf); -} - /* -------------------------------------------------------- * private functions * -------------------------------------------------------- */ @@ -412,22 +239,22 @@ */ static void * -ini_read(struct ini_config *conf, FILE *fp) +ini_read(INI_Config *conf, FILE *fp) { while (ini_getline(conf, fp) == 0) { if (ini_readline(conf) == NULL) { - free(conf->line); + free(conf->_line); ini_free(conf, 1, 1); fclose(fp); return NULL; } - ++ conf->lineno; + ++ conf->_lineno; } /* Clean up */ - free(conf->line); + free(conf->_line); fclose(fp); return conf; @@ -440,11 +267,11 @@ */ static int -ini_getline(struct ini_config *conf, FILE *fp) +ini_getline(INI_Config *conf, FILE *fp) { int ch, pos; - memset(conf->line, 0, conf->linesize); + memset(conf->_line, 0, conf->_linesize); pos = 0; while ((ch = fgetc(fp)) != '\n') { @@ -452,18 +279,18 @@ return -1; /* End of buffer, realloc */ - if (pos == conf->linesize) { - conf->line = realloc(conf->line, conf->linesize + 513); - if (conf->line == NULL) + if (pos == conf->_linesize) { + conf->_line = realloc(conf->_line, conf->_linesize + 513); + if (conf->_line == NULL) return -1; - conf->linesize += 512; + conf->_linesize += 512; } - conf->line[pos++] = ch; + conf->_line[pos++] = ch; } - conf->line[pos] = '\0'; + conf->_line[pos] = '\0'; return 0; } @@ -475,12 +302,12 @@ */ static void * -ini_readline(struct ini_config *conf) +ini_readline(INI_Config *conf) { char *lp; - int (*handler)(struct ini_config *, char **); + int (*handler)(INI_Config *, char **); - lp = conf->line; + lp = conf->_line; SKIP_SPACES(lp); /* Ignore empty line or comment */ @@ -495,12 +322,12 @@ return conf; #ifdef INI_DEBUG - printf("-- line[%d] == [%s]\n", conf->lineno, conf->line); + printf("-- line[%d] == [%s]\n", conf->_lineno, conf->_line); #endif if (*lp == '[') handler = &ini_switch; - else if (!conf->ignore) + else if (!conf->_ignore) handler = &ini_register; else { handler = NULL; @@ -522,10 +349,6 @@ for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) \ continue; -/* On other failure seek until the next line '\0' */ -#define SEEK_NL(lp) \ - for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) - /* * Parse the new section (function called when '[' is found). Returns * 0 or parse error (to avoid ini_readling from stopping) or -1 on @@ -533,45 +356,50 @@ */ static int -ini_switch(struct ini_config *conf, char **lp) +ini_switch(INI_Config *conf, char **lp) { char *endSection; /* Section not parsed, ignore next option to prevent breakage */ if ((endSection = strchr(*lp, ']')) == NULL) { - WARN(conf, "line %d: parse error\n", conf->lineno); + WARN(conf, "line %d: parse error\n", conf->_lineno); SEEK_NEXT(lp); - return (conf->ignore = 1) - 2; /* single line rocks */ + return (conf->_ignore = 1) - 2; /* single line rocks */ } - /* Redefinition of previous and not allowed */ + /* Redefinition of previous and not allowed? */ ++(*lp); - if (conf->current != NULL && I_NOREDEFINE(conf) && - (strncmp(conf->current->key, *lp, endSection - *lp)) == 0) { - WARN(conf, "line %d: redefining %s\n", conf->lineno, conf->current->key); + if (conf->_current != NULL + && I_NOREDEFINE(conf) + && strncmp(conf->_current->key, *lp, endSection - *lp) == 0) + { + WARN(conf, "line %d: redefining %s\n", conf->_lineno, + conf->_current->key); SEEK_NEXT(lp); - return (conf->ignore = 1) - 2; + return (conf->_ignore = 1) - 2; } - if ((conf->current = malloc(sizeof (struct ini_section))) == NULL) + if ((conf->_current = malloc(sizeof (INI_Section))) == NULL) return -1; - if ((conf->current->key = xstrndup(*lp, endSection - *lp)) == NULL) { - free(conf->current); + memset(conf->_current, 0, sizeof (INI_Section)); + if ((conf->_current->key = xstrndup(*lp, endSection - *lp)) == NULL) { + free(conf->_current); + conf->_current = NULL; return -1; } *lp = endSection + 1; #ifdef INI_DEBUG - printf("-- current section is now [%s]\n", conf->current->key); + printf("-- current section is now [%s]\n", conf->_current->key); #endif /* Finally add the new section to the config */ - STAILQ_INIT(&conf->current->options); - STAILQ_INSERT_TAIL(&conf->sections, conf->current, next); + conf->_current->next = conf->sections; + conf->sections = conf->_current; - return (conf->ignore = 0); + return (conf->_ignore = 0); } /* @@ -581,15 +409,15 @@ */ static int -ini_register(struct ini_config *conf, char **lp) +ini_register(INI_Config *conf, char **lp) { + INI_Option *option, tmp; char *assignKey, *endKey, *endValue, token = '\0'; int length = 0; - struct ini_option *option, tmp; /* No '=' found */ if ((assignKey = strchr(*lp, '=')) == NULL) { - WARN(conf, "line %d: missing '='\n", conf->lineno); + WARN(conf, "line %d: missing '='\n", conf->_lineno); SEEK_NEXT(lp); return -1; } @@ -601,12 +429,12 @@ /* Do not register empty option name */ if (endKey - *lp <= 0) { - WARN(conf, "line %d: 0-length option\n", conf->lineno); - SEEK_NL(lp); + WARN(conf, "line %d: 0-length option\n", conf->_lineno); + SEEK_NEXT(lp); return -1; } - memset(&tmp, 0, sizeof (struct ini_option)); + memset(&tmp, 0, sizeof (INI_Option)); if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL) return -1; @@ -626,7 +454,7 @@ if (token != '\0' && *endValue == token) ++ endValue; else - WARN(conf, "line %d: missing '%c'\n", conf->lineno, token); + WARN(conf, "line %d: missing '%c'\n", conf->_lineno, token); } else { for (*lp = endValue; !isspace(*endValue) && *endValue != '\0' && *endValue != '#' && *endValue != ';'; ++endValue) @@ -647,37 +475,22 @@ /* Finally add the new option to the current section */ *lp = endValue; - STAILQ_INSERT_TAIL(&conf->current->options, option, next); + option->next = conf->_current->options; + conf->_current->options = option; return 0; } static void * -ini_fatal(struct ini_config *conf, FILE *fp, const char *fmt, ...) +ini_fatal(INI_Config *conf, FILE *fp, const char *fmt, ...) { va_list ap; if (conf != NULL) { - struct ini_section *s, *stmp; - struct ini_option *o, *otmp; - - if (conf->line != NULL) - free(conf->line); + if (conf->_line != NULL) + free(conf->_line); - STAILQ_FOREACH_SAFE(s, &conf->sections, next, stmp) { - STAILQ_FOREACH_SAFE(o, &s->options, next, otmp) { - free(o->key); - free(o->value); - - STAILQ_REMOVE(&s->options, o, ini_option, next); - - free(o); - } - - free(s->key); - free(s); - } - + ini_free(conf, 1, 1); } if (fp != NULL) @@ -690,8 +503,6 @@ /* Directly print error if VERBOSE is enabled */ WARN(conf, "%s\n", ini_error); - free(conf); - return NULL; } @@ -713,10 +524,10 @@ return res; } -static struct ini_option * -xoptiondup(const struct ini_option *option) +static INI_Option * +xoptiondup(const INI_Option *option) { - struct ini_option *res; + INI_Option *res; /* * Value may be NULL but not option's key. @@ -728,11 +539,10 @@ return NULL; } - if ((res = malloc(sizeof (struct ini_option))) == NULL) + if ((res = malloc(sizeof (INI_Option))) == NULL) return NULL; - res->key = option->key; - res->value = option->value; + memcpy(res, option, sizeof (INI_Option)); return res; }
--- a/ini.h Thu Jan 12 20:12:48 2012 +0100 +++ b/ini.h Fri Jan 13 19:05:16 2012 +0100 @@ -23,65 +23,84 @@ #define INI_NOREDEFINE (1 << 1) /* do not allow redefinitions */ #define INI_FAILERROR (1 << 2) /* abort parsing on first error */ -struct ini_config; -struct ini_section; +typedef struct ini_config INI_Config; +typedef struct ini_section INI_Section; +typedef struct ini_option INI_Option; +typedef struct ini_handler INI_Handler; + +typedef void (*INI_ConvertFunc)(void *, const char *, void *); + +/* -------------------------------------------------------- + * structure definitions + * -------------------------------------------------------- */ + +struct ini_config { + /* General settings */ + const char *path; /* file path */ + int flags; /* optional flags */ + + /* Sections that have been parsed */ + INI_Section *sections; /* linked-list of sections */ + + /* Private fields */ + INI_Section *_current; /* current working section */ + char *_line; /* line buffer */ + int _lineno; /* number of line */ + int _linesize; /* initial line size */ + int _ignore; /* must ignore (no redefine) */ +}; + +struct ini_option { + char *key; /* option name */ + char *value; /* option value */ + INI_Option *next; /* next option */ +}; + +struct ini_section { + char *key; /* section key */ + INI_Option *options; /* linked-list of options */ + INI_Section *next; /* linked-list of sections */ +}; struct ini_handler { - const char *section; /* section to query */ - const char *option; /* option to check */ + char *key; /* option to check */ void *dst; /* where to store */ /* Conversion function */ - void (*handler)(void *, const char *, void *); + INI_ConvertFunc handler; void *userdata; /* optional user data */ }; /* -------------------------------------------------------- - * struct ini_config functions. + * INI_Config functions. * -------------------------------------------------------- */ -struct ini_config * +INI_Config * ini_load(const char *, int); -char ** -ini_get_sections_names(const struct ini_config *, int *); +INI_Section * +ini_select_section(const INI_Config *, const char *); -struct ini_section ** -ini_get_sections(const struct ini_config *, const char *, int *); +INI_Option * +ini_select_option(const INI_Section *, const char *); -struct ini_section * -ini_select_section(const struct ini_config *, const char *); +void +ini_free(INI_Config *, int, int); char * -ini_get_option_once(const struct ini_config *, const char *, const char *); - -void -ini_free(struct ini_config *, int, int); - -/* -------------------------------------------------------- - * struct ini_section functions. - * -------------------------------------------------------- */ - -char ** -ini_get_option_names(const struct ini_section *, int *); - -char * -ini_get_option(const struct ini_section *, const char *key); +ini_get_error(void); /* -------------------------------------------------------- * convenient api to query and convert data * -------------------------------------------------------- */ -char * -ini_get_error(void); - /* * For the config, read all available value and store them in * the array ini_handler. */ void -ini_value_dispatch(struct ini_config *, struct ini_handler *, int); +ini_dispatch(INI_Section *, INI_Handler *, int); /* * Convert to bool. dst must be (char *).