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);
302 memset((char *) a->ptr + offset, 0, size - offset);
312 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
314 struct ucimap_fixup *f, tmp;
315 struct uci_map *map = sd->map;
317 tmp.sm = om->data.sm;
321 if (ucimap_handle_fixup(map, &tmp))
324 f = malloc(sizeof(struct ucimap_fixup));
328 memcpy(f, &tmp, sizeof(tmp));
330 *map->fixup_tail = f;
331 map->fixup_tail = &f->next;
335 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
337 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
339 a->section = ucimap_section_ptr(sd);
345 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
347 union ucimap_data tdata = *data;
353 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
354 data = ucimap_list_append(data->list);
359 switch(om->type & UCIMAP_SUBTYPE) {
361 if ((om->data.s.maxlen > 0) &&
362 (strlen(str) > om->data.s.maxlen))
367 ucimap_add_alloc(sd, s);
370 if (!strcmp(str, "on"))
372 else if (!strcmp(str, "1"))
374 else if (!strcmp(str, "enabled"))
376 else if (!strcmp(str, "off"))
378 else if (!strcmp(str, "0"))
380 else if (!strcmp(str, "disabled"))
388 lval = strtol(str, &eptr, om->data.i.base);
389 if (lval < INT_MIN || lval > INT_MAX)
392 if (!eptr || *eptr == '\0')
393 tdata.i = (int) lval;
398 ucimap_add_fixup(sd, data, om, str);
404 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
406 if (ucimap_is_custom(om->type) && om->free) {
407 if (tdata.ptr != data->ptr)
408 ucimap_add_custom_alloc(sd, om, data->ptr);
411 if (ucimap_is_custom(om->type))
413 memcpy(data, &tdata, sizeof(union ucimap_data));
418 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
426 ucimap_add_alloc(sd, s);
436 while (*s && !isspace(*s))
444 ucimap_add_value(data, om, sd, p);
449 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
451 struct uci_element *e, *l;
452 struct uci_option *o;
453 union ucimap_data *data;
455 uci_foreach_element(&s->options, e) {
456 struct uci_optmap *om = NULL, *tmp;
458 ucimap_foreach_option(sm, tmp) {
459 if (strcmp(e->name, tmp->name) == 0) {
467 data = ucimap_get_data(sd, om);
468 o = uci_to_option(e);
469 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
470 ucimap_add_value(data, om, sd, o->v.string);
471 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
472 uci_foreach_element(&o->v.list, l) {
473 ucimap_add_value(data, om, sd, l->name);
475 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
476 ucimap_convert_list(data, om, sd, o->v.string);
484 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
486 sd->ref = map->sdata_tail;
488 map->sdata_tail = &sd->next;
492 ucimap_add_section(struct ucimap_section_data *sd)
494 struct uci_map *map = sd->map;
497 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
498 ucimap_free_section(map, sd);
500 ucimap_add_section_list(map, sd);
504 static const char *ucimap_type_names[] = {
505 [UCIMAP_STRING] = "string",
506 [UCIMAP_INT] = "integer",
507 [UCIMAP_BOOL] = "boolean",
508 [UCIMAP_SECTION] = "section",
509 [UCIMAP_LIST] = "list",
513 ucimap_get_type_name(int type)
518 if (ucimap_is_list(type))
519 return ucimap_type_names[UCIMAP_LIST];
521 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
523 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
532 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
536 if (unlikely(sm->type_name != om->type_name) &&
537 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
538 DPRINTF("Option '%s' of section type '%s' refereces unknown "
539 "section type '%s', should be '%s'.\n",
540 om->name, sm->type, om->type_name, sm->type_name);
544 if (om->detected_type < 0)
547 if (ucimap_is_custom(om->type))
550 if (ucimap_is_list(om->type) !=
551 ucimap_is_list(om->detected_type))
554 if (ucimap_is_list(om->type))
557 type = om->type & UCIMAP_SUBTYPE;
562 if (type != om->detected_type)
573 DPRINTF("Invalid type in option '%s' of section type '%s', "
574 "declared type is %s, detected type is %s\n",
576 ucimap_get_type_name(om->type),
577 ucimap_get_type_name(om->detected_type));
582 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
584 if (ucimap_is_alloc(om->type))
586 else if (ucimap_is_custom(om->type) && om->free)
591 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
593 struct uci_optmap *om;
597 int n_alloc_custom = 0;
603 ucimap_foreach_option(sm, om) {
604 if (!ucimap_check_optmap_type(sm, om))
607 if (ucimap_is_list(om->type)) {
608 union ucimap_data *data;
609 struct uci_element *e;
611 int n_elements_alloc = 0;
612 int n_elements_custom = 0;
615 data = ucimap_get_data(sd, om);
616 uci_foreach_element(&s->options, e) {
617 struct uci_option *o = uci_to_option(e);
618 struct uci_element *tmp;
620 if (strcmp(e->name, om->name) != 0)
623 if (o->type == UCI_TYPE_LIST) {
624 uci_foreach_element(&o->v.list, tmp) {
625 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
628 } else if ((o->type == UCI_TYPE_STRING) &&
629 ucimap_is_list_auto(om->type)) {
630 const char *data = o->v.string;
632 while (isspace(*data))
639 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
641 while (*data && !isspace(*data))
645 /* for the duplicated data string */
651 /* add one more for the ucimap_list */
652 n_alloc += n_elements_alloc + 1;
653 n_alloc_custom += n_elements_custom;
654 size = sizeof(struct ucimap_list) +
655 n_elements * sizeof(union ucimap_data);
657 data->list = malloc(size);
661 memset(data->list, 0, size);
662 data->list->size = n_elements;
664 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
668 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
672 if (n_alloc_custom > 0) {
673 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
674 if (!sd->alloc_custom)
678 section_name = strdup(s->e.name);
682 sd->section_name = section_name;
684 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
688 ucimap_add_alloc(sd, (void *)section_name);
689 ucimap_add_alloc(sd, (void *)sd->cmap);
690 ucimap_foreach_option(sm, om) {
691 if (!ucimap_is_list(om->type))
694 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
697 section = ucimap_section_ptr(sd);
698 err = sm->init(map, section, s);
703 ucimap_add_section(sd);
705 ucimap_add_section_list(map, sd);
708 err = ucimap_parse_options(map, sm, sd, s);
721 ucimap_free_section(map, sd);
726 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
728 struct uci_package *p = s->package;
730 memset(ptr, 0, sizeof(struct uci_ptr));
732 ptr->package = p->e.name;
735 ptr->section = s->e.name;
738 ptr->option = option;
739 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
743 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
745 void *section = ucimap_section_ptr(sd);
746 struct uci_sectionmap *sm = sd->sm;
747 struct uci_optmap *om;
748 int ofs = (char *)field - (char *)section;
751 ucimap_foreach_option(sm, om) {
752 if (om->offset == ofs) {
753 SET_BIT(sd->cmap, i);
761 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
766 switch(om->type & UCIMAP_SUBTYPE) {
771 sprintf(buf, "%d", data->i);
775 sprintf(buf, "%d", !!data->b);
780 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
791 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
801 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
803 struct uci_sectionmap *sm = sd->sm;
804 struct uci_section *s = NULL;
805 struct uci_optmap *om;
806 struct uci_element *e;
811 uci_foreach_element(&p->sections, e) {
812 if (!strcmp(e->name, sd->section_name)) {
813 s = uci_to_section(e);
818 return UCI_ERR_NOTFOUND;
820 ucimap_foreach_option(sm, om) {
821 union ucimap_data *data;
824 data = ucimap_get_data(sd, om);
825 if (!TEST_BIT(sd->cmap, i - 1))
828 ucimap_fill_ptr(&ptr, s, om->name);
829 if (ucimap_is_list(om->type)) {
830 struct ucimap_list *list = data->list;
834 for (j = 0; j < list->n_items; j++) {
835 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
840 ret = uci_set(s->package->ctx, &ptr);
843 ret = uci_add_list(s->package->ctx, &ptr);
849 ptr.value = ucimap_data_to_string(sd, om, data);
853 ret = uci_set(s->package->ctx, &ptr);
858 CLR_BIT(sd->cmap, i - 1);
865 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
867 struct uci_element *e;
868 struct ucimap_section_data *sd, **sd_tail;
869 struct ucimap_fixup *f;
872 sd_tail = map->sdata_tail;
874 map->sdata_tail = &map->pending;
875 uci_foreach_element(&pkg->sections, e) {
876 struct uci_section *s = uci_to_section(e);
878 for (i = 0; i < map->n_sections; i++) {
879 struct uci_sectionmap *sm = map->sections[i];
880 struct ucimap_section_data *sd;
882 if (strcmp(s->type, map->sections[i]->type) != 0)
886 sd = sm->alloc(map, sm, s);
887 memset(sd, 0, sizeof(struct ucimap_section_data));
889 sd = malloc(sm->alloc_len);
890 memset(sd, 0, sm->alloc_len);
891 sd = ucimap_ptr_section(sm, sd);
896 ucimap_parse_section(map, sm, sd, s);
901 map->sdata_tail = sd_tail;
906 struct ucimap_fixup *next = f->next;
907 ucimap_handle_fixup(map, f);
911 map->fixup_tail = &map->fixup;
916 struct ucimap_section_data *next = sd->next;
917 ucimap_add_section(sd);