diff options
-rw-r--r-- | cli.c | 35 | ||||
-rw-r--r-- | file.c | 44 | ||||
-rw-r--r-- | history.c | 69 | ||||
-rw-r--r-- | list.c | 44 | ||||
-rw-r--r-- | uci.h | 9 | ||||
-rw-r--r-- | util.c | 22 |
6 files changed, 149 insertions, 74 deletions
@@ -43,6 +43,7 @@ enum { CMD_EXPORT, CMD_COMMIT, /* other cmds */ + CMD_ADD, CMD_IMPORT, CMD_HELP, }; @@ -58,6 +59,7 @@ static void uci_usage(void) "\texport [<config>]\n" "\timport [<config>]\n" "\tchanges [<config>]\n" + "\tadd <config> <section-type>\n" "\tshow [<config>[.<section>[.<option>]]]\n" "\tget <config>.<section>[.<option>]\n" "\tset <config>.<section>[.<option>]=<value>\n" @@ -233,6 +235,33 @@ static int uci_do_package_cmd(int cmd, int argc, char **argv) return 0; } +static int uci_do_add(int argc, char **argv) +{ + struct uci_package *p = NULL; + struct uci_section *s = NULL; + int ret; + + if (argc != 3) + return 255; + + ret = uci_load(ctx, argv[1], &p); + if (ret != UCI_OK) + goto done; + + ret = uci_add_section(ctx, p, argv[2], &s); + if (ret != UCI_OK) + goto done; + + ret = uci_save(ctx, p); + +done: + if (ret != UCI_OK) + cli_perror(); + else if (s) + fprintf(stdout, "%s\n", s->e.name); + + return ret; +} static int uci_do_section_cmd(int cmd, int argc, char **argv) { @@ -258,6 +287,8 @@ static int uci_do_section_cmd(int cmd, int argc, char **argv) } if (uci_parse_tuple(ctx, argv[1], &package, §ion, &option, ptr) != UCI_OK) return 1; + if (section && !section[0]) + return 1; if (uci_load(ctx, package, &p) != UCI_OK) { cli_perror(); @@ -409,6 +440,8 @@ static int uci_cmd(int argc, char **argv) cmd = CMD_IMPORT; else if (!strcasecmp(argv[0], "help")) cmd = CMD_HELP; + else if (!strcasecmp(argv[0], "add")) + cmd = CMD_ADD; else cmd = -1; @@ -426,6 +459,8 @@ static int uci_cmd(int argc, char **argv) return uci_do_package_cmd(cmd, argc, argv); case CMD_IMPORT: return uci_do_import(argc, argv); + case CMD_ADD: + return uci_do_add(argc, argv); case CMD_HELP: uci_usage(); return 0; @@ -119,50 +119,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) */ @@ -88,30 +88,42 @@ int uci_add_history_path(struct uci_context *ctx, const char *dir) return 0; } -static inline void uci_parse_history_tuple(struct uci_context *ctx, char **buf, char **package, char **section, char **option, char **value, bool *delete, bool *rename) +static inline void uci_parse_history_tuple(struct uci_context *ctx, char **buf, char **package, char **section, char **option, char **value, int *cmd) { + int c = UCI_CMD_CHANGE; + if (**buf == '-') { - if (delete) - *delete = true; + c = UCI_CMD_REMOVE; *buf += 1; } else if (**buf == '@') { - if (rename) - *rename = true; + c = UCI_CMD_RENAME; + *buf += 1; + } else if (**buf == '+') { + /* UCI_CMD_ADD is used for anonymous sections */ + c = UCI_CMD_ADD; *buf += 1; } + if (cmd) + *cmd = c; UCI_INTERNAL(uci_parse_tuple, ctx, *buf, package, section, option, value); + if (!*section[0]) + UCI_THROW(ctx, UCI_ERR_PARSE); + } + static void uci_parse_history_line(struct uci_context *ctx, struct uci_package *p, char *buf) { + struct uci_element *e = NULL; bool delete = false; bool rename = false; char *package = NULL; char *section = NULL; char *option = NULL; char *value = NULL; + int cmd; - uci_parse_history_tuple(ctx, &buf, &package, §ion, &option, &value, &delete, &rename); + uci_parse_history_tuple(ctx, &buf, &package, §ion, &option, &value, &cmd); if (!package || (strcmp(package, p->e.name) != 0)) goto error; if (!uci_validate_name(section)) @@ -121,27 +133,23 @@ static void uci_parse_history_line(struct uci_context *ctx, struct uci_package * if (rename && !uci_validate_str(value, (option || delete))) goto error; - if (ctx->flags & UCI_FLAG_SAVED_HISTORY) { - int cmd; - - /* NB: no distinction between CMD_CHANGE and CMD_ADD possible at this point */ - if(delete) - cmd = UCI_CMD_REMOVE; - else if (rename) - cmd = UCI_CMD_RENAME; - else - cmd = UCI_CMD_CHANGE; - + if (ctx->flags & UCI_FLAG_SAVED_HISTORY) uci_add_history(ctx, &p->saved_history, cmd, section, option, value); - } - if (rename) + switch(cmd) { + case UCI_CMD_RENAME: UCI_INTERNAL(uci_rename, ctx, p, section, option, value); - else if (delete) + break; + case UCI_CMD_REMOVE: UCI_INTERNAL(uci_delete, ctx, p, section, option); - else - UCI_INTERNAL(uci_set, ctx, p, section, option, value, NULL); - + break; + case UCI_CMD_ADD: + case UCI_CMD_CHANGE: + UCI_INTERNAL(uci_set, ctx, p, section, option, value, &e); + if (!option && e && (cmd == UCI_CMD_ADD)) + uci_to_section(e)->anonymous = true; + break; + } return; error: UCI_THROW(ctx, UCI_ERR_PARSE); @@ -274,7 +282,7 @@ static void uci_filter_history(struct uci_context *ctx, const char *name, char * e = uci_alloc_generic(ctx, UCI_TYPE_HISTORY, pctx->buf, sizeof(struct uci_element)); uci_list_add(&list, &e->list); - uci_parse_history_tuple(ctx, &buf, &p, &s, &o, &v, NULL, NULL); + uci_parse_history_tuple(ctx, &buf, &p, &s, &o, &v, NULL); if (section) { if (!s || (strcmp(section, s) != 0)) continue; @@ -377,10 +385,19 @@ int uci_save(struct uci_context *ctx, struct uci_package *p) uci_foreach_element_safe(&p->history, tmp, e) { struct uci_history *h = uci_to_history(e); - if (h->cmd == UCI_CMD_REMOVE) + switch(h->cmd) { + case UCI_CMD_REMOVE: fprintf(f, "-"); - else if (h->cmd == UCI_CMD_RENAME) + break; + case UCI_CMD_RENAME: fprintf(f, "@"); + break; + case UCI_CMD_ADD: + fprintf(f, "+"); + break; + default: + break; + } fprintf(f, "%s.%s", p->e.name, h->section); if (e->name) @@ -115,6 +115,34 @@ uci_free_option(struct uci_option *o) uci_free_element(&o->e); } +/* fix up an unnamed section, e.g. after adding options to it */ +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); +} + static struct uci_section * uci_alloc_section(struct uci_package *p, const char *type, const char *name) { @@ -131,6 +159,7 @@ uci_alloc_section(struct uci_package *p, const char *type, const char *name) strcpy(s->type, type); if (name == NULL) s->anonymous = true; + p->n_section++; uci_list_add(&p->sections, &s->e.list); @@ -370,6 +399,19 @@ int uci_rename(struct uci_context *ctx, struct uci_package *p, char *section, ch return 0; } +int uci_add_section(struct uci_context *ctx, struct uci_package *p, char *type, struct uci_section **res) +{ + struct uci_section *s; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, p != NULL); + s = uci_alloc_section(p, type, NULL); + uci_fixup_section(ctx, s); + *res = s; + uci_add_history(ctx, &p->history, UCI_CMD_ADD, s->e.name, NULL, type); + + return 0; +} int uci_delete(struct uci_context *ctx, struct uci_package *p, char *section, char *option) { @@ -452,7 +494,7 @@ notfound: /* now add the missing entry */ if (!internal && p->confdir) - uci_add_history(ctx, &p->history, UCI_CMD_ADD, section, option, value); + uci_add_history(ctx, &p->history, UCI_CMD_CHANGE, section, option, value); if (s) { o = uci_alloc_option(s, option, value); if (result) @@ -157,6 +157,15 @@ extern int uci_cleanup(struct uci_context *ctx); extern int uci_lookup(struct uci_context *ctx, struct uci_element **res, struct uci_package *package, char *section, char *option); /** + * uci_add_section: Add an unnamed section + * @ctx: uci context + * @p: package to add the section to + * @type: section type + * @res: pointer to store a reference to the new section in + */ +extern int uci_add_section(struct uci_context *ctx, struct uci_package *p, char *type, struct uci_section **res); + +/** * uci_set_element_value: Replace an element's value with a new one * @ctx: uci context * @element: pointer to an uci_element struct pointer @@ -13,8 +13,8 @@ */ /* - * This file contains wrappers to standard functions, which - * throw exceptions upon failure. + * This file contains misc utility functions and wrappers to standard + * functions, which throw exceptions upon failure. */ #include <sys/types.h> #include <sys/stat.h> @@ -59,6 +59,22 @@ static char *uci_strdup(struct uci_context *ctx, const char *str) return ptr; } +/* 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); +} + /* * validate strings for names and types, reject special characters * for names, only alphanum and _ is allowed (shell compatibility) @@ -125,7 +141,7 @@ lastval: *value = last; } - if (*section && !uci_validate_name(*section)) + if (*section && *section[0] && !uci_validate_name(*section)) goto error; if (*option && !uci_validate_name(*option)) goto error; |