Browse Source

ucimap: add custom free() callbacks for options, only used on custom datatypes

Felix Fietkau 14 years ago
parent
commit
6c05c721c7
3 changed files with 82 additions and 13 deletions
  1. 22 5
      ucimap-example.c
  2. 53 7
      ucimap.c
  3. 7 1
      ucimap.h

+ 22 - 5
ucimap-example.c

@@ -27,7 +27,7 @@ struct uci_network {
 	const char *name;
 	const char *proto;
 	const char *ifname;
-	unsigned char ipaddr[4];
+	unsigned char *ipaddr;
 	int test;
 	bool enabled;
 	struct ucimap_list *aliases;
@@ -44,13 +44,18 @@ struct uci_alias {
 static int
 network_parse_ip(void *section, struct uci_optmap *om, union ucimap_data *data, const char *str)
 {
-	unsigned char *target = (unsigned char *) data->s;
+	unsigned char *target;
 	int tmp[4];
 	int i;
 
 	if (sscanf(str, "%d.%d.%d.%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3]) != 4)
 		return -1;
 
+	target = malloc(4);
+	if (!target)
+		return -1;
+
+	*data->data = target;
 	for (i = 0; i < 4; i++)
 		target[i] = (char) tmp[i];
 
@@ -61,7 +66,7 @@ static int
 network_format_ip(void *sction, struct uci_optmap *om, union ucimap_data *data, char **str)
 {
 	static char buf[16];
-	unsigned char *ip = (unsigned char *) data->s;
+	unsigned char *ip = (unsigned char *) data->data[0];
 
 	sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
 	*str = buf;
@@ -69,6 +74,12 @@ network_format_ip(void *sction, struct uci_optmap *om, union ucimap_data *data,
 	return 0;
 }
 
+static void
+network_free_ip(void *section, struct uci_optmap *om, void *ptr)
+{
+	free(ptr);
+}
+
 static int
 network_init_interface(struct uci_map *map, void *section, struct uci_section *s)
 {
@@ -151,6 +162,7 @@ static struct my_optmap network_interface_options[] = {
 			.name = "ipaddr",
 			.parse = network_parse_ip,
 			.format = network_format_ip,
+			.free = network_free_ip,
 		}
 	},
 	{
@@ -234,7 +246,13 @@ int main(int argc, char **argv)
 	ucimap_parse(&network_map, pkg);
 
 	list_for_each(p, &ifs) {
+		const unsigned char *ipaddr;
+
 		net = list_entry(p, struct uci_network, list);
+		ipaddr = net->ipaddr;
+		if (!ipaddr)
+			ipaddr = (const unsigned char *) "\x00\x00\x00\x00";
+
 		printf("New network section '%s'\n"
 			"	type: %s\n"
 			"	ifname: %s\n"
@@ -244,8 +262,7 @@ int main(int argc, char **argv)
 			net->name,
 			net->proto,
 			net->ifname,
-			net->ipaddr[0],	net->ipaddr[1],
-			net->ipaddr[2],	net->ipaddr[3],
+			ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3],
 			net->test,
 			(net->enabled ? "on" : "off"));
 

+ 53 - 7
ucimap.c

@@ -29,6 +29,12 @@ struct uci_alloc {
 	} data;
 };
 
+struct uci_alloc_custom {
+	void *section;
+	struct uci_optmap *om;
+	void *ptr;
+};
+
 struct uci_fixup {
 	struct list_head list;
 	struct uci_sectionmap *sm;
@@ -153,6 +159,14 @@ ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
 		ucimap_free_item(&sd->allocmap[i]);
 	}
 
+	if (sd->alloc_custom) {
+		for (i = 0; i < sd->alloc_custom_len; i++) {
+			struct uci_alloc_custom *a = &sd->alloc_custom[i];
+			a->om->free(a->section, a->om, a->ptr);
+		}
+		free(sd->alloc_custom);
+	}
+
 	free(sd->allocmap);
 	free(sd);
 }
@@ -236,6 +250,16 @@ ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct
 	list_add_tail(&f->list, &map->fixup);
 }
 
+static void
+ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
+{
+	struct uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
+
+	a->section = ucimap_section_ptr(sd);
+	a->om = om;
+	a->ptr = ptr;
+}
+
 static void
 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
 {
@@ -296,6 +320,10 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s
 	if (om->parse) {
 		if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
 			return;
+		if (ucimap_is_custom(om->type) && om->free) {
+			if (tdata.ptr != data->ptr)
+				ucimap_add_custom_alloc(sd, om, data->ptr);
+		}
 	}
 	if (ucimap_is_custom(om->type))
 		return;
@@ -448,6 +476,15 @@ failed:
 	return false;
 }
 
+static void
+ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
+{
+	if (ucimap_is_alloc(om->type))
+		(*n_alloc)++;
+	else if (ucimap_is_custom(om->type) && om->free)
+		(*n_custom)++;
+}
+
 int
 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
 {
@@ -455,6 +492,7 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim
 	char *section_name;
 	void *section;
 	int n_alloc = 2;
+	int n_alloc_custom = 0;
 	int err;
 
 	INIT_LIST_HEAD(&sd->list);
@@ -469,6 +507,7 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim
 			union ucimap_data *data;
 			struct uci_element *e;
 			int n_elements = 0;
+			int n_elements_custom = 0;
 			int size;
 
 			data = ucimap_get_data(sd, om);
@@ -481,7 +520,7 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim
 
 				if (o->type == UCI_TYPE_LIST) {
 					uci_foreach_element(&o->v.list, tmp) {
-						n_elements++;
+						ucimap_count_alloc(om, &n_elements, &n_elements_custom);
 					}
 				} else if ((o->type == UCI_TYPE_STRING) &&
 				           ucimap_is_list_auto(om->type)) {
@@ -494,43 +533,50 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim
 							break;
 
 						n_elements++;
+						ucimap_count_alloc(om, &n_elements, &n_elements_custom);
 
 						while (*data && !isspace(*data))
 							data++;
 					} while (*data);
 
 					/* for the duplicated data string */
-					if (n_elements > 0)
+					if (n_elements)
 						n_alloc++;
 				}
 				break;
 			}
 			/* add one more for the ucimap_list */
 			n_alloc += n_elements + 1;
+			n_alloc_custom += n_elements_custom;
 			size = sizeof(struct ucimap_list) +
 				n_elements * sizeof(union ucimap_data);
 			data->list = malloc(size);
 			memset(data->list, 0, size);
-		} else if (ucimap_is_alloc(om->type)) {
-			n_alloc++;
+		} else {
+			ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
 		}
 	}
 
-	sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
+	sd->allocmap = calloc(n_alloc, sizeof(struct uci_alloc));
 	if (!sd->allocmap)
 		goto error_mem;
 
+	if (n_alloc_custom > 0) {
+		sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom));
+		if (!sd->alloc_custom)
+			goto error_mem;
+	}
+
 	section_name = strdup(s->e.name);
 	if (!section_name)
 		goto error_mem;
 
 	sd->section_name = section_name;
 
-	sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
+	sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
 	if (!sd->cmap)
 		goto error_mem;
 
-	memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
 	ucimap_add_alloc(sd, (void *)section_name);
 	ucimap_add_alloc(sd, (void *)sd->cmap);
 	ucimap_foreach_option(sm, om) {

+ 7 - 1
ucimap.h

@@ -84,6 +84,8 @@
 struct uci_sectionmap;
 struct uci_optmap;
 struct ucimap_list;
+struct uci_alloc;
+struct uci_alloc_custom;
 
 struct uci_map {
 	struct uci_sectionmap **sections;
@@ -121,6 +123,7 @@ union ucimap_data {
 	bool b;
 	char *s;
 	void *ptr;
+	void **data;
 	struct ucimap_list *list;
 };
 
@@ -132,7 +135,9 @@ struct ucimap_section_data {
 
 	/* list of allocations done by ucimap */
 	struct uci_alloc *allocmap;
-	unsigned long allocmap_len;
+	struct uci_alloc_custom *alloc_custom;
+	unsigned int allocmap_len;
+	unsigned int alloc_custom_len;
 
 	/* map for changed fields */
 	unsigned char *cmap;
@@ -181,6 +186,7 @@ struct uci_optmap {
 	int detected_type;
 	int (*parse)(void *section, struct uci_optmap *om, union ucimap_data *data, const char *string);
 	int (*format)(void *section, struct uci_optmap *om, union ucimap_data *data, char **string);
+	void (*free)(void *section, struct uci_optmap *om, void *ptr);
 	union {
 		struct {
 			int base;