X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=ucimap.c;h=b4f9518f5b5af5a4d7d294389b650d6576f71a4d;hp=3c5fb31d11a52f0498eb33ce6c24cc8b0692be92;hb=471dcf63d6ed4689cfd56f2648e6d1b48f46f14c;hpb=d788264ddc370ffb5f9fe1f451ac6845da0dfda0;ds=sidebyside diff --git a/ucimap.c b/ucimap.c index 3c5fb31..b4f9518 100644 --- a/ucimap.c +++ b/ucimap.c @@ -1,45 +1,158 @@ /* - * ucimap - library for mapping uci sections into data structures - * Copyright (C) 2008 Felix Fietkau + * ucimap.c - Library for the Unified Configuration Interface + * Copyright (C) 2008-2009 Felix Fietkau * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 + * it under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Lesser General Public License for more details. */ + +/* + * This file contains ucimap, an API for mapping UCI to C data structures + */ + #include +#include #include #include #include +#include +#include +#include +#include #include "ucimap.h" +#include "uci_internal.h" + +struct ucimap_alloc { + void *ptr; +}; + +struct ucimap_alloc_custom { + void *section; + struct uci_optmap *om; + void *ptr; +}; + +struct ucimap_fixup { + struct ucimap_fixup *next; + struct uci_sectionmap *sm; + const char *name; + enum ucimap_type type; + union ucimap_data *data; +}; + +#define ucimap_foreach_option(_sm, _o) \ + if (!(_sm)->options_size) \ + (_sm)->options_size = sizeof(struct uci_optmap); \ + for (_o = &(_sm)->options[0]; \ + ((char *)(_o)) < ((char *) &(_sm)->options[0] + \ + (_sm)->options_size * (_sm)->n_options); \ + _o = (struct uci_optmap *) ((char *)(_o) + \ + (_sm)->options_size)) + + +static inline bool +ucimap_is_alloc(enum ucimap_type type) +{ + return (type & UCIMAP_SUBTYPE) == UCIMAP_STRING; +} +static inline bool +ucimap_is_fixup(enum ucimap_type type) +{ + return (type & UCIMAP_SUBTYPE) == UCIMAP_SECTION; +} + +static inline bool +ucimap_is_simple(enum ucimap_type type) +{ + return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE); +} + +static inline bool +ucimap_is_list(enum ucimap_type type) +{ + return ((type & UCIMAP_TYPE) == UCIMAP_LIST); +} + +static inline bool +ucimap_is_list_auto(enum ucimap_type type) +{ + return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO); +} + +static inline bool +ucimap_is_custom(enum ucimap_type type) +{ + return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM); +} + +static inline void * +ucimap_section_ptr(struct ucimap_section_data *sd) +{ + return ((char *) sd - sd->sm->smap_offset); +} + +static inline struct ucimap_section_data * +ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) { + ptr = (char *) ptr + sm->smap_offset; + return ptr; +} + +static inline union ucimap_data * +ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om) +{ + void *data; + + data = (char *) ucimap_section_ptr(sd) + om->offset; + return data; +} int ucimap_init(struct uci_map *map) { - INIT_LIST_HEAD(&map->sdata); + map->fixup = NULL; + map->sdata = NULL; + map->fixup_tail = &map->fixup; + map->sdata_tail = &map->sdata; return 0; } static void -ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd) +ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr) +{ + struct ucimap_alloc *a = &sd->allocmap[sd->allocmap_len++]; + a->ptr = ptr; +} + +void +ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd) { - void *section = sd; + void *section; int i; - section = (char *) section + sizeof(struct uci_sectmap_data); - if (!list_empty(&sd->list)) - list_del(&sd->list); + section = ucimap_section_ptr(sd); + if (sd->ref) + *sd->ref = sd->next; - if (sd->sm->free_section) - sd->sm->free_section(map, section); + if (sd->sm->free) + sd->sm->free(map, section); for (i = 0; i < sd->allocmap_len; i++) { - free(sd->allocmap[i]); + free(sd->allocmap[i].ptr); + } + + if (sd->alloc_custom) { + for (i = 0; i < sd->alloc_custom_len; i++) { + struct ucimap_alloc_custom *a = &sd->alloc_custom[i]; + a->om->free(a->section, a->om, a->ptr); + } + free(sd->alloc_custom); } free(sd->allocmap); @@ -49,131 +162,559 @@ ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd) void ucimap_cleanup(struct uci_map *map) { - struct list_head *ptr, *tmp; + struct ucimap_section_data *sd, *sd_next; - list_for_each_safe(ptr, tmp, &map->sdata) { - struct uci_sectmap_data *sd = list_entry(ptr, struct uci_sectmap_data, list); + for (sd = map->sdata; sd; sd = sd_next) { + sd_next = sd->next; ucimap_free_section(map, sd); } } +static void * +ucimap_find_section(struct uci_map *map, struct ucimap_fixup *f) +{ + struct ucimap_section_data *sd; + + for (sd = map->sdata; sd; sd = sd->next) { + if (sd->sm != f->sm) + continue; + if (strcmp(f->name, sd->section_name) != 0) + continue; + return ucimap_section_ptr(sd); + } + for (sd = map->pending; sd; sd = sd->next) { + if (sd->sm != f->sm) + continue; + if (strcmp(f->name, sd->section_name) != 0) + continue; + return ucimap_section_ptr(sd); + } + return NULL; +} + +static union ucimap_data * +ucimap_list_append(struct ucimap_list *list) +{ + if (unlikely(list->size <= list->n_items)) { + /* should not happen */ + DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list->size); + return NULL; + } + return &list->item[list->n_items++]; +} + + +static bool +ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f) +{ + void *ptr = ucimap_find_section(map, f); + union ucimap_data *data; + + if (!ptr) + return false; + + switch(f->type & UCIMAP_TYPE) { + case UCIMAP_SIMPLE: + f->data->ptr = ptr; + break; + case UCIMAP_LIST: + data = ucimap_list_append(f->data->list); + if (!data) + return false; + + data->ptr = ptr; + break; + } + return true; +} + +void +ucimap_free_item(struct ucimap_section_data *sd, void *item) +{ + struct ucimap_alloc_custom *ac; + struct ucimap_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 ucimap_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 ucimap_alloc *a; + int i, offset = 0; + int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data); + + if (!*list) { + new = calloc(1, size); + if (!new) + return -ENOMEM; + + 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 (!a->ptr) + return -ENOMEM; + + 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) +{ + struct ucimap_fixup *f, tmp; + struct uci_map *map = sd->map; + + tmp.next = NULL; + tmp.sm = om->data.sm; + tmp.name = str; + tmp.type = om->type; + tmp.data = data; + if (ucimap_handle_fixup(map, &tmp)) + return; + + f = malloc(sizeof(struct ucimap_fixup)); + if (!f) + return; + + memcpy(f, &tmp, sizeof(tmp)); + f->next = NULL; + *map->fixup_tail = f; + map->fixup_tail = &f->next; +} + +static void +ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr) +{ + struct ucimap_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) +{ + union ucimap_data tdata = *data; + char *eptr = NULL; + long lval; + char *s; + int val; + + if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) { + data = ucimap_list_append(data->list); + if (!data) + return; + } + + switch(om->type & UCIMAP_SUBTYPE) { + case UCIMAP_STRING: + if ((om->data.s.maxlen > 0) && + (strlen(str) > om->data.s.maxlen)) + return; + + s = strdup(str); + tdata.s = s; + ucimap_add_alloc(sd, s); + break; + case UCIMAP_BOOL: + 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; + else + return; + + tdata.b = val; + break; + case UCIMAP_INT: + lval = strtol(str, &eptr, om->data.i.base); + if (lval < INT_MIN || lval > INT_MAX) + return; + + if (!eptr || *eptr == '\0') + tdata.i = (int) lval; + else + return; + break; + case UCIMAP_SECTION: + ucimap_add_fixup(sd, data, om, str); + return; + case UCIMAP_CUSTOM: + break; + } + if (om->parse) { + if (om->parse(ucimap_section_ptr(sd), om, data, 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; + memcpy(data, &tdata, sizeof(union ucimap_data)); +} + + +static void +ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str) +{ + char *s, *p; + + s = strdup(str); + if (!s) + return; + + ucimap_add_alloc(sd, s); + + do { + while (isspace(*s)) + s++; + + if (!*s) + break; + + p = s; + while (*s && !isspace(*s)) + s++; + + if (isspace(*s)) { + *s = 0; + s++; + } + + ucimap_add_value(data, om, sd, p); + } while (*s); +} static int -ucimap_parse_options(struct uci_map *map, struct uci_sectmap *sm, struct uci_sectmap_data *sd, struct uci_section *s) +ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s) { - struct uci_element *e; + struct uci_element *e, *l; struct uci_option *o; - void *section = sd; - int i; + union ucimap_data *data; - section = (unsigned char *) section + sizeof(struct uci_sectmap_data); uci_foreach_element(&s->options, e) { - struct uci_optmap *om = NULL; + struct uci_optmap *om = NULL, *tmp; - for (i = 0; i < sm->n_options; i++) { - if (strcmp(e->name, sm->options[i].name) == 0) { - om = &sm->options[i]; + ucimap_foreach_option(sm, tmp) { + if (strcmp(e->name, tmp->name) == 0) { + om = tmp; break; } } if (!om) - goto next_element; + continue; + data = ucimap_get_data(sd, om); 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) && ucimap_is_simple(om->type)) { + ucimap_add_value(data, om, sd, o->v.string); + } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) { + uci_foreach_element(&o->v.list, l) { + ucimap_add_value(data, om, sd, l->name); + } + } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) { + ucimap_convert_list(data, om, sd, o->v.string); } -next_element: - continue; } return 0; } +static void +ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd) +{ + sd->ref = map->sdata_tail; + *sd->ref = sd; + map->sdata_tail = &sd->next; +} -static int -ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_section *s) +static void +ucimap_add_section(struct ucimap_section_data *sd) { - struct uci_sectmap_data *sd = NULL; - void *section = NULL; - int err; + struct uci_map *map = sd->map; + + sd->next = NULL; + if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0) + ucimap_free_section(map, sd); + else + ucimap_add_section_list(map, sd); +} + +#ifdef UCI_DEBUG +static const char *ucimap_type_names[] = { + [UCIMAP_STRING] = "string", + [UCIMAP_INT] = "integer", + [UCIMAP_BOOL] = "boolean", + [UCIMAP_SECTION] = "section", + [UCIMAP_LIST] = "list", +}; + +static const char * +ucimap_get_type_name(int type) +{ + static char buf[32]; + const char *name; + + if (ucimap_is_list(type)) + return ucimap_type_names[UCIMAP_LIST]; - sd = malloc(sm->alloc_len + sizeof(struct uci_sectmap_data)); - if (!sd) - return UCI_ERR_MEM; + name = ucimap_type_names[type & UCIMAP_SUBTYPE]; + if (!name) { + sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE); + name = buf; + } + + return name; +} +#endif + +static bool +ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om) +{ + unsigned int type; + + if (unlikely(sm->type_name != om->type_name) && + unlikely(strcmp(sm->type_name, om->type_name) != 0)) { + DPRINTF("Option '%s' of section type '%s' refereces unknown " + "section type '%s', should be '%s'.\n", + om->name, sm->type, om->type_name, sm->type_name); + return false; + } + + if (om->detected_type < 0) + return true; + + if (ucimap_is_custom(om->type)) + return true; + + if (ucimap_is_list(om->type) != + ucimap_is_list(om->detected_type)) + goto failed; + + if (ucimap_is_list(om->type)) + return true; + + type = om->type & UCIMAP_SUBTYPE; + switch(type) { + case UCIMAP_STRING: + case UCIMAP_INT: + case UCIMAP_BOOL: + if (type != om->detected_type) + goto failed; + break; + case UCIMAP_SECTION: + goto failed; + default: + break; + } + return true; + +failed: + DPRINTF("Invalid type in option '%s' of section type '%s', " + "declared type is %s, detected type is %s\n", + om->name, sm->type, + ucimap_get_type_name(om->type), + ucimap_get_type_name(om->detected_type)); + 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)++; +} - memset(sd, 0, sm->alloc_len + sizeof(struct uci_sectmap_data)); - INIT_LIST_HEAD(&sd->list); +int +ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s) +{ + struct uci_optmap *om; + char *section_name; + void *section; + int n_alloc = 2; + int n_alloc_custom = 0; + int err; + sd->map = map; sd->sm = sm; - sd->allocmap = malloc(sm->n_options * sizeof(void *)); + + ucimap_foreach_option(sm, om) { + if (!ucimap_check_optmap_type(sm, om)) + continue; + + if (ucimap_is_list(om->type)) { + union ucimap_data *data; + struct uci_element *e; + int n_elements = 0; + int n_elements_alloc = 0; + int n_elements_custom = 0; + int size; + + data = ucimap_get_data(sd, om); + uci_foreach_element(&s->options, e) { + struct uci_option *o = uci_to_option(e); + struct uci_element *tmp; + + if (strcmp(e->name, om->name) != 0) + continue; + + if (o->type == UCI_TYPE_LIST) { + uci_foreach_element(&o->v.list, tmp) { + ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom); + n_elements++; + } + } else if ((o->type == UCI_TYPE_STRING) && + ucimap_is_list_auto(om->type)) { + const char *data = o->v.string; + do { + while (isspace(*data)) + data++; + + if (!*data) + break; + + n_elements++; + ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom); + + while (*data && !isspace(*data)) + data++; + } while (*data); + + /* for the duplicated data string */ + if (n_elements) + n_alloc++; + } + break; + } + /* add one more for the ucimap_list */ + n_alloc += n_elements_alloc + 1; + 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; + + memset(data->list, 0, size); + data->list->size = n_elements; + } else { + ucimap_count_alloc(om, &n_alloc, &n_alloc_custom); + } + } + + sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc)); if (!sd->allocmap) goto error_mem; - sd->section_name = strdup(s->e.name); - if (!sd->section_name) + if (n_alloc_custom > 0) { + sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom)); + if (!sd->alloc_custom) + goto error_mem; + } + + section_name = strdup(s->e.name); + if (!section_name) goto error_mem; - sd->cmap = malloc(BITFIELD_SIZE(sm->n_options)); + sd->section_name = section_name; + + sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options)); if (!sd->cmap) 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, (void *)section_name); + ucimap_add_alloc(sd, (void *)sd->cmap); + ucimap_foreach_option(sm, om) { + if (!ucimap_is_list(om->type)) + continue; - section = (char *)sd + sizeof(struct uci_sectmap_data); + ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list); + } - err = sm->init_section(map, section, s); + section = ucimap_section_ptr(sd); + err = sm->init(map, section, s); if (err) goto error; - list_add(&sd->list, &map->sdata); - err = ucimap_parse_options(map, sm, sd, s); - if (err) - goto error; + if (map->parsed) { + ucimap_add_section(sd); + } else { + ucimap_add_section_list(map, sd); + } - err = sm->add_section(map, section); + err = ucimap_parse_options(map, sm, sd, s); if (err) goto error; return 0; error_mem: - if (sd->allocmap) - free(sd->allocmap); + free(sd->alloc_custom); + free(sd->allocmap); free(sd); return UCI_ERR_MEM; @@ -200,32 +741,73 @@ ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option) } void -ucimap_set_changed(void *section, void *field) +ucimap_set_changed(struct ucimap_section_data *sd, void *field) { - char *sptr = (char *)section - sizeof(struct uci_sectmap_data); - struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr; - struct uci_sectmap *sm = sd->sm; + void *section = ucimap_section_ptr(sd); + struct uci_sectionmap *sm = sd->sm; + struct uci_optmap *om; int ofs = (char *)field - (char *)section; - int i; + int i = 0; - for (i = 0; i < sm->n_options; i++) { - if (sm->options[i].offset == ofs) { + ucimap_foreach_option(sm, om) { + if (om->offset == ofs) { SET_BIT(sd->cmap, i); break; } + i++; } } +static char * +ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data) +{ + static char buf[32]; + char *str = NULL; + + switch(om->type & UCIMAP_SUBTYPE) { + case UCIMAP_STRING: + str = data->s; + break; + case UCIMAP_INT: + sprintf(buf, "%d", data->i); + str = buf; + break; + case UCIMAP_BOOL: + sprintf(buf, "%d", !!data->b); + str = buf; + break; + case UCIMAP_SECTION: + if (data->ptr) + str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name; + else + str = ""; + break; + case UCIMAP_CUSTOM: + break; + default: + return NULL; + } + + if (om->format) { + if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0) + return NULL; + + if (!str) + str = ""; + } + return str; +} + int -ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) +ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd) { - char *sptr = (char *)section - sizeof(struct uci_sectmap_data); - struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr; - struct uci_sectmap *sm = sd->sm; + struct uci_sectionmap *sm = sd->sm; struct uci_section *s = NULL; + struct uci_optmap *om; struct uci_element *e; struct uci_ptr ptr; - int i, ret; + int i = 0; + int ret; uci_foreach_element(&p->sections, e) { if (!strcmp(e->name, sd->section_name)) { @@ -236,55 +818,105 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) if (!s) return UCI_ERR_NOTFOUND; - for (i = 0; i < sm->n_options; i++) { - struct uci_optmap *om = &sm->options[i]; - static char buf[32]; - const char *str = NULL; - void *p = (char *)section + om->offset; + ucimap_foreach_option(sm, om) { + union ucimap_data *data; - if (!TEST_BIT(sd->cmap, i)) + i++; + data = ucimap_get_data(sd, om); + if (!TEST_BIT(sd->cmap, i - 1)) continue; ucimap_fill_ptr(&ptr, s, om->name); - switch(om->type) { - case UCIMAP_STRING: - str = *((char **) p); - break; - case UCIMAP_INT: - sprintf(buf, "%d", *((int *) p)); - str = buf; - break; - case UCIMAP_BOOL: - sprintf(buf, "%d", !!*((bool *)p)); - str = buf; - break; - } - ptr.value = str; + if (ucimap_is_list(om->type)) { + struct ucimap_list *list = data->list; + bool first = true; + int j; + + for (j = 0; j < list->n_items; j++) { + ptr.value = ucimap_data_to_string(sd, om, &list->item[j]); + if (!ptr.value) + continue; + + if (first) { + ret = uci_set(s->package->ctx, &ptr); + first = false; + } else { + ret = uci_add_list(s->package->ctx, &ptr); + } + if (ret) + return ret; + } + } else { + ptr.value = ucimap_data_to_string(sd, om, data); + if (!ptr.value) + continue; - ret = uci_set(s->package->ctx, &ptr); - if (ret) - return ret; + ret = uci_set(s->package->ctx, &ptr); + if (ret) + return ret; + } - CLR_BIT(sd->cmap, i); + CLR_BIT(sd->cmap, i - 1); } return 0; } - void ucimap_parse(struct uci_map *map, struct uci_package *pkg) { struct uci_element *e; + struct ucimap_section_data *sd, **sd_tail; + struct ucimap_fixup *f; int i; + sd_tail = map->sdata_tail; + map->parsed = false; + map->sdata_tail = &map->pending; 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) + struct uci_sectionmap *sm = map->sections[i]; + struct ucimap_section_data *sd; + + if (strcmp(s->type, map->sections[i]->type) != 0) + continue; + + if (sm->alloc) { + sd = sm->alloc(map, sm, s); + memset(sd, 0, sizeof(struct ucimap_section_data)); + } else { + sd = malloc(sm->alloc_len); + memset(sd, 0, sm->alloc_len); + sd = ucimap_ptr_section(sm, sd); + } + if (!sd) continue; - ucimap_parse_section(map, &map->sections[i], s); + + ucimap_parse_section(map, sm, sd, s); } } + if (!map->parsed) { + map->parsed = true; + map->sdata_tail = sd_tail; + } + + f = map->fixup; + while (f) { + struct ucimap_fixup *next = f->next; + ucimap_handle_fixup(map, f); + free(f); + f = next; + } + map->fixup_tail = &map->fixup; + map->fixup = NULL; + + sd = map->pending; + while (sd) { + struct ucimap_section_data *next = sd->next; + ucimap_add_section(sd); + sd = next; + } + map->pending = NULL; }