Browse Source

reorganize some code, add an extra command for adding unnamed sections

Felix Fietkau 16 years ago
parent
commit
9f540f2106
6 changed files with 149 additions and 74 deletions
  1. 35 0
      cli.c
  2. 0 44
      file.c
  3. 43 26
      history.c
  4. 43 1
      list.c
  5. 9 0
      uci.h
  6. 19 3
      util.c

+ 35 - 0
cli.c

@@ -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, &section, &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;

+ 0 - 44
file.c

@@ -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)
  */

+ 43 - 26
history.c

@@ -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, &section, &option, &value, &delete, &rename);
+	uci_parse_history_tuple(ctx, &buf, &package, &section, &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)

+ 43 - 1
list.c

@@ -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)

+ 9 - 0
uci.h

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

+ 19 - 3
util.c

@@ -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;