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.
23 #include "uci_internal.h"
26 enum ucimap_type type;
32 struct uci_alloc_custom {
34 struct uci_optmap *om;
39 struct list_head list;
40 struct uci_sectionmap *sm;
42 enum ucimap_type type;
43 union ucimap_data *data;
46 #define ucimap_foreach_option(_sm, _o) \
47 if (!(_sm)->options_size) \
48 (_sm)->options_size = sizeof(struct uci_optmap); \
49 for (_o = &(_sm)->options[0]; \
50 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
51 (_sm)->options_size * (_sm)->n_options); \
52 _o = (struct uci_optmap *) ((char *)(_o) + \
57 ucimap_is_alloc(enum ucimap_type type)
59 switch(type & UCIMAP_SUBTYPE) {
68 ucimap_is_fixup(enum ucimap_type type)
70 switch(type & UCIMAP_SUBTYPE) {
79 ucimap_is_simple(enum ucimap_type type)
81 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
85 ucimap_is_list(enum ucimap_type type)
87 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
91 ucimap_is_list_auto(enum ucimap_type type)
93 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
97 ucimap_is_custom(enum ucimap_type type)
99 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
103 ucimap_section_ptr(struct ucimap_section_data *sd)
105 return ((char *) sd - sd->sm->smap_offset);
108 static inline union ucimap_data *
109 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
113 data = (char *) ucimap_section_ptr(sd) + om->offset;
118 ucimap_init(struct uci_map *map)
120 INIT_LIST_HEAD(&map->pending);
121 INIT_LIST_HEAD(&map->sdata);
122 INIT_LIST_HEAD(&map->fixup);
127 ucimap_free_item(struct uci_alloc *a)
129 switch(a->type & UCIMAP_TYPE) {
138 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
140 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
141 a->type = UCIMAP_SIMPLE;
146 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
151 section = ucimap_section_ptr(sd);
152 if (!list_empty(&sd->list))
156 sd->sm->free(map, section);
158 for (i = 0; i < sd->allocmap_len; i++) {
159 ucimap_free_item(&sd->allocmap[i]);
162 if (sd->alloc_custom) {
163 for (i = 0; i < sd->alloc_custom_len; i++) {
164 struct uci_alloc_custom *a = &sd->alloc_custom[i];
165 a->om->free(a->section, a->om, a->ptr);
167 free(sd->alloc_custom);
175 ucimap_cleanup(struct uci_map *map)
177 struct list_head *ptr, *tmp;
179 list_for_each_safe(ptr, tmp, &map->sdata) {
180 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
181 ucimap_free_section(map, sd);
186 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
188 struct ucimap_section_data *sd;
191 list_for_each(p, &map->sdata) {
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);
199 list_for_each(p, &map->pending) {
200 sd = list_entry(p, struct ucimap_section_data, list);
203 if (strcmp(f->name, sd->section_name) != 0)
205 return ucimap_section_ptr(sd);
211 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
213 void *ptr = ucimap_find_section(map, f);
214 struct ucimap_list *list;
219 switch(f->type & UCIMAP_TYPE) {
224 list = f->data->list;
225 list->item[list->n_items++].ptr = ptr;
232 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
234 struct uci_fixup *f, tmp;
235 struct uci_map *map = sd->map;
237 INIT_LIST_HEAD(&tmp.list);
238 tmp.sm = om->data.sm;
242 if (ucimap_handle_fixup(map, &tmp))
245 f = malloc(sizeof(struct uci_fixup));
249 memcpy(f, &tmp, sizeof(tmp));
250 list_add_tail(&f->list, &map->fixup);
254 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
256 struct uci_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
258 a->section = ucimap_section_ptr(sd);
264 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
266 union ucimap_data tdata = *data;
272 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
273 data = &data->list->item[data->list->n_items++];
275 switch(om->type & UCIMAP_SUBTYPE) {
277 if ((om->data.s.maxlen > 0) &&
278 (strlen(str) > om->data.s.maxlen))
283 ucimap_add_alloc(sd, s);
286 if (!strcmp(str, "on"))
288 else if (!strcmp(str, "1"))
290 else if (!strcmp(str, "enabled"))
292 else if (!strcmp(str, "off"))
294 else if (!strcmp(str, "0"))
296 else if (!strcmp(str, "disabled"))
304 lval = strtol(str, &eptr, om->data.i.base);
305 if (lval < INT_MIN || lval > INT_MAX)
308 if (!eptr || *eptr == '\0')
309 tdata.i = (int) lval;
314 ucimap_add_fixup(sd, data, om, str);
317 tdata.s = (char *) data;
321 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
323 if (ucimap_is_custom(om->type) && om->free) {
324 if (tdata.ptr != data->ptr)
325 ucimap_add_custom_alloc(sd, om, data->ptr);
328 if (ucimap_is_custom(om->type))
330 memcpy(data, &tdata, sizeof(union ucimap_data));
335 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
343 ucimap_add_alloc(sd, s);
353 while (*s && !isspace(*s))
361 ucimap_add_value(data, om, sd, p);
366 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
368 struct uci_element *e, *l;
369 struct uci_option *o;
370 union ucimap_data *data;
372 uci_foreach_element(&s->options, e) {
373 struct uci_optmap *om = NULL, *tmp;
375 ucimap_foreach_option(sm, tmp) {
376 if (strcmp(e->name, tmp->name) == 0) {
384 data = ucimap_get_data(sd, om);
385 o = uci_to_option(e);
386 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
387 ucimap_add_value(data, om, sd, o->v.string);
388 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
389 uci_foreach_element(&o->v.list, l) {
390 ucimap_add_value(data, om, sd, l->name);
392 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
393 ucimap_convert_list(data, om, sd, o->v.string);
401 ucimap_add_section(struct ucimap_section_data *sd)
403 struct uci_map *map = sd->map;
405 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
406 ucimap_free_section(map, sd);
408 list_add_tail(&sd->list, &map->sdata);
411 static const char *ucimap_type_names[] = {
412 [UCIMAP_STRING] = "string",
413 [UCIMAP_INT] = "integer",
414 [UCIMAP_BOOL] = "boolean",
415 [UCIMAP_SECTION] = "section",
416 [UCIMAP_LIST] = "list",
419 static inline const char *
420 ucimap_get_type_name(int type)
425 if (ucimap_is_list(type))
426 return ucimap_type_names[UCIMAP_LIST];
428 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
430 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
438 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
442 if (unlikely(sm->type_name != om->type_name) &&
443 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
444 DPRINTF("Option '%s' of section type '%s' refereces unknown "
445 "section type '%s', should be '%s'.\n",
446 om->name, sm->type, om->type_name, sm->type_name);
450 if (om->detected_type < 0)
453 if (ucimap_is_custom(om->type))
456 if (ucimap_is_list(om->type) !=
457 ucimap_is_list(om->detected_type))
460 if (ucimap_is_list(om->type))
463 type = om->type & UCIMAP_SUBTYPE;
468 if (type != om->detected_type)
479 DPRINTF("Invalid type in option '%s' of section type '%s', "
480 "declared type is %s, detected type is %s\n",
482 ucimap_get_type_name(om->type),
483 ucimap_get_type_name(om->detected_type));
488 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
490 if (ucimap_is_alloc(om->type))
492 else if (ucimap_is_custom(om->type) && om->free)
497 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
499 struct uci_optmap *om;
503 int n_alloc_custom = 0;
506 INIT_LIST_HEAD(&sd->list);
510 ucimap_foreach_option(sm, om) {
511 if (!ucimap_check_optmap_type(sm, om))
514 if (ucimap_is_list(om->type)) {
515 union ucimap_data *data;
516 struct uci_element *e;
518 int n_elements_custom = 0;
521 data = ucimap_get_data(sd, om);
522 uci_foreach_element(&s->options, e) {
523 struct uci_option *o = uci_to_option(e);
524 struct uci_element *tmp;
526 if (strcmp(e->name, om->name) != 0)
529 if (o->type == UCI_TYPE_LIST) {
530 uci_foreach_element(&o->v.list, tmp) {
531 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
533 } else if ((o->type == UCI_TYPE_STRING) &&
534 ucimap_is_list_auto(om->type)) {
535 const char *data = o->v.string;
537 while (isspace(*data))
544 ucimap_count_alloc(om, &n_elements, &n_elements_custom);
546 while (*data && !isspace(*data))
550 /* for the duplicated data string */
556 /* add one more for the ucimap_list */
557 n_alloc += n_elements + 1;
558 n_alloc_custom += n_elements_custom;
559 size = sizeof(struct ucimap_list) +
560 n_elements * sizeof(union ucimap_data);
561 data->list = malloc(size);
562 memset(data->list, 0, size);
564 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
568 sd->allocmap = calloc(n_alloc, sizeof(struct uci_alloc));
572 if (n_alloc_custom > 0) {
573 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct uci_alloc_custom));
574 if (!sd->alloc_custom)
578 section_name = strdup(s->e.name);
582 sd->section_name = section_name;
584 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
588 ucimap_add_alloc(sd, (void *)section_name);
589 ucimap_add_alloc(sd, (void *)sd->cmap);
590 ucimap_foreach_option(sm, om) {
591 if (!ucimap_is_list(om->type))
594 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
597 section = ucimap_section_ptr(sd);
598 err = sm->init(map, section, s);
603 ucimap_add_section(sd);
605 list_add_tail(&sd->list, &map->pending);
608 err = ucimap_parse_options(map, sm, sd, s);
621 ucimap_free_section(map, sd);
626 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
628 struct uci_package *p = s->package;
630 memset(ptr, 0, sizeof(struct uci_ptr));
632 ptr->package = p->e.name;
635 ptr->section = s->e.name;
638 ptr->option = option;
639 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
643 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
645 void *section = ucimap_section_ptr(sd);
646 struct uci_sectionmap *sm = sd->sm;
647 struct uci_optmap *om;
648 int ofs = (char *)field - (char *)section;
651 ucimap_foreach_option(sm, om) {
652 if (om->offset == ofs) {
653 SET_BIT(sd->cmap, i);
661 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
663 struct uci_sectionmap *sm = sd->sm;
664 struct uci_section *s = NULL;
665 struct uci_optmap *om;
666 struct uci_element *e;
671 uci_foreach_element(&p->sections, e) {
672 if (!strcmp(e->name, sd->section_name)) {
673 s = uci_to_section(e);
678 return UCI_ERR_NOTFOUND;
680 ucimap_foreach_option(sm, om) {
681 union ucimap_data *data;
686 if (ucimap_is_list(om->type))
689 data = ucimap_get_data(sd, om);
690 if (!TEST_BIT(sd->cmap, i - 1))
693 ucimap_fill_ptr(&ptr, s, om->name);
694 switch(om->type & UCIMAP_SUBTYPE) {
699 sprintf(buf, "%d", data->i);
703 sprintf(buf, "%d", !!data->b);
712 union ucimap_data tdata, *data;
714 data = ucimap_get_data(sd, om);
715 if (ucimap_is_custom(om->type)) {
716 tdata.s = (char *)data;
720 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
727 ret = uci_set(s->package->ctx, &ptr);
731 CLR_BIT(sd->cmap, i - 1);
738 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
740 struct uci_element *e;
741 struct list_head *p, *tmp;
744 INIT_LIST_HEAD(&map->fixup);
745 uci_foreach_element(&pkg->sections, e) {
746 struct uci_section *s = uci_to_section(e);
748 for (i = 0; i < map->n_sections; i++) {
749 struct uci_sectionmap *sm = map->sections[i];
750 struct ucimap_section_data *sd;
752 if (strcmp(s->type, map->sections[i]->type) != 0)
756 sd = sm->alloc(map, sm, s);
757 memset(sd, 0, sizeof(struct ucimap_section_data));
759 sd = malloc(sm->alloc_len);
760 memset(sd, 0, sm->alloc_len);
765 ucimap_parse_section(map, sm, sd, s);
770 list_for_each_safe(p, tmp, &map->fixup) {
771 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
772 ucimap_handle_fixup(map, f);
777 list_for_each_safe(p, tmp, &map->pending) {
778 struct ucimap_section_data *sd;
779 sd = list_entry(p, struct ucimap_section_data, list);
781 list_del_init(&sd->list);
782 ucimap_add_section(sd);