Browse Source

ucimap: add helper function for resizing lists and freeing items (both using ucimap internal allocation list), add test coverage for config change operations

Felix Fietkau 14 years ago
parent
commit
25b34febb4

+ 0 - 2
test/config/network

@@ -9,8 +9,6 @@ config 'interface' 'lan'
 	option 'ifname' 'eth0'
 	option 'test' '123'
 	option 'enabled' 'off'
-	list 'aliases' 'a'
-	list 'aliases' 'b'
 	option 'ipaddr' '2.3.4.5'
 
 config 'interface' 'wan'

+ 1 - 2
test/references/ucimap_example.result → test/references/ucimap_example_1.result

@@ -12,5 +12,4 @@ New network section 'wan'
 	ipaddr: 0.0.0.0
 	test: -1
 	enabled: on
-New alias: c
-New alias: d
+Configured aliases: c d

+ 15 - 0
test/references/ucimap_example_2.result

@@ -0,0 +1,15 @@
+New network section 'lan'
+	type: static
+	ifname: eth0
+	ipaddr: 0.0.0.0
+	test: 123
+	enabled: off
+New alias: a
+New alias: b
+New network section 'wan'
+	type: dhcp
+	ifname: eth1
+	ipaddr: 0.0.0.0
+	test: -1
+	enabled: on
+Configured aliases: c d

+ 6 - 2
test/tests.d/060-ucimap_example

@@ -1,5 +1,9 @@
 test_ucimap_example()
 {
-	( cd ..; ./ucimap-example ) > "${TMP_DIR}/ucimap_example.result"
-	assertSameFile "${TMP_DIR}/ucimap_example.result" "${REF_DIR}/ucimap_example.result"
+	rm -rf ./save
+	( cd ..; ./ucimap-example -s ) > "${TMP_DIR}/ucimap_example.result"
+	assertSameFile "${TMP_DIR}/ucimap_example.result" "${REF_DIR}/ucimap_example_1.result"
+	( cd ..; ./ucimap-example -s ) > "${TMP_DIR}/ucimap_example.result"
+	assertSameFile "${TMP_DIR}/ucimap_example.result" "${REF_DIR}/ucimap_example_2.result"
+	rm -rf ./save
 }

+ 33 - 10
ucimap-example.c

@@ -68,8 +68,12 @@ network_format_ip(void *sction, struct uci_optmap *om, union ucimap_data *data,
 	static char buf[16];
 	unsigned char *ip = (unsigned char *) data->data[0];
 
-	sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
-	*str = buf;
+	if (ip) {
+		sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+		*str = buf;
+	} else {
+		*str = NULL;
+	}
 
 	return 0;
 }
@@ -234,12 +238,18 @@ int main(int argc, char **argv)
 	struct list_head *p;
 	struct uci_network *net;
 	struct uci_alias *alias;
+	bool set = false;
 	int i;
 
 	INIT_LIST_HEAD(&ifs);
 	ctx = uci_alloc_context();
 	ucimap_init(&network_map);
 
+	if ((argc >= 2) && !strcmp(argv[1], "-s")) {
+		uci_set_savedir(ctx, "./test/save");
+		set = true;
+	}
+
 	uci_set_confdir(ctx, "./test/config");
 	uci_load(ctx, "network", &pkg);
 
@@ -266,16 +276,29 @@ int main(int argc, char **argv)
 			net->test,
 			(net->enabled ? "on" : "off"));
 
-		for (i = 0; i < net->aliases->n_items; i++) {
-			alias = net->aliases->item[i].ptr;
+		if (net->aliases->n_items > 0) {
+			printf("Configured aliases:");
+			for (i = 0; i < net->aliases->n_items; i++) {
+				alias = net->aliases->item[i].ptr;
+				printf(" %s", alias->name);
+			}
+			printf("\n");
+		}
+		list_for_each_entry(alias, &net->alias, list) {
+			for (i = 0; i < net->aliases->n_items; i++) {
+				if (alias == net->aliases->item[i].ptr)
+					goto next_alias;
+			}
 			printf("New alias: %s\n", alias->name);
+next_alias:
+			continue;
+		}
+		if (set && !strcmp(net->name, "lan")) {
+			ucimap_free_item(&net->map, &net->ipaddr);
+			ucimap_set_changed(&net->map, &net->ipaddr);
+			ucimap_store_section(&network_map, pkg, &net->map);
+			uci_save(ctx, pkg);
 		}
-#if 0
-		memcpy(net->ipaddr, "\x01\x03\x04\x05", 4);
-		ucimap_set_changed(&net->map, &net->ipaddr);
-		ucimap_store_section(&network_map, pkg, &net->map);
-		uci_save(ctx, pkg);
-#endif
 	}
 
 

+ 95 - 19
ucimap.c

@@ -19,14 +19,12 @@
 #include <limits.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <errno.h>
 #include "ucimap.h"
 #include "uci_internal.h"
 
 struct uci_alloc {
-	enum ucimap_type type;
-	union {
-		void **ptr;
-	} data;
+	void *ptr;
 };
 
 struct uci_alloc_custom {
@@ -123,23 +121,11 @@ ucimap_init(struct uci_map *map)
 	return 0;
 }
 
-static void
-ucimap_free_item(struct uci_alloc *a)
-{
-	switch(a->type & UCIMAP_TYPE) {
-	case UCIMAP_SIMPLE:
-	case UCIMAP_LIST:
-		free(a->data.ptr);
-		break;
-	}
-}
-
 static void
 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
 {
 	struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
-	a->type = UCIMAP_SIMPLE;
-	a->data.ptr = ptr;
+	a->ptr = ptr;
 }
 
 void
@@ -156,7 +142,7 @@ ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
 		sd->sm->free(map, section);
 
 	for (i = 0; i < sd->allocmap_len; i++) {
-		ucimap_free_item(&sd->allocmap[i]);
+		free(sd->allocmap[i].ptr);
 	}
 
 	if (sd->alloc_custom) {
@@ -228,6 +214,81 @@ ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
 	return true;
 }
 
+void
+ucimap_free_item(struct ucimap_section_data *sd, void *item)
+{
+	struct uci_alloc_custom *ac;
+	struct uci_alloc *a;
+	void *ptr = *((void **) item);
+	int i;
+
+	if (!ptr)
+		return;
+
+	*((void **)item) = NULL;
+	for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
+		if (a->ptr != ptr)
+			continue;
+
+		if (i != sd->allocmap_len - 1)
+			a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
+
+		sd->allocmap_len--;
+		return;
+	}
+
+	for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
+		if (ac->ptr != ptr)
+			continue;
+
+		if (i != sd->alloc_custom_len - 1)
+			memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
+				sizeof(struct uci_alloc_custom));
+
+		ac->om->free(ac->section, ac->om, ac->ptr);
+		sd->alloc_custom_len--;
+		return;
+	}
+}
+
+int
+ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
+{
+	struct ucimap_list *new;
+	struct uci_alloc *a;
+	int i, offset = 0;
+	int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
+
+	if (!*list) {
+		new = calloc(1, size);
+
+		ucimap_add_alloc(sd, new);
+		goto set;
+	}
+
+	for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
+		if (a->ptr != *list)
+			continue;
+
+		goto realloc;
+	}
+	return -ENOENT;
+
+realloc:
+	if (items > (*list)->size)
+		offset = (items - (*list)->size) * sizeof(union ucimap_data);
+
+	a->ptr = realloc(a->ptr, size);
+	if (offset)
+		memset((char *) a->ptr + offset, 0, size - offset);
+	new = a->ptr;
+
+set:
+	new->size = items;
+	*list = new;
+	return 0;
+}
+
 static void
 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
 {
@@ -269,8 +330,15 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s
 	char *s;
 	int val;
 
-	if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
+	if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
+		if (unlikely(data->list->size <= data->list->n_items)) {
+			/* should not happen */
+			DPRINTF("ERROR: overflow while filling a list\n");
+			return;
+		}
+
 		data = &data->list->item[data->list->n_items++];
+	}
 
 	switch(om->type & UCIMAP_SUBTYPE) {
 	case UCIMAP_STRING:
@@ -558,7 +626,12 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim
 			n_alloc_custom += n_elements_custom;
 			size = sizeof(struct ucimap_list) +
 				n_elements * sizeof(union ucimap_data);
+
 			data->list = malloc(size);
+			if (!data->list)
+				goto error_mem;
+
+			data->list->size = n_elements;
 			memset(data->list, 0, size);
 		} else {
 			ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
@@ -719,6 +792,9 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_s
 
 			if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
 				continue;
+
+			if (!str)
+				str = "";
 		}
 		if (!str)
 			continue;

+ 3 - 6
ucimap.h

@@ -164,12 +164,6 @@ struct ucimap_section_data {
 	bool done;
 };
 
-
-struct uci_listmap {
-	struct list_head list;
-	union ucimap_data data;
-};
-
 struct uci_sectionmap {
 	/* type string for the uci section */
 	const char *type;
@@ -228,6 +222,7 @@ struct uci_optmap {
 
 struct ucimap_list {
 	int n_items;
+	int size;
 	union ucimap_data item[];
 };
 
@@ -238,5 +233,7 @@ extern int ucimap_store_section(struct uci_map *map, struct uci_package *p, stru
 extern void ucimap_parse(struct uci_map *map, struct uci_package *pkg);
 extern int ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s);
 extern void ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd);
+extern int ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items);
+extern void ucimap_free_item(struct ucimap_section_data *sd, void *item);
 
 #endif