Browse Source

keep unnamed sections unnamed on export
add some safety to altering unnamed sections by generating the name from a hash of its type and its option/value pairs

Felix Fietkau 16 years ago
parent
commit
6042bb553c
3 changed files with 57 additions and 7 deletions
  1. 50 1
      file.c
  2. 6 6
      list.c
  3. 1 0
      uci.h

+ 50 - 1
file.c

@@ -323,14 +323,60 @@ 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)
  */
 static void uci_parse_config(struct uci_context *ctx, char **str)
 {
+	struct uci_section *s;
 	char *name = NULL;
 	char *type = NULL;
 
+	uci_fixup_section(ctx, ctx->pctx->section);
 	if (!ctx->pctx->package) {
 		if (!ctx->pctx->name)
 			uci_parse_error(ctx, *str, "attempting to import a file without a package name");
@@ -458,7 +504,9 @@ static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
 	uci_foreach_element(&p->sections, s) {
 		struct uci_section *sec = uci_to_section(s);
 		fprintf(stream, "\nconfig '%s'", uci_escape(ctx, sec->type));
-		fprintf(stream, " '%s'\n", uci_escape(ctx, sec->e.name));
+		if (!sec->anonymous)
+			fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name));
+		fprintf(stream, "\n");
 		uci_foreach_element(&sec->options, o) {
 			struct uci_option *opt = uci_to_option(o);
 			fprintf(stream, "\toption '%s'", uci_escape(ctx, opt->e.name));
@@ -520,6 +568,7 @@ error:
 			UCI_THROW(ctx, ctx->errno);
 	}
 
+	uci_fixup_section(ctx, ctx->pctx->section);
 	if (package)
 		*package = pctx->package;
 

+ 6 - 6
list.c

@@ -113,18 +113,18 @@ uci_alloc_section(struct uci_package *p, const char *type, const char *name)
 {
 	struct uci_context *ctx = p->ctx;
 	struct uci_section *s;
-	char buf[16];
 
-	if (!name || !name[0]) {
-		snprintf(buf, 16, "cfg%d", p->n_section);
-		name = buf;
-	}
+	if (name && !name[0])
+		name = NULL;
 
 	s = uci_alloc_element(ctx, section, name, strlen(type) + 1);
+	uci_list_init(&s->options);
 	s->type = uci_dataptr(s);
 	s->package = p;
 	strcpy(s->type, type);
-	uci_list_init(&s->options);
+	if (name == NULL)
+		s->anonymous = true;
+
 	uci_list_add(&p->sections, &s->e.list);
 
 	return s;

+ 1 - 0
uci.h

@@ -290,6 +290,7 @@ struct uci_section
 	struct uci_element e;
 	struct uci_list options;
 	struct uci_package *package;
+	bool anonymous;
 	char *type;
 };