Browse Source

implement extended uci lookup syntax

Felix Fietkau 15 years ago
parent
commit
976e6db54e
4 changed files with 186 additions and 45 deletions
  1. 47 43
      cli.c
  2. 109 0
      list.c
  3. 27 0
      uci.h
  4. 3 2
      util.c

+ 47 - 43
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, &section, &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, &section, &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);

+ 109 - 0
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, &section, &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;

+ 27 - 0
uci.h

@@ -148,6 +148,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
@@ -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,

+ 3 - 2
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;