2 * ucimap - library for mapping uci sections into data structures
3 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
24 #include "uci_internal.h"
30 struct uci_alloc_custom {
32 struct uci_optmap *om;
37 struct list_head list;
38 struct uci_sectionmap *sm;
40 enum ucimap_type type;
41 union ucimap_data *data;
44 #define ucimap_foreach_option(_sm, _o) \
45 if (!(_sm)->options_size) \
46 (_sm)->options_size = sizeof(struct uci_optmap); \
47 for (_o = &(_sm)->options[0]; \
48 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
49 (_sm)->options_size * (_sm)->n_options); \
50 _o = (struct uci_optmap *) ((char *)(_o) + \
55 ucimap_is_alloc(enum ucimap_type type)
57 switch(type & UCIMAP_SUBTYPE) {
66 ucimap_is_fixup(enum ucimap_type type)
68 switch(type & UCIMAP_SUBTYPE) {
77 ucimap_is_simple(enum ucimap_type type)
79 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
83 ucimap_is_list(enum ucimap_type type)
85 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
89 ucimap_is_list_auto(enum ucimap_type type)
91 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
95 ucimap_is_custom(enum ucimap_type type)
97 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
101 ucimap_section_ptr(struct ucimap_section_data *sd)
103 return ((char *) sd - sd->sm->smap_offset);
106 static inline union ucimap_data *
107 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
111 data = (char *) ucimap_section_ptr(sd) + om->offset;
116 ucimap_init(struct uci_map *map)
118 INIT_LIST_HEAD(&map->pending);
119 INIT_LIST_HEAD(&map->sdata);
120 INIT_LIST_HEAD(&map->fixup);
125 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
127 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
132 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
137 section = ucimap_section_ptr(sd);
138 if (!list_empty(&sd->list))
142 sd->sm->free(map, section);
144 for (i = 0; i < sd->allocmap_len; i++) {
145 free(sd->allocmap[i].ptr);
148 if (sd->alloc_custom) {
149 for (i = 0; i < sd->alloc_custom_len; i++) {
150 struct uci_alloc_custom *a = &sd->alloc_custom[i];
151 a->om->free(a->section, a->om, a->ptr);
153 free(sd->alloc_custom);
161 ucimap_cleanup(struct uci_map *map)
163 struct list_head *ptr, *tmp;
165 list_for_each_safe(ptr, tmp, &map->sdata) {
166 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
167 ucimap_free_section(map, sd);
172 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
174 struct ucimap_section_data *sd;
177 list_for_each(p, &map->sdata) {
178 sd = list_entry(p, struct ucimap_section_data, list);
181 if (strcmp(f->name, sd->section_name) != 0)
183 return ucimap_section_ptr(sd);
185 list_for_each(p, &map->pending) {
186 sd = list_entry(p, struct ucimap_section_data, list);
189 if (strcmp(f->name, sd->section_name) != 0)
191 return ucimap_section_ptr(sd);
197 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
199 void *ptr = ucimap_find_section(map, f);
200 struct ucimap_list *list;
205 switch(f->type & UCIMAP_TYPE) {
210 list = f->data->list;
211 list->item[list->n_items++].ptr = ptr;
218 ucimap_free_item(struct ucimap_section_data *sd, void *item)
220 struct uci_alloc_custom *ac;
222 void *ptr = *((void **) item);
228 *((void **)item) = NULL;
229 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
233 if (i != sd->allocmap_len - 1)
234 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
240 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
244 if (i != sd->alloc_custom_len - 1)
245 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
246 sizeof(struct uci_alloc_custom));
248 ac->om->free(ac->section, ac->om, ac->ptr);
249 sd->alloc_custom_len--;
255 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
257 struct ucimap_list *new;
260 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
263 new = calloc(1, size);
265 ucimap_add_alloc(sd, new);
269 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
278 if (items > (*list)->size)
279 offset = (items - (*list)->size) * sizeof(union ucimap_data);
281 a->ptr = realloc(a->ptr, size);
283 memset((char *) a->ptr + offset, 0, size - offset);
293 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
295 struct uci_fixup *f, tmp;
296 struct uci_map *map = sd->map;
298 INIT_LIST_HEAD(&tmp.list);
299 tmp.sm = om->data.sm;
303 if (ucimap_handle_fixup(map, &tmp))
306 f = malloc(sizeof(struct uci_fixup));
310 memcpy(f, &tmp, sizeof(tmp));
311 list_add_tail(&f->list, &map->fixup);
315 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
317 struct uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
319 a->section = ucimap_section_ptr(sd);
325 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
327 union ucimap_data tdata = *data;
333 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
334 if (unlikely(data->list->size <= data->list->n_items)) {
335 /* should not happen */
336 DPRINTF("ERROR: overflow while filling a list\n");
340 data = &data->list->item[data->list->n_items++];
343 switch(om->type & UCIMAP_SUBTYPE) {
345 if ((om->data.s.maxlen > 0) &&
346 (strlen(str) > om->data.s.maxlen))
351 ucimap_add_alloc(sd, s);
354 if (!strcmp(str, "on"))
356 else if (!strcmp(str, "1"))
358 else if (!strcmp(str, "enabled"))
360 else if (!strcmp(str, "off"))
362 else if (!strcmp(str, "0"))
364 else if (!strcmp(str, "disabled"))
372 lval = strtol(str, &eptr, om->data.i.base);
373 if (lval < INT_MIN || lval > INT_MAX)
376 if (!eptr || *eptr == '\0')
377 tdata.i = (int) lval;
382 ucimap_add_fixup(sd, data, om, str);
385 tdata.s = (char *) data;
389 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
391 if (ucimap_is_custom(om->type) && om->free) {
392 if (tdata.ptr != data->ptr)
393 ucimap_add_custom_alloc(sd, om, data->ptr);
396 if (ucimap_is_custom(om->type))
398 memcpy(data, &tdata, sizeof(union ucimap_data));
403 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
411 ucimap_add_alloc(sd, s);
421 while (*s && !isspace(*s))
429 ucimap_add_value(data, om, sd, p);
434 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
436 struct uci_element *e, *l;
437 struct uci_option *o;
438 union ucimap_data *data;
440 uci_foreach_element(&s->options, e) {
441 struct uci_optmap *om = NULL, *tmp;
443 ucimap_foreach_option(sm, tmp) {
444 if (strcmp(e->name, tmp->name) == 0) {
452 data = ucimap_get_data(sd, om);
453 o = uci_to_option(e);
454 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
455 ucimap_add_value(data, om, sd, o->v.string);
456 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
457 uci_foreach_element(&o->v.list, l) {
458 ucimap_add_value(data, om, sd, l->name);
460 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
461 ucimap_convert_list(data, om, sd, o->v.string);
469 ucimap_add_section(struct ucimap_section_data *sd)
471 struct uci_map *map = sd->map;
473 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
474 ucimap_free_section(map, sd);
476 list_add_tail(&sd->list, &map->sdata);
479 static const char *ucimap_type_names[] = {
480 [UCIMAP_STRING] = "string",
481 [UCIMAP_INT] = "integer",
482 [UCIMAP_BOOL] = "boolean",
483 [UCIMAP_SECTION] = "section",
484 [UCIMAP_LIST] = "list",
487 static inline const char *
488 ucimap_get_type_name(int type)
493 if (ucimap_is_list(type))
494 return ucimap_type_names[UCIMAP_LIST];
496 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
498 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
506 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
510 if (unlikely(sm->type_name != om->type_name) &&
511 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
512 DPRINTF("Option '%s' of section type '%s' refereces unknown "
513 "section type '%s', should be '%s'.\n",
514 om->name, sm->type, om->type_name, sm->type_name);
518 if (om->detected_type < 0)
521 if (ucimap_is_custom(om->type))
524 if (ucimap_is_list(om->type) !=
525 ucimap_is_list(om->detected_type))
528 if (ucimap_is_list(om->type))
531 type = om->type & UCIMAP_SUBTYPE;
536 if (type != om->detected_type)
547 DPRINTF("Invalid type in option '%s' of section type '%s', "
548 "declared type is %s, detected type is %s\n",
550 ucimap_get_type_name(om->type),
551 ucimap_get_type_name(om->detected_type));
556 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
558 if (ucimap_is_alloc(om->type))
560 else if (ucimap_is_custom(om->type) && om->free)
565 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
567 struct uci_optmap *om;
571 int n_alloc_custom = 0;
574 INIT_LIST_HEAD(&sd->list);
578 ucimap_foreach_option(sm, om) {
579 if (!ucimap_check_optmap_type(sm, om))
582 if (ucimap_is_list(om->type)) {
583 union ucimap_data *data;
584 struct uci_element *e;
586 int n_elements_custom = 0;
589 data = ucimap_get_data(sd, om);
590 uci_foreach_element(&s->options, e) {
591 struct uci_option *o = uci_to_option(e);
592 struct uci_element *tmp;
594 if (strcmp(e->name, om->name) != 0)
597 if (o->type == UCI_TYPE_LIST) {
598 uci_foreach_element(&o->v.list, tmp) {
599 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
601 } else if ((o->type == UCI_TYPE_STRING) &&
602 ucimap_is_list_auto(om->type)) {
603 const char *data = o->v.string;
605 while (isspace(*data))
612 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
614 while (*data && !isspace(*data))
618 /* for the duplicated data string */
624 /* add one more for the ucimap_list */
625 n_alloc += n_elements + 1;
626 n_alloc_custom += n_elements_custom;
627 size = sizeof(struct ucimap_list) +
628 n_elements * sizeof(union ucimap_data);
630 data->list = malloc(size);
634 data->list->size = n_elements;
635 memset(data->list, 0, size);
637 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
641 sd->allocmap = calloc(n_alloc, sizeof(struct uci_alloc));
645 if (n_alloc_custom > 0) {
646 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom));
647 if (!sd->alloc_custom)
651 section_name = strdup(s->e.name);
655 sd->section_name = section_name;
657 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
661 ucimap_add_alloc(sd, (void *)section_name);
662 ucimap_add_alloc(sd, (void *)sd->cmap);
663 ucimap_foreach_option(sm, om) {
664 if (!ucimap_is_list(om->type))
667 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
670 section = ucimap_section_ptr(sd);
671 err = sm->init(map, section, s);
676 ucimap_add_section(sd);
678 list_add_tail(&sd->list, &map->pending);
681 err = ucimap_parse_options(map, sm, sd, s);
694 ucimap_free_section(map, sd);
699 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
701 struct uci_package *p = s->package;
703 memset(ptr, 0, sizeof(struct uci_ptr));
705 ptr->package = p->e.name;
708 ptr->section = s->e.name;
711 ptr->option = option;
712 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
716 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
718 void *section = ucimap_section_ptr(sd);
719 struct uci_sectionmap *sm = sd->sm;
720 struct uci_optmap *om;
721 int ofs = (char *)field - (char *)section;
724 ucimap_foreach_option(sm, om) {
725 if (om->offset == ofs) {
726 SET_BIT(sd->cmap, i);
734 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
736 struct uci_sectionmap *sm = sd->sm;
737 struct uci_section *s = NULL;
738 struct uci_optmap *om;
739 struct uci_element *e;
744 uci_foreach_element(&p->sections, e) {
745 if (!strcmp(e->name, sd->section_name)) {
746 s = uci_to_section(e);
751 return UCI_ERR_NOTFOUND;
753 ucimap_foreach_option(sm, om) {
754 union ucimap_data *data;
759 if (ucimap_is_list(om->type))
762 data = ucimap_get_data(sd, om);
763 if (!TEST_BIT(sd->cmap, i - 1))
766 ucimap_fill_ptr(&ptr, s, om->name);
767 switch(om->type & UCIMAP_SUBTYPE) {
772 sprintf(buf, "%d", data->i);
776 sprintf(buf, "%d", !!data->b);
785 union ucimap_data tdata, *data;
787 data = ucimap_get_data(sd, om);
788 if (ucimap_is_custom(om->type)) {
789 tdata.s = (char *)data;
793 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
803 ret = uci_set(s->package->ctx, &ptr);
807 CLR_BIT(sd->cmap, i - 1);
814 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
816 struct uci_element *e;
817 struct list_head *p, *tmp;
820 INIT_LIST_HEAD(&map->fixup);
821 uci_foreach_element(&pkg->sections, e) {
822 struct uci_section *s = uci_to_section(e);
824 for (i = 0; i < map->n_sections; i++) {
825 struct uci_sectionmap *sm = map->sections[i];
826 struct ucimap_section_data *sd;
828 if (strcmp(s->type, map->sections[i]->type) != 0)
832 sd = sm->alloc(map, sm, s);
833 memset(sd, 0, sizeof(struct ucimap_section_data));
835 sd = malloc(sm->alloc_len);
836 memset(sd, 0, sm->alloc_len);
841 ucimap_parse_section(map, sm, sd, s);
846 list_for_each_safe(p, tmp, &map->fixup) {
847 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
848 ucimap_handle_fixup(map, f);
853 list_for_each_safe(p, tmp, &map->pending) {
854 struct ucimap_section_data *sd;
855 sd = list_entry(p, struct ucimap_section_data, list);
857 list_del_init(&sd->list);
858 ucimap_add_section(sd);