2 * ucimap.c - Library for the Unified Configuration Interface
3 * Copyright (C) 2008-2009 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 Lesser General Public License version 2.1
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.
16 * This file contains ucimap, an API for mapping UCI to C data structures
29 #include "uci_internal.h"
35 struct ucimap_alloc_custom {
37 struct uci_optmap *om;
42 struct ucimap_fixup *next;
43 struct uci_sectionmap *sm;
45 enum ucimap_type type;
46 union ucimap_data *data;
49 #define ucimap_foreach_option(_sm, _o) \
50 if (!(_sm)->options_size) \
51 (_sm)->options_size = sizeof(struct uci_optmap); \
52 for (_o = &(_sm)->options[0]; \
53 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
54 (_sm)->options_size * (_sm)->n_options); \
55 _o = (struct uci_optmap *) ((char *)(_o) + \
60 ucimap_is_alloc(enum ucimap_type type)
62 switch(type & UCIMAP_SUBTYPE) {
71 ucimap_is_fixup(enum ucimap_type type)
73 switch(type & UCIMAP_SUBTYPE) {
82 ucimap_is_simple(enum ucimap_type type)
84 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
88 ucimap_is_list(enum ucimap_type type)
90 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
94 ucimap_is_list_auto(enum ucimap_type type)
96 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
100 ucimap_is_custom(enum ucimap_type type)
102 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
106 ucimap_section_ptr(struct ucimap_section_data *sd)
108 return ((char *) sd - sd->sm->smap_offset);
111 static inline struct ucimap_section_data *
112 ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) {
113 ptr = (char *) ptr + sm->smap_offset;
117 static inline union ucimap_data *
118 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
122 data = (char *) ucimap_section_ptr(sd) + om->offset;
127 ucimap_init(struct uci_map *map)
131 map->fixup_tail = &map->fixup;
132 map->sdata_tail = &map->sdata;
137 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
139 struct ucimap_alloc *a = &sd->allocmap[sd->allocmap_len++];
144 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
149 section = ucimap_section_ptr(sd);
154 sd->sm->free(map, section);
156 for (i = 0; i < sd->allocmap_len; i++) {
157 free(sd->allocmap[i].ptr);
160 if (sd->alloc_custom) {
161 for (i = 0; i < sd->alloc_custom_len; i++) {
162 struct ucimap_alloc_custom *a = &sd->alloc_custom[i];
163 a->om->free(a->section, a->om, a->ptr);
165 free(sd->alloc_custom);
173 ucimap_cleanup(struct uci_map *map)
175 struct ucimap_section_data *sd;
177 for (sd = map->sdata; sd; sd = sd->next) {
178 ucimap_free_section(map, sd);
183 ucimap_find_section(struct uci_map *map, struct ucimap_fixup *f)
185 struct ucimap_section_data *sd;
187 for (sd = map->sdata; sd; sd = sd->next) {
190 if (strcmp(f->name, sd->section_name) != 0)
192 return ucimap_section_ptr(sd);
194 for (sd = map->pending; sd; sd = sd->next) {
197 if (strcmp(f->name, sd->section_name) != 0)
199 return ucimap_section_ptr(sd);
204 static union ucimap_data *
205 ucimap_list_append(struct ucimap_list *list)
207 if (unlikely(list->size <= list->n_items)) {
208 /* should not happen */
209 DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list->size);
212 return &list->item[list->n_items++];
217 ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f)
219 void *ptr = ucimap_find_section(map, f);
220 struct ucimap_list *list;
221 union ucimap_data *data;
226 switch(f->type & UCIMAP_TYPE) {
231 list = f->data->list;
232 data = ucimap_list_append(f->data->list);
243 ucimap_free_item(struct ucimap_section_data *sd, void *item)
245 struct ucimap_alloc_custom *ac;
246 struct ucimap_alloc *a;
247 void *ptr = *((void **) item);
253 *((void **)item) = NULL;
254 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
258 if (i != sd->allocmap_len - 1)
259 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
265 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
269 if (i != sd->alloc_custom_len - 1)
270 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
271 sizeof(struct ucimap_alloc_custom));
273 ac->om->free(ac->section, ac->om, ac->ptr);
274 sd->alloc_custom_len--;
280 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
282 struct ucimap_list *new;
283 struct ucimap_alloc *a;
285 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
288 new = calloc(1, size);
290 ucimap_add_alloc(sd, new);
294 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
303 if (items > (*list)->size)
304 offset = (items - (*list)->size) * sizeof(union ucimap_data);
306 a->ptr = realloc(a->ptr, size);
308 memset((char *) a->ptr + offset, 0, size - offset);
318 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
320 struct ucimap_fixup *f, tmp;
321 struct uci_map *map = sd->map;
323 tmp.sm = om->data.sm;
327 if (ucimap_handle_fixup(map, &tmp))
330 f = malloc(sizeof(struct ucimap_fixup));
334 memcpy(f, &tmp, sizeof(tmp));
336 *map->fixup_tail = f;
337 map->fixup_tail = &f->next;
341 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
343 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
345 a->section = ucimap_section_ptr(sd);
351 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
353 union ucimap_data tdata = *data;
359 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
360 data = ucimap_list_append(data->list);
365 switch(om->type & UCIMAP_SUBTYPE) {
367 if ((om->data.s.maxlen > 0) &&
368 (strlen(str) > om->data.s.maxlen))
373 ucimap_add_alloc(sd, s);
376 if (!strcmp(str, "on"))
378 else if (!strcmp(str, "1"))
380 else if (!strcmp(str, "enabled"))
382 else if (!strcmp(str, "off"))
384 else if (!strcmp(str, "0"))
386 else if (!strcmp(str, "disabled"))
394 lval = strtol(str, &eptr, om->data.i.base);
395 if (lval < INT_MIN || lval > INT_MAX)
398 if (!eptr || *eptr == '\0')
399 tdata.i = (int) lval;
404 ucimap_add_fixup(sd, data, om, str);
407 tdata.s = (char *) data;
411 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
413 if (ucimap_is_custom(om->type) && om->free) {
414 if (tdata.ptr != data->ptr)
415 ucimap_add_custom_alloc(sd, om, data->ptr);
418 if (ucimap_is_custom(om->type))
420 memcpy(data, &tdata, sizeof(union ucimap_data));
425 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
433 ucimap_add_alloc(sd, s);
443 while (*s && !isspace(*s))
451 ucimap_add_value(data, om, sd, p);
456 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
458 struct uci_element *e, *l;
459 struct uci_option *o;
460 union ucimap_data *data;
462 uci_foreach_element(&s->options, e) {
463 struct uci_optmap *om = NULL, *tmp;
465 ucimap_foreach_option(sm, tmp) {
466 if (strcmp(e->name, tmp->name) == 0) {
474 data = ucimap_get_data(sd, om);
475 o = uci_to_option(e);
476 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
477 ucimap_add_value(data, om, sd, o->v.string);
478 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
479 uci_foreach_element(&o->v.list, l) {
480 ucimap_add_value(data, om, sd, l->name);
482 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
483 ucimap_convert_list(data, om, sd, o->v.string);
491 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
493 sd->ref = map->sdata_tail;
495 map->sdata_tail = &sd->next;
499 ucimap_add_section(struct ucimap_section_data *sd)
501 struct uci_map *map = sd->map;
504 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
505 ucimap_free_section(map, sd);
507 ucimap_add_section_list(map, sd);
511 static const char *ucimap_type_names[] = {
512 [UCIMAP_STRING] = "string",
513 [UCIMAP_INT] = "integer",
514 [UCIMAP_BOOL] = "boolean",
515 [UCIMAP_SECTION] = "section",
516 [UCIMAP_LIST] = "list",
520 ucimap_get_type_name(int type)
525 if (ucimap_is_list(type))
526 return ucimap_type_names[UCIMAP_LIST];
528 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
530 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
539 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
543 if (unlikely(sm->type_name != om->type_name) &&
544 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
545 DPRINTF("Option '%s' of section type '%s' refereces unknown "
546 "section type '%s', should be '%s'.\n",
547 om->name, sm->type, om->type_name, sm->type_name);
551 if (om->detected_type < 0)
554 if (ucimap_is_custom(om->type))
557 if (ucimap_is_list(om->type) !=
558 ucimap_is_list(om->detected_type))
561 if (ucimap_is_list(om->type))
564 type = om->type & UCIMAP_SUBTYPE;
569 if (type != om->detected_type)
580 DPRINTF("Invalid type in option '%s' of section type '%s', "
581 "declared type is %s, detected type is %s\n",
583 ucimap_get_type_name(om->type),
584 ucimap_get_type_name(om->detected_type));
589 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
591 if (ucimap_is_alloc(om->type))
593 else if (ucimap_is_custom(om->type) && om->free)
598 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
600 struct uci_optmap *om;
604 int n_alloc_custom = 0;
610 ucimap_foreach_option(sm, om) {
611 if (!ucimap_check_optmap_type(sm, om))
614 if (ucimap_is_list(om->type)) {
615 union ucimap_data *data;
616 struct uci_element *e;
618 int n_elements_alloc = 0;
619 int n_elements_custom = 0;
622 data = ucimap_get_data(sd, om);
623 uci_foreach_element(&s->options, e) {
624 struct uci_option *o = uci_to_option(e);
625 struct uci_element *tmp;
627 if (strcmp(e->name, om->name) != 0)
630 if (o->type == UCI_TYPE_LIST) {
631 uci_foreach_element(&o->v.list, tmp) {
632 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
635 } else if ((o->type == UCI_TYPE_STRING) &&
636 ucimap_is_list_auto(om->type)) {
637 const char *data = o->v.string;
639 while (isspace(*data))
646 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
648 while (*data && !isspace(*data))
652 /* for the duplicated data string */
658 /* add one more for the ucimap_list */
659 n_alloc += n_elements_alloc + 1;
660 n_alloc_custom += n_elements_custom;
661 size = sizeof(struct ucimap_list) +
662 n_elements * sizeof(union ucimap_data);
664 data->list = malloc(size);
668 memset(data->list, 0, size);
669 data->list->size = n_elements;
671 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
675 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
679 if (n_alloc_custom > 0) {
680 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
681 if (!sd->alloc_custom)
685 section_name = strdup(s->e.name);
689 sd->section_name = section_name;
691 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
695 ucimap_add_alloc(sd, (void *)section_name);
696 ucimap_add_alloc(sd, (void *)sd->cmap);
697 ucimap_foreach_option(sm, om) {
698 if (!ucimap_is_list(om->type))
701 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
704 section = ucimap_section_ptr(sd);
705 err = sm->init(map, section, s);
710 ucimap_add_section(sd);
712 ucimap_add_section_list(map, sd);
715 err = ucimap_parse_options(map, sm, sd, s);
728 ucimap_free_section(map, sd);
733 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
735 struct uci_package *p = s->package;
737 memset(ptr, 0, sizeof(struct uci_ptr));
739 ptr->package = p->e.name;
742 ptr->section = s->e.name;
745 ptr->option = option;
746 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
750 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
752 void *section = ucimap_section_ptr(sd);
753 struct uci_sectionmap *sm = sd->sm;
754 struct uci_optmap *om;
755 int ofs = (char *)field - (char *)section;
758 ucimap_foreach_option(sm, om) {
759 if (om->offset == ofs) {
760 SET_BIT(sd->cmap, i);
768 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
773 switch(om->type & UCIMAP_SUBTYPE) {
778 sprintf(buf, "%d", data->i);
782 sprintf(buf, "%d", !!data->b);
787 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
798 union ucimap_data tdata;
800 if (ucimap_is_custom(om->type)) {
801 tdata.s = (char *)data;
805 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
815 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
817 struct uci_sectionmap *sm = sd->sm;
818 struct uci_section *s = NULL;
819 struct uci_optmap *om;
820 struct uci_element *e;
825 uci_foreach_element(&p->sections, e) {
826 if (!strcmp(e->name, sd->section_name)) {
827 s = uci_to_section(e);
832 return UCI_ERR_NOTFOUND;
834 ucimap_foreach_option(sm, om) {
835 union ucimap_data *data;
838 data = ucimap_get_data(sd, om);
839 if (!TEST_BIT(sd->cmap, i - 1))
842 ucimap_fill_ptr(&ptr, s, om->name);
843 if (ucimap_is_list(om->type)) {
844 struct ucimap_list *list = data->list;
848 for (j = 0; j < list->n_items; j++) {
849 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
854 ret = uci_set(s->package->ctx, &ptr);
857 ret = uci_add_list(s->package->ctx, &ptr);
863 ptr.value = ucimap_data_to_string(sd, om, data);
867 ret = uci_set(s->package->ctx, &ptr);
872 CLR_BIT(sd->cmap, i - 1);
879 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
881 struct uci_element *e;
882 struct ucimap_section_data *sd, **sd_tail;
883 struct ucimap_fixup *f;
886 sd_tail = map->sdata_tail;
888 map->sdata_tail = &map->pending;
889 uci_foreach_element(&pkg->sections, e) {
890 struct uci_section *s = uci_to_section(e);
892 for (i = 0; i < map->n_sections; i++) {
893 struct uci_sectionmap *sm = map->sections[i];
894 struct ucimap_section_data *sd;
896 if (strcmp(s->type, map->sections[i]->type) != 0)
900 sd = sm->alloc(map, sm, s);
901 memset(sd, 0, sizeof(struct ucimap_section_data));
903 sd = malloc(sm->alloc_len);
904 memset(sd, 0, sm->alloc_len);
909 ucimap_parse_section(map, sm, sd, s);
914 map->sdata_tail = sd_tail;
919 struct ucimap_fixup *next = f->next;
920 ucimap_handle_fixup(map, f);
924 map->fixup_tail = &map->fixup;
929 struct ucimap_section_data *next = sd->next;
930 ucimap_add_section(sd);