X-Git-Url: http://git.archive.openwrt.org/?a=blobdiff_plain;ds=sidebyside;f=ucimap.c;h=0d16bb469d672fa8ab0f787524d3b1f9a1f93d12;hb=25b34febb4851f605f35b45df6eaebc45b122a12;hp=d4dc05fe3d8781cc5986b6f2df256422049643ac;hpb=ceaa2fbb3ce565b8d952179f2ac4cfe9190580ef;p=project%2Fuci.git diff --git a/ucimap.c b/ucimap.c index d4dc05f..0d16bb4 100644 --- a/ucimap.c +++ b/ucimap.c @@ -16,13 +16,21 @@ #include #include #include +#include +#include +#include +#include #include "ucimap.h" +#include "uci_internal.h" struct uci_alloc { - enum ucimap_type type; - union { - void **ptr; - } data; + void *ptr; +}; + +struct uci_alloc_custom { + void *section; + struct uci_optmap *om; + void *ptr; }; struct uci_fixup { @@ -78,6 +86,12 @@ ucimap_is_list(enum ucimap_type type) } 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); @@ -101,31 +115,20 @@ ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om) int ucimap_init(struct uci_map *map) { + INIT_LIST_HEAD(&map->pending); INIT_LIST_HEAD(&map->sdata); INIT_LIST_HEAD(&map->fixup); 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; } -static void +void ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd) { void *section; @@ -139,7 +142,15 @@ 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) { + 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); @@ -157,21 +168,157 @@ ucimap_cleanup(struct uci_map *map) } } +static void * +ucimap_find_section(struct uci_map *map, struct uci_fixup *f) +{ + struct ucimap_section_data *sd; + struct list_head *p; + + list_for_each(p, &map->sdata) { + sd = list_entry(p, struct ucimap_section_data, list); + if (sd->sm != f->sm) + continue; + if (strcmp(f->name, sd->section_name) != 0) + continue; + return ucimap_section_ptr(sd); + } + list_for_each(p, &map->pending) { + sd = list_entry(p, struct ucimap_section_data, list); + if (sd->sm != f->sm) + continue; + if (strcmp(f->name, sd->section_name) != 0) + continue; + return ucimap_section_ptr(sd); + } + return NULL; +} + +static bool +ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f) +{ + void *ptr = ucimap_find_section(map, f); + struct ucimap_list *list; + + if (!ptr) + return false; + + switch(f->type & UCIMAP_TYPE) { + case UCIMAP_SIMPLE: + f->data->ptr = ptr; + break; + case UCIMAP_LIST: + list = f->data->list; + list->item[list->n_items++].ptr = ptr; + break; + } + 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 uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str) +ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str) { - struct uci_fixup *f; + struct uci_fixup *f, tmp; + struct uci_map *map = sd->map; + + INIT_LIST_HEAD(&tmp.list); + 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 uci_fixup)); if (!f) return; - INIT_LIST_HEAD(&f->list); - f->sm = om->data.sm; - f->name = str; - f->type = om->type; - f->data = data; - list_add(&f->list, &map->fixup); + memcpy(f, &tmp, sizeof(tmp)); + 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 @@ -179,11 +326,19 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s { 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)) + 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: @@ -196,33 +351,35 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s ucimap_add_alloc(sd, s); break; case UCIMAP_BOOL: - val = -1; - if (strcmp(str, "on")) + if (!strcmp(str, "on")) val = true; - else if (strcmp(str, "1")) + else if (!strcmp(str, "1")) val = true; - else if (strcmp(str, "enabled")) + else if (!strcmp(str, "enabled")) val = true; - else if (strcmp(str, "off")) + else if (!strcmp(str, "off")) val = false; - else if (strcmp(str, "0")) + else if (!strcmp(str, "0")) val = false; - else if (strcmp(str, "disabled")) + else if (!strcmp(str, "disabled")) val = false; - if (val == -1) + else return; tdata.b = val; break; case UCIMAP_INT: - val = strtol(str, &eptr, om->data.i.base); + lval = strtol(str, &eptr, om->data.i.base); + if (lval < INT_MIN || lval > INT_MAX) + return; + if (!eptr || *eptr == '\0') - tdata.i = val; + tdata.i = (int) lval; else return; break; case UCIMAP_SECTION: - ucimap_add_fixup(sd->map, data, om, str); + ucimap_add_fixup(sd, data, om, str); return; case UCIMAP_CUSTOM: tdata.s = (char *) data; @@ -231,6 +388,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; @@ -238,6 +399,37 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s } +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_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s) { @@ -265,43 +457,133 @@ ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucim 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); } } return 0; } +static void +ucimap_add_section(struct ucimap_section_data *sd) +{ + struct uci_map *map = sd->map; -static int -ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_section *s) + if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0) + ucimap_free_section(map, sd); + else + list_add_tail(&sd->list, &map->sdata); +} + +static const char *ucimap_type_names[] = { + [UCIMAP_STRING] = "string", + [UCIMAP_INT] = "integer", + [UCIMAP_BOOL] = "boolean", + [UCIMAP_SECTION] = "section", + [UCIMAP_LIST] = "list", +}; + +static inline 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]; + + name = ucimap_type_names[type & UCIMAP_SUBTYPE]; + if (!name) { + sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE); + name = buf; + } + + return name; +} + +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)++; +} + +int +ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s) { - struct ucimap_section_data *sd = NULL; struct uci_optmap *om; char *section_name; void *section; int n_alloc = 2; + int n_alloc_custom = 0; int err; - 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); - } - - if (!sd) - return UCI_ERR_MEM; - INIT_LIST_HEAD(&sd->list); sd->map = map; sd->sm = sm; 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_custom = 0; int size; data = ucimap_get_data(sd, om); @@ -312,36 +594,70 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_ if (strcmp(e->name, om->name) != 0) continue; - uci_foreach_element(&o->v.list, tmp) { - n_elements++; + if (o->type == UCI_TYPE_LIST) { + uci_foreach_element(&o->v.list, tmp) { + ucimap_count_alloc(om, &n_elements, &n_elements_custom); + } + } 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, &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 + 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; + + data->list->size = n_elements; 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) { @@ -356,7 +672,12 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_ if (err) goto error; - list_add(&sd->list, &map->sdata); + if (map->parsed) { + ucimap_add_section(sd); + } else { + list_add_tail(&sd->list, &map->pending); + } + err = ucimap_parse_options(map, sm, sd, s); if (err) goto error; @@ -410,10 +731,8 @@ ucimap_set_changed(struct ucimap_section_data *sd, void *field) } 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 ucimap_section_data); - struct ucimap_section_data *sd = (struct ucimap_section_data *) sptr; struct uci_sectionmap *sm = sd->sm; struct uci_section *s = NULL; struct uci_optmap *om; @@ -434,13 +753,14 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) ucimap_foreach_option(sm, om) { union ucimap_data *data; static char buf[32]; - const char *str = NULL; + char *str = NULL; + i++; if (ucimap_is_list(om->type)) continue; data = ucimap_get_data(sd, om); - if (!TEST_BIT(sd->cmap, i)) + if (!TEST_BIT(sd->cmap, i - 1)) continue; ucimap_fill_ptr(&ptr, s, om->name); @@ -456,39 +776,40 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section) sprintf(buf, "%d", !!data->b); str = buf; break; + case UCIMAP_CUSTOM: + break; default: continue; } + if (om->format) { + union ucimap_data tdata, *data; + + data = ucimap_get_data(sd, om); + if (ucimap_is_custom(om->type)) { + tdata.s = (char *)data; + data = &tdata; + } + + if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0) + continue; + + if (!str) + str = ""; + } + if (!str) + continue; ptr.value = str; ret = uci_set(s->package->ctx, &ptr); if (ret) return ret; - CLR_BIT(sd->cmap, i); - i++; + CLR_BIT(sd->cmap, i - 1); } return 0; } -void * -ucimap_find_section(struct uci_map *map, struct uci_fixup *f) -{ - struct ucimap_section_data *sd; - struct list_head *p; - - list_for_each(p, &map->sdata) { - sd = list_entry(p, struct ucimap_section_data, list); - if (sd->sm != f->sm) - continue; - if (strcmp(f->name, sd->section_name) != 0) - continue; - return ucimap_section_ptr(sd); - } - return NULL; -} - void ucimap_parse(struct uci_map *map, struct uci_package *pkg) { @@ -501,39 +822,39 @@ ucimap_parse(struct uci_map *map, struct uci_package *pkg) struct uci_section *s = uci_to_section(e); for (i = 0; i < map->n_sections; i++) { + struct uci_sectionmap *sm = map->sections[i]; + struct ucimap_section_data *sd; + if (strcmp(s->type, map->sections[i]->type) != 0) continue; - ucimap_parse_section(map, map->sections[i], s); + + 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); + } + if (!sd) + continue; + + ucimap_parse_section(map, sm, sd, s); } } + map->parsed = true; + 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 ucimap_list *list; - - if (!ptr) - continue; - - switch(f->type & UCIMAP_TYPE) { - case UCIMAP_SIMPLE: - f->data->section = ptr; - break; - case UCIMAP_LIST: - list = f->data->list; - list->item[list->n_items++].section = ptr; - break; - } + ucimap_handle_fixup(map, f); + list_del(&f->list); free(f); } - list_for_each_safe(p, tmp, &map->sdata) { - struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list); - void *section; - if (sd->done) - continue; + list_for_each_safe(p, tmp, &map->pending) { + struct ucimap_section_data *sd; + sd = list_entry(p, struct ucimap_section_data, list); - section = ucimap_section_ptr(sd); - if (sd->sm->add(map, section) != 0) - ucimap_free_section(map, sd); + list_del_init(&sd->list); + ucimap_add_section(sd); } }