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);
205 ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f)
207 void *ptr = ucimap_find_section(map, f);
208 struct ucimap_list *list;
213 switch(f->type & UCIMAP_TYPE) {
218 list = f->data->list;
219 list->item[list->n_items++].ptr = ptr;
226 ucimap_free_item(struct ucimap_section_data *sd, void *item)
228 struct ucimap_alloc_custom *ac;
229 struct ucimap_alloc *a;
230 void *ptr = *((void **) item);
236 *((void **)item) = NULL;
237 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
241 if (i != sd->allocmap_len - 1)
242 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
248 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
252 if (i != sd->alloc_custom_len - 1)
253 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
254 sizeof(struct ucimap_alloc_custom));
256 ac->om->free(ac->section, ac->om, ac->ptr);
257 sd->alloc_custom_len--;
263 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
265 struct ucimap_list *new;
266 struct ucimap_alloc *a;
268 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
271 new = calloc(1, size);
273 ucimap_add_alloc(sd, new);
277 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
286 if (items > (*list)->size)
287 offset = (items - (*list)->size) * sizeof(union ucimap_data);
289 a->ptr = realloc(a->ptr, size);
291 memset((char *) a->ptr + offset, 0, size - offset);
301 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
303 struct ucimap_fixup *f, tmp;
304 struct uci_map *map = sd->map;
306 tmp.sm = om->data.sm;
310 if (ucimap_handle_fixup(map, &tmp))
313 f = malloc(sizeof(struct ucimap_fixup));
317 memcpy(f, &tmp, sizeof(tmp));
319 *map->fixup_tail = f;
320 map->fixup_tail = &f->next;
324 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
326 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
328 a->section = ucimap_section_ptr(sd);
334 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
336 union ucimap_data tdata = *data;
342 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
343 if (unlikely(data->list->size <= data->list->n_items)) {
344 /* should not happen */
345 DPRINTF("ERROR: overflow while filling a list\n");
349 data = &data->list->item[data->list->n_items++];
352 switch(om->type & UCIMAP_SUBTYPE) {
354 if ((om->data.s.maxlen > 0) &&
355 (strlen(str) > om->data.s.maxlen))
360 ucimap_add_alloc(sd, s);
363 if (!strcmp(str, "on"))
365 else if (!strcmp(str, "1"))
367 else if (!strcmp(str, "enabled"))
369 else if (!strcmp(str, "off"))
371 else if (!strcmp(str, "0"))
373 else if (!strcmp(str, "disabled"))
381 lval = strtol(str, &eptr, om->data.i.base);
382 if (lval < INT_MIN || lval > INT_MAX)
385 if (!eptr || *eptr == '\0')
386 tdata.i = (int) lval;
391 ucimap_add_fixup(sd, data, om, str);
394 tdata.s = (char *) data;
398 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
400 if (ucimap_is_custom(om->type) && om->free) {
401 if (tdata.ptr != data->ptr)
402 ucimap_add_custom_alloc(sd, om, data->ptr);
405 if (ucimap_is_custom(om->type))
407 memcpy(data, &tdata, sizeof(union ucimap_data));
412 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
420 ucimap_add_alloc(sd, s);
430 while (*s && !isspace(*s))
438 ucimap_add_value(data, om, sd, p);
443 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
445 struct uci_element *e, *l;
446 struct uci_option *o;
447 union ucimap_data *data;
449 uci_foreach_element(&s->options, e) {
450 struct uci_optmap *om = NULL, *tmp;
452 ucimap_foreach_option(sm, tmp) {
453 if (strcmp(e->name, tmp->name) == 0) {
461 data = ucimap_get_data(sd, om);
462 o = uci_to_option(e);
463 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
464 ucimap_add_value(data, om, sd, o->v.string);
465 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
466 uci_foreach_element(&o->v.list, l) {
467 ucimap_add_value(data, om, sd, l->name);
469 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
470 ucimap_convert_list(data, om, sd, o->v.string);
478 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
480 sd->ref = map->sdata_tail;
482 map->sdata_tail = &sd->next;
486 ucimap_add_section(struct ucimap_section_data *sd)
488 struct uci_map *map = sd->map;
491 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
492 ucimap_free_section(map, sd);
494 ucimap_add_section_list(map, sd);
498 static const char *ucimap_type_names[] = {
499 [UCIMAP_STRING] = "string",
500 [UCIMAP_INT] = "integer",
501 [UCIMAP_BOOL] = "boolean",
502 [UCIMAP_SECTION] = "section",
503 [UCIMAP_LIST] = "list",
507 ucimap_get_type_name(int type)
512 if (ucimap_is_list(type))
513 return ucimap_type_names[UCIMAP_LIST];
515 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
517 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
526 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
530 if (unlikely(sm->type_name != om->type_name) &&
531 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
532 DPRINTF("Option '%s' of section type '%s' refereces unknown "
533 "section type '%s', should be '%s'.\n",
534 om->name, sm->type, om->type_name, sm->type_name);
538 if (om->detected_type < 0)
541 if (ucimap_is_custom(om->type))
544 if (ucimap_is_list(om->type) !=
545 ucimap_is_list(om->detected_type))
548 if (ucimap_is_list(om->type))
551 type = om->type & UCIMAP_SUBTYPE;
556 if (type != om->detected_type)
567 DPRINTF("Invalid type in option '%s' of section type '%s', "
568 "declared type is %s, detected type is %s\n",
570 ucimap_get_type_name(om->type),
571 ucimap_get_type_name(om->detected_type));
576 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
578 if (ucimap_is_alloc(om->type))
580 else if (ucimap_is_custom(om->type) && om->free)
585 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
587 struct uci_optmap *om;
591 int n_alloc_custom = 0;
597 ucimap_foreach_option(sm, om) {
598 if (!ucimap_check_optmap_type(sm, om))
601 if (ucimap_is_list(om->type)) {
602 union ucimap_data *data;
603 struct uci_element *e;
605 int n_elements_custom = 0;
608 data = ucimap_get_data(sd, om);
609 uci_foreach_element(&s->options, e) {
610 struct uci_option *o = uci_to_option(e);
611 struct uci_element *tmp;
613 if (strcmp(e->name, om->name) != 0)
616 if (o->type == UCI_TYPE_LIST) {
617 uci_foreach_element(&o->v.list, tmp) {
618 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
620 } else if ((o->type == UCI_TYPE_STRING) &&
621 ucimap_is_list_auto(om->type)) {
622 const char *data = o->v.string;
624 while (isspace(*data))
631 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
633 while (*data && !isspace(*data))
637 /* for the duplicated data string */
643 /* add one more for the ucimap_list */
644 n_alloc += n_elements + 1;
645 n_alloc_custom += n_elements_custom;
646 size = sizeof(struct ucimap_list) +
647 n_elements * sizeof(union ucimap_data);
649 data->list = malloc(size);
653 memset(data->list, 0, size);
654 data->list->size = n_elements;
656 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
660 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
664 if (n_alloc_custom > 0) {
665 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
666 if (!sd->alloc_custom)
670 section_name = strdup(s->e.name);
674 sd->section_name = section_name;
676 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
680 ucimap_add_alloc(sd, (void *)section_name);
681 ucimap_add_alloc(sd, (void *)sd->cmap);
682 ucimap_foreach_option(sm, om) {
683 if (!ucimap_is_list(om->type))
686 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
689 section = ucimap_section_ptr(sd);
690 err = sm->init(map, section, s);
695 ucimap_add_section(sd);
697 ucimap_add_section_list(map, sd);
700 err = ucimap_parse_options(map, sm, sd, s);
713 ucimap_free_section(map, sd);
718 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
720 struct uci_package *p = s->package;
722 memset(ptr, 0, sizeof(struct uci_ptr));
724 ptr->package = p->e.name;
727 ptr->section = s->e.name;
730 ptr->option = option;
731 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
735 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
737 void *section = ucimap_section_ptr(sd);
738 struct uci_sectionmap *sm = sd->sm;
739 struct uci_optmap *om;
740 int ofs = (char *)field - (char *)section;
743 ucimap_foreach_option(sm, om) {
744 if (om->offset == ofs) {
745 SET_BIT(sd->cmap, i);
753 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
758 switch(om->type & UCIMAP_SUBTYPE) {
763 sprintf(buf, "%d", data->i);
767 sprintf(buf, "%d", !!data->b);
772 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
783 union ucimap_data tdata;
785 if (ucimap_is_custom(om->type)) {
786 tdata.s = (char *)data;
790 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
800 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
802 struct uci_sectionmap *sm = sd->sm;
803 struct uci_section *s = NULL;
804 struct uci_optmap *om;
805 struct uci_element *e;
810 uci_foreach_element(&p->sections, e) {
811 if (!strcmp(e->name, sd->section_name)) {
812 s = uci_to_section(e);
817 return UCI_ERR_NOTFOUND;
819 ucimap_foreach_option(sm, om) {
820 union ucimap_data *data;
823 data = ucimap_get_data(sd, om);
824 if (!TEST_BIT(sd->cmap, i - 1))
827 ucimap_fill_ptr(&ptr, s, om->name);
828 if (ucimap_is_list(om->type)) {
829 struct ucimap_list *list = data->list;
833 for (j = 0; j < list->n_items; j++) {
834 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
839 ret = uci_set(s->package->ctx, &ptr);
842 ret = uci_add_list(s->package->ctx, &ptr);
848 ptr.value = ucimap_data_to_string(sd, om, data);
852 ret = uci_set(s->package->ctx, &ptr);
857 CLR_BIT(sd->cmap, i - 1);
864 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
866 struct uci_element *e;
867 struct ucimap_section_data *sd, **sd_tail;
868 struct ucimap_fixup *f;
871 sd_tail = map->sdata_tail;
873 map->sdata_tail = &map->pending;
874 uci_foreach_element(&pkg->sections, e) {
875 struct uci_section *s = uci_to_section(e);
877 for (i = 0; i < map->n_sections; i++) {
878 struct uci_sectionmap *sm = map->sections[i];
879 struct ucimap_section_data *sd;
881 if (strcmp(s->type, map->sections[i]->type) != 0)
885 sd = sm->alloc(map, sm, s);
886 memset(sd, 0, sizeof(struct ucimap_section_data));
888 sd = malloc(sm->alloc_len);
889 memset(sd, 0, sm->alloc_len);
894 ucimap_parse_section(map, sm, sd, s);
899 map->sdata_tail = sd_tail;
904 struct ucimap_fixup *next = f->next;
905 ucimap_handle_fixup(map, f);
909 map->fixup_tail = &map->fixup;
914 struct ucimap_section_data *next = sd->next;
915 ucimap_add_section(sd);