Mercurial > code
changeset 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 |
files | inifile.c inifile.h |
diffstat | 2 files changed, 220 insertions(+), 151 deletions(-) [+] |
line wrap: on
line diff
--- a/inifile.c Fri Nov 11 16:07:48 2011 +0100 +++ b/inifile.c Sun Nov 13 15:43:30 2011 +0100 @@ -30,11 +30,6 @@ * structure definitions * -------------------------------------------------------- */ -/* - * We use STAILQ as stack, it means if the user supplied once - * or more times the same options the last one is taken. - */ - struct option { char *key; /* option name */ char *value; /* option value */ @@ -45,7 +40,6 @@ struct section { char *key; /* section key */ - int length; /* number of options */ OptionList options; /* list of options */ STAILQ_ENTRY(section) next; }; @@ -56,11 +50,13 @@ const char *path; /* file path */ SectionList sections; /* list of sections */ struct section *current; /* current section */ + int flags; /* inifile_options */ + int ignore; /* must ignore (no redefine) */ + + /* Current file line properties */ + char *line; /* line buffer */ int lineno; /* number of line */ - int linesize; /* size of line */ - int options; /* inifile_options */ - int ignore; /* must ignore (no redefine) */ - char *line; /* line buffer */ + int linesize; /* initial line size */ /* For querying functions */ struct section *q_section; /* current section query */ @@ -73,15 +69,18 @@ * -------------------------------------------------------- */ static void *inifile_read(struct inifile *, FILE *); -static void inifile_switch(struct inifile *, char *); -static int inifile_register(struct inifile *, char *); +static int inifile_getline(struct inifile *, FILE *); +static void *inifile_readline(struct inifile *); +static int inifile_switch(struct inifile *, char **); +static int inifile_register(struct inifile *, char **); static void *inifile_fatal(struct inifile *, FILE *, const char *, ...); static char *xstrndup(const char *, size_t); +static struct option *xoptiondup(const struct option *option); -#define I_VERBOSE(file) ((file)->options & INIFILE_VERBOSE) -#define I_NOREDEFINE(file) ((file)->options & INIFILE_NOREDEFINE) +#define I_VERBOSE(file) ((file)->flags & INIFILE_VERBOSE) +#define I_NOREDEFINE(file) ((file)->flags & INIFILE_NOREDEFINE) -#define SKIP_SPACES(lp) while (isspace(*(lp))) ++(lp) +#define SKIP_SPACES(lp) while (isspace(*lp) && *lp != '\0') ++lp #define NFREE(p) if ((p) != NULL) free((p)) #define WARN(file, fmt, ...) \ if (I_VERBOSE((file))) \ @@ -92,7 +91,7 @@ * -------------------------------------------------------- */ struct inifile * -inifile_load(const char *path, int options, int linesize) +inifile_load(const char *path, int flags) { FILE *fp; struct inifile *file; @@ -103,14 +102,15 @@ if ((fp = fopen(path, "r")) == NULL) return inifile_fatal(file, fp, "%s: %s", path, strerror(errno)); - if ((file->line = calloc(linesize + 1, 1)) == NULL) + if ((file->line = calloc(1024 + 1, 1)) == NULL) return inifile_fatal(file, fp, "%s", strerror(errno)); STAILQ_INIT(&file->sections); file->path = path; - file->options = options; - file->linesize = (linesize == 0) ? 1024 : linesize; + file->flags = flags; + file->ignore = 1; file->lineno = 1; + file->linesize = 1024; return inifile_read(file, fp); } @@ -261,35 +261,13 @@ static void * inifile_read(struct inifile *file, FILE *fp) { - char *lp; int status; - for (; fgets(file->line, file->linesize, fp); ++file->lineno) { - /* Move the file char pointer to the next '\n' if truncated */ - if ((lp = strchr(file->line, '\n')) != NULL) - *lp = '\0'; - else { - int ch; - while ((ch = fgetc(fp)) != '\n' && ch != EOF); - } - - lp = file->line; - SKIP_SPACES(lp); + while (inifile_getline(file, fp) == 0) { + if (inifile_readline(file) == NULL) + return inifile_fatal(file, fp, "%s", strerror(errno)); - if (*lp == '\0' || *lp == '#') - continue; - - if (*lp == '[') { - inifile_switch(file, lp); - - /* Fail to create a new section? */ - if (file->current == NULL) - return inifile_fatal(file, fp, "%s", - strerror(errno)); - } else if (!file->ignore) - if (inifile_register(file, lp) < 0) - return inifile_fatal(file, fp, "%s", - strerror(errno)); + ++ file->lineno; } /* Clean up */ @@ -300,144 +278,211 @@ } /* - * Switch to the next section. If option NOREDEFINE is set and - * user wrote two or more times the same section, every options are - * simply ignore and a warning may be issued if VERBOSE is set. - * - * This function set file->current to NULL on allocation failure. + * 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 void -inifile_switch(struct inifile *file, char *lp) +static int +inifile_getline(struct inifile *file, FILE *fp) { - char *endlp; - int length; - struct section *newsection; + int ch, pos; + + memset(file->line, 0, file->linesize); + pos = 0; - /* Is complete? [example] */ - if ((endlp = strchr(lp, ']')) != NULL) { - length = endlp - &lp[1]; /* geek */ - ++ lp; + while ((ch = fgetc(fp)) != '\n') { + if (feof(fp) || ferror(fp)) + return -1; - if (length == 0) { - WARN(file, "line %d: 0-length section\n", file->lineno);; - return ; + /* End of buffer, realloc */ + if (pos == file->linesize) { + file->line = realloc(file->line, file->linesize + 513); + if (file->line == NULL) + return -1; + + file->linesize += 512; } - /* - * Do not redefine? If yes then the next options must - * be ignored. - */ - if (file->current != NULL && I_NOREDEFINE(file) && - strncmp(file->current->key, lp, length) == 0) { - WARN(file, "line %d: warning redefining '%s'\n", - file->lineno, file->current->key); - file->ignore = 1; - } else { - newsection = malloc(sizeof (struct section)); - - /* Add a new section */ - if (newsection != NULL) { - newsection->key = xstrndup(lp, length); + file->line[pos++] = ch; + } - if (newsection->key == NULL) - free(newsection); - else { - STAILQ_INIT(&newsection->options); - newsection->length = 0; - file->current = newsection; + file->line[pos] = '\0'; - STAILQ_INSERT_TAIL(&file->sections, - newsection, next); - } - } - } - } else { - /* - * The section has not been parsed, next options must - * be ignored until a new section is found. - */ - file->ignore = 1; - } + return 0; } /* - * Register a new option in the current section. The value may be emptym then - * the value is set to NULL. If a string is not correctly quoted a warning - * may be printed if VERBOSE is enabled, if the assigment key is missing - * a warning may be printed too. + * Read the next line that have been successfully read. Returns NULL + * on allocation failure (instanciation of new section or option) + * or the file on success. + */ + +static void * +inifile_readline(struct inifile *file) +{ + char *lp; + int (*handler)(struct inifile *, char **); + + lp = file->line; + SKIP_SPACES(lp); + + /* Ignore empty line or comment */ + if (*lp == '\0' || *lp == '#') + return file; + + while (*lp != '\0') { + SKIP_SPACES(lp); + + /* Skip comments again and empty lines */ + if (*lp == '\0' || *lp == '#' || *lp == ';') + return file; + +#ifdef DEBUG + printf("-- line[%d] == [%s]\n", file->lineno, file->line); +#endif + + if (*lp == '[') + handler = &inifile_switch; + else if (!file->ignore) + handler = &inifile_register; + + /* Success or not? */ + if (handler(file, &lp) < 0) + return NULL; + + SKIP_SPACES(lp); + } + return file; +} + +/* On failure, seek next space */ +#define SEEK_NEXT(lp) \ + 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 inifile_readling from stopping) or -1 on + * allocation failure. */ static int -inifile_register(struct inifile *file, char *lp) +inifile_switch(struct inifile *file, char **lp) { - char *endlp, *endvalue, tok; - int length; - struct option *newoption; + char *endSection; - /* Parse until '=' is found to store the key */ - for (endlp = lp; !isspace(*endlp) && *endlp != '='; ++endlp) - continue; - - for (endvalue = lp; *endvalue != '=' && *endvalue != '\0'; ++ endvalue) - continue; - - /* Missing = */ - if (*endvalue != '=') { - WARN(file, "line %d: missing '='\n", file->lineno); - return 0; + /* Section not parsed, ignore next option to prevent breakage */ + if ((endSection = strchr(*lp, ']')) == NULL) { + WARN(file, "line %d: parse error\n", file->lineno); + SEEK_NEXT(lp); + return (file->ignore = 1) - 1; /* single line rocks */ } - /* Empty option key */ - if (endlp - lp == 0) { - WARN(file, "line %d: missing keyword before '='\n", file->lineno); - return 0; + /* Redefinition of previous and not allowed */ + ++(*lp); + if (file->current != NULL && I_NOREDEFINE(file) && + (strncmp(file->current->key, *lp, endSection - *lp)) == 0) { + WARN(file, "line %d: redefining %s\n", file->lineno, file->current->key); + SEEK_NEXT(lp); + return (file->ignore = 1) - 1; } - if ((newoption = malloc(sizeof (struct option))) == NULL) + if ((file->current = malloc(sizeof (struct section))) == NULL) return -1; - if ((newoption->key = xstrndup(lp, endlp - lp)) == NULL) { - free(newoption); + if ((file->current->key = xstrndup(*lp, endSection - *lp)) == NULL) { + free(file->current); return -1; } - /* Parse until "" '' or space is found. */ - ++ endvalue; - SKIP_SPACES(endvalue); - lp = endvalue; + *lp = endSection + 1; + +#ifdef DEBUG + printf("-- current section is now [%s]\n", file->current->key); +#endif - tok = *endvalue; - if (tok == '"' || tok == '\'') { - ++ endvalue; - while (*endvalue != tok && *endvalue != '\0' && *endvalue != '#') - ++ endvalue; + /* Finally add the new section to the config */ + STAILQ_INIT(&file->current->options); + STAILQ_INSERT_TAIL(&file->sections, file->current, next); + + return (file->ignore = 0); +} - /* Print a warning but store what have been parsed */ - if (*endvalue != tok) - WARN(file, "line %d: missing '%c'\n", file->lineno, tok); +/* + * Store the new option that have been parsed. Returns 0 on success or + * on parse error (to avoid inifile_readline from stopping) and -1 on + * allocation failure. + */ - length = endvalue - lp++ - 1; - } else { - while (!isspace(*endvalue) && *endvalue != '\0') - ++ endvalue; +static int +inifile_register(struct inifile *file, char **lp) +{ + char *assignKey, *endKey, *endValue, token = '\0'; + int length = 0; + struct option *option, tmp = { NULL }; - length = endvalue - lp; + /* No '=' found */ + if ((assignKey = strchr(*lp, '=')) == NULL) { + WARN(file, "line %d: missing '='\n", file->lineno); + SEEK_NEXT(lp); + return 0; } - if (length > 0) { - newoption->value = xstrndup(lp, length); - if (newoption->value == NULL) { - free(newoption->key); - free(newoption); - return -1; - } - } else - newoption->value = NULL; + /* Find end of option */ + for (endKey = *lp; !isspace(*endKey) && *endKey != '\0'; ++endKey) + continue; + + /* Do not register empty option name */ + if (endKey - *lp == 0) { + WARN(file, "line %d: 0-length option\n", file->lineno); + SEEK_NL(lp); + return 0; + } + + 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; - /* And finally add the option to the current section */ - file->current->length ++; - STAILQ_INSERT_TAIL(&file->current->options, newoption, next); + /* Correctly closed */ + if (token != '\0' && *endValue == token) + ++ endValue; + else + WARN(file, "line %d: missing '%c'\n", file->lineno, token); + } else { + for (*lp = endValue; !isspace(*endValue) && *endValue != '\0'; ++endValue) + continue; + + length = endValue - *lp; + } + + if (length != 0) + tmp.value = xstrndup(*lp, length); + + if ((option = xoptiondup(&tmp)) == NULL) + return -1; + +#ifdef 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; + STAILQ_INSERT_TAIL(&file->current->options, option, next); return 0; } @@ -502,3 +547,27 @@ return res; } + +static struct option * +xoptiondup(const struct option *option) +{ + struct option *res; + + /* + * Value may be NULL but not option's key. + */ + if (option->key == NULL) { + if (option->value != NULL) + free(option->value); + + return NULL; + } + + if ((res = malloc(sizeof (struct option))) == NULL) + return NULL; + + res->key = option->key; + res->value = option->value; + + return res; +}
--- a/inifile.h Fri Nov 11 16:07:48 2011 +0100 +++ b/inifile.h Sun Nov 13 15:43:30 2011 +0100 @@ -24,7 +24,7 @@ struct inifile; -struct inifile *inifile_load(const char *, int, int); +struct inifile *inifile_load(const char *, int); char **inifile_sections(struct inifile *, int *); int inifile_select(struct inifile *, const char *); char *inifile_option(struct inifile *, const char *);