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);
410 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
412 if (ucimap_is_custom(om->type) && om->free) {
413 if (tdata.ptr != data->ptr)
414 ucimap_add_custom_alloc(sd, om, data->ptr);
417 if (ucimap_is_custom(om->type))
419 memcpy(data, &tdata, sizeof(union ucimap_data));
424 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
432 ucimap_add_alloc(sd, s);
442 while (*s && !isspace(*s))
450 ucimap_add_value(data, om, sd, p);
455 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
457 struct uci_element *e, *l;
458 struct uci_option *o;
459 union ucimap_data *data;
461 uci_foreach_element(&s->options, e) {
462 struct uci_optmap *om = NULL, *tmp;
464 ucimap_foreach_option(sm, tmp) {
465 if (strcmp(e->name, tmp->name) == 0) {
473 data = ucimap_get_data(sd, om);
474 o = uci_to_option(e);
475 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
476 ucimap_add_value(data, om, sd, o->v.string);
477 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
478 uci_foreach_element(&o->v.list, l) {
479 ucimap_add_value(data, om, sd, l->name);
481 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
482 ucimap_convert_list(data, om, sd, o->v.string);
490 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
492 sd->ref = map->sdata_tail;
494 map->sdata_tail = &sd->next;
498 ucimap_add_section(struct ucimap_section_data *sd)
500 struct uci_map *map = sd->map;
503 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
504 ucimap_free_section(map, sd);
506 ucimap_add_section_list(map, sd);
510 static const char *ucimap_type_names[] = {
511 [UCIMAP_STRING] = "string",
512 [UCIMAP_INT] = "integer",
513 [UCIMAP_BOOL] = "boolean",
514 [UCIMAP_SECTION] = "section",
515 [UCIMAP_LIST] = "list",
519 ucimap_get_type_name(int type)
524 if (ucimap_is_list(type))
525 return ucimap_type_names[UCIMAP_LIST];
527 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
529 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
538 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
542 if (unlikely(sm->type_name != om->type_name) &&
543 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
544 DPRINTF("Option '%s' of section type '%s' refereces unknown "
545 "section type '%s', should be '%s'.\n",
546 om->name, sm->type, om->type_name, sm->type_name);
550 if (om->detected_type < 0)
553 if (ucimap_is_custom(om->type))
556 if (ucimap_is_list(om->type) !=
557 ucimap_is_list(om->detected_type))
560 if (ucimap_is_list(om->type))
563 type = om->type & UCIMAP_SUBTYPE;
568 if (type != om->detected_type)
579 DPRINTF("Invalid type in option '%s' of section type '%s', "
580 "declared type is %s, detected type is %s\n",
582 ucimap_get_type_name(om->type),
583 ucimap_get_type_name(om->detected_type));
588 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
590 if (ucimap_is_alloc(om->type))
592 else if (ucimap_is_custom(om->type) && om->free)
597 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
599 struct uci_optmap *om;
603 int n_alloc_custom = 0;
609 ucimap_foreach_option(sm, om) {
610 if (!ucimap_check_optmap_type(sm, om))
613 if (ucimap_is_list(om->type)) {
614 union ucimap_data *data;
615 struct uci_element *e;
617 int n_elements_alloc = 0;
618 int n_elements_custom = 0;
621 data = ucimap_get_data(sd, om);
622 uci_foreach_element(&s->options, e) {
623 struct uci_option *o = uci_to_option(e);
624 struct uci_element *tmp;
626 if (strcmp(e->name, om->name) != 0)
629 if (o->type == UCI_TYPE_LIST) {
630 uci_foreach_element(&o->v.list, tmp) {
631 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
634 } else if ((o->type == UCI_TYPE_STRING) &&
635 ucimap_is_list_auto(om->type)) {
636 const char *data = o->v.string;
638 while (isspace(*data))
645 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
647 while (*data && !isspace(*data))
651 /* for the duplicated data string */
657 /* add one more for the ucimap_list */
658 n_alloc += n_elements_alloc + 1;
659 n_alloc_custom += n_elements_custom;
660 size = sizeof(struct ucimap_list) +
661 n_elements * sizeof(union ucimap_data);
663 data->list = malloc(size);
667 memset(data->list, 0, size);
668 data->list->size = n_elements;
670 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
674 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
678 if (n_alloc_custom > 0) {
679 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
680 if (!sd->alloc_custom)
684 section_name = strdup(s->e.name);
688 sd->section_name = section_name;
690 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
694 ucimap_add_alloc(sd, (void *)section_name);
695 ucimap_add_alloc(sd, (void *)sd->cmap);
696 ucimap_foreach_option(sm, om) {
697 if (!ucimap_is_list(om->type))
700 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
703 section = ucimap_section_ptr(sd);
704 err = sm->init(map, section, s);
709 ucimap_add_section(sd);
711 ucimap_add_section_list(map, sd);
714 err = ucimap_parse_options(map, sm, sd, s);
727 ucimap_free_section(map, sd);
732 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
734 struct uci_package *p = s->package;
736 memset(ptr, 0, sizeof(struct uci_ptr));
738 ptr->package = p->e.name;
741 ptr->section = s->e.name;
744 ptr->option = option;
745 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
749 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
751 void *section = ucimap_section_ptr(sd);
752 struct uci_sectionmap *sm = sd->sm;
753 struct uci_optmap *om;
754 int ofs = (char *)field - (char *)section;
757 ucimap_foreach_option(sm, om) {
758 if (om->offset == ofs) {
759 SET_BIT(sd->cmap, i);
767 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
772 switch(om->type & UCIMAP_SUBTYPE) {
777 sprintf(buf, "%d", data->i);
781 sprintf(buf, "%d", !!data->b);
786 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
797 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
807 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
809 struct uci_sectionmap *sm = sd->sm;
810 struct uci_section *s = NULL;
811 struct uci_optmap *om;
812 struct uci_element *e;
817 uci_foreach_element(&p->sections, e) {
818 if (!strcmp(e->name, sd->section_name)) {
819 s = uci_to_section(e);
824 return UCI_ERR_NOTFOUND;
826 ucimap_foreach_option(sm, om) {
827 union ucimap_data *data;
830 data = ucimap_get_data(sd, om);
831 if (!TEST_BIT(sd->cmap, i - 1))
834 ucimap_fill_ptr(&ptr, s, om->name);
835 if (ucimap_is_list(om->type)) {
836 struct ucimap_list *list = data->list;
840 for (j = 0; j < list->n_items; j++) {
841 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
846 ret = uci_set(s->package->ctx, &ptr);
849 ret = uci_add_list(s->package->ctx, &ptr);
855 ptr.value = ucimap_data_to_string(sd, om, data);
859 ret = uci_set(s->package->ctx, &ptr);
864 CLR_BIT(sd->cmap, i - 1);
871 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
873 struct uci_element *e;
874 struct ucimap_section_data *sd, **sd_tail;
875 struct ucimap_fixup *f;
878 sd_tail = map->sdata_tail;
880 map->sdata_tail = &map->pending;
881 uci_foreach_element(&pkg->sections, e) {
882 struct uci_section *s = uci_to_section(e);
884 for (i = 0; i < map->n_sections; i++) {
885 struct uci_sectionmap *sm = map->sections[i];
886 struct ucimap_section_data *sd;
888 if (strcmp(s->type, map->sections[i]->type) != 0)
892 sd = sm->alloc(map, sm, s);
893 memset(sd, 0, sizeof(struct ucimap_section_data));
895 sd = malloc(sm->alloc_len);
896 memset(sd, 0, sm->alloc_len);
901 ucimap_parse_section(map, sm, sd, s);
906 map->sdata_tail = sd_tail;
911 struct ucimap_fixup *next = f->next;
912 ucimap_handle_fixup(map, f);
916 map->fixup_tail = &map->fixup;
921 struct ucimap_section_data *next = sd->next;
922 ucimap_add_section(sd);