From: Felix Fietkau Date: Sun, 17 Aug 2008 16:03:13 +0000 (+0200) Subject: implement extended uci lookup syntax X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=commitdiff_plain;h=976e6db54ee86c7198e68b48cea2f9f1db9c3425 implement extended uci lookup syntax --- diff --git a/cli.c b/cli.c index 6e2a5ff..9787988 100644 --- a/cli.c +++ b/cli.c @@ -142,27 +142,30 @@ static void uci_show_changes(struct uci_package *p) static int package_cmd(int cmd, char *tuple) { - struct uci_package *p = NULL; + struct uci_package *p; + struct uci_section *s; struct uci_element *e = NULL; - char *package = NULL; - char *section = NULL; - char *option = NULL; - char **ptr = NULL; - int ret; - if (uci_parse_tuple(ctx, tuple, &package, §ion, &option, ptr) != UCI_OK) - return 1; - if (section && !section[0]) - return 1; - - ret = uci_load(ctx, package, &p); - - if (ret != UCI_OK) { + if (uci_lookup_ext(ctx, &e, tuple) != UCI_OK) { cli_perror(); return 1; } - if (!p) + switch(e->type) { + case UCI_TYPE_PACKAGE: + p = uci_to_package(e); + break; + case UCI_TYPE_SECTION: + s = uci_to_section(e); + p = s->package; + break; + case UCI_TYPE_OPTION: + s = uci_to_option(e)->section; + p = s->package; + break; + default: return 0; + } + switch(cmd) { case CMD_CHANGES: uci_show_changes(p); @@ -177,14 +180,10 @@ static int package_cmd(int cmd, char *tuple) uci_export(ctx, stdout, p, true); break; case CMD_SHOW: - if (!section) { - uci_show_package(p); - return 0; - } - if (uci_lookup(ctx, &e, p, section, option) != UCI_OK) - return 1; - switch(e->type) { + case UCI_TYPE_PACKAGE: + uci_show_package(p); + break; case UCI_TYPE_SECTION: uci_show_section(uci_to_section(e)); break; @@ -300,52 +299,57 @@ done: static int uci_do_section_cmd(int cmd, int argc, char **argv) { struct uci_package *p = NULL; + struct uci_section *s = NULL; struct uci_element *e = NULL; - char *package = NULL; char *section = NULL; char *option = NULL; char *value = NULL; - char **ptr = NULL; int ret = UCI_OK; if (argc != 2) return 255; - switch(cmd) { - case CMD_SET: - case CMD_RENAME: - ptr = &value; - break; - default: - break; + value = strchr(argv[1], '='); + if (value) { + *value = 0; + value++; + if (!uci_validate_text(value)) + return 1; } - if (uci_parse_tuple(ctx, argv[1], &package, §ion, &option, ptr) != UCI_OK) - return 1; - if (section && !section[0]) + + if (value && (cmd != CMD_SET) && (cmd != CMD_RENAME)) return 1; - if (uci_load(ctx, package, &p) != UCI_OK) { + if (uci_lookup_ext(ctx, &e, argv[1]) != UCI_OK) { cli_perror(); return 1; } - if (!p) - return 0; + + switch(e->type) { + case UCI_TYPE_SECTION: + s = uci_to_section(e); + break; + case UCI_TYPE_OPTION: + option = e->name; + s = uci_to_option(e)->section; + break; + default: + return 1; + } + section = s->e.name; + p = s->package; switch(cmd) { case CMD_GET: - if (uci_lookup(ctx, &e, p, section, option) != UCI_OK) - return 1; - switch(e->type) { case UCI_TYPE_SECTION: - value = uci_to_section(e)->type; + value = s->type; break; case UCI_TYPE_OPTION: value = uci_to_option(e)->value; break; default: - /* should not happen */ - return 1; + break; } /* throw the value to stdout */ printf("%s\n", value); diff --git a/list.c b/list.c index b20ee71..41cfc24 100644 --- a/list.c +++ b/list.c @@ -228,6 +228,115 @@ static struct uci_element *uci_lookup_list(struct uci_list *list, const char *na return NULL; } +int uci_lookup_ext(struct uci_context *ctx, struct uci_element **res, char *ptr) +{ + struct uci_package *p = NULL; + struct uci_element *e; + struct uci_section *s; + char *package = NULL; + char *section = NULL; + char *option = NULL; + char *idxstr, *t; + int idx, c; + + UCI_HANDLE_ERR(ctx); + UCI_ASSERT(ctx, res != NULL); + UCI_ASSERT(ctx, ptr != NULL); + + UCI_INTERNAL(uci_parse_tuple, ctx, ptr, &package, §ion, &option, NULL); + + /* look up the package first */ + e = uci_lookup_list(&ctx->root, package); + if (!e) { + UCI_INTERNAL(uci_load, ctx, package, &p); + if (!p) + goto notfound; + e = &p->e; + } else { + p = uci_to_package(e); + } + + if (!section) + goto done; + + /* if the section name validates as a regular name, pass through + * to the regular uci_lookup function call */ + if (!*section || uci_validate_name(section)) { + UCI_INTERNAL(uci_lookup, ctx, &e, p, section, option); + goto done; + } + + /* name did not validate, that means we have an extended lookup call + * parse it here. for now only the section index syntax is supported */ + if (section[0] != '@') + goto error; + + section++; + + /* parse the section index part */ + idxstr = strchr(section, '['); + if (!idxstr) + goto error; + *idxstr = 0; + idxstr++; + + t = strchr(idxstr, ']'); + if (!t) + goto error; + if (t[1] != 0) + goto error; + *t = 0; + + t = NULL; + idx = strtol(idxstr, &t, 10); + if (t && *t) + goto error; + + if (!*section) + section = NULL; + if (section && !uci_validate_str(section, false)) + goto error; + + /* if the given index is negative, it specifies the section number from + * the end of the list */ + if (idx < 0) { + c = 0; + uci_foreach_element(&p->sections, e) { + s = uci_to_section(e); + if (section && (strcmp(s->type, section) != 0)) + continue; + + c++; + } + idx += c; + } + + c = 0; + uci_foreach_element(&p->sections, e) { + s = uci_to_section(e); + if (section && (strcmp(s->type, section) != 0)) + continue; + + if (idx == c) + goto found; + c++; + } + goto notfound; + +found: + if (option) + e = uci_lookup_list(&s->options, option); +done: + *res = e; + return 0; + +notfound: + UCI_THROW(ctx, UCI_ERR_NOTFOUND); +error: + UCI_THROW(ctx, UCI_ERR_INVAL); + return 0; +} + int uci_lookup(struct uci_context *ctx, struct uci_element **res, struct uci_package *p, const char *section, const char *option) { struct uci_element *e; diff --git a/uci.h b/uci.h index cb9fd66..16c6966 100644 --- a/uci.h +++ b/uci.h @@ -149,6 +149,24 @@ extern int uci_unload(struct uci_context *ctx, struct uci_package *p); extern int uci_lookup(struct uci_context *ctx, struct uci_element **res, struct uci_package *package, const char *section, const char *option); /** + * uci_lookup_ext: Extended lookup for an uci element + * + * @ctx: uci context + * @res: where to store the result + * @ptr: uci pointer tuple + * + * Looks up an element using the extended syntax, which is a superset of what + * uci_parse_tuple accepts. It can look up sections by an index with an optional + * type. + * + * Examples: + * network.@interface[0].ifname ('ifname' option of the first interface section) + * network.@interface[-1] (last interface section) + * Note: uci_lookup_ext will automatically load a config package if necessary + */ +extern int uci_lookup_ext(struct uci_context *ctx, struct uci_element **res, char *ptr); + +/** * uci_add_section: Add an unnamed section * @ctx: uci context * @p: package to add the section to @@ -282,6 +300,15 @@ extern int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, */ extern int uci_set_backend(struct uci_context *ctx, const char *name); +/** + * uci_validate_text: validate a value string for uci options + * @str: value + * + * this function checks whether a given string is acceptable as value + * for uci options + */ +extern bool uci_validate_text(const char *str); + /* UCI data structures */ enum uci_type { UCI_TYPE_HISTORY = 0, diff --git a/util.c b/util.c index e1ef585..dde7bfc 100644 --- a/util.c +++ b/util.c @@ -101,7 +101,7 @@ static inline bool uci_validate_name(const char *str) return uci_validate_str(str, true); } -static inline bool uci_validate_text(const char *str) +bool uci_validate_text(const char *str) { while (*str) { if ((*str == '\r') || (*str == '\n') || @@ -120,6 +120,7 @@ static void uci_alloc_parse_context(struct uci_context *ctx) int uci_parse_tuple(struct uci_context *ctx, char *str, char **package, char **section, char **option, char **value) { char *last = NULL; + bool internal = ctx->internal; UCI_HANDLE_ERR(ctx); UCI_ASSERT(ctx, str && package && section && option); @@ -155,7 +156,7 @@ lastval: *value = last; } - if (*section && *section[0] && !uci_validate_name(*section)) + if (*section && *section[0] && !internal && !uci_validate_name(*section)) goto error; if (*option && !uci_validate_name(*option)) goto error;