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;
167 for (sd = map->sdata; sd; sd = sd->next) {
168 ucimap_free_section(map, sd);
173 ucimap_find_section(struct uci_map *map, struct ucimap_fixup *f)
175 struct ucimap_section_data *sd;
177 for (sd = map->sdata; sd; sd = sd->next) {
180 if (strcmp(f->name, sd->section_name) != 0)
182 return ucimap_section_ptr(sd);
184 for (sd = map->pending; sd; sd = sd->next) {
187 if (strcmp(f->name, sd->section_name) != 0)
189 return ucimap_section_ptr(sd);
194 static union ucimap_data *
195 ucimap_list_append(struct ucimap_list *list)
197 if (unlikely(list->size <= list->n_items)) {
198 /* should not happen */
199 DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list->size);
202 return &list->item[list->n_items++];
207 ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f)
209 void *ptr = ucimap_find_section(map, f);
210 union ucimap_data *data;
215 switch(f->type & UCIMAP_TYPE) {
220 data = ucimap_list_append(f->data->list);
231 ucimap_free_item(struct ucimap_section_data *sd, void *item)
233 struct ucimap_alloc_custom *ac;
234 struct ucimap_alloc *a;
235 void *ptr = *((void **) item);
241 *((void **)item) = NULL;
242 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
246 if (i != sd->allocmap_len - 1)
247 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
253 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
257 if (i != sd->alloc_custom_len - 1)
258 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
259 sizeof(struct ucimap_alloc_custom));
261 ac->om->free(ac->section, ac->om, ac->ptr);
262 sd->alloc_custom_len--;
268 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
270 struct ucimap_list *new;
271 struct ucimap_alloc *a;
273 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
276 new = calloc(1, size);
278 ucimap_add_alloc(sd, new);
282 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
291 if (items > (*list)->size)
292 offset = (items - (*list)->size) * sizeof(union ucimap_data);
294 a->ptr = realloc(a->ptr, size);
296 memset((char *) a->ptr + offset, 0, size - offset);
306 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
308 struct ucimap_fixup *f, tmp;
309 struct uci_map *map = sd->map;
311 tmp.sm = om->data.sm;
315 if (ucimap_handle_fixup(map, &tmp))
318 f = malloc(sizeof(struct ucimap_fixup));
322 memcpy(f, &tmp, sizeof(tmp));
324 *map->fixup_tail = f;
325 map->fixup_tail = &f->next;
329 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
331 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
333 a->section = ucimap_section_ptr(sd);
339 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
341 union ucimap_data tdata = *data;
347 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
348 data = ucimap_list_append(data->list);
353 switch(om->type & UCIMAP_SUBTYPE) {
355 if ((om->data.s.maxlen > 0) &&
356 (strlen(str) > om->data.s.maxlen))
361 ucimap_add_alloc(sd, s);
364 if (!strcmp(str, "on"))
366 else if (!strcmp(str, "1"))
368 else if (!strcmp(str, "enabled"))
370 else if (!strcmp(str, "off"))
372 else if (!strcmp(str, "0"))
374 else if (!strcmp(str, "disabled"))
382 lval = strtol(str, &eptr, om->data.i.base);
383 if (lval < INT_MIN || lval > INT_MAX)
386 if (!eptr || *eptr == '\0')
387 tdata.i = (int) lval;
392 ucimap_add_fixup(sd, data, om, str);
398 if (om->parse(ucimap_section_ptr(sd), om, data, 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_alloc = 0;
606 int n_elements_custom = 0;
609 data = ucimap_get_data(sd, om);
610 uci_foreach_element(&s->options, e) {
611 struct uci_option *o = uci_to_option(e);
612 struct uci_element *tmp;
614 if (strcmp(e->name, om->name) != 0)
617 if (o->type == UCI_TYPE_LIST) {
618 uci_foreach_element(&o->v.list, tmp) {
619 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
622 } else if ((o->type == UCI_TYPE_STRING) &&
623 ucimap_is_list_auto(om->type)) {
624 const char *data = o->v.string;
626 while (isspace(*data))
633 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
635 while (*data && !isspace(*data))
639 /* for the duplicated data string */
645 /* add one more for the ucimap_list */
646 n_alloc += n_elements_alloc + 1;
647 n_alloc_custom += n_elements_custom;
648 size = sizeof(struct ucimap_list) +
649 n_elements * sizeof(union ucimap_data);
651 data->list = malloc(size);
655 memset(data->list, 0, size);
656 data->list->size = n_elements;
658 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
662 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
666 if (n_alloc_custom > 0) {
667 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
668 if (!sd->alloc_custom)
672 section_name = strdup(s->e.name);
676 sd->section_name = section_name;
678 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
682 ucimap_add_alloc(sd, (void *)section_name);
683 ucimap_add_alloc(sd, (void *)sd->cmap);
684 ucimap_foreach_option(sm, om) {
685 if (!ucimap_is_list(om->type))
688 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
691 section = ucimap_section_ptr(sd);
692 err = sm->init(map, section, s);
697 ucimap_add_section(sd);
699 ucimap_add_section_list(map, sd);
702 err = ucimap_parse_options(map, sm, sd, s);
715 ucimap_free_section(map, sd);
720 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
722 struct uci_package *p = s->package;
724 memset(ptr, 0, sizeof(struct uci_ptr));
726 ptr->package = p->e.name;
729 ptr->section = s->e.name;
732 ptr->option = option;
733 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
737 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
739 void *section = ucimap_section_ptr(sd);
740 struct uci_sectionmap *sm = sd->sm;
741 struct uci_optmap *om;
742 int ofs = (char *)field - (char *)section;
745 ucimap_foreach_option(sm, om) {
746 if (om->offset == ofs) {
747 SET_BIT(sd->cmap, i);
755 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
760 switch(om->type & UCIMAP_SUBTYPE) {
765 sprintf(buf, "%d", data->i);
769 sprintf(buf, "%d", !!data->b);
774 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
785 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
795 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
797 struct uci_sectionmap *sm = sd->sm;
798 struct uci_section *s = NULL;
799 struct uci_optmap *om;
800 struct uci_element *e;
805 uci_foreach_element(&p->sections, e) {
806 if (!strcmp(e->name, sd->section_name)) {
807 s = uci_to_section(e);
812 return UCI_ERR_NOTFOUND;
814 ucimap_foreach_option(sm, om) {
815 union ucimap_data *data;
818 data = ucimap_get_data(sd, om);
819 if (!TEST_BIT(sd->cmap, i - 1))
822 ucimap_fill_ptr(&ptr, s, om->name);
823 if (ucimap_is_list(om->type)) {
824 struct ucimap_list *list = data->list;
828 for (j = 0; j < list->n_items; j++) {
829 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
834 ret = uci_set(s->package->ctx, &ptr);
837 ret = uci_add_list(s->package->ctx, &ptr);
843 ptr.value = ucimap_data_to_string(sd, om, data);
847 ret = uci_set(s->package->ctx, &ptr);
852 CLR_BIT(sd->cmap, i - 1);
859 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
861 struct uci_element *e;
862 struct ucimap_section_data *sd, **sd_tail;
863 struct ucimap_fixup *f;
866 sd_tail = map->sdata_tail;
868 map->sdata_tail = &map->pending;
869 uci_foreach_element(&pkg->sections, e) {
870 struct uci_section *s = uci_to_section(e);
872 for (i = 0; i < map->n_sections; i++) {
873 struct uci_sectionmap *sm = map->sections[i];
874 struct ucimap_section_data *sd;
876 if (strcmp(s->type, map->sections[i]->type) != 0)
880 sd = sm->alloc(map, sm, s);
881 memset(sd, 0, sizeof(struct ucimap_section_data));
883 sd = malloc(sm->alloc_len);
884 memset(sd, 0, sm->alloc_len);
885 sd = ucimap_ptr_section(sm, sd);
890 ucimap_parse_section(map, sm, sd, s);
895 map->sdata_tail = sd_tail;
900 struct ucimap_fixup *next = f->next;
901 ucimap_handle_fixup(map, f);
905 map->fixup_tail = &map->fixup;
910 struct ucimap_section_data *next = sd->next;
911 ucimap_add_section(sd);