2 * ucimap - library for mapping uci sections into data structures
3 * Copyright (C) 2008 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 General Public License version 2
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.
24 #include "uci_internal.h"
30 struct uci_alloc_custom {
32 struct uci_optmap *om;
37 struct list_head list;
38 struct uci_sectionmap *sm;
40 enum ucimap_type type;
41 union ucimap_data *data;
44 #define ucimap_foreach_option(_sm, _o) \
45 if (!(_sm)->options_size) \
46 (_sm)->options_size = sizeof(struct uci_optmap); \
47 for (_o = &(_sm)->options[0]; \
48 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
49 (_sm)->options_size * (_sm)->n_options); \
50 _o = (struct uci_optmap *) ((char *)(_o) + \
55 ucimap_is_alloc(enum ucimap_type type)
57 switch(type & UCIMAP_SUBTYPE) {
66 ucimap_is_fixup(enum ucimap_type type)
68 switch(type & UCIMAP_SUBTYPE) {
77 ucimap_is_simple(enum ucimap_type type)
79 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
83 ucimap_is_list(enum ucimap_type type)
85 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
89 ucimap_is_list_auto(enum ucimap_type type)
91 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
95 ucimap_is_custom(enum ucimap_type type)
97 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
101 ucimap_section_ptr(struct ucimap_section_data *sd)
103 return ((char *) sd - sd->sm->smap_offset);
106 static inline struct ucimap_section_data *
107 ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) {
108 ptr = (char *) ptr + sm->smap_offset;
112 static inline union ucimap_data *
113 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
117 data = (char *) ucimap_section_ptr(sd) + om->offset;
122 ucimap_init(struct uci_map *map)
124 INIT_LIST_HEAD(&map->pending);
125 INIT_LIST_HEAD(&map->sdata);
126 INIT_LIST_HEAD(&map->fixup);
131 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
133 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
138 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
143 section = ucimap_section_ptr(sd);
144 if (!list_empty(&sd->list))
148 sd->sm->free(map, section);
150 for (i = 0; i < sd->allocmap_len; i++) {
151 free(sd->allocmap[i].ptr);
154 if (sd->alloc_custom) {
155 for (i = 0; i < sd->alloc_custom_len; i++) {
156 struct uci_alloc_custom *a = &sd->alloc_custom[i];
157 a->om->free(a->section, a->om, a->ptr);
159 free(sd->alloc_custom);
167 ucimap_cleanup(struct uci_map *map)
169 struct list_head *ptr, *tmp;
171 list_for_each_safe(ptr, tmp, &map->sdata) {
172 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
173 ucimap_free_section(map, sd);
178 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
180 struct ucimap_section_data *sd;
183 list_for_each(p, &map->sdata) {
184 sd = list_entry(p, struct ucimap_section_data, list);
187 if (strcmp(f->name, sd->section_name) != 0)
189 return ucimap_section_ptr(sd);
191 list_for_each(p, &map->pending) {
192 sd = list_entry(p, struct ucimap_section_data, list);
195 if (strcmp(f->name, sd->section_name) != 0)
197 return ucimap_section_ptr(sd);
203 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
205 void *ptr = ucimap_find_section(map, f);
206 struct ucimap_list *list;
211 switch(f->type & UCIMAP_TYPE) {
216 list = f->data->list;
217 list->item[list->n_items++].ptr = ptr;
224 ucimap_free_item(struct ucimap_section_data *sd, void *item)
226 struct uci_alloc_custom *ac;
228 void *ptr = *((void **) item);
234 *((void **)item) = NULL;
235 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
239 if (i != sd->allocmap_len - 1)
240 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
246 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
250 if (i != sd->alloc_custom_len - 1)
251 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
252 sizeof(struct uci_alloc_custom));
254 ac->om->free(ac->section, ac->om, ac->ptr);
255 sd->alloc_custom_len--;
261 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
263 struct ucimap_list *new;
266 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
269 new = calloc(1, size);
271 ucimap_add_alloc(sd, new);
275 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
284 if (items > (*list)->size)
285 offset = (items - (*list)->size) * sizeof(union ucimap_data);
287 a->ptr = realloc(a->ptr, size);
289 memset((char *) a->ptr + offset, 0, size - offset);
299 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
301 struct uci_fixup *f, tmp;
302 struct uci_map *map = sd->map;
304 INIT_LIST_HEAD(&tmp.list);
305 tmp.sm = om->data.sm;
309 if (ucimap_handle_fixup(map, &tmp))
312 f = malloc(sizeof(struct uci_fixup));
316 memcpy(f, &tmp, sizeof(tmp));
317 list_add_tail(&f->list, &map->fixup);
321 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
323 struct uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
325 a->section = ucimap_section_ptr(sd);
331 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
333 union ucimap_data tdata = *data;
339 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
340 if (unlikely(data->list->size <= data->list->n_items)) {
341 /* should not happen */
342 DPRINTF("ERROR: overflow while filling a list\n");
346 data = &data->list->item[data->list->n_items++];
349 switch(om->type & UCIMAP_SUBTYPE) {
351 if ((om->data.s.maxlen > 0) &&
352 (strlen(str) > om->data.s.maxlen))
357 ucimap_add_alloc(sd, s);
360 if (!strcmp(str, "on"))
362 else if (!strcmp(str, "1"))
364 else if (!strcmp(str, "enabled"))
366 else if (!strcmp(str, "off"))
368 else if (!strcmp(str, "0"))
370 else if (!strcmp(str, "disabled"))
378 lval = strtol(str, &eptr, om->data.i.base);
379 if (lval < INT_MIN || lval > INT_MAX)
382 if (!eptr || *eptr == '\0')
383 tdata.i = (int) lval;
388 ucimap_add_fixup(sd, data, om, str);
391 tdata.s = (char *) data;
395 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
397 if (ucimap_is_custom(om->type) && om->free) {
398 if (tdata.ptr != data->ptr)
399 ucimap_add_custom_alloc(sd, om, data->ptr);
402 if (ucimap_is_custom(om->type))
404 memcpy(data, &tdata, sizeof(union ucimap_data));
409 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
417 ucimap_add_alloc(sd, s);
427 while (*s && !isspace(*s))
435 ucimap_add_value(data, om, sd, p);
440 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
442 struct uci_element *e, *l;
443 struct uci_option *o;
444 union ucimap_data *data;
446 uci_foreach_element(&s->options, e) {
447 struct uci_optmap *om = NULL, *tmp;
449 ucimap_foreach_option(sm, tmp) {
450 if (strcmp(e->name, tmp->name) == 0) {
458 data = ucimap_get_data(sd, om);
459 o = uci_to_option(e);
460 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
461 ucimap_add_value(data, om, sd, o->v.string);
462 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
463 uci_foreach_element(&o->v.list, l) {
464 ucimap_add_value(data, om, sd, l->name);
466 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
467 ucimap_convert_list(data, om, sd, o->v.string);
475 ucimap_add_section(struct ucimap_section_data *sd)
477 struct uci_map *map = sd->map;
479 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
480 ucimap_free_section(map, sd);
482 list_add_tail(&sd->list, &map->sdata);
485 static const char *ucimap_type_names[] = {
486 [UCIMAP_STRING] = "string",
487 [UCIMAP_INT] = "integer",
488 [UCIMAP_BOOL] = "boolean",
489 [UCIMAP_SECTION] = "section",
490 [UCIMAP_LIST] = "list",
493 static inline const char *
494 ucimap_get_type_name(int type)
499 if (ucimap_is_list(type))
500 return ucimap_type_names[UCIMAP_LIST];
502 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
504 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
512 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
516 if (unlikely(sm->type_name != om->type_name) &&
517 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
518 DPRINTF("Option '%s' of section type '%s' refereces unknown "
519 "section type '%s', should be '%s'.\n",
520 om->name, sm->type, om->type_name, sm->type_name);
524 if (om->detected_type < 0)
527 if (ucimap_is_custom(om->type))
530 if (ucimap_is_list(om->type) !=
531 ucimap_is_list(om->detected_type))
534 if (ucimap_is_list(om->type))
537 type = om->type & UCIMAP_SUBTYPE;
542 if (type != om->detected_type)
553 DPRINTF("Invalid type in option '%s' of section type '%s', "
554 "declared type is %s, detected type is %s\n",
556 ucimap_get_type_name(om->type),
557 ucimap_get_type_name(om->detected_type));
562 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
564 if (ucimap_is_alloc(om->type))
566 else if (ucimap_is_custom(om->type) && om->free)
571 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
573 struct uci_optmap *om;
577 int n_alloc_custom = 0;
580 INIT_LIST_HEAD(&sd->list);
584 ucimap_foreach_option(sm, om) {
585 if (!ucimap_check_optmap_type(sm, om))
588 if (ucimap_is_list(om->type)) {
589 union ucimap_data *data;
590 struct uci_element *e;
592 int n_elements_custom = 0;
595 data = ucimap_get_data(sd, om);
596 uci_foreach_element(&s->options, e) {
597 struct uci_option *o = uci_to_option(e);
598 struct uci_element *tmp;
600 if (strcmp(e->name, om->name) != 0)
603 if (o->type == UCI_TYPE_LIST) {
604 uci_foreach_element(&o->v.list, tmp) {
605 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
607 } else if ((o->type == UCI_TYPE_STRING) &&
608 ucimap_is_list_auto(om->type)) {
609 const char *data = o->v.string;
611 while (isspace(*data))
618 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
620 while (*data && !isspace(*data))
624 /* for the duplicated data string */
630 /* add one more for the ucimap_list */
631 n_alloc += n_elements + 1;
632 n_alloc_custom += n_elements_custom;
633 size = sizeof(struct ucimap_list) +
634 n_elements * sizeof(union ucimap_data);
636 data->list = malloc(size);
640 data->list->size = n_elements;
641 memset(data->list, 0, size);
643 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
647 sd->allocmap = calloc(n_alloc, sizeof(struct uci_alloc));
651 if (n_alloc_custom > 0) {
652 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom));
653 if (!sd->alloc_custom)
657 section_name = strdup(s->e.name);
661 sd->section_name = section_name;
663 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
667 ucimap_add_alloc(sd, (void *)section_name);
668 ucimap_add_alloc(sd, (void *)sd->cmap);
669 ucimap_foreach_option(sm, om) {
670 if (!ucimap_is_list(om->type))
673 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
676 section = ucimap_section_ptr(sd);
677 err = sm->init(map, section, s);
682 ucimap_add_section(sd);
684 list_add_tail(&sd->list, &map->pending);
687 err = ucimap_parse_options(map, sm, sd, s);
700 ucimap_free_section(map, sd);
705 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
707 struct uci_package *p = s->package;
709 memset(ptr, 0, sizeof(struct uci_ptr));
711 ptr->package = p->e.name;
714 ptr->section = s->e.name;
717 ptr->option = option;
718 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
722 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
724 void *section = ucimap_section_ptr(sd);
725 struct uci_sectionmap *sm = sd->sm;
726 struct uci_optmap *om;
727 int ofs = (char *)field - (char *)section;
730 ucimap_foreach_option(sm, om) {
731 if (om->offset == ofs) {
732 SET_BIT(sd->cmap, i);
740 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
745 switch(om->type & UCIMAP_SUBTYPE) {
750 sprintf(buf, "%d", data->i);
754 sprintf(buf, "%d", !!data->b);
759 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
770 union ucimap_data tdata;
772 if (ucimap_is_custom(om->type)) {
773 tdata.s = (char *)data;
777 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
787 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
789 struct uci_sectionmap *sm = sd->sm;
790 struct uci_section *s = NULL;
791 struct uci_optmap *om;
792 struct uci_element *e;
797 uci_foreach_element(&p->sections, e) {
798 if (!strcmp(e->name, sd->section_name)) {
799 s = uci_to_section(e);
804 return UCI_ERR_NOTFOUND;
806 ucimap_foreach_option(sm, om) {
807 union ucimap_data *data;
810 data = ucimap_get_data(sd, om);
811 if (!TEST_BIT(sd->cmap, i - 1))
814 ucimap_fill_ptr(&ptr, s, om->name);
815 if (ucimap_is_list(om->type)) {
816 struct ucimap_list *list = data->list;
820 for (j = 0; j < list->n_items; j++) {
821 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
826 ret = uci_set(s->package->ctx, &ptr);
829 ret = uci_add_list(s->package->ctx, &ptr);
835 ptr.value = ucimap_data_to_string(sd, om, data);
839 ret = uci_set(s->package->ctx, &ptr);
844 CLR_BIT(sd->cmap, i - 1);
851 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
853 struct uci_element *e;
854 struct list_head *p, *tmp;
857 INIT_LIST_HEAD(&map->fixup);
858 uci_foreach_element(&pkg->sections, e) {
859 struct uci_section *s = uci_to_section(e);
861 for (i = 0; i < map->n_sections; i++) {
862 struct uci_sectionmap *sm = map->sections[i];
863 struct ucimap_section_data *sd;
865 if (strcmp(s->type, map->sections[i]->type) != 0)
869 sd = sm->alloc(map, sm, s);
870 memset(sd, 0, sizeof(struct ucimap_section_data));
872 sd = malloc(sm->alloc_len);
873 memset(sd, 0, sm->alloc_len);
878 ucimap_parse_section(map, sm, sd, s);
883 list_for_each_safe(p, tmp, &map->fixup) {
884 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
885 ucimap_handle_fixup(map, f);
890 list_for_each_safe(p, tmp, &map->pending) {
891 struct ucimap_section_data *sd;
892 sd = list_entry(p, struct ucimap_section_data, list);
894 list_del_init(&sd->list);
895 ucimap_add_section(sd);