Mercurial > code
changeset 139:d62f2f657768
Introducing new ini API, some changes and use sys/queue.h instead
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 11 May 2012 00:09:30 +0200 |
parents | cf738da3ad60 |
children | 0cf53c588a83 |
files | ini.c ini.h |
diffstat | 2 files changed, 563 insertions(+), 555 deletions(-) [+] |
line wrap: on
line diff
--- a/ini.c Mon Mar 26 10:48:39 2012 +0200 +++ b/ini.c Fri May 11 00:09:30 2012 +0200 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -25,505 +26,88 @@ #include "ini.h" -static char ini_error[1024 + 1]; +/* + * sys/queue.h bits. + */ + +#if !defined(TAILQ_FIRST) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#endif + +#if !defined(TAILQ_FOREACH) +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) +#endif + +#if !defined(TAILQ_FOREACH_SAFE) +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + +#if !defined(TAILQ_INIT) +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ +} while (0) +#endif + +#if !defined(TAILQ_INSERT_TAIL) +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ +} while (0) +#endif + +#if !defined(TAILQ_NEXT) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#endif + +#if !defined(TAILQ_REMOVE) +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ +} while (0) +#endif + +/* -------------------------------------------------------- + * Structure definitions + * -------------------------------------------------------- */ + +struct ini_private { + struct ini_section *current; /* current working section */ + int ignore; /* must ignore (no redefine) */ + FILE *fp; /* file pointer to read */ + + /* Line buffer */ + char *line; /* line data */ + int linesize; /* line allocated size */ + int lineno; /* number of line in file */ + + /* Error reporting */ + char error[1024]; +}; /* -------------------------------------------------------- * Prototypes * -------------------------------------------------------- */ -static void *ini_read(INI_Config *, FILE *); -static int ini_getline(INI_Config *, FILE *); -static INI_Config *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) -#define I_FAILERROR(file) ((file)->flags & INI_FAILERROR) - -#define SKIP_SPACES(lp) while (isspace(*lp) && *lp != '\0') ++lp -#define WARN(file, fmt, ...) \ - if (I_VERBOSE((file))) \ - fprintf(stderr, fmt, __VA_ARGS__) - -/* -------------------------------------------------------- - * Public functions - * -------------------------------------------------------- */ - -INI_Config * -ini_load(const char *path, int flags) -{ - INI_Config *conf; - FILE *fp = 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->ic_line = calloc(1024 + 1, 1)) == NULL) - return ini_fatal(conf, fp, "%s", strerror(errno)); - - conf->path = path; - conf->flags = flags; - conf->ic_ignore = 1; - conf->ic_lineno = 1; - conf->ic_linesize = 1024; - - return ini_read(conf, fp); -} - -/* - * Search the section `sectname' in the config structure. If the section - * is not found, this function returns NULL. - */ - -INI_Section * -ini_select_section(const INI_Config *conf, const char *sectname) -{ - INI_Section *s; - - for (s = conf->sections; s != NULL; s = s->next) - if (strcmp(s->key, sectname) == 0) - return s; - - return NULL; -} - -/* - * Search the option `optname' in the section structure, argument - * section mustn't be NULL. If the option is not found, this function - * returns NULL. - */ - -INI_Option * -ini_select_option(const INI_Section *section, const char *optname) -{ - INI_Option *o; - - for (o = section->options; o != NULL; o = o->next) - if (strcmp(o->key, optname) == 0) - return o; - - return NULL; -} - -/* - * Find an option from the config, by section and option names. - * Returns the option or NULL if not found. - */ - -INI_Option * -ini_find(const INI_Config *config, const char *sname, const char *oname) -{ - INI_Section *sc; - - if ((sc = ini_select_section(config, sname))) - return ini_select_option(sc, oname); - - return NULL; -} - -/* - * Return the last error or "No error" if there is no error. - */ - -char * -ini_get_error(void) -{ - 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; - - for (o = onext = s->options; o != NULL; o = onext) { - onext = o->next; - - if (freeOptions) { - free(o->key); - free(o->value); - free(o); - } - } - - if (freeSections) - free(s->key); - - free(s); - } - - free(conf); -} - -/* - * This section provides a very small API to convert values - * and store them to the dst pointer. Because you can also - * use your own handlers, the user data here is unused. - */ - -void -ini_dispatch(INI_Section *section, INI_Handler *hdrs, int length) -{ - INI_Option *option; - int i; - - for (i = 0; i < length; ++i) { - option = ini_select_option(section, hdrs[i].key); - - if (option != NULL) - hdrs[i].handler(hdrs[i].dst, option->value, - hdrs[i].userdata); - } -} - -void -ini_convert_bool(void *dst, const char *value, void *dummy) -{ - char *p = dst; - - if (value == NULL) - return ; - - if (strcmp(value, "yes") == 0 || - strcmp(value, "true") == 0 || - strcmp(value, "1") == 0) - *p = (char)1; - else - *p = (char)0; - (void)dummy; -} - -void -ini_convert_int(void *dst, const char *value, void *dummy) -{ - int *p = dst; - - if (value == NULL) - return ; - - *p = (int) strtol(value, NULL, 10); - (void)dummy; -} - -void -ini_convert_short(void *dst, const char *value, void *dummy) -{ - short *p = dst; - - if (value == NULL) - return ; - - *p = (short) strtol(value, NULL, 10); - (void)dummy; -} - -void -ini_convert_string(void *dst, const char *value, void *dummy) -{ - char **p = dst; - - if (value == NULL) - *p = NULL; - else - *p = strdup(value); - - (void)dummy; -} - -/* -------------------------------------------------------- - * Private functions - * -------------------------------------------------------- */ - -/* - * Read file line per line and try to parse ini like file. Lines - * starting with '#', ';' or empty lines are ignored. Returns the file - * on success and NULL on failure. - */ - -static void * -ini_read(INI_Config *conf, FILE *fp) -{ - while (ini_getline(conf, fp) == 0) { - if (ini_readline(conf) == NULL) { - free(conf->ic_line); - ini_free(conf, 1, 1); - - fclose(fp); - return NULL; - } - - ++ conf->ic_lineno; - } - - /* Clean up */ - free(conf->ic_line); - fclose(fp); - - return conf; -} - -/* - * Read the next line until the next '\n' character is found. Returns 0 - * if the system had enough memory or -1 on allocation failure or on - * end of file. - */ - -static int -ini_getline(INI_Config *conf, FILE *fp) -{ - int ch, pos; - - memset(conf->ic_line, 0, conf->ic_linesize); - pos = 0; - - while ((ch = fgetc(fp)) != '\n') { - if (feof(fp) || ferror(fp)) - return -1; - - /* End of buffer, realloc */ - if (pos == conf->ic_linesize) { - conf->ic_line = realloc(conf->ic_line, conf->ic_linesize + 513); - if (conf->ic_line == NULL) - return -1; - - conf->ic_linesize += 512; - } - - conf->ic_line[pos++] = ch; - } - - conf->ic_line[pos] = '\0'; - - return 0; -} - -/* - * Parse the line that has been successfully read from the file. - * Returns NULL on allocation failure (instanciation of new - * section or option) or the file on success. - */ - -static INI_Config * -ini_readline(INI_Config *conf) -{ - char *lp; - int (*handler)(INI_Config *, char **); - - lp = conf->ic_line; - SKIP_SPACES(lp); - - /* Ignore empty line or comment */ - if (*lp == '\0' || *lp == '#' || *lp == ';') - return conf; - - while (*lp != '\0') { - SKIP_SPACES(lp); - - /* Skip comments again and empty lines */ - if (*lp == '\0' || *lp == '#' || *lp == ';') - return conf; - -#ifdef INI_DEBUG - printf("-- line[%d] == [%s]\n", conf->ic_lineno, conf->ic_line); -#endif - - if (*lp == '[') - handler = &ini_switch; - else if (!conf->ic_ignore) - handler = &ini_register; - else { - handler = NULL; - ++ lp; - } - - /* Success or not? */ - if (handler != NULL && handler(conf, &lp) < 0) - return (I_FAILERROR(conf)) ? NULL : conf; - - SKIP_SPACES(lp); - } - - return conf; -} - -/* On failure, seek next space */ -#define SEEK_NEXT(lp) \ - for (; !isspace(**lp) && **lp != '\0'; ++(*lp)) \ - continue; - -/* - * Parse the new section (function called when '[' is found). Returns - * 0 even on parse error (to avoid ini_readline from stopping) or -1 on - * allocation failure. - */ - -static int -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->ic_lineno); - SEEK_NEXT(lp); - return (conf->ic_ignore = 1) - 2; /* single line rocks */ - } - - /* Redefinition of previous section and not allowed? */ - ++(*lp); - if (conf->ic_current != NULL - && I_NOREDEFINE(conf) - && strncmp(conf->ic_current->key, *lp, endSection - *lp) == 0) - { - WARN(conf, "line %d: redefining %s\n", conf->ic_lineno, - conf->ic_current->key); - SEEK_NEXT(lp); - return (conf->ic_ignore = 1) - 2; - } - - if ((conf->ic_current = malloc(sizeof (INI_Section))) == NULL) - return -1; - - memset(conf->ic_current, 0, sizeof (INI_Section)); - if ((conf->ic_current->key = xstrndup(*lp, endSection - *lp)) == NULL) { - free(conf->ic_current); - conf->ic_current = NULL; - return -1; - } - - *lp = endSection + 1; - -#ifdef INI_DEBUG - printf("-- current section is now [%s]\n", conf->ic_current->key); -#endif - - /* Finally add the new section to the config */ - conf->ic_current->next = conf->sections; - conf->sections = conf->ic_current; - - return (conf->ic_ignore = 0); -} - -/* - * Store the new option that has been parsed. Returns 0 on success - * even on parse error (to avoid ini_readline from stopping) and -1 on - * allocation failure. - */ - -static int -ini_register(INI_Config *conf, char **lp) -{ - INI_Option *option, tmp; - char *assignKey, *endKey, *endValue, token = '\0'; - int length = 0; - - /* No '=' found */ - if ((assignKey = strchr(*lp, '=')) == NULL) { - WARN(conf, "line %d: missing '='\n", conf->ic_lineno); - SEEK_NEXT(lp); - return -1; - } - - /* Find end of option */ - endKey = *lp; - for (; !isspace(*endKey) && *endKey != '\0' && *endKey != '='; ++endKey) - continue; - - /* Do not register empty option name */ - if (endKey - *lp <= 0) { - WARN(conf, "line %d: 0-length option\n", conf->ic_lineno); - SEEK_NEXT(lp); - return -1; - } - - memset(&tmp, 0, sizeof (INI_Option)); - if ((tmp.key = xstrndup(*lp, endKey - *lp)) == NULL) - return -1; - - endValue = &assignKey[1]; - SKIP_SPACES(endValue); - - /* Find end of option value */ - token = *endValue; - if (token == '\'' || token == '"') { - for (*lp = ++endValue; *endValue != token && - *endValue != '\0'; ++endValue) - continue; - - length = endValue - *lp; - - /* Correctly closed */ - if (token != '\0' && *endValue == token) - ++ endValue; - else - WARN(conf, "line %d: missing '%c'\n", conf->ic_lineno, token); - } else { - for (*lp = endValue; !isspace(*endValue) && *endValue != '\0' && - *endValue != '#' && *endValue != ';'; ++endValue) - continue; - - length = endValue - *lp; - } - - if (length != 0) - tmp.value = xstrndup(*lp, length); - - if ((option = xoptiondup(&tmp)) == NULL) - return -1; - -#ifdef INI_DEBUG - printf("-- next option [%s] is set to [%s]\n", tmp.key, tmp.value); -#endif - - /* Finally add the new option to the current section */ - *lp = endValue; - option->next = conf->ic_current->options; - conf->ic_current->options = option; - - return 0; -} - -static void * -ini_fatal(INI_Config *conf, FILE *fp, const char *fmt, ...) -{ - va_list ap; - - if (conf != NULL) { - if (conf->ic_line != NULL) - free(conf->ic_line); - - ini_free(conf, 1, 1); - } - - if (fp != NULL) - fclose(fp); - - va_start(ap, fmt); - vsnprintf(ini_error, 1024, fmt, ap); - va_end(ap); - - /* Directly print error if VERBOSE is enabled */ - WARN(conf, "%s\n", ini_error); - - return NULL; -} +#define F_VERBOSE(cg) ((cg)->flags & INI_VERBOSE) +#define F_NOREDEFINE(cg) ((cg)->flags & INI_NOREDEFINE) +#define F_FAILERROR(cg) ((cg)->flags & INI_FAILERROR) static char * -xstrndup(const char *src, size_t max) +sstrndup(const char *src, size_t max) { char *res; size_t length; @@ -540,25 +124,420 @@ return res; } -static INI_Option * -xoptiondup(const INI_Option *option) +static void +sskip(char **lp) +{ + while (isspace (**lp) && **lp != '\0') + ++(*lp); +} + +static void +sseek(char **lp) +{ + while (!isspace(**lp) && **lp != '\0') + ++(*lp); +} + +static void +ini_set_error(struct ini_config *cg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (cg->pv->error) + vsnprintf(cg->pv->error, sizeof (cg->pv->error), fmt, ap); + va_end(ap); +} + +static void +ini_warn(struct ini_config *cg, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (F_VERBOSE(cg)) { + vprintf(fmt, ap); + fputc('\n', stdout); + } + + va_end(ap); +} + +/* + * Add a new section, this function only returns -1 on fatal error that may + * be impossible to continue parsing for the current line. + */ + +static int +ini_add_section(struct ini_config *cg, char **lp) { - INI_Option *res; + char *begin = *lp, *end; + size_t length; + struct ini_section *section = NULL; + + if (!(end = strchr(begin, ']'))) { + ini_warn(cg, "line %d: syntax error after [", cg->pv->lineno); + goto bad; + } + + length = end - begin; + + /* May not redefine? */ + if (cg->pv->current && F_NOREDEFINE(cg)) + if (strncmp(cg->pv->current->key, begin, length) == 0) { + ini_warn(cg, "line %d: redefining %s", cg->pv->lineno, + cg->pv->current->key); + goto bad; + } + + /* Allocate a new section and add it */ + if (!(section = calloc(1, sizeof (*section)))) + goto bad; + + if (!(section->key = sstrndup(begin, length))) + goto bad; + + TAILQ_INIT(§ion->options); + TAILQ_INSERT_TAIL(&cg->sections, section, link); + cg->pv->current = section; + cg->pv->ignore = 0; + + /* Trigger an event for the new section */ + if (cg->open) + cg->open(cg->data, section->key); + + *lp = &end[1]; + + return 0; + +bad: if (section) + free(section->key); + free(section); + + cg->pv->ignore = 1; + *lp = strchr(*lp, '\0'); + + return F_FAILERROR(cg) ? -1 : 0; +} - /* - * Value may be NULL but the option key may not. - */ - if (option->key == NULL) { - if (option->value != NULL) - free(option->value); +/* + * Add a new option, the returns value is same as ini_add_section. + */ + +static int +ini_add_option(struct ini_config *cg, char **lp) +{ + char *begin = *lp, *equal, *end; + struct ini_option *option = NULL; + + if (!cg->pv->current) { + ini_warn(cg, "line %d: option within no section", cg->pv->lineno); + goto bad; + } + if (cg->pv->ignore) + goto bad; + + if (!(option = calloc(1, sizeof (*option)))) + goto bad; + + /* Not valid */ + if (!(equal = strchr(begin, '='))) { + ini_warn(cg, "line %d: missing =", cg->pv->lineno); + goto bad; + } + + /* End of option */ + end = begin; + sseek(&end); + + option->key = sstrndup(begin, end - begin); + + /* End of value */ + begin = &equal[1]; + sskip(&begin); + end = begin; + + if (*end == '\'' || *end == '"') { + for (++end; *end != *begin && *end != '\0'; ++end) + continue; + if (*end != *begin) { + ini_warn(cg, "line %d: missing %c", + cg->pv->lineno, *begin); + *lp = end; + goto bad; + } else + ++ begin; + } else + sseek(&end); - return NULL; + option->value = sstrndup(begin, end - begin); + + if (!option->key || !option->value) { + ini_warn(cg, "line %d: syntax error", cg->pv->lineno); + goto bad; + } + + TAILQ_INSERT_TAIL(&cg->pv->current->options, option, link); + + /* Trigger an event for the new section */ + if (cg->get) + cg->get(cg->data, cg->pv->current->key, option); + + *lp = &end[1]; + + return 0; + +bad: if (option) { + free(option->key); + free(option->value); + } + free(option); + + *lp = strchr(*lp, '\0'); + + return F_FAILERROR(cg) ? -1 : 0; +} + +static int +ini_read_line(struct ini_config *cg) +{ + char *lp = cg->pv->line; + int rv; + + /* Ignore empty line */ + if (*lp == '\0' || *lp == '#' || *lp == ';') + return 0; + + while (*lp != '\0') { + sskip(&lp); + + /* Begin of section */ + if (*lp == '[') { + ++ lp; + rv = ini_add_section(cg, &lp); + /* Begin of option */ + } else { + rv = ini_add_option(cg, &lp); + } + + if (rv < 0) + return -1; } - if ((res = malloc(sizeof (INI_Option))) == NULL) + return 0; +} + +/* + * Read the next line until the next '\n' character is found. Returns 0 + * if the system had enough memory or -1 on allocation failure or on + * end of file. Only for mode == INI_LINEAR + */ + +static int +ini_get_line(struct ini_config *cg) +{ + int ch, pos; + + memset(cg->pv->line, 0, cg->pv->linesize); + pos = 0; + + while ((ch = fgetc(cg->pv->fp)) != '\n') { + if (feof(cg->pv->fp) || ferror(cg->pv->fp)) + return -1; + + /* End of buffer, realloc */ + if (pos == cg->pv->linesize) { + cg->pv->line = realloc(cg->pv->line, cg->pv->linesize + 512); + if (!cg->pv->line) + return -1; + + cg->pv->linesize += 512; + } + + cg->pv->line[pos++] = ch; + } + + return 0; +} + +static int +ini_open_file(struct ini_config *cg) +{ + if (!(cg->pv->fp = fopen(cg->path, "r"))) { + ini_set_error(cg, "open: %s", cg->path); + return -1; + } + + cg->pv->linesize = INI_DEFAULT_LINESIZE; + cg->pv->line = calloc(sizeof (char), cg->pv->linesize); + + if (!cg->pv->line) { + ini_set_error(cg, "malloc: %s", strerror(errno)); + return -1; + } + + return 0; +} + +/* -------------------------------------------------------- + * Public functions + * -------------------------------------------------------- */ + +struct ini_config * +ini_create(const char *path, int flags) +{ + struct ini_config *cg; + + cg = calloc(1, sizeof (*cg)); + if (!cg) + return NULL; + + cg->pv = calloc(1, sizeof (struct ini_private)); + if (!cg->pv) return NULL; - memcpy(res, option, sizeof (INI_Option)); + cg->path = path; + cg->flags = flags; + + return cg; +} + +void +ini_set_handlers(struct ini_config *cg, void *data, ini_open_t open, + ini_get_t get) +{ + if (!cg) + return; + + cg->data = data; + cg->open = open; + cg->get = get; +} + +int +ini_open(struct ini_config *cg) +{ + int rv = 0; + + /* Open the file and prepare data for reading */ + if (ini_open_file(cg) < 0) + return -1; + + cg->pv->ignore = 1; + cg->pv->lineno = 1; + + TAILQ_INIT(&cg->sections); + + while (ini_get_line(cg) != -1 && !rv) { + rv = ini_read_line(cg); + ++ cg->pv->lineno; + } + + fclose(cg->pv->fp); + + return rv; +} + +/* + * Search the section `sectname' in the config structure. If the section + * is not found, this function returns NULL. + */ + +struct ini_section * +ini_select_section(const struct ini_config *cg, const char *sectname) +{ + struct ini_section *s; + + TAILQ_FOREACH(s, &cg->sections, link) + if (strcmp(s->key, sectname) == 0) + return s; + + return NULL; +} + +/* + * Search the option `optname' in the section structure, argument + * section mustn't be NULL. If the option is not found, this function + * returns NULL. + */ + +struct ini_option * +ini_select_option(const struct ini_section *sc, const char *name) +{ + struct ini_option *o; - return res; + TAILQ_FOREACH(o, &sc->options, link) + if (strcmp(o->key, name) == 0) + return o; + + return NULL; +} + +/* + * Find an option from the config, by section and option names. + * Returns the option or NULL if not found. + */ + +struct ini_option * +ini_find(const struct ini_config *cg, const char *scname, const char *optname) +{ + struct ini_section *sc; + + if ((sc = ini_select_section(cg, scname))) + return ini_select_option(sc, optname); + + return NULL; +} + +/* + * Return the last error or "No error" if there is no error. + */ + +const char * +ini_get_error(const struct ini_config *cg) +{ + if (!cg && errno != 0) + return strerror(errno); + + if (cg->pv->error[0] == '\0') + return "No error"; + + return cg->pv->error; } + +/* + * Free everything, level may be set like this: + * 0, only free parser and private resources. + * 1, free all sections and their keys, + * 2, free all sections and options keys and values. + */ + +void +ini_free(struct ini_config *cg, int level) +{ + struct ini_section *s, *stmp; + struct ini_option *o, *otmp; + + if (level >= 1) { + TAILQ_FOREACH_SAFE(s, &cg->sections, link, stmp) { + if (level >= 2) { + TAILQ_FOREACH_SAFE(o, &s->options, link, otmp) { + TAILQ_REMOVE(&s->options, o, link); + free(o->key); + free(o->value); + free(o); + } + } + + TAILQ_REMOVE(&cg->sections, s, link); + free(s->key); + free(s); + } + } + + if (level >= 0) { + free(cg->pv->line); + free(cg->pv); + free(cg); + } +}
--- a/ini.h Mon Mar 26 10:48:39 2012 +0200 +++ b/ini.h Fri May 11 00:09:30 2012 +0200 @@ -23,81 +23,108 @@ extern "C" { #endif -#define INI_VERBOSE (1 << 0) /* be verbose */ -#define INI_NOREDEFINE (1 << 1) /* do not allow redefinitions */ -#define INI_FAILERROR (1 << 2) /* abort parsing on first error */ +#if !defined(INI_DEFAULT_LINESIZE) +# define INI_DEFAULT_LINESIZE 1024 +#endif + +/* + * sys/queue.h bits. + */ + +#if !defined(TAILQ_ENTRY) +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif -typedef struct ini_config INI_Config; -typedef struct ini_section INI_Section; -typedef struct ini_option INI_Option; -typedef struct ini_handler INI_Handler; +#if !defined(TAILQ_HEAD) +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} +#endif -typedef void (*INI_ConvertFunc)(void *, const char *, void *); +enum { + INI_VERBOSE = (1 << 0), /* be verbose */ + INI_NOREDEFINE = (1 << 1), /* do not allow redefinitions */ + INI_FAILERROR = (1 << 2) /* abort parsing on first error */ +}; + +typedef struct ini_config ini_config_t; +typedef struct ini_section ini_section_t; +typedef struct ini_option ini_option_t; +typedef struct ini_handler ini_handler_t; + +typedef void (*ini_open_t)(void *, const char *); +typedef void (*ini_get_t)(void *, const char *, const ini_option_t *); /* -------------------------------------------------------- * Structure definitions * -------------------------------------------------------- */ +struct ini_private; + struct ini_config { - /* - * General settings - */ - const char *path; /* (ro) file path */ - int flags; /* (ro) optional flags */ + const char *path; /* (ro) file path */ + int flags; /* (ro) optional flags */ + void *data; /* (rw) user data */ - /* - * Sections that have been parsed - */ - INI_Section *sections; /* (ro) linked-list of sections */ + TAILQ_HEAD(, ini_section) sections; /* (ro) linked-list of sections */ - /* Private fields */ - INI_Section *ic_current; /* current working section */ - char *ic_line; /* line buffer */ - int ic_lineno; /* number of current line */ - int ic_linesize; /* initial line size */ - int ic_ignore; /* must ignore (no redefine) */ + /* Event driven method */ + void (*open)(void *, const char *); + void (*get)(void *, const char *, const struct ini_option *); + + /* Private data */ + struct ini_private *pv; }; struct ini_option { - char *key; /* (rw) option name */ - char *value; /* (rw) option value */ - INI_Option *next; /* (ro) next option */ + char *key; /* (rw) option name */ + char *value; /* (rw) option value */ + + TAILQ_ENTRY(ini_option) link; }; struct ini_section { - char *key; /* (rw) section key */ - INI_Option *options; /* (ro) linked-list of options */ - INI_Section *next; /* (ro) linked-list of sections */ -}; + char *key; /* (rw) section key */ + TAILQ_HEAD(, ini_option) options; /* (rw) list of options */ -struct ini_handler { - char *key; /* (rw) option to check */ - void *dst; /* (rw) where to store */ - INI_ConvertFunc handler; /* (rw) conversion function */ - void *userdata; /* (rw) optional user data */ + TAILQ_ENTRY(ini_section) link; }; /* -------------------------------------------------------- * Main functions * -------------------------------------------------------- */ -INI_Config * -ini_load(const char *, int); - -INI_Section * -ini_select_section(const INI_Config *, const char *); - -INI_Option * -ini_select_option(const INI_Section *, const char *); - -INI_Option * -ini_find(const INI_Config *, const char *, const char *); - -char * -ini_get_error(void); +struct ini_config * +ini_create(const char *, int); void -ini_free(INI_Config *, int, int); +ini_set_handlers(struct ini_config *, void *, ini_open_t, ini_get_t); + +int +ini_open(struct ini_config *); + +struct ini_section * +ini_select_section(const struct ini_config *, const char *); + +struct ini_option * +ini_select_option(const struct ini_section *, const char *); + +struct ini_option * +ini_find(const struct ini_config *, const char *, const char *); + +const char * +ini_get_error(const struct ini_config *); + +void +ini_free(struct ini_config *, int); + +#if 0 /* -------------------------------------------------------- * Convenient api to query and convert data @@ -143,6 +170,8 @@ void ini_convert_string(void *, const char *, void *); +#endif + #ifdef __cplusplus } #endif