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);
281 ucimap_add_alloc(sd, new);
285 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
294 if (items > (*list)->size)
295 offset = (items - (*list)->size) * sizeof(union ucimap_data);
297 a->ptr = realloc(a->ptr, size);
299 memset((char *) a->ptr + offset, 0, size - offset);
309 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
311 struct ucimap_fixup *f, tmp;
312 struct uci_map *map = sd->map;
314 tmp.sm = om->data.sm;
318 if (ucimap_handle_fixup(map, &tmp))
321 f = malloc(sizeof(struct ucimap_fixup));
325 memcpy(f, &tmp, sizeof(tmp));
327 *map->fixup_tail = f;
328 map->fixup_tail = &f->next;
332 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
334 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
336 a->section = ucimap_section_ptr(sd);
342 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
344 union ucimap_data tdata = *data;
350 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
351 data = ucimap_list_append(data->list);
356 switch(om->type & UCIMAP_SUBTYPE) {
358 if ((om->data.s.maxlen > 0) &&
359 (strlen(str) > om->data.s.maxlen))
364 ucimap_add_alloc(sd, s);
367 if (!strcmp(str, "on"))
369 else if (!strcmp(str, "1"))
371 else if (!strcmp(str, "enabled"))
373 else if (!strcmp(str, "off"))
375 else if (!strcmp(str, "0"))
377 else if (!strcmp(str, "disabled"))
385 lval = strtol(str, &eptr, om->data.i.base);
386 if (lval < INT_MIN || lval > INT_MAX)
389 if (!eptr || *eptr == '\0')
390 tdata.i = (int) lval;
395 ucimap_add_fixup(sd, data, om, str);
401 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
403 if (ucimap_is_custom(om->type) && om->free) {
404 if (tdata.ptr != data->ptr)
405 ucimap_add_custom_alloc(sd, om, data->ptr);
408 if (ucimap_is_custom(om->type))
410 memcpy(data, &tdata, sizeof(union ucimap_data));
415 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
423 ucimap_add_alloc(sd, s);
433 while (*s && !isspace(*s))
441 ucimap_add_value(data, om, sd, p);
446 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
448 struct uci_element *e, *l;
449 struct uci_option *o;
450 union ucimap_data *data;
452 uci_foreach_element(&s->options, e) {
453 struct uci_optmap *om = NULL, *tmp;
455 ucimap_foreach_option(sm, tmp) {
456 if (strcmp(e->name, tmp->name) == 0) {
464 data = ucimap_get_data(sd, om);
465 o = uci_to_option(e);
466 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
467 ucimap_add_value(data, om, sd, o->v.string);
468 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
469 uci_foreach_element(&o->v.list, l) {
470 ucimap_add_value(data, om, sd, l->name);
472 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
473 ucimap_convert_list(data, om, sd, o->v.string);
481 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
483 sd->ref = map->sdata_tail;
485 map->sdata_tail = &sd->next;
489 ucimap_add_section(struct ucimap_section_data *sd)
491 struct uci_map *map = sd->map;
494 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
495 ucimap_free_section(map, sd);
497 ucimap_add_section_list(map, sd);
501 static const char *ucimap_type_names[] = {
502 [UCIMAP_STRING] = "string",
503 [UCIMAP_INT] = "integer",
504 [UCIMAP_BOOL] = "boolean",
505 [UCIMAP_SECTION] = "section",
506 [UCIMAP_LIST] = "list",
510 ucimap_get_type_name(int type)
515 if (ucimap_is_list(type))
516 return ucimap_type_names[UCIMAP_LIST];
518 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
520 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
529 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
533 if (unlikely(sm->type_name != om->type_name) &&
534 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
535 DPRINTF("Option '%s' of section type '%s' refereces unknown "
536 "section type '%s', should be '%s'.\n",
537 om->name, sm->type, om->type_name, sm->type_name);
541 if (om->detected_type < 0)
544 if (ucimap_is_custom(om->type))
547 if (ucimap_is_list(om->type) !=
548 ucimap_is_list(om->detected_type))
551 if (ucimap_is_list(om->type))
554 type = om->type & UCIMAP_SUBTYPE;
559 if (type != om->detected_type)
570 DPRINTF("Invalid type in option '%s' of section type '%s', "
571 "declared type is %s, detected type is %s\n",
573 ucimap_get_type_name(om->type),
574 ucimap_get_type_name(om->detected_type));
579 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
581 if (ucimap_is_alloc(om->type))
583 else if (ucimap_is_custom(om->type) && om->free)
588 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
590 struct uci_optmap *om;
594 int n_alloc_custom = 0;
600 ucimap_foreach_option(sm, om) {
601 if (!ucimap_check_optmap_type(sm, om))
604 if (ucimap_is_list(om->type)) {
605 union ucimap_data *data;
606 struct uci_element *e;
608 int n_elements_alloc = 0;
609 int n_elements_custom = 0;
612 data = ucimap_get_data(sd, om);
613 uci_foreach_element(&s->options, e) {
614 struct uci_option *o = uci_to_option(e);
615 struct uci_element *tmp;
617 if (strcmp(e->name, om->name) != 0)
620 if (o->type == UCI_TYPE_LIST) {
621 uci_foreach_element(&o->v.list, tmp) {
622 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
625 } else if ((o->type == UCI_TYPE_STRING) &&
626 ucimap_is_list_auto(om->type)) {
627 const char *data = o->v.string;
629 while (isspace(*data))
636 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
638 while (*data && !isspace(*data))
642 /* for the duplicated data string */
648 /* add one more for the ucimap_list */
649 n_alloc += n_elements_alloc + 1;
650 n_alloc_custom += n_elements_custom;
651 size = sizeof(struct ucimap_list) +
652 n_elements * sizeof(union ucimap_data);
654 data->list = malloc(size);
658 memset(data->list, 0, size);
659 data->list->size = n_elements;
661 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
665 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
669 if (n_alloc_custom > 0) {
670 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
671 if (!sd->alloc_custom)
675 section_name = strdup(s->e.name);
679 sd->section_name = section_name;
681 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
685 ucimap_add_alloc(sd, (void *)section_name);
686 ucimap_add_alloc(sd, (void *)sd->cmap);
687 ucimap_foreach_option(sm, om) {
688 if (!ucimap_is_list(om->type))
691 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
694 section = ucimap_section_ptr(sd);
695 err = sm->init(map, section, s);
700 ucimap_add_section(sd);
702 ucimap_add_section_list(map, sd);
705 err = ucimap_parse_options(map, sm, sd, s);
718 ucimap_free_section(map, sd);
723 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
725 struct uci_package *p = s->package;
727 memset(ptr, 0, sizeof(struct uci_ptr));
729 ptr->package = p->e.name;
732 ptr->section = s->e.name;
735 ptr->option = option;
736 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
740 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
742 void *section = ucimap_section_ptr(sd);
743 struct uci_sectionmap *sm = sd->sm;
744 struct uci_optmap *om;
745 int ofs = (char *)field - (char *)section;
748 ucimap_foreach_option(sm, om) {
749 if (om->offset == ofs) {
750 SET_BIT(sd->cmap, i);
758 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
763 switch(om->type & UCIMAP_SUBTYPE) {
768 sprintf(buf, "%d", data->i);
772 sprintf(buf, "%d", !!data->b);
777 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
788 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
798 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
800 struct uci_sectionmap *sm = sd->sm;
801 struct uci_section *s = NULL;
802 struct uci_optmap *om;
803 struct uci_element *e;
808 uci_foreach_element(&p->sections, e) {
809 if (!strcmp(e->name, sd->section_name)) {
810 s = uci_to_section(e);
815 return UCI_ERR_NOTFOUND;
817 ucimap_foreach_option(sm, om) {
818 union ucimap_data *data;
821 data = ucimap_get_data(sd, om);
822 if (!TEST_BIT(sd->cmap, i - 1))
825 ucimap_fill_ptr(&ptr, s, om->name);
826 if (ucimap_is_list(om->type)) {
827 struct ucimap_list *list = data->list;
831 for (j = 0; j < list->n_items; j++) {
832 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
837 ret = uci_set(s->package->ctx, &ptr);
840 ret = uci_add_list(s->package->ctx, &ptr);
846 ptr.value = ucimap_data_to_string(sd, om, data);
850 ret = uci_set(s->package->ctx, &ptr);
855 CLR_BIT(sd->cmap, i - 1);
862 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
864 struct uci_element *e;
865 struct ucimap_section_data *sd, **sd_tail;
866 struct ucimap_fixup *f;
869 sd_tail = map->sdata_tail;
871 map->sdata_tail = &map->pending;
872 uci_foreach_element(&pkg->sections, e) {
873 struct uci_section *s = uci_to_section(e);
875 for (i = 0; i < map->n_sections; i++) {
876 struct uci_sectionmap *sm = map->sections[i];
877 struct ucimap_section_data *sd;
879 if (strcmp(s->type, map->sections[i]->type) != 0)
883 sd = sm->alloc(map, sm, s);
884 memset(sd, 0, sizeof(struct ucimap_section_data));
886 sd = malloc(sm->alloc_len);
887 memset(sd, 0, sm->alloc_len);
888 sd = ucimap_ptr_section(sm, sd);
893 ucimap_parse_section(map, sm, sd, s);
898 map->sdata_tail = sd_tail;
903 struct ucimap_fixup *next = f->next;
904 ucimap_handle_fixup(map, f);
908 map->fixup_tail = &map->fixup;
913 struct ucimap_section_data *next = sd->next;
914 ucimap_add_section(sd);