+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);
+}