From: Felix Fietkau Date: Fri, 28 Nov 2008 10:33:23 +0000 (+0100) Subject: add foreign key and list support for ucimap X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=99ae0f9196c8f8f334624c05a23e7e930be6c5eb;p=uci.git add foreign key and list support for ucimap --- diff --git a/ucimap-example.c b/ucimap-example.c index 4309a76..3fd3765 100644 --- a/ucimap-example.c +++ b/ucimap-example.c @@ -17,8 +17,11 @@ #include #include +struct list_head ifs; + struct uci_network { struct list_head list; + struct list_head alias; const char *name; const char *proto; @@ -26,46 +29,96 @@ struct uci_network { const char *ipaddr; int test; bool enabled; + struct list_head aliases; +}; + +struct uci_alias { + struct list_head list; + + const char *name; + struct uci_network *interface; }; static int -network_init_section(struct uci_map *map, void *section, struct uci_section *s) +network_init_interface(struct uci_map *map, void *section, struct uci_section *s) { struct uci_network *net = section; INIT_LIST_HEAD(&net->list); + INIT_LIST_HEAD(&net->alias); net->name = s->e.name; net->test = -1; return 0; } static int -network_add_section(struct uci_map *map, void *section) +network_init_alias(struct uci_map *map, void *section, struct uci_section *s) +{ + struct uci_alias *alias = section; + + INIT_LIST_HEAD(&alias->list); + alias->name = s->e.name; + return 0; +} + +static int +network_add_interface(struct uci_map *map, void *section) { struct uci_network *net = section; - struct uci_network **nptr = map->priv; - *nptr = net; + list_add(&net->list, &ifs); + return 0; } -struct uci_optmap network_smap_options[] = { +static int +network_add_alias(struct uci_map *map, void *section) +{ + struct uci_alias *a = section; + + if (a->interface) + list_add(&a->list, &a->interface->alias); + + return 0; +} + +struct uci_sectmap network_interface; +struct uci_sectmap network_alias; + +struct uci_optmap network_interface_options[] = { OPTMAP_OPTION(UCIMAP_STRING, struct uci_network, proto, .data.s.maxlen = 32), OPTMAP_OPTION(UCIMAP_STRING, struct uci_network, ifname), OPTMAP_OPTION(UCIMAP_STRING, struct uci_network, ipaddr), OPTMAP_OPTION(UCIMAP_BOOL, struct uci_network, enabled), OPTMAP_OPTION(UCIMAP_INT, struct uci_network, test), + OPTMAP_OPTION(UCIMAP_LIST | UCIMAP_SECTION, struct uci_network, aliases, .data.sm = &network_alias), }; -struct uci_sectmap network_smap[] = { - { - .type = "interface", - .options = network_smap_options, - .alloc_len = sizeof(struct uci_network), - .init_section = network_init_section, - .add_section = network_add_section, - .n_options = ARRAY_SIZE(network_smap_options), - } +struct uci_sectmap network_interface = { + .type = "interface", + .options = network_interface_options, + .alloc_len = sizeof(struct uci_network), + .init_section = network_init_interface, + .add_section = network_add_interface, + .n_options = ARRAY_SIZE(network_interface_options), +}; + +struct uci_optmap network_alias_options[] = { + OPTMAP_OPTION(UCIMAP_SECTION, struct uci_alias, interface, .data.sm = &network_interface), +}; + +struct uci_sectmap network_alias = { + .type = "alias", + .options = network_alias_options, + .alloc_len = sizeof(struct uci_network), + .init_section = network_init_alias, + .add_section = network_add_alias, + .n_options = ARRAY_SIZE(network_alias_options), +}; + +struct uci_sectmap *network_smap[] = { + &network_interface, + &network_alias, }; struct uci_map network_map = { @@ -78,20 +131,21 @@ int main(int argc, char **argv) { struct uci_context *ctx; struct uci_package *pkg; - struct uci_network *net = NULL; + struct list_head *p, *p2; + struct uci_network *net; + struct uci_alias *alias; + INIT_LIST_HEAD(&ifs); ctx = uci_alloc_context(); ucimap_init(&network_map); uci_load(ctx, "network", &pkg); - network_map.priv = &net; ucimap_parse(&network_map, pkg); - if (!net) - goto done; - - printf("New network section '%s'\n" + list_for_each(p, &ifs) { + net = list_entry(p, struct uci_network, list); + printf("New network section '%s'\n" " type: %s\n" " ifname: %s\n" " ipaddr: %s\n" @@ -104,10 +158,19 @@ int main(int argc, char **argv) net->test, (net->enabled ? "on" : "off")); - net->ipaddr = "2.3.4.5"; - ucimap_set_changed(net, &net->ipaddr); - ucimap_store_section(&network_map, pkg, net); - uci_save(ctx, pkg); + list_for_each(p2, &net->aliases) { + struct uci_listmap *li = list_entry(p2, struct uci_listmap, list); + alias = li->data.section; + printf("New alias: %s\n", alias->name); + } +#if 0 + net->ipaddr = "2.3.4.5"; + ucimap_set_changed(net, &net->ipaddr); + ucimap_store_section(&network_map, pkg, net); + uci_save(ctx, pkg); +#endif + } + done: ucimap_cleanup(&network_map); diff --git a/ucimap.c b/ucimap.c index 3c5fb31..9306036 100644 --- a/ucimap.c +++ b/ucimap.c @@ -17,14 +17,70 @@ #include #include "ucimap.h" +struct uci_alloc { + enum ucimap_type type; + union { + void **ptr; + struct list_head *list; + } data; +}; + +struct uci_fixup { + struct list_head list; + struct uci_sectmap *sm; + const char *name; + struct uci_alloc target; +}; + +struct uci_sectmap_data { + struct list_head list; + struct uci_map *map; + struct uci_sectmap *sm; + const char *section_name; + + /* list of allocations done by ucimap */ + struct uci_alloc *allocmap; + unsigned long allocmap_len; + + /* map for changed fields */ + unsigned char *cmap; + bool done; +}; + int ucimap_init(struct uci_map *map) { INIT_LIST_HEAD(&map->sdata); + INIT_LIST_HEAD(&map->fixup); return 0; } +static void +ucimap_free_item(struct uci_alloc *a) +{ + struct list_head *p, *tmp; + switch(a->type & UCIMAP_TYPE) { + case UCIMAP_SIMPLE: + free(a->data.ptr); + break; + case UCIMAP_LIST: + list_for_each_safe(p, tmp, a->data.list) { + struct uci_listmap *l = list_entry(p, struct uci_listmap, list); + list_del(p); + free(l); + } + break; + } +} + +static inline void +ucimap_add_alloc(struct uci_alloc *a, void *ptr) +{ + a->type = UCIMAP_SIMPLE; + a->data.ptr = ptr; +} + static void ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd) { @@ -39,7 +95,7 @@ ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd) sd->sm->free_section(map, section); for (i = 0; i < sd->allocmap_len; i++) { - free(sd->allocmap[i]); + ucimap_free_item(&sd->allocmap[i]); } free(sd->allocmap); @@ -57,16 +113,107 @@ ucimap_cleanup(struct uci_map *map) } } +static void +ucimap_add_fixup(struct uci_map *map, void *data, struct uci_optmap *om, const char *str) +{ + struct uci_fixup *f; + + f = malloc(sizeof(struct uci_fixup)); + if (!f) + return; + + INIT_LIST_HEAD(&f->list); + f->sm = om->data.sm; + f->name = str; + f->target.type = om->type; + f->target.data.ptr = data; + list_add(&f->list, &map->fixup); +} + +static void +ucimap_add_value(union uci_datamap *data, struct uci_optmap *om, struct uci_sectmap_data *sd, const char *str) +{ + union uci_datamap tdata; + struct list_head *list = NULL; + char *eptr = NULL; + char *s; + int val; + + if ((om->type & UCIMAP_TYPE) == UCIMAP_LIST) { + if ((om->type & UCIMAP_SUBTYPE) == UCIMAP_SECTION) { + ucimap_add_fixup(sd->map, data, om, str); + return; + } + memset(&tdata, 0, sizeof(tdata)); + list = &data->list; + data = &tdata; + } + + switch(om->type & UCIMAP_SUBTYPE) { + case UCIMAP_STRING: + if ((om->data.s.maxlen > 0) && + (strlen(str) > om->data.s.maxlen)) + return; + + s = strdup(str); + data->s = s; + ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], s); + break; + case UCIMAP_BOOL: + val = -1; + if (strcmp(str, "on")) + val = true; + else if (strcmp(str, "1")) + val = true; + else if (strcmp(str, "enabled")) + val = true; + else if (strcmp(str, "off")) + val = false; + else if (strcmp(str, "0")) + val = false; + else if (strcmp(str, "disabled")) + val = false; + if (val == -1) + return; + + data->b = val; + break; + case UCIMAP_INT: + val = strtol(str, &eptr, om->data.i.base); + if (!eptr || *eptr == '\0') + data->i = val; + else + return; + break; + case UCIMAP_SECTION: + ucimap_add_fixup(sd->map, data, om, str); + break; + } + + if ((om->type & UCIMAP_TYPE) == UCIMAP_LIST) { + struct uci_listmap *item; + + item = malloc(sizeof(struct uci_listmap)); + if (!item) + return; + + INIT_LIST_HEAD(&item->list); + memcpy(&item->data, &tdata, sizeof(tdata)); + list_add(&item->list, list); + } +} + static int ucimap_parse_options(struct uci_map *map, struct uci_sectmap *sm, struct uci_sectmap_data *sd, struct uci_section *s) { - struct uci_element *e; + struct uci_element *e, *l; struct uci_option *o; - void *section = sd; + unsigned long section; + union uci_datamap *data; int i; - section = (unsigned char *) section + sizeof(struct uci_sectmap_data); + section = (unsigned long) sd + sizeof(struct uci_sectmap_data); uci_foreach_element(&s->options, e) { struct uci_optmap *om = NULL; @@ -77,46 +224,26 @@ ucimap_parse_options(struct uci_map *map, struct uci_sectmap *sm, struct uci_sec } } if (!om) - goto next_element; + continue; + data = (union uci_datamap *) (section + om->offset); o = uci_to_option(e); - if(o->type != UCI_TYPE_STRING) - goto next_element; - - switch(om->type) { - case UCIMAP_STRING: { - char **ptr; - if ((om->data.s.maxlen > 0) && - (strlen(o->v.string) > om->data.s.maxlen)) - goto next_element; - - ptr = (char **) ((char *) section + om->offset); - *ptr = strdup(o->v.string); - sd->allocmap[sd->allocmap_len++] = *ptr; - } break; - case UCIMAP_BOOL: { - bool *ptr = (bool *)((char *)section + om->offset); - if (strcmp(o->v.string, "on")) - *ptr = true; - else if (strcmp(o->v.string, "1")) - *ptr = true; - else if (strcmp(o->v.string, "enabled")) - *ptr = true; - else - *ptr = false; - } break; - case UCIMAP_INT: { - int *ptr = (int *)((char *)section + om->offset); - char *eptr = NULL; - int val; - - val = strtol(o->v.string, &eptr, om->data.i.base); - if (!eptr || *eptr == '\0') - *ptr = val; - } break; + if ((o->type == UCI_TYPE_STRING) && ((om->type & UCIMAP_TYPE) == UCIMAP_SIMPLE)) { + ucimap_add_value(data, om, sd, o->v.string); + continue; + } + if ((o->type == UCI_TYPE_LIST) && ((om->type & UCIMAP_TYPE) == UCIMAP_LIST)) { + struct list_head *list; + + list = (struct list_head *) (section + om->offset); + INIT_LIST_HEAD(list); + sd->allocmap[sd->allocmap_len].type = UCIMAP_LIST; + sd->allocmap[sd->allocmap_len++].data.list = list; + uci_foreach_element(&o->v.list, l) { + ucimap_add_value(data, om, sd, l->name); + } + continue; } -next_element: - continue; } return 0; @@ -137,8 +264,9 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_sec memset(sd, 0, sm->alloc_len + sizeof(struct uci_sectmap_data)); INIT_LIST_HEAD(&sd->list); + sd->map = map; sd->sm = sm; - sd->allocmap = malloc(sm->n_options * sizeof(void *)); + sd->allocmap = malloc(sm->n_options * sizeof(struct uci_alloc)); if (!sd->allocmap) goto error_mem; @@ -151,8 +279,8 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_sec goto error_mem; memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options)); - sd->allocmap[sd->allocmap_len++] = (void *)sd->section_name; - sd->allocmap[sd->allocmap_len++] = (void *)sd->cmap; + ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], (void *)sd->section_name); + ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], (void *)sd->cmap); section = (char *)sd + sizeof(struct uci_sectmap_data); @@ -165,10 +293,6 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_sec if (err) goto error; - err = sm->add_section(map, section); - if (err) - goto error; - return 0; error_mem: @@ -246,7 +370,7 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) continue; ucimap_fill_ptr(&ptr, s, om->name); - switch(om->type) { + switch(om->type & UCIMAP_SUBTYPE) { case UCIMAP_STRING: str = *((char **) p); break; @@ -271,20 +395,72 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) return 0; } +void * +ucimap_find_section(struct uci_map *map, struct uci_fixup *f) +{ + struct uci_sectmap_data *sd; + struct list_head *p; + void *ret; + + list_for_each(p, &map->sdata) { + sd = list_entry(p, struct uci_sectmap_data, list); + if (sd->sm != f->sm) + continue; + if (strcmp(f->name, sd->section_name) != 0) + continue; + ret = (char *)sd + sizeof(struct uci_sectmap_data); + return ret; + } + return NULL; +} void ucimap_parse(struct uci_map *map, struct uci_package *pkg) { struct uci_element *e; + struct list_head *p, *tmp; int i; + INIT_LIST_HEAD(&map->fixup); uci_foreach_element(&pkg->sections, e) { struct uci_section *s = uci_to_section(e); for (i = 0; i < map->n_sections; i++) { - if (strcmp(s->type, map->sections[i].type) != 0) + if (strcmp(s->type, map->sections[i]->type) != 0) continue; - ucimap_parse_section(map, &map->sections[i], s); + ucimap_parse_section(map, map->sections[i], s); } } + list_for_each_safe(p, tmp, &map->fixup) { + struct uci_fixup *f = list_entry(p, struct uci_fixup, list); + void *ptr = ucimap_find_section(map, f); + struct uci_listmap *li; + + if (!ptr) + continue; + + switch(f->target.type & UCIMAP_TYPE) { + case UCIMAP_SIMPLE: + *f->target.data.ptr = ptr; + break; + case UCIMAP_LIST: + li = malloc(sizeof(struct uci_listmap)); + memset(li, 0, sizeof(struct uci_listmap)); + INIT_LIST_HEAD(&li->list); + li->data.section = ptr; + list_add(&li->list, f->target.data.list); + break; + } + } + list_for_each_safe(p, tmp, &map->sdata) { + struct uci_sectmap_data *sd = list_entry(p, struct uci_sectmap_data, list); + void *section; + + if (sd->done) + continue; + + section = (char *) sd + sizeof(struct uci_sectmap_data); + if (sd->sm->add_section(map, section) != 0) + ucimap_free_section(map, sd); + } } diff --git a/ucimap.h b/ucimap.h index 0237fb1..43f54e0 100644 --- a/ucimap.h +++ b/ucimap.h @@ -44,31 +44,39 @@ struct uci_sectmap; struct uci_optmap; struct uci_map { - struct uci_sectmap *sections; + struct uci_sectmap **sections; unsigned int n_sections; struct list_head sdata; + struct list_head fixup; void *priv; /* user data */ }; enum ucimap_type { - UCIMAP_STRING, - UCIMAP_BOOL, - UCIMAP_INT, + /* types */ + UCIMAP_SIMPLE = 0x00, + UCIMAP_LIST = 0x10, + UCIMAP_TYPE = 0xf0, /* type mask */ + + /* subtypes */ + UCIMAP_STRING = 0x0, + UCIMAP_BOOL = 0x1, + UCIMAP_INT = 0x2, + UCIMAP_SECTION = 0x3, + UCIMAP_SUBTYPE = 0xf, /* subtype mask */ }; -/* ucimap internal */ -struct uci_sectmap_data { +union uci_datamap { + int i; + bool b; + const char *s; + void *section; struct list_head list; - struct uci_sectmap *sm; - const char *section_name; - unsigned long allocmap_len; - - /* list of allocations done by ucimap */ - void **allocmap; +}; - /* map for changed fields */ - unsigned char *cmap; +struct uci_listmap { + struct list_head list; + union uci_datamap data; }; struct uci_sectmap { @@ -105,6 +113,7 @@ struct uci_optmap { struct { int maxlen; } s; + struct uci_sectmap *sm; } data; };