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 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 struct ucimap_list *list;
211 union ucimap_data *data;
216 switch(f->type & UCIMAP_TYPE) {
221 list = f->data->list;
222 data = ucimap_list_append(f->data->list);
233 ucimap_free_item(struct ucimap_section_data *sd, void *item)
235 struct ucimap_alloc_custom *ac;
236 struct ucimap_alloc *a;
237 void *ptr = *((void **) item);
243 *((void **)item) = NULL;
244 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
248 if (i != sd->allocmap_len - 1)
249 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
255 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
259 if (i != sd->alloc_custom_len - 1)
260 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
261 sizeof(struct ucimap_alloc_custom));
263 ac->om->free(ac->section, ac->om, ac->ptr);
264 sd->alloc_custom_len--;
270 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
272 struct ucimap_list *new;
273 struct ucimap_alloc *a;
275 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
278 new = calloc(1, size);
280 ucimap_add_alloc(sd, new);
284 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
293 if (items > (*list)->size)
294 offset = (items - (*list)->size) * sizeof(union ucimap_data);
296 a->ptr = realloc(a->ptr, size);
298 memset((char *) a->ptr + offset, 0, size - offset);
308 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
310 struct ucimap_fixup *f, tmp;
311 struct uci_map *map = sd->map;
313 tmp.sm = om->data.sm;
317 if (ucimap_handle_fixup(map, &tmp))
320 f = malloc(sizeof(struct ucimap_fixup));
324 memcpy(f, &tmp, sizeof(tmp));
326 *map->fixup_tail = f;
327 map->fixup_tail = &f->next;
331 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
333 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
335 a->section = ucimap_section_ptr(sd);
341 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
343 union ucimap_data tdata = *data;
349 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
350 data = ucimap_list_append(data->list);
355 switch(om->type & UCIMAP_SUBTYPE) {
357 if ((om->data.s.maxlen > 0) &&
358 (strlen(str) > om->data.s.maxlen))
363 ucimap_add_alloc(sd, s);
366 if (!strcmp(str, "on"))
368 else if (!strcmp(str, "1"))
370 else if (!strcmp(str, "enabled"))
372 else if (!strcmp(str, "off"))
374 else if (!strcmp(str, "0"))
376 else if (!strcmp(str, "disabled"))
384 lval = strtol(str, &eptr, om->data.i.base);
385 if (lval < INT_MIN || lval > INT_MAX)
388 if (!eptr || *eptr == '\0')
389 tdata.i = (int) lval;
394 ucimap_add_fixup(sd, data, om, str);
400 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
402 if (ucimap_is_custom(om->type) && om->free) {
403 if (tdata.ptr != data->ptr)
404 ucimap_add_custom_alloc(sd, om, data->ptr);
407 if (ucimap_is_custom(om->type))
409 memcpy(data, &tdata, sizeof(union ucimap_data));
414 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
422 ucimap_add_alloc(sd, s);
432 while (*s && !isspace(*s))
440 ucimap_add_value(data, om, sd, p);
445 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
447 struct uci_element *e, *l;
448 struct uci_option *o;
449 union ucimap_data *data;
451 uci_foreach_element(&s->options, e) {
452 struct uci_optmap *om = NULL, *tmp;
454 ucimap_foreach_option(sm, tmp) {
455 if (strcmp(e->name, tmp->name) == 0) {
463 data = ucimap_get_data(sd, om);
464 o = uci_to_option(e);
465 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
466 ucimap_add_value(data, om, sd, o->v.string);
467 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
468 uci_foreach_element(&o->v.list, l) {
469 ucimap_add_value(data, om, sd, l->name);
471 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
472 ucimap_convert_list(data, om, sd, o->v.string);
480 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
482 sd->ref = map->sdata_tail;
484 map->sdata_tail = &sd->next;
488 ucimap_add_section(struct ucimap_section_data *sd)
490 struct uci_map *map = sd->map;
493 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
494 ucimap_free_section(map, sd);
496 ucimap_add_section_list(map, sd);
500 static const char *ucimap_type_names[] = {
501 [UCIMAP_STRING] = "string",
502 [UCIMAP_INT] = "integer",
503 [UCIMAP_BOOL] = "boolean",
504 [UCIMAP_SECTION] = "section",
505 [UCIMAP_LIST] = "list",
509 ucimap_get_type_name(int type)
514 if (ucimap_is_list(type))
515 return ucimap_type_names[UCIMAP_LIST];
517 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
519 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
528 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
532 if (unlikely(sm->type_name != om->type_name) &&
533 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
534 DPRINTF("Option '%s' of section type '%s' refereces unknown "
535 "section type '%s', should be '%s'.\n",
536 om->name, sm->type, om->type_name, sm->type_name);
540 if (om->detected_type < 0)
543 if (ucimap_is_custom(om->type))
546 if (ucimap_is_list(om->type) !=
547 ucimap_is_list(om->detected_type))
550 if (ucimap_is_list(om->type))
553 type = om->type & UCIMAP_SUBTYPE;
558 if (type != om->detected_type)
569 DPRINTF("Invalid type in option '%s' of section type '%s', "
570 "declared type is %s, detected type is %s\n",
572 ucimap_get_type_name(om->type),
573 ucimap_get_type_name(om->detected_type));
578 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
580 if (ucimap_is_alloc(om->type))
582 else if (ucimap_is_custom(om->type) && om->free)
587 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
589 struct uci_optmap *om;
593 int n_alloc_custom = 0;
599 ucimap_foreach_option(sm, om) {
600 if (!ucimap_check_optmap_type(sm, om))
603 if (ucimap_is_list(om->type)) {
604 union ucimap_data *data;
605 struct uci_element *e;
607 int n_elements_alloc = 0;
608 int n_elements_custom = 0;
611 data = ucimap_get_data(sd, om);
612 uci_foreach_element(&s->options, e) {
613 struct uci_option *o = uci_to_option(e);
614 struct uci_element *tmp;
616 if (strcmp(e->name, om->name) != 0)
619 if (o->type == UCI_TYPE_LIST) {
620 uci_foreach_element(&o->v.list, tmp) {
621 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
624 } else if ((o->type == UCI_TYPE_STRING) &&
625 ucimap_is_list_auto(om->type)) {
626 const char *data = o->v.string;
628 while (isspace(*data))
635 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
637 while (*data && !isspace(*data))
641 /* for the duplicated data string */
647 /* add one more for the ucimap_list */
648 n_alloc += n_elements_alloc + 1;
649 n_alloc_custom += n_elements_custom;
650 size = sizeof(struct ucimap_list) +
651 n_elements * sizeof(union ucimap_data);
653 data->list = malloc(size);
657 memset(data->list, 0, size);
658 data->list->size = n_elements;
660 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
664 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
668 if (n_alloc_custom > 0) {
669 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
670 if (!sd->alloc_custom)
674 section_name = strdup(s->e.name);
678 sd->section_name = section_name;
680 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
684 ucimap_add_alloc(sd, (void *)section_name);
685 ucimap_add_alloc(sd, (void *)sd->cmap);
686 ucimap_foreach_option(sm, om) {
687 if (!ucimap_is_list(om->type))
690 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
693 section = ucimap_section_ptr(sd);
694 err = sm->init(map, section, s);
699 ucimap_add_section(sd);
701 ucimap_add_section_list(map, sd);
704 err = ucimap_parse_options(map, sm, sd, s);
717 ucimap_free_section(map, sd);
722 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
724 struct uci_package *p = s->package;
726 memset(ptr, 0, sizeof(struct uci_ptr));
728 ptr->package = p->e.name;
731 ptr->section = s->e.name;
734 ptr->option = option;
735 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
739 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
741 void *section = ucimap_section_ptr(sd);
742 struct uci_sectionmap *sm = sd->sm;
743 struct uci_optmap *om;
744 int ofs = (char *)field - (char *)section;
747 ucimap_foreach_option(sm, om) {
748 if (om->offset == ofs) {
749 SET_BIT(sd->cmap, i);
757 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
762 switch(om->type & UCIMAP_SUBTYPE) {
767 sprintf(buf, "%d", data->i);
771 sprintf(buf, "%d", !!data->b);
776 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
787 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
797 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
799 struct uci_sectionmap *sm = sd->sm;
800 struct uci_section *s = NULL;
801 struct uci_optmap *om;
802 struct uci_element *e;
807 uci_foreach_element(&p->sections, e) {
808 if (!strcmp(e->name, sd->section_name)) {
809 s = uci_to_section(e);
814 return UCI_ERR_NOTFOUND;
816 ucimap_foreach_option(sm, om) {
817 union ucimap_data *data;
820 data = ucimap_get_data(sd, om);
821 if (!TEST_BIT(sd->cmap, i - 1))
824 ucimap_fill_ptr(&ptr, s, om->name);
825 if (ucimap_is_list(om->type)) {
826 struct ucimap_list *list = data->list;
830 for (j = 0; j < list->n_items; j++) {
831 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
836 ret = uci_set(s->package->ctx, &ptr);
839 ret = uci_add_list(s->package->ctx, &ptr);
845 ptr.value = ucimap_data_to_string(sd, om, data);
849 ret = uci_set(s->package->ctx, &ptr);
854 CLR_BIT(sd->cmap, i - 1);
861 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
863 struct uci_element *e;
864 struct ucimap_section_data *sd, **sd_tail;
865 struct ucimap_fixup *f;
868 sd_tail = map->sdata_tail;
870 map->sdata_tail = &map->pending;
871 uci_foreach_element(&pkg->sections, e) {
872 struct uci_section *s = uci_to_section(e);
874 for (i = 0; i < map->n_sections; i++) {
875 struct uci_sectionmap *sm = map->sections[i];
876 struct ucimap_section_data *sd;
878 if (strcmp(s->type, map->sections[i]->type) != 0)
882 sd = sm->alloc(map, sm, s);
883 memset(sd, 0, sizeof(struct ucimap_section_data));
885 sd = malloc(sm->alloc_len);
886 memset(sd, 0, sm->alloc_len);
887 sd = ucimap_ptr_section(sm, sd);
892 ucimap_parse_section(map, sm, sd, s);
897 map->sdata_tail = sd_tail;
902 struct ucimap_fixup *next = f->next;
903 ucimap_handle_fixup(map, f);
907 map->fixup_tail = &map->fixup;
912 struct ucimap_section_data *next = sd->next;
913 ucimap_add_section(sd);