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 struct ucimap_section_data *
107 ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) {
108 ptr = (char *) ptr + sm->smap_offset;
112 static inline union ucimap_data *
113 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
117 data = (char *) ucimap_section_ptr(sd) + om->offset;
122 ucimap_init(struct uci_map *map)
124 INIT_LIST_HEAD(&map->pending);
125 INIT_LIST_HEAD(&map->sdata);
126 INIT_LIST_HEAD(&map->fixup);
131 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
133 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
138 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
143 section = ucimap_section_ptr(sd);
144 if (!list_empty(&sd->list))
148 sd->sm->free(map, section);
150 for (i = 0; i < sd->allocmap_len; i++) {
151 free(sd->allocmap[i].ptr);
154 if (sd->alloc_custom) {
155 for (i = 0; i < sd->alloc_custom_len; i++) {
156 struct uci_alloc_custom *a = &sd->alloc_custom[i];
157 a->om->free(a->section, a->om, a->ptr);
159 free(sd->alloc_custom);
167 ucimap_cleanup(struct uci_map *map)
169 struct list_head *ptr, *tmp;
171 list_for_each_safe(ptr, tmp, &map->sdata) {
172 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
173 ucimap_free_section(map, sd);
178 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
180 struct ucimap_section_data *sd;
183 list_for_each(p, &map->sdata) {
184 sd = list_entry(p, struct ucimap_section_data, list);
187 if (strcmp(f->name, sd->section_name) != 0)
189 return ucimap_section_ptr(sd);
191 list_for_each(p, &map->pending) {
192 sd = list_entry(p, struct ucimap_section_data, list);
195 if (strcmp(f->name, sd->section_name) != 0)
197 return ucimap_section_ptr(sd);
203 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
205 void *ptr = ucimap_find_section(map, f);
206 struct ucimap_list *list;
211 switch(f->type & UCIMAP_TYPE) {
216 list = f->data->list;
217 list->item[list->n_items++].ptr = ptr;
224 ucimap_free_item(struct ucimap_section_data *sd, void *item)
226 struct uci_alloc_custom *ac;
228 void *ptr = *((void **) item);
234 *((void **)item) = NULL;
235 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
239 if (i != sd->allocmap_len - 1)
240 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
246 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
250 if (i != sd->alloc_custom_len - 1)
251 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
252 sizeof(struct uci_alloc_custom));
254 ac->om->free(ac->section, ac->om, ac->ptr);
255 sd->alloc_custom_len--;
261 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
263 struct ucimap_list *new;
266 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
269 new = calloc(1, size);
271 ucimap_add_alloc(sd, new);
275 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
284 if (items > (*list)->size)
285 offset = (items - (*list)->size) * sizeof(union ucimap_data);
287 a->ptr = realloc(a->ptr, size);
289 memset((char *) a->ptr + offset, 0, size - offset);
299 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
301 struct uci_fixup *f, tmp;
302 struct uci_map *map = sd->map;
304 INIT_LIST_HEAD(&tmp.list);
305 tmp.sm = om->data.sm;
309 if (ucimap_handle_fixup(map, &tmp))
312 f = malloc(sizeof(struct uci_fixup));
316 memcpy(f, &tmp, sizeof(tmp));
317 list_add_tail(&f->list, &map->fixup);
321 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
323 struct uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
325 a->section = ucimap_section_ptr(sd);
331 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
333 union ucimap_data tdata = *data;
339 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
340 if (unlikely(data->list->size <= data->list->n_items)) {
341 /* should not happen */
342 DPRINTF("ERROR: overflow while filling a list\n");
346 data = &data->list->item[data->list->n_items++];
349 switch(om->type & UCIMAP_SUBTYPE) {
351 if ((om->data.s.maxlen > 0) &&
352 (strlen(str) > om->data.s.maxlen))
357 ucimap_add_alloc(sd, s);
360 if (!strcmp(str, "on"))
362 else if (!strcmp(str, "1"))
364 else if (!strcmp(str, "enabled"))
366 else if (!strcmp(str, "off"))
368 else if (!strcmp(str, "0"))
370 else if (!strcmp(str, "disabled"))
378 lval = strtol(str, &eptr, om->data.i.base);
379 if (lval < INT_MIN || lval > INT_MAX)
382 if (!eptr || *eptr == '\0')
383 tdata.i = (int) lval;
388 ucimap_add_fixup(sd, data, om, str);
391 tdata.s = (char *) data;
395 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
397 if (ucimap_is_custom(om->type) && om->free) {
398 if (tdata.ptr != data->ptr)
399 ucimap_add_custom_alloc(sd, om, data->ptr);
402 if (ucimap_is_custom(om->type))
404 memcpy(data, &tdata, sizeof(union ucimap_data));
409 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
417 ucimap_add_alloc(sd, s);
427 while (*s && !isspace(*s))
435 ucimap_add_value(data, om, sd, p);
440 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
442 struct uci_element *e, *l;
443 struct uci_option *o;
444 union ucimap_data *data;
446 uci_foreach_element(&s->options, e) {
447 struct uci_optmap *om = NULL, *tmp;
449 ucimap_foreach_option(sm, tmp) {
450 if (strcmp(e->name, tmp->name) == 0) {
458 data = ucimap_get_data(sd, om);
459 o = uci_to_option(e);
460 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
461 ucimap_add_value(data, om, sd, o->v.string);
462 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
463 uci_foreach_element(&o->v.list, l) {
464 ucimap_add_value(data, om, sd, l->name);
466 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
467 ucimap_convert_list(data, om, sd, o->v.string);
475 ucimap_add_section(struct ucimap_section_data *sd)
477 struct uci_map *map = sd->map;
479 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
480 ucimap_free_section(map, sd);
482 list_add_tail(&sd->list, &map->sdata);
486 static const char *ucimap_type_names[] = {
487 [UCIMAP_STRING] = "string",
488 [UCIMAP_INT] = "integer",
489 [UCIMAP_BOOL] = "boolean",
490 [UCIMAP_SECTION] = "section",
491 [UCIMAP_LIST] = "list",
495 ucimap_get_type_name(int type)
500 if (ucimap_is_list(type))
501 return ucimap_type_names[UCIMAP_LIST];
503 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
505 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
514 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
518 if (unlikely(sm->type_name != om->type_name) &&
519 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
520 DPRINTF("Option '%s' of section type '%s' refereces unknown "
521 "section type '%s', should be '%s'.\n",
522 om->name, sm->type, om->type_name, sm->type_name);
526 if (om->detected_type < 0)
529 if (ucimap_is_custom(om->type))
532 if (ucimap_is_list(om->type) !=
533 ucimap_is_list(om->detected_type))
536 if (ucimap_is_list(om->type))
539 type = om->type & UCIMAP_SUBTYPE;
544 if (type != om->detected_type)
555 DPRINTF("Invalid type in option '%s' of section type '%s', "
556 "declared type is %s, detected type is %s\n",
558 ucimap_get_type_name(om->type),
559 ucimap_get_type_name(om->detected_type));
564 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
566 if (ucimap_is_alloc(om->type))
568 else if (ucimap_is_custom(om->type) && om->free)
573 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
575 struct uci_optmap *om;
579 int n_alloc_custom = 0;
582 INIT_LIST_HEAD(&sd->list);
586 ucimap_foreach_option(sm, om) {
587 if (!ucimap_check_optmap_type(sm, om))
590 if (ucimap_is_list(om->type)) {
591 union ucimap_data *data;
592 struct uci_element *e;
594 int n_elements_custom = 0;
597 data = ucimap_get_data(sd, om);
598 uci_foreach_element(&s->options, e) {
599 struct uci_option *o = uci_to_option(e);
600 struct uci_element *tmp;
602 if (strcmp(e->name, om->name) != 0)
605 if (o->type == UCI_TYPE_LIST) {
606 uci_foreach_element(&o->v.list, tmp) {
607 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
609 } else if ((o->type == UCI_TYPE_STRING) &&
610 ucimap_is_list_auto(om->type)) {
611 const char *data = o->v.string;
613 while (isspace(*data))
620 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
622 while (*data && !isspace(*data))
626 /* for the duplicated data string */
632 /* add one more for the ucimap_list */
633 n_alloc += n_elements + 1;
634 n_alloc_custom += n_elements_custom;
635 size = sizeof(struct ucimap_list) +
636 n_elements * sizeof(union ucimap_data);
638 data->list = malloc(size);
642 data->list->size = n_elements;
643 memset(data->list, 0, size);
645 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
649 sd->allocmap = calloc(n_alloc, sizeof(struct uci_alloc));
653 if (n_alloc_custom > 0) {
654 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom));
655 if (!sd->alloc_custom)
659 section_name = strdup(s->e.name);
663 sd->section_name = section_name;
665 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
669 ucimap_add_alloc(sd, (void *)section_name);
670 ucimap_add_alloc(sd, (void *)sd->cmap);
671 ucimap_foreach_option(sm, om) {
672 if (!ucimap_is_list(om->type))
675 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
678 section = ucimap_section_ptr(sd);
679 err = sm->init(map, section, s);
684 ucimap_add_section(sd);
686 list_add_tail(&sd->list, &map->pending);
689 err = ucimap_parse_options(map, sm, sd, s);
702 ucimap_free_section(map, sd);
707 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
709 struct uci_package *p = s->package;
711 memset(ptr, 0, sizeof(struct uci_ptr));
713 ptr->package = p->e.name;
716 ptr->section = s->e.name;
719 ptr->option = option;
720 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
724 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
726 void *section = ucimap_section_ptr(sd);
727 struct uci_sectionmap *sm = sd->sm;
728 struct uci_optmap *om;
729 int ofs = (char *)field - (char *)section;
732 ucimap_foreach_option(sm, om) {
733 if (om->offset == ofs) {
734 SET_BIT(sd->cmap, i);
742 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
747 switch(om->type & UCIMAP_SUBTYPE) {
752 sprintf(buf, "%d", data->i);
756 sprintf(buf, "%d", !!data->b);
761 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
772 union ucimap_data tdata;
774 if (ucimap_is_custom(om->type)) {
775 tdata.s = (char *)data;
779 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
789 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
791 struct uci_sectionmap *sm = sd->sm;
792 struct uci_section *s = NULL;
793 struct uci_optmap *om;
794 struct uci_element *e;
799 uci_foreach_element(&p->sections, e) {
800 if (!strcmp(e->name, sd->section_name)) {
801 s = uci_to_section(e);
806 return UCI_ERR_NOTFOUND;
808 ucimap_foreach_option(sm, om) {
809 union ucimap_data *data;
812 data = ucimap_get_data(sd, om);
813 if (!TEST_BIT(sd->cmap, i - 1))
816 ucimap_fill_ptr(&ptr, s, om->name);
817 if (ucimap_is_list(om->type)) {
818 struct ucimap_list *list = data->list;
822 for (j = 0; j < list->n_items; j++) {
823 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
828 ret = uci_set(s->package->ctx, &ptr);
831 ret = uci_add_list(s->package->ctx, &ptr);
837 ptr.value = ucimap_data_to_string(sd, om, data);
841 ret = uci_set(s->package->ctx, &ptr);
846 CLR_BIT(sd->cmap, i - 1);
853 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
855 struct uci_element *e;
856 struct list_head *p, *tmp;
859 INIT_LIST_HEAD(&map->fixup);
860 uci_foreach_element(&pkg->sections, e) {
861 struct uci_section *s = uci_to_section(e);
863 for (i = 0; i < map->n_sections; i++) {
864 struct uci_sectionmap *sm = map->sections[i];
865 struct ucimap_section_data *sd;
867 if (strcmp(s->type, map->sections[i]->type) != 0)
871 sd = sm->alloc(map, sm, s);
872 memset(sd, 0, sizeof(struct ucimap_section_data));
874 sd = malloc(sm->alloc_len);
875 memset(sd, 0, sm->alloc_len);
880 ucimap_parse_section(map, sm, sd, s);
885 list_for_each_safe(p, tmp, &map->fixup) {
886 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
887 ucimap_handle_fixup(map, f);
892 list_for_each_safe(p, tmp, &map->pending) {
893 struct ucimap_section_data *sd;
894 sd = list_entry(p, struct ucimap_section_data, list);
896 list_del_init(&sd->list);
897 ucimap_add_section(sd);