X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuci.git;a=blobdiff_plain;f=ucimap.c;h=b4f9518f5b5af5a4d7d294389b650d6576f71a4d;hp=6d97b070c7c2a986d3237c20e4f06c0988142ab8;hb=471dcf63d6ed4689cfd56f2648e6d1b48f46f14c;hpb=6c05c721c77c70f25c46fa20b5d5e4c817e8a651;ds=sidebyside diff --git a/ucimap.c b/ucimap.c index 6d97b07..b4f9518 100644 --- a/ucimap.c +++ b/ucimap.c @@ -1,16 +1,21 @@ /* - * 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 @@ -19,24 +24,22 @@ #include #include #include +#include #include "ucimap.h" #include "uci_internal.h" -struct uci_alloc { - enum ucimap_type type; - union { - void **ptr; - } data; +struct ucimap_alloc { + void *ptr; }; -struct uci_alloc_custom { +struct ucimap_alloc_custom { void *section; struct uci_optmap *om; void *ptr; }; -struct uci_fixup { - struct list_head list; +struct ucimap_fixup { + struct ucimap_fixup *next; struct uci_sectionmap *sm; const char *name; enum ucimap_type type; @@ -56,23 +59,13 @@ struct uci_fixup { static inline bool ucimap_is_alloc(enum ucimap_type type) { - switch(type & UCIMAP_SUBTYPE) { - case UCIMAP_STRING: - return true; - default: - return false; - } + return (type & UCIMAP_SUBTYPE) == UCIMAP_STRING; } static inline bool ucimap_is_fixup(enum ucimap_type type) { - switch(type & UCIMAP_SUBTYPE) { - case UCIMAP_SECTION: - return true; - default: - return false; - } + return (type & UCIMAP_SUBTYPE) == UCIMAP_SECTION; } static inline bool @@ -105,6 +98,12 @@ 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) { @@ -117,29 +116,18 @@ 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); + map->fixup = NULL; + map->sdata = NULL; + map->fixup_tail = &map->fixup; + map->sdata_tail = &map->sdata; 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; + struct ucimap_alloc *a = &sd->allocmap[sd->allocmap_len++]; + a->ptr = ptr; } void @@ -149,19 +137,19 @@ ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd) int i; section = ucimap_section_ptr(sd); - if (!list_empty(&sd->list)) - list_del(&sd->list); + if (sd->ref) + *sd->ref = sd->next; if (sd->sm->free) 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]; + struct ucimap_alloc_custom *a = &sd->alloc_custom[i]; a->om->free(a->section, a->om, a->ptr); } free(sd->alloc_custom); @@ -174,30 +162,27 @@ ucimap_free_section(struct uci_map *map, struct ucimap_section_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 ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_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 uci_fixup *f) +ucimap_find_section(struct uci_map *map, struct ucimap_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); + 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); } - list_for_each(p, &map->pending) { - sd = list_entry(p, struct ucimap_section_data, list); + for (sd = map->pending; sd; sd = sd->next) { if (sd->sm != f->sm) continue; if (strcmp(f->name, sd->section_name) != 0) @@ -207,11 +192,23 @@ ucimap_find_section(struct uci_map *map, struct uci_fixup *f) 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 uci_fixup *f) +ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f) { void *ptr = ucimap_find_section(map, f); - struct ucimap_list *list; + union ucimap_data *data; if (!ptr) return false; @@ -221,20 +218,103 @@ ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f) f->data->ptr = ptr; break; case UCIMAP_LIST: - list = f->data->list; - list->item[list->n_items++].ptr = ptr; + 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 uci_fixup *f, tmp; + struct ucimap_fixup *f, tmp; struct uci_map *map = sd->map; - INIT_LIST_HEAD(&tmp.list); + tmp.next = NULL; tmp.sm = om->data.sm; tmp.name = str; tmp.type = om->type; @@ -242,18 +322,20 @@ ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct if (ucimap_handle_fixup(map, &tmp)) return; - f = malloc(sizeof(struct uci_fixup)); + f = malloc(sizeof(struct ucimap_fixup)); if (!f) return; memcpy(f, &tmp, sizeof(tmp)); - list_add_tail(&f->list, &map->fixup); + 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 uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++]; + struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++]; a->section = ucimap_section_ptr(sd); a->om = om; @@ -269,8 +351,11 @@ 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)) - data = &data->list->item[data->list->n_items++]; + 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: @@ -314,11 +399,10 @@ ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_s ucimap_add_fixup(sd, data, om, str); return; case UCIMAP_CUSTOM: - tdata.s = (char *) data; break; } if (om->parse) { - if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0) + 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) @@ -398,16 +482,26 @@ ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucim } 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 void ucimap_add_section(struct ucimap_section_data *sd) { 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 - list_add_tail(&sd->list, &map->sdata); + ucimap_add_section_list(map, sd); } +#ifdef UCI_DEBUG static const char *ucimap_type_names[] = { [UCIMAP_STRING] = "string", [UCIMAP_INT] = "integer", @@ -416,7 +510,7 @@ static const char *ucimap_type_names[] = { [UCIMAP_LIST] = "list", }; -static inline const char * +static const char * ucimap_get_type_name(int type) { static char buf[32]; @@ -433,12 +527,21 @@ ucimap_get_type_name(int type) 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; @@ -495,7 +598,6 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim int n_alloc_custom = 0; int err; - INIT_LIST_HEAD(&sd->list); sd->map = map; sd->sm = sm; @@ -507,6 +609,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_alloc = 0; int n_elements_custom = 0; int size; @@ -520,7 +623,8 @@ 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) { - ucimap_count_alloc(om, &n_elements, &n_elements_custom); + 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)) { @@ -533,7 +637,7 @@ 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); + ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom); while (*data && !isspace(*data)) data++; @@ -546,23 +650,28 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim break; } /* add one more for the ucimap_list */ - n_alloc += n_elements + 1; + 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 uci_alloc)); + sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc)); if (!sd->allocmap) goto error_mem; if (n_alloc_custom > 0) { - sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom)); + sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom)); if (!sd->alloc_custom) goto error_mem; } @@ -594,7 +703,7 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim if (map->parsed) { ucimap_add_section(sd); } else { - list_add_tail(&sd->list, &map->pending); + ucimap_add_section_list(map, sd); } err = ucimap_parse_options(map, sm, sd, s); @@ -604,8 +713,8 @@ ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucim return 0; error_mem: - if (sd->allocmap) - free(sd->allocmap); + free(sd->alloc_custom); + free(sd->allocmap); free(sd); return UCI_ERR_MEM; @@ -649,6 +758,46 @@ ucimap_set_changed(struct ucimap_section_data *sd, void *field) } } +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, struct ucimap_section_data *sd) { @@ -671,54 +820,41 @@ ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_s ucimap_foreach_option(sm, om) { union ucimap_data *data; - static char buf[32]; - char *str = NULL; i++; - if (ucimap_is_list(om->type)) - continue; - data = ucimap_get_data(sd, om); if (!TEST_BIT(sd->cmap, i - 1)) continue; ucimap_fill_ptr(&ptr, s, om->name); - 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_CUSTOM: - break; - default: - continue; - } - if (om->format) { - union ucimap_data tdata, *data; + if (ucimap_is_list(om->type)) { + struct ucimap_list *list = data->list; + bool first = true; + int j; - data = ucimap_get_data(sd, om); - if (ucimap_is_custom(om->type)) { - tdata.s = (char *)data; - data = &tdata; - } + for (j = 0; j < list->n_items; j++) { + ptr.value = ucimap_data_to_string(sd, om, &list->item[j]); + if (!ptr.value) + continue; - if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0) + 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; - } - if (!str) - continue; - ptr.value = str; - 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 - 1); } @@ -730,10 +866,13 @@ void ucimap_parse(struct uci_map *map, struct uci_package *pkg) { struct uci_element *e; - struct list_head *p, *tmp; + struct ucimap_section_data *sd, **sd_tail; + struct ucimap_fixup *f; int i; - INIT_LIST_HEAD(&map->fixup); + 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); @@ -750,6 +889,7 @@ ucimap_parse(struct uci_map *map, struct uci_package *pkg) } else { sd = malloc(sm->alloc_len); memset(sd, 0, sm->alloc_len); + sd = ucimap_ptr_section(sm, sd); } if (!sd) continue; @@ -757,20 +897,26 @@ ucimap_parse(struct uci_map *map, struct uci_package *pkg) ucimap_parse_section(map, sm, sd, s); } } - map->parsed = true; + if (!map->parsed) { + map->parsed = true; + map->sdata_tail = sd_tail; + } - list_for_each_safe(p, tmp, &map->fixup) { - struct uci_fixup *f = list_entry(p, struct uci_fixup, list); + f = map->fixup; + while (f) { + struct ucimap_fixup *next = f->next; ucimap_handle_fixup(map, f); - list_del(&f->list); free(f); + f = next; } + map->fixup_tail = &map->fixup; + map->fixup = NULL; - list_for_each_safe(p, tmp, &map->pending) { - struct ucimap_section_data *sd; - sd = list_entry(p, struct ucimap_section_data, list); - - list_del_init(&sd->list); + sd = map->pending; + while (sd) { + struct ucimap_section_data *next = sd->next; ucimap_add_section(sd); + sd = next; } + map->pending = NULL; }