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 Lesser 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 return (type & UCIMAP_SUBTYPE) == UCIMAP_STRING;
66 ucimap_is_fixup(enum ucimap_type type)
68 return (type & UCIMAP_SUBTYPE) == UCIMAP_SECTION;
72 ucimap_is_simple(enum ucimap_type type)
74 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
78 ucimap_is_list(enum ucimap_type type)
80 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
84 ucimap_is_list_auto(enum ucimap_type type)
86 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
90 ucimap_is_custom(enum ucimap_type type)
92 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
96 ucimap_section_ptr(struct ucimap_section_data *sd)
98 return ((char *) sd - sd->sm->smap_offset);
101 static inline struct ucimap_section_data *
102 ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) {
103 ptr = (char *) ptr + sm->smap_offset;
107 static inline union ucimap_data *
108 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
112 data = (char *) ucimap_section_ptr(sd) + om->offset;
117 ucimap_init(struct uci_map *map)
121 map->fixup_tail = &map->fixup;
122 map->sdata_tail = &map->sdata;
127 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
129 struct ucimap_alloc *a = &sd->allocmap[sd->allocmap_len++];
134 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
139 section = ucimap_section_ptr(sd);
144 sd->sm->free(map, section);
146 for (i = 0; i < sd->allocmap_len; i++) {
147 free(sd->allocmap[i].ptr);
150 if (sd->alloc_custom) {
151 for (i = 0; i < sd->alloc_custom_len; i++) {
152 struct ucimap_alloc_custom *a = &sd->alloc_custom[i];
153 a->om->free(a->section, a->om, a->ptr);
155 free(sd->alloc_custom);
163 ucimap_cleanup(struct uci_map *map)
165 struct ucimap_section_data *sd, *sd_next;
167 for (sd = map->sdata; sd; sd = sd_next) {
169 ucimap_free_section(map, sd);
174 ucimap_find_section(struct uci_map *map, struct ucimap_fixup *f)
176 struct ucimap_section_data *sd;
178 for (sd = map->sdata; sd; sd = sd->next) {
181 if (strcmp(f->name, sd->section_name) != 0)
183 return ucimap_section_ptr(sd);
185 for (sd = map->pending; sd; sd = sd->next) {
188 if (strcmp(f->name, sd->section_name) != 0)
190 return ucimap_section_ptr(sd);
195 static union ucimap_data *
196 ucimap_list_append(struct ucimap_list *list)
198 if (unlikely(list->size <= list->n_items)) {
199 /* should not happen */
200 DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list->size);
203 return &list->item[list->n_items++];
208 ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f)
210 void *ptr = ucimap_find_section(map, f);
211 union ucimap_data *data;
216 switch(f->type & UCIMAP_TYPE) {
221 data = ucimap_list_append(f->data->list);
232 ucimap_free_item(struct ucimap_section_data *sd, void *item)
234 struct ucimap_alloc_custom *ac;
235 struct ucimap_alloc *a;
236 void *ptr = *((void **) item);
242 *((void **)item) = NULL;
243 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
247 if (i != sd->allocmap_len - 1)
248 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
254 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
258 if (i != sd->alloc_custom_len - 1)
259 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
260 sizeof(struct ucimap_alloc_custom));
262 ac->om->free(ac->section, ac->om, ac->ptr);
263 sd->alloc_custom_len--;
269 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
271 struct ucimap_list *new;
272 struct ucimap_alloc *a;
274 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
277 new = calloc(1, size);
279 ucimap_add_alloc(sd, new);
283 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
292 if (items > (*list)->size)
293 offset = (items - (*list)->size) * sizeof(union ucimap_data);
295 a->ptr = realloc(a->ptr, size);
297 memset((char *) a->ptr + offset, 0, size - offset);
307 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
309 struct ucimap_fixup *f, tmp;
310 struct uci_map *map = sd->map;
312 tmp.sm = om->data.sm;
316 if (ucimap_handle_fixup(map, &tmp))
319 f = malloc(sizeof(struct ucimap_fixup));
323 memcpy(f, &tmp, sizeof(tmp));
325 *map->fixup_tail = f;
326 map->fixup_tail = &f->next;
330 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
332 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
334 a->section = ucimap_section_ptr(sd);
340 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
342 union ucimap_data tdata = *data;
348 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
349 data = ucimap_list_append(data->list);
354 switch(om->type & UCIMAP_SUBTYPE) {
356 if ((om->data.s.maxlen > 0) &&
357 (strlen(str) > om->data.s.maxlen))
362 ucimap_add_alloc(sd, s);
365 if (!strcmp(str, "on"))
367 else if (!strcmp(str, "1"))
369 else if (!strcmp(str, "enabled"))
371 else if (!strcmp(str, "off"))
373 else if (!strcmp(str, "0"))
375 else if (!strcmp(str, "disabled"))
383 lval = strtol(str, &eptr, om->data.i.base);
384 if (lval < INT_MIN || lval > INT_MAX)
387 if (!eptr || *eptr == '\0')
388 tdata.i = (int) lval;
393 ucimap_add_fixup(sd, data, om, str);
399 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
401 if (ucimap_is_custom(om->type) && om->free) {
402 if (tdata.ptr != data->ptr)
403 ucimap_add_custom_alloc(sd, om, data->ptr);
406 if (ucimap_is_custom(om->type))
408 memcpy(data, &tdata, sizeof(union ucimap_data));
413 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
421 ucimap_add_alloc(sd, s);
431 while (*s && !isspace(*s))
439 ucimap_add_value(data, om, sd, p);
444 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
446 struct uci_element *e, *l;
447 struct uci_option *o;
448 union ucimap_data *data;
450 uci_foreach_element(&s->options, e) {
451 struct uci_optmap *om = NULL, *tmp;
453 ucimap_foreach_option(sm, tmp) {
454 if (strcmp(e->name, tmp->name) == 0) {
462 data = ucimap_get_data(sd, om);
463 o = uci_to_option(e);
464 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
465 ucimap_add_value(data, om, sd, o->v.string);
466 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
467 uci_foreach_element(&o->v.list, l) {
468 ucimap_add_value(data, om, sd, l->name);
470 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
471 ucimap_convert_list(data, om, sd, o->v.string);
479 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
481 sd->ref = map->sdata_tail;
483 map->sdata_tail = &sd->next;
487 ucimap_add_section(struct ucimap_section_data *sd)
489 struct uci_map *map = sd->map;
492 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
493 ucimap_free_section(map, sd);
495 ucimap_add_section_list(map, sd);
499 static const char *ucimap_type_names[] = {
500 [UCIMAP_STRING] = "string",
501 [UCIMAP_INT] = "integer",
502 [UCIMAP_BOOL] = "boolean",
503 [UCIMAP_SECTION] = "section",
504 [UCIMAP_LIST] = "list",
508 ucimap_get_type_name(int type)
513 if (ucimap_is_list(type))
514 return ucimap_type_names[UCIMAP_LIST];
516 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
518 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
527 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
531 if (unlikely(sm->type_name != om->type_name) &&
532 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
533 DPRINTF("Option '%s' of section type '%s' refereces unknown "
534 "section type '%s', should be '%s'.\n",
535 om->name, sm->type, om->type_name, sm->type_name);
539 if (om->detected_type < 0)
542 if (ucimap_is_custom(om->type))
545 if (ucimap_is_list(om->type) !=
546 ucimap_is_list(om->detected_type))
549 if (ucimap_is_list(om->type))
552 type = om->type & UCIMAP_SUBTYPE;
557 if (type != om->detected_type)
568 DPRINTF("Invalid type in option '%s' of section type '%s', "
569 "declared type is %s, detected type is %s\n",
571 ucimap_get_type_name(om->type),
572 ucimap_get_type_name(om->detected_type));
577 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
579 if (ucimap_is_alloc(om->type))
581 else if (ucimap_is_custom(om->type) && om->free)
586 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
588 struct uci_optmap *om;
592 int n_alloc_custom = 0;
598 ucimap_foreach_option(sm, om) {
599 if (!ucimap_check_optmap_type(sm, om))
602 if (ucimap_is_list(om->type)) {
603 union ucimap_data *data;
604 struct uci_element *e;
606 int n_elements_alloc = 0;
607 int n_elements_custom = 0;
610 data = ucimap_get_data(sd, om);
611 uci_foreach_element(&s->options, e) {
612 struct uci_option *o = uci_to_option(e);
613 struct uci_element *tmp;
615 if (strcmp(e->name, om->name) != 0)
618 if (o->type == UCI_TYPE_LIST) {
619 uci_foreach_element(&o->v.list, tmp) {
620 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
623 } else if ((o->type == UCI_TYPE_STRING) &&
624 ucimap_is_list_auto(om->type)) {
625 const char *data = o->v.string;
627 while (isspace(*data))
634 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
636 while (*data && !isspace(*data))
640 /* for the duplicated data string */
646 /* add one more for the ucimap_list */
647 n_alloc += n_elements_alloc + 1;
648 n_alloc_custom += n_elements_custom;
649 size = sizeof(struct ucimap_list) +
650 n_elements * sizeof(union ucimap_data);
652 data->list = malloc(size);
656 memset(data->list, 0, size);
657 data->list->size = n_elements;
659 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
663 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
667 if (n_alloc_custom > 0) {
668 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
669 if (!sd->alloc_custom)
673 section_name = strdup(s->e.name);
677 sd->section_name = section_name;
679 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
683 ucimap_add_alloc(sd, (void *)section_name);
684 ucimap_add_alloc(sd, (void *)sd->cmap);
685 ucimap_foreach_option(sm, om) {
686 if (!ucimap_is_list(om->type))
689 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
692 section = ucimap_section_ptr(sd);
693 err = sm->init(map, section, s);
698 ucimap_add_section(sd);
700 ucimap_add_section_list(map, sd);
703 err = ucimap_parse_options(map, sm, sd, s);
716 ucimap_free_section(map, sd);
721 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
723 struct uci_package *p = s->package;
725 memset(ptr, 0, sizeof(struct uci_ptr));
727 ptr->package = p->e.name;
730 ptr->section = s->e.name;
733 ptr->option = option;
734 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
738 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
740 void *section = ucimap_section_ptr(sd);
741 struct uci_sectionmap *sm = sd->sm;
742 struct uci_optmap *om;
743 int ofs = (char *)field - (char *)section;
746 ucimap_foreach_option(sm, om) {
747 if (om->offset == ofs) {
748 SET_BIT(sd->cmap, i);
756 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
761 switch(om->type & UCIMAP_SUBTYPE) {
766 sprintf(buf, "%d", data->i);
770 sprintf(buf, "%d", !!data->b);
775 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
786 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
796 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
798 struct uci_sectionmap *sm = sd->sm;
799 struct uci_section *s = NULL;
800 struct uci_optmap *om;
801 struct uci_element *e;
806 uci_foreach_element(&p->sections, e) {
807 if (!strcmp(e->name, sd->section_name)) {
808 s = uci_to_section(e);
813 return UCI_ERR_NOTFOUND;
815 ucimap_foreach_option(sm, om) {
816 union ucimap_data *data;
819 data = ucimap_get_data(sd, om);
820 if (!TEST_BIT(sd->cmap, i - 1))
823 ucimap_fill_ptr(&ptr, s, om->name);
824 if (ucimap_is_list(om->type)) {
825 struct ucimap_list *list = data->list;
829 for (j = 0; j < list->n_items; j++) {
830 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
835 ret = uci_set(s->package->ctx, &ptr);
838 ret = uci_add_list(s->package->ctx, &ptr);
844 ptr.value = ucimap_data_to_string(sd, om, data);
848 ret = uci_set(s->package->ctx, &ptr);
853 CLR_BIT(sd->cmap, i - 1);
860 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
862 struct uci_element *e;
863 struct ucimap_section_data *sd, **sd_tail;
864 struct ucimap_fixup *f;
867 sd_tail = map->sdata_tail;
869 map->sdata_tail = &map->pending;
870 uci_foreach_element(&pkg->sections, e) {
871 struct uci_section *s = uci_to_section(e);
873 for (i = 0; i < map->n_sections; i++) {
874 struct uci_sectionmap *sm = map->sections[i];
875 struct ucimap_section_data *sd;
877 if (strcmp(s->type, map->sections[i]->type) != 0)
881 sd = sm->alloc(map, sm, s);
882 memset(sd, 0, sizeof(struct ucimap_section_data));
884 sd = malloc(sm->alloc_len);
885 memset(sd, 0, sm->alloc_len);
886 sd = ucimap_ptr_section(sm, sd);
891 ucimap_parse_section(map, sm, sd, s);
896 map->sdata_tail = sd_tail;
901 struct ucimap_fixup *next = f->next;
902 ucimap_handle_fixup(map, f);
906 map->fixup_tail = &map->fixup;
911 struct ucimap_section_data *next = sd->next;
912 ucimap_add_section(sd);