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;
275 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
278 new = calloc(1, size);
282 ucimap_add_alloc(sd, new);
286 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
295 if (items > (*list)->size)
296 offset = (items - (*list)->size) * sizeof(union ucimap_data);
298 a->ptr = realloc(a->ptr, size);
303 memset((char *) a->ptr + offset, 0, size - offset);
313 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
315 struct ucimap_fixup *f, tmp;
316 struct uci_map *map = sd->map;
319 tmp.sm = om->data.sm;
323 if (ucimap_handle_fixup(map, &tmp))
326 f = malloc(sizeof(struct ucimap_fixup));
330 memcpy(f, &tmp, sizeof(tmp));
332 *map->fixup_tail = f;
333 map->fixup_tail = &f->next;
337 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
339 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
341 a->section = ucimap_section_ptr(sd);
347 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
349 union ucimap_data tdata = *data;
355 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
356 data = ucimap_list_append(data->list);
361 switch(om->type & UCIMAP_SUBTYPE) {
363 if ((om->data.s.maxlen > 0) &&
364 (strlen(str) > (unsigned) om->data.s.maxlen))
369 ucimap_add_alloc(sd, s);
372 if (!strcmp(str, "on"))
374 else if (!strcmp(str, "1"))
376 else if (!strcmp(str, "enabled"))
378 else if (!strcmp(str, "off"))
380 else if (!strcmp(str, "0"))
382 else if (!strcmp(str, "disabled"))
390 lval = strtol(str, &eptr, om->data.i.base);
391 if (lval < INT_MIN || lval > INT_MAX)
394 if (!eptr || *eptr == '\0')
395 tdata.i = (int) lval;
400 ucimap_add_fixup(sd, data, om, str);
406 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
408 if (ucimap_is_custom(om->type) && om->free) {
409 if (tdata.ptr != data->ptr)
410 ucimap_add_custom_alloc(sd, om, data->ptr);
413 if (ucimap_is_custom(om->type))
415 memcpy(data, &tdata, sizeof(union ucimap_data));
420 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
428 ucimap_add_alloc(sd, s);
438 while (*s && !isspace(*s))
446 ucimap_add_value(data, om, sd, p);
451 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
453 struct uci_element *e, *l;
454 struct uci_option *o;
455 union ucimap_data *data;
457 uci_foreach_element(&s->options, e) {
458 struct uci_optmap *om = NULL, *tmp;
460 ucimap_foreach_option(sm, tmp) {
461 if (strcmp(e->name, tmp->name) == 0) {
469 data = ucimap_get_data(sd, om);
470 o = uci_to_option(e);
471 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
472 ucimap_add_value(data, om, sd, o->v.string);
473 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
474 uci_foreach_element(&o->v.list, l) {
475 ucimap_add_value(data, om, sd, l->name);
477 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
478 ucimap_convert_list(data, om, sd, o->v.string);
486 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
488 sd->ref = map->sdata_tail;
490 map->sdata_tail = &sd->next;
494 ucimap_add_section(struct ucimap_section_data *sd)
497 struct uci_map *map = sd->map;
500 r = sd->sm->add(map, ucimap_section_ptr(sd));
502 ucimap_free_section(map, sd);
505 ucimap_add_section_list(map, sd);
511 static const char *ucimap_type_names[] = {
512 [UCIMAP_STRING] = "string",
513 [UCIMAP_INT] = "integer",
514 [UCIMAP_BOOL] = "boolean",
515 [UCIMAP_SECTION] = "section",
516 [UCIMAP_LIST] = "list",
520 ucimap_get_type_name(int type)
525 if (ucimap_is_list(type))
526 return ucimap_type_names[UCIMAP_LIST];
528 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
530 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
539 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
543 if (unlikely(sm->type_name != om->type_name) &&
544 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
545 DPRINTF("Option '%s' of section type '%s' refereces unknown "
546 "section type '%s', should be '%s'.\n",
547 om->name, sm->type, om->type_name, sm->type_name);
551 if (om->detected_type < 0)
554 if (ucimap_is_custom(om->type))
557 if (ucimap_is_list(om->type) !=
558 ucimap_is_list(om->detected_type))
561 if (ucimap_is_list(om->type))
564 type = om->type & UCIMAP_SUBTYPE;
569 if (type != om->detected_type)
580 DPRINTF("Invalid type in option '%s' of section type '%s', "
581 "declared type is %s, detected type is %s\n",
583 ucimap_get_type_name(om->type),
584 ucimap_get_type_name(om->detected_type));
589 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
591 if (ucimap_is_alloc(om->type))
593 else if (ucimap_is_custom(om->type) && om->free)
598 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
600 struct uci_optmap *om;
604 int n_alloc_custom = 0;
610 ucimap_foreach_option(sm, om) {
611 if (!ucimap_check_optmap_type(sm, om))
614 if (ucimap_is_list(om->type)) {
615 union ucimap_data *data;
616 struct uci_element *e;
618 int n_elements_alloc = 0;
619 int n_elements_custom = 0;
622 data = ucimap_get_data(sd, om);
623 uci_foreach_element(&s->options, e) {
624 struct uci_option *o = uci_to_option(e);
625 struct uci_element *tmp;
627 if (strcmp(e->name, om->name) != 0)
630 if (o->type == UCI_TYPE_LIST) {
631 uci_foreach_element(&o->v.list, tmp) {
632 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
635 } else if ((o->type == UCI_TYPE_STRING) &&
636 ucimap_is_list_auto(om->type)) {
637 const char *data = o->v.string;
639 while (isspace(*data))
646 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
648 while (*data && !isspace(*data))
652 /* for the duplicated data string */
658 /* add one more for the ucimap_list */
659 n_alloc += n_elements_alloc + 1;
660 n_alloc_custom += n_elements_custom;
661 size = sizeof(struct ucimap_list) +
662 n_elements * sizeof(union ucimap_data);
664 data->list = malloc(size);
668 memset(data->list, 0, size);
669 data->list->size = n_elements;
671 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
675 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
679 if (n_alloc_custom > 0) {
680 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
681 if (!sd->alloc_custom)
685 section_name = strdup(s->e.name);
689 sd->section_name = section_name;
691 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
695 ucimap_add_alloc(sd, (void *)section_name);
696 ucimap_add_alloc(sd, (void *)sd->cmap);
697 ucimap_foreach_option(sm, om) {
698 if (!ucimap_is_list(om->type))
701 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
704 section = ucimap_section_ptr(sd);
705 err = sm->init(map, section, s);
710 err = ucimap_add_section(sd);
714 ucimap_add_section_list(map, sd);
717 err = ucimap_parse_options(map, sm, sd, s);
724 free(sd->alloc_custom);
730 ucimap_free_section(map, sd);
735 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
737 struct uci_package *p = s->package;
739 memset(ptr, 0, sizeof(struct uci_ptr));
741 ptr->package = p->e.name;
744 ptr->section = s->e.name;
747 ptr->option = option;
748 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
752 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
754 void *section = ucimap_section_ptr(sd);
755 struct uci_sectionmap *sm = sd->sm;
756 struct uci_optmap *om;
757 unsigned int ofs = (char *)field - (char *)section;
760 ucimap_foreach_option(sm, om) {
761 if (om->offset == ofs) {
762 SET_BIT(sd->cmap, i);
770 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
775 switch(om->type & UCIMAP_SUBTYPE) {
780 sprintf(buf, "%d", data->i);
784 sprintf(buf, "%d", !!data->b);
789 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
800 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
810 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
812 struct uci_sectionmap *sm = sd->sm;
813 struct uci_section *s = NULL;
814 struct uci_optmap *om;
815 struct uci_element *e;
820 uci_foreach_element(&p->sections, e) {
821 if (!strcmp(e->name, sd->section_name)) {
822 s = uci_to_section(e);
827 return UCI_ERR_NOTFOUND;
829 ucimap_foreach_option(sm, om) {
830 union ucimap_data *data;
833 data = ucimap_get_data(sd, om);
834 if (!TEST_BIT(sd->cmap, i - 1))
837 ucimap_fill_ptr(&ptr, s, om->name);
838 if (ucimap_is_list(om->type)) {
839 struct ucimap_list *list = data->list;
843 for (j = 0; j < list->n_items; j++) {
844 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
849 ret = uci_set(s->package->ctx, &ptr);
852 ret = uci_add_list(s->package->ctx, &ptr);
858 ptr.value = ucimap_data_to_string(sd, om, data);
862 ret = uci_set(s->package->ctx, &ptr);
867 CLR_BIT(sd->cmap, i - 1);
874 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
876 struct uci_element *e;
877 struct ucimap_section_data *sd, **sd_tail;
878 struct ucimap_fixup *f;
881 sd_tail = map->sdata_tail;
883 map->sdata_tail = &map->pending;
884 uci_foreach_element(&pkg->sections, e) {
885 struct uci_section *s = uci_to_section(e);
887 for (i = 0; i < map->n_sections; i++) {
888 struct uci_sectionmap *sm = map->sections[i];
889 struct ucimap_section_data *sd;
891 if (strcmp(s->type, map->sections[i]->type) != 0)
895 sd = sm->alloc(map, sm, s);
896 memset(sd, 0, sizeof(struct ucimap_section_data));
898 sd = malloc(sm->alloc_len);
899 memset(sd, 0, sm->alloc_len);
900 sd = ucimap_ptr_section(sm, sd);
905 ucimap_parse_section(map, sm, sd, s);
910 map->sdata_tail = sd_tail;
915 struct ucimap_fixup *next = f->next;
916 ucimap_handle_fixup(map, f);
920 map->fixup_tail = &map->fixup;
925 struct ucimap_section_data *next = sd->next;
926 ucimap_add_section(sd);