X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=file.c;h=5360ef764a15cb9b9d3bbd5e17c64135fac26c45;hp=c0141acd17964863f47e5c8b2447a49b89895802;hb=d4bab379481c8ef0d5710a616660f1d7144d768f;hpb=e828bcf07c0602746dffcc9927c446d1fe4c3ad9 diff --git a/file.c b/file.c index c0141ac..5360ef7 100644 --- a/file.c +++ b/file.c @@ -25,192 +25,7 @@ #include #include -/* - * Clean up all extra memory used by the parser and exporter - */ -static void uci_file_cleanup(struct uci_context *ctx) -{ - struct uci_parse_context *pctx; - - if (ctx->buf) { - free(ctx->buf); - ctx->buf = NULL; - ctx->bufsz = 0; - } - - pctx = ctx->pctx; - if (!pctx) - return; - - ctx->pctx = NULL; - if (pctx->package) - uci_free_package(&pctx->package); - - if (pctx->buf) - free(pctx->buf); - - free(pctx); -} - - -/* - * parse a character escaped by '\' - * returns true if the escaped character is to be parsed - * returns false if the escaped character is to be ignored - */ -static inline bool parse_backslash(struct uci_context *ctx, char **str) -{ - /* skip backslash */ - *str += 1; - - /* undecoded backslash at the end of line, fetch the next line */ - if (!**str) { - *str += 1; - uci_getln(ctx, *str - ctx->pctx->buf); - return false; - } - - /* FIXME: decode escaped char, necessary? */ - return true; -} - -/* - * move the string pointer forward until a non-whitespace character or - * EOL is reached - */ -static void skip_whitespace(struct uci_context *ctx, char **str) -{ -restart: - while (**str && isspace(**str)) - *str += 1; - - if (**str == '\\') { - if (!parse_backslash(ctx, str)) - goto restart; - } -} - -static inline void addc(char **dest, char **src) -{ - **dest = **src; - *dest += 1; - *src += 1; -} - -/* - * parse a double quoted string argument from the command line - */ -static void parse_double_quote(struct uci_context *ctx, char **str, char **target) -{ - char c; - - /* skip quote character */ - *str += 1; - - while ((c = **str)) { - switch(c) { - case '"': - **target = 0; - *str += 1; - return; - case '\\': - if (!parse_backslash(ctx, str)) - continue; - /* fall through */ - default: - addc(target, str); - break; - } - } - uci_parse_error(ctx, *str, "unterminated \""); -} - -/* - * parse a single quoted string argument from the command line - */ -static void parse_single_quote(struct uci_context *ctx, char **str, char **target) -{ - char c; - /* skip quote character */ - *str += 1; - - while ((c = **str)) { - switch(c) { - case '\'': - **target = 0; - *str += 1; - return; - default: - addc(target, str); - } - } - uci_parse_error(ctx, *str, "unterminated '"); -} - -/* - * parse a string from the command line and detect the quoting style - */ -static void parse_str(struct uci_context *ctx, char **str, char **target) -{ - do { - switch(**str) { - case '\'': - parse_single_quote(ctx, str, target); - break; - case '"': - parse_double_quote(ctx, str, target); - break; - case '#': - **str = 0; - /* fall through */ - case 0: - goto done; - case '\\': - if (!parse_backslash(ctx, str)) - continue; - /* fall through */ - default: - addc(target, str); - break; - } - } while (**str && !isspace(**str)); -done: - - /* - * if the string was unquoted and we've stopped at a whitespace - * character, skip to the next one, because the whitespace will - * be overwritten by a null byte here - */ - if (**str) - *str += 1; - - /* terminate the parsed string */ - **target = 0; -} - -/* - * extract the next argument from the command line - */ -static char *next_arg(struct uci_context *ctx, char **str, bool required, bool name) -{ - char *val; - char *ptr; - - val = ptr = *str; - skip_whitespace(ctx, str); - parse_str(ctx, str, &ptr); - if (!*val) { - if (required) - uci_parse_error(ctx, *str, "insufficient arguments"); - goto done; - } - - if (name && !uci_validate_name(val)) - uci_parse_error(ctx, val, "invalid character in field"); - -done: - return val; -} +static struct uci_backend uci_file_backend; /* * verify that the end of the line or command is reached. @@ -220,8 +35,9 @@ static void assert_eol(struct uci_context *ctx, char **str) { char *tmp; + skip_whitespace(ctx, str); tmp = next_arg(ctx, str, false, false); - if (tmp && *tmp && (ctx->flags & UCI_FLAG_STRICT)) + if (*tmp && (ctx->flags & UCI_FLAG_STRICT)) uci_parse_error(ctx, *str, "too many arguments"); } @@ -240,6 +56,7 @@ static void uci_switch_config(struct uci_context *ctx) /* add the last config to main config file list */ if (pctx->package) { + pctx->package->backend = ctx->backend; uci_list_add(&ctx->root, &pctx->package->e.list); pctx->package = NULL; @@ -253,7 +70,7 @@ static void uci_switch_config(struct uci_context *ctx) * if an older config under the same name exists, unload it * ignore errors here, e.g. if the config was not found */ - e = uci_lookup_list(ctx, &ctx->root, name); + e = uci_lookup_list(&ctx->root, name); if (e) UCI_THROW(ctx, UCI_ERR_DUPLICATE); pctx->package = uci_alloc_package(ctx, name); @@ -278,50 +95,6 @@ static void uci_parse_package(struct uci_context *ctx, char **str, bool single) uci_switch_config(ctx); } -/* Based on an efficient hash function published by D. J. Bernstein */ -static unsigned int djbhash(unsigned int hash, char *str) -{ - int len = strlen(str); - int i; - - /* initial value */ - if (hash == ~0) - hash = 5381; - - for(i = 0; i < len; i++) { - hash = ((hash << 5) + hash) + str[i]; - } - return (hash & 0x7FFFFFFF); -} - -/* fix up an unnamed section */ -static void uci_fixup_section(struct uci_context *ctx, struct uci_section *s) -{ - unsigned int hash = ~0; - struct uci_element *e; - char buf[16]; - - if (!s || s->e.name) - return; - - /* - * Generate a name for unnamed sections. This is used as reference - * when locating or updating the section from apps/scripts. - * To make multiple concurrent versions somewhat safe for updating, - * the name is generated from a hash of its type and name/value - * pairs of its option, and it is prefixed by a counter value. - * If the order of the unnamed sections changes for some reason, - * updates to them will be rejected. - */ - hash = djbhash(hash, s->type); - uci_foreach_element(&s->options, e) { - hash = djbhash(hash, e->name); - hash = djbhash(hash, uci_to_option(e)->value); - } - sprintf(buf, "cfg%02x%04x", ++s->package->n_section, hash % (1 << 16)); - s->e.name = uci_strdup(ctx, buf); -} - /* * parse the 'config' uci command (open a section) */ @@ -350,7 +123,8 @@ static void uci_parse_config(struct uci_context *ctx, char **str) if (pctx->merge) { UCI_TRAP_SAVE(ctx, error); - uci_set(ctx, pctx->package, name, NULL, type); + if (uci_set(ctx, pctx->package, name, NULL, type, NULL) != UCI_OK) + goto error; UCI_TRAP_RESTORE(ctx); return; error: @@ -380,7 +154,7 @@ static void uci_parse_option(struct uci_context *ctx, char **str) if (pctx->merge) { UCI_TRAP_SAVE(ctx, error); - uci_set(ctx, pctx->package, pctx->section->e.name, name, value); + uci_set(ctx, pctx->package, pctx->section->e.name, name, value, NULL); UCI_TRAP_RESTORE(ctx); return; error: @@ -396,19 +170,17 @@ error: static void uci_parse_line(struct uci_context *ctx, bool single) { struct uci_parse_context *pctx = ctx->pctx; - char *word, *brk = NULL; - - for (word = strtok_r(pctx->buf, ";", &brk); - word; - word = strtok_r(NULL, ";", &brk)) { - - char *pbrk = NULL; - word = strtok_r(word, " \t", &pbrk); + char *word, *brk; + word = pctx->buf; + do { + brk = NULL; + word = strtok_r(word, " \t", &brk); if (!word) - continue; + return; switch(word[0]) { + case 0: case '#': return; case 'p': @@ -427,7 +199,7 @@ static void uci_parse_line(struct uci_context *ctx, bool single) uci_parse_error(ctx, word, "unterminated command"); break; } - } + } while (1); } /* max number of characters that escaping adds to the string */ @@ -521,7 +293,7 @@ int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct u UCI_HANDLE_ERR(ctx); /* make sure no memory from previous parse attempts is leaked */ - uci_file_cleanup(ctx); + uci_cleanup(ctx); uci_alloc_parse_context(ctx); pctx = ctx->pctx; @@ -557,6 +329,8 @@ error: } uci_fixup_section(ctx, ctx->pctx->section); + if (!pctx->package && name) + uci_switch_config(ctx); if (package) *package = pctx->package; if (pctx->merge) @@ -566,7 +340,7 @@ error: uci_switch_config(ctx); /* no error happened, we can get rid of the parser context now */ - uci_file_cleanup(ctx); + uci_cleanup(ctx); return 0; } @@ -583,62 +357,13 @@ static char *uci_config_path(struct uci_context *ctx, const char *name) return filename; } -int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package) -{ - char *filename; - bool confdir; - FILE *file = NULL; - - UCI_HANDLE_ERR(ctx); - - switch (name[0]) { - case '.': - /* relative path outside of /etc/config */ - if (name[1] != '/') - UCI_THROW(ctx, UCI_ERR_NOTFOUND); - /* fall through */ - case '/': - /* absolute path outside of /etc/config */ - filename = uci_strdup(ctx, name); - name = strrchr(name, '/') + 1; - confdir = false; - break; - default: - /* config in /etc/config */ - filename = uci_config_path(ctx, name); - confdir = true; - break; - } - - file = uci_open_stream(ctx, filename, SEEK_SET, false, false); - ctx->errno = 0; - UCI_TRAP_SAVE(ctx, done); - UCI_INTERNAL(uci_import, ctx, file, name, package, true); - UCI_TRAP_RESTORE(ctx); - - if (*package) { - (*package)->path = filename; - (*package)->confdir = confdir; - uci_load_history(ctx, *package, false); - } - -done: - uci_close_stream(file); - return ctx->errno; -} - -int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite) +void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite) { - struct uci_package *p; + struct uci_package *p = *package; FILE *f = NULL; char *name = NULL; char *path = NULL; - UCI_HANDLE_ERR(ctx); - UCI_ASSERT(ctx, package != NULL); - p = *package; - - UCI_ASSERT(ctx, p != NULL); if (!p->path) { if (overwrite) p->path = uci_config_path(ctx, p->e.name); @@ -646,13 +371,12 @@ int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overw UCI_THROW(ctx, UCI_ERR_INVAL); } - /* open the config file for writing now, so that it is locked */ f = uci_open_stream(ctx, p->path, SEEK_SET, true, true); /* flush unsaved changes and reload from history file */ UCI_TRAP_SAVE(ctx, done); - if (p->confdir) { + if (p->has_history) { if (!overwrite) { name = uci_strdup(ctx, p->e.name); path = uci_strdup(ctx, p->path); @@ -665,21 +389,23 @@ int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overw * as well. dump and reload */ uci_free_package(&p); - uci_file_cleanup(ctx); + uci_cleanup(ctx); UCI_INTERNAL(uci_import, ctx, f, name, &p, true); p->path = path; - p->confdir = true; + p->has_history = true; *package = p; /* freed together with the uci_package */ path = NULL; /* check for updated history, flush */ - uci_load_history(ctx, p, true); + if (!uci_load_history(ctx, p, true)) + goto done; } else { /* flush history */ - uci_load_history(ctx, NULL, true); + if (!uci_load_history(ctx, NULL, true)) + goto done; } } @@ -697,8 +423,6 @@ done: uci_close_stream(f); if (ctx->errno) UCI_THROW(ctx, ctx->errno); - - return 0; } @@ -719,7 +443,7 @@ static inline char *get_filename(char *path) return p; } -int uci_list_configs(struct uci_context *ctx, char ***list) +static char **uci_list_config_files(struct uci_context *ctx) { char **configs; glob_t globbuf; @@ -727,8 +451,6 @@ int uci_list_configs(struct uci_context *ctx, char ***list) char *buf; char *dir; - UCI_HANDLE_ERR(ctx); - dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*")); sprintf(dir, "%s/*", ctx->confdir); if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0) @@ -758,9 +480,57 @@ int uci_list_configs(struct uci_context *ctx, char ***list) strcpy(buf, p); buf += strlen(buf) + 1; } - *list = configs; free(dir); + return configs; +} - return 0; +static struct uci_package *uci_file_load(struct uci_context *ctx, const char *name) +{ + struct uci_package *package = NULL; + char *filename; + bool confdir; + FILE *file = NULL; + + switch (name[0]) { + case '.': + /* relative path outside of /etc/config */ + if (name[1] != '/') + UCI_THROW(ctx, UCI_ERR_NOTFOUND); + /* fall through */ + case '/': + /* absolute path outside of /etc/config */ + filename = uci_strdup(ctx, name); + name = strrchr(name, '/') + 1; + confdir = false; + break; + default: + /* config in /etc/config */ + filename = uci_config_path(ctx, name); + confdir = true; + break; + } + + file = uci_open_stream(ctx, filename, SEEK_SET, false, false); + ctx->errno = 0; + UCI_TRAP_SAVE(ctx, done); + UCI_INTERNAL(uci_import, ctx, file, name, &package, true); + UCI_TRAP_RESTORE(ctx); + + if (package) { + package->path = filename; + package->has_history = confdir; + uci_load_history(ctx, package, false); + } + +done: + uci_close_stream(file); + if (ctx->errno) + UCI_THROW(ctx, ctx->errno); + return package; } +static UCI_BACKEND(uci_file_backend, "file", + .load = uci_file_load, + .commit = uci_file_commit, + .list_configs = uci_list_config_files, +);