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;
318 tmp.sm = om->data.sm;
322 if (ucimap_handle_fixup(map, &tmp))
325 f = malloc(sizeof(struct ucimap_fixup));
329 memcpy(f, &tmp, sizeof(tmp));
331 *map->fixup_tail = f;
332 map->fixup_tail = &f->next;
336 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
338 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
340 a->section = ucimap_section_ptr(sd);
346 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
348 union ucimap_data tdata = *data;
354 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
355 data = ucimap_list_append(data->list);
360 switch(om->type & UCIMAP_SUBTYPE) {
362 if ((om->data.s.maxlen > 0) &&
363 (strlen(str) > om->data.s.maxlen))
368 ucimap_add_alloc(sd, s);
371 if (!strcmp(str, "on"))
373 else if (!strcmp(str, "1"))
375 else if (!strcmp(str, "enabled"))
377 else if (!strcmp(str, "off"))
379 else if (!strcmp(str, "0"))
381 else if (!strcmp(str, "disabled"))
389 lval = strtol(str, &eptr, om->data.i.base);
390 if (lval < INT_MIN || lval > INT_MAX)
393 if (!eptr || *eptr == '\0')
394 tdata.i = (int) lval;
399 ucimap_add_fixup(sd, data, om, str);
405 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
407 if (ucimap_is_custom(om->type) && om->free) {
408 if (tdata.ptr != data->ptr)
409 ucimap_add_custom_alloc(sd, om, data->ptr);
412 if (ucimap_is_custom(om->type))
414 memcpy(data, &tdata, sizeof(union ucimap_data));
419 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
427 ucimap_add_alloc(sd, s);
437 while (*s && !isspace(*s))
445 ucimap_add_value(data, om, sd, p);
450 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
452 struct uci_element *e, *l;
453 struct uci_option *o;
454 union ucimap_data *data;
456 uci_foreach_element(&s->options, e) {
457 struct uci_optmap *om = NULL, *tmp;
459 ucimap_foreach_option(sm, tmp) {
460 if (strcmp(e->name, tmp->name) == 0) {
468 data = ucimap_get_data(sd, om);
469 o = uci_to_option(e);
470 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
471 ucimap_add_value(data, om, sd, o->v.string);
472 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
473 uci_foreach_element(&o->v.list, l) {
474 ucimap_add_value(data, om, sd, l->name);
476 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
477 ucimap_convert_list(data, om, sd, o->v.string);
485 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
487 sd->ref = map->sdata_tail;
489 map->sdata_tail = &sd->next;
493 ucimap_add_section(struct ucimap_section_data *sd)
495 struct uci_map *map = sd->map;
498 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
499 ucimap_free_section(map, sd);
501 ucimap_add_section_list(map, sd);
505 static const char *ucimap_type_names[] = {
506 [UCIMAP_STRING] = "string",
507 [UCIMAP_INT] = "integer",
508 [UCIMAP_BOOL] = "boolean",
509 [UCIMAP_SECTION] = "section",
510 [UCIMAP_LIST] = "list",
514 ucimap_get_type_name(int type)
519 if (ucimap_is_list(type))
520 return ucimap_type_names[UCIMAP_LIST];
522 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
524 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
533 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
537 if (unlikely(sm->type_name != om->type_name) &&
538 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
539 DPRINTF("Option '%s' of section type '%s' refereces unknown "
540 "section type '%s', should be '%s'.\n",
541 om->name, sm->type, om->type_name, sm->type_name);
545 if (om->detected_type < 0)
548 if (ucimap_is_custom(om->type))
551 if (ucimap_is_list(om->type) !=
552 ucimap_is_list(om->detected_type))
555 if (ucimap_is_list(om->type))
558 type = om->type & UCIMAP_SUBTYPE;
563 if (type != om->detected_type)
574 DPRINTF("Invalid type in option '%s' of section type '%s', "
575 "declared type is %s, detected type is %s\n",
577 ucimap_get_type_name(om->type),
578 ucimap_get_type_name(om->detected_type));
583 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
585 if (ucimap_is_alloc(om->type))
587 else if (ucimap_is_custom(om->type) && om->free)
592 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
594 struct uci_optmap *om;
598 int n_alloc_custom = 0;
604 ucimap_foreach_option(sm, om) {
605 if (!ucimap_check_optmap_type(sm, om))
608 if (ucimap_is_list(om->type)) {
609 union ucimap_data *data;
610 struct uci_element *e;
612 int n_elements_alloc = 0;
613 int n_elements_custom = 0;
616 data = ucimap_get_data(sd, om);
617 uci_foreach_element(&s->options, e) {
618 struct uci_option *o = uci_to_option(e);
619 struct uci_element *tmp;
621 if (strcmp(e->name, om->name) != 0)
624 if (o->type == UCI_TYPE_LIST) {
625 uci_foreach_element(&o->v.list, tmp) {
626 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
629 } else if ((o->type == UCI_TYPE_STRING) &&
630 ucimap_is_list_auto(om->type)) {
631 const char *data = o->v.string;
633 while (isspace(*data))
640 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
642 while (*data && !isspace(*data))
646 /* for the duplicated data string */
652 /* add one more for the ucimap_list */
653 n_alloc += n_elements_alloc + 1;
654 n_alloc_custom += n_elements_custom;
655 size = sizeof(struct ucimap_list) +
656 n_elements * sizeof(union ucimap_data);
658 data->list = malloc(size);
662 memset(data->list, 0, size);
663 data->list->size = n_elements;
665 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
669 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
673 if (n_alloc_custom > 0) {
674 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
675 if (!sd->alloc_custom)
679 section_name = strdup(s->e.name);
683 sd->section_name = section_name;
685 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
689 ucimap_add_alloc(sd, (void *)section_name);
690 ucimap_add_alloc(sd, (void *)sd->cmap);
691 ucimap_foreach_option(sm, om) {
692 if (!ucimap_is_list(om->type))
695 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
698 section = ucimap_section_ptr(sd);
699 err = sm->init(map, section, s);
704 ucimap_add_section(sd);
706 ucimap_add_section_list(map, sd);
709 err = ucimap_parse_options(map, sm, sd, s);
716 if (sd->alloc_custom)
717 free(sd->alloc_custom);
724 ucimap_free_section(map, sd);
729 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
731 struct uci_package *p = s->package;
733 memset(ptr, 0, sizeof(struct uci_ptr));
735 ptr->package = p->e.name;
738 ptr->section = s->e.name;
741 ptr->option = option;
742 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
746 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
748 void *section = ucimap_section_ptr(sd);
749 struct uci_sectionmap *sm = sd->sm;
750 struct uci_optmap *om;
751 int ofs = (char *)field - (char *)section;
754 ucimap_foreach_option(sm, om) {
755 if (om->offset == ofs) {
756 SET_BIT(sd->cmap, i);
764 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
769 switch(om->type & UCIMAP_SUBTYPE) {
774 sprintf(buf, "%d", data->i);
778 sprintf(buf, "%d", !!data->b);
783 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
794 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
804 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
806 struct uci_sectionmap *sm = sd->sm;
807 struct uci_section *s = NULL;
808 struct uci_optmap *om;
809 struct uci_element *e;
814 uci_foreach_element(&p->sections, e) {
815 if (!strcmp(e->name, sd->section_name)) {
816 s = uci_to_section(e);
821 return UCI_ERR_NOTFOUND;
823 ucimap_foreach_option(sm, om) {
824 union ucimap_data *data;
827 data = ucimap_get_data(sd, om);
828 if (!TEST_BIT(sd->cmap, i - 1))
831 ucimap_fill_ptr(&ptr, s, om->name);
832 if (ucimap_is_list(om->type)) {
833 struct ucimap_list *list = data->list;
837 for (j = 0; j < list->n_items; j++) {
838 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
843 ret = uci_set(s->package->ctx, &ptr);
846 ret = uci_add_list(s->package->ctx, &ptr);
852 ptr.value = ucimap_data_to_string(sd, om, data);
856 ret = uci_set(s->package->ctx, &ptr);
861 CLR_BIT(sd->cmap, i - 1);
868 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
870 struct uci_element *e;
871 struct ucimap_section_data *sd, **sd_tail;
872 struct ucimap_fixup *f;
875 sd_tail = map->sdata_tail;
877 map->sdata_tail = &map->pending;
878 uci_foreach_element(&pkg->sections, e) {
879 struct uci_section *s = uci_to_section(e);
881 for (i = 0; i < map->n_sections; i++) {
882 struct uci_sectionmap *sm = map->sections[i];
883 struct ucimap_section_data *sd;
885 if (strcmp(s->type, map->sections[i]->type) != 0)
889 sd = sm->alloc(map, sm, s);
890 memset(sd, 0, sizeof(struct ucimap_section_data));
892 sd = malloc(sm->alloc_len);
893 memset(sd, 0, sm->alloc_len);
894 sd = ucimap_ptr_section(sm, sd);
899 ucimap_parse_section(map, sm, sd, s);
904 map->sdata_tail = sd_tail;
909 struct ucimap_fixup *next = f->next;
910 ucimap_handle_fixup(map, f);
914 map->fixup_tail = &map->fixup;
919 struct ucimap_section_data *next = sd->next;
920 ucimap_add_section(sd);