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;
33 struct list_head list;
34 struct uci_sectionmap *sm;
36 enum ucimap_type type;
37 union ucimap_data *data;
40 #define ucimap_foreach_option(_sm, _o) \
41 if (!(_sm)->options_size) \
42 (_sm)->options_size = sizeof(struct uci_optmap); \
43 for (_o = &(_sm)->options[0]; \
44 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
45 (_sm)->options_size * (_sm)->n_options); \
46 _o = (struct uci_optmap *) ((char *)(_o) + \
51 ucimap_is_alloc(enum ucimap_type type)
53 switch(type & UCIMAP_SUBTYPE) {
62 ucimap_is_fixup(enum ucimap_type type)
64 switch(type & UCIMAP_SUBTYPE) {
73 ucimap_is_simple(enum ucimap_type type)
75 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
79 ucimap_is_list(enum ucimap_type type)
81 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
85 ucimap_is_list_auto(enum ucimap_type type)
87 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
91 ucimap_is_custom(enum ucimap_type type)
93 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
97 ucimap_section_ptr(struct ucimap_section_data *sd)
99 return ((char *) sd - sd->sm->smap_offset);
102 static inline union ucimap_data *
103 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
107 data = (char *) ucimap_section_ptr(sd) + om->offset;
112 ucimap_init(struct uci_map *map)
114 INIT_LIST_HEAD(&map->pending);
115 INIT_LIST_HEAD(&map->sdata);
116 INIT_LIST_HEAD(&map->fixup);
121 ucimap_free_item(struct uci_alloc *a)
123 switch(a->type & UCIMAP_TYPE) {
132 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
134 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
135 a->type = UCIMAP_SIMPLE;
140 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
145 section = ucimap_section_ptr(sd);
146 if (!list_empty(&sd->list))
150 sd->sm->free(map, section);
152 for (i = 0; i < sd->allocmap_len; i++) {
153 ucimap_free_item(&sd->allocmap[i]);
161 ucimap_cleanup(struct uci_map *map)
163 struct list_head *ptr, *tmp;
165 list_for_each_safe(ptr, tmp, &map->sdata) {
166 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
167 ucimap_free_section(map, sd);
172 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
174 struct ucimap_section_data *sd;
177 list_for_each(p, &map->sdata) {
178 sd = list_entry(p, struct ucimap_section_data, list);
181 if (strcmp(f->name, sd->section_name) != 0)
183 return ucimap_section_ptr(sd);
185 list_for_each(p, &map->pending) {
186 sd = list_entry(p, struct ucimap_section_data, list);
189 if (strcmp(f->name, sd->section_name) != 0)
191 return ucimap_section_ptr(sd);
197 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
199 void *ptr = ucimap_find_section(map, f);
200 struct ucimap_list *list;
205 switch(f->type & UCIMAP_TYPE) {
210 list = f->data->list;
211 list->item[list->n_items++].ptr = ptr;
218 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
220 struct uci_fixup *f, tmp;
221 struct uci_map *map = sd->map;
223 INIT_LIST_HEAD(&tmp.list);
224 tmp.sm = om->data.sm;
228 if (ucimap_handle_fixup(map, &tmp))
231 f = malloc(sizeof(struct uci_fixup));
235 memcpy(f, &tmp, sizeof(tmp));
236 list_add_tail(&f->list, &map->fixup);
240 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
242 union ucimap_data tdata = *data;
248 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
249 data = &data->list->item[data->list->n_items++];
251 switch(om->type & UCIMAP_SUBTYPE) {
253 if ((om->data.s.maxlen > 0) &&
254 (strlen(str) > om->data.s.maxlen))
259 ucimap_add_alloc(sd, s);
262 if (!strcmp(str, "on"))
264 else if (!strcmp(str, "1"))
266 else if (!strcmp(str, "enabled"))
268 else if (!strcmp(str, "off"))
270 else if (!strcmp(str, "0"))
272 else if (!strcmp(str, "disabled"))
280 lval = strtol(str, &eptr, om->data.i.base);
281 if (lval < INT_MIN || lval > INT_MAX)
284 if (!eptr || *eptr == '\0')
285 tdata.i = (int) lval;
290 ucimap_add_fixup(sd, data, om, str);
293 tdata.s = (char *) data;
297 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
300 if (ucimap_is_custom(om->type))
302 memcpy(data, &tdata, sizeof(union ucimap_data));
307 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
315 ucimap_add_alloc(sd, s);
325 while (*s && !isspace(*s))
333 ucimap_add_value(data, om, sd, p);
338 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
340 struct uci_element *e, *l;
341 struct uci_option *o;
342 union ucimap_data *data;
344 uci_foreach_element(&s->options, e) {
345 struct uci_optmap *om = NULL, *tmp;
347 ucimap_foreach_option(sm, tmp) {
348 if (strcmp(e->name, tmp->name) == 0) {
356 data = ucimap_get_data(sd, om);
357 o = uci_to_option(e);
358 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
359 ucimap_add_value(data, om, sd, o->v.string);
360 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
361 uci_foreach_element(&o->v.list, l) {
362 ucimap_add_value(data, om, sd, l->name);
364 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
365 ucimap_convert_list(data, om, sd, o->v.string);
373 ucimap_add_section(struct ucimap_section_data *sd)
375 struct uci_map *map = sd->map;
377 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
378 ucimap_free_section(map, sd);
380 list_add_tail(&sd->list, &map->sdata);
383 static const char *ucimap_type_names[] = {
384 [UCIMAP_STRING] = "string",
385 [UCIMAP_INT] = "integer",
386 [UCIMAP_BOOL] = "boolean",
387 [UCIMAP_SECTION] = "section",
388 [UCIMAP_LIST] = "list",
391 static inline const char *
392 ucimap_get_type_name(int type)
397 if (ucimap_is_list(type))
398 return ucimap_type_names[UCIMAP_LIST];
400 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
402 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
410 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
414 if (om->detected_type < 0)
417 if (ucimap_is_custom(om->type))
420 if (ucimap_is_list(om->type) !=
421 ucimap_is_list(om->detected_type))
424 if (ucimap_is_list(om->type))
427 type = om->type & UCIMAP_SUBTYPE;
432 if (type != om->detected_type)
443 DPRINTF("Invalid type in option '%s' of section type '%s', "
444 "declared type is %s, detected type is %s\n",
446 ucimap_get_type_name(om->type),
447 ucimap_get_type_name(om->detected_type));
452 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
454 struct uci_optmap *om;
460 INIT_LIST_HEAD(&sd->list);
464 ucimap_foreach_option(sm, om) {
465 if (!ucimap_check_optmap_type(sm, om))
468 if (ucimap_is_list(om->type)) {
469 union ucimap_data *data;
470 struct uci_element *e;
474 data = ucimap_get_data(sd, om);
475 uci_foreach_element(&s->options, e) {
476 struct uci_option *o = uci_to_option(e);
477 struct uci_element *tmp;
479 if (strcmp(e->name, om->name) != 0)
482 if (o->type == UCI_TYPE_LIST) {
483 uci_foreach_element(&o->v.list, tmp) {
486 } else if ((o->type == UCI_TYPE_STRING) &&
487 ucimap_is_list_auto(om->type)) {
488 const char *data = o->v.string;
490 while (isspace(*data))
498 while (*data && !isspace(*data))
502 /* for the duplicated data string */
508 /* add one more for the ucimap_list */
509 n_alloc += n_elements + 1;
510 size = sizeof(struct ucimap_list) +
511 n_elements * sizeof(union ucimap_data);
512 data->list = malloc(size);
513 memset(data->list, 0, size);
514 } else if (ucimap_is_alloc(om->type)) {
519 sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
523 section_name = strdup(s->e.name);
527 sd->section_name = section_name;
529 sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
533 memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
534 ucimap_add_alloc(sd, (void *)section_name);
535 ucimap_add_alloc(sd, (void *)sd->cmap);
536 ucimap_foreach_option(sm, om) {
537 if (!ucimap_is_list(om->type))
540 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
543 section = ucimap_section_ptr(sd);
544 err = sm->init(map, section, s);
549 ucimap_add_section(sd);
551 list_add_tail(&sd->list, &map->pending);
554 err = ucimap_parse_options(map, sm, sd, s);
567 ucimap_free_section(map, sd);
572 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
574 struct uci_package *p = s->package;
576 memset(ptr, 0, sizeof(struct uci_ptr));
578 ptr->package = p->e.name;
581 ptr->section = s->e.name;
584 ptr->option = option;
585 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
589 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
591 void *section = ucimap_section_ptr(sd);
592 struct uci_sectionmap *sm = sd->sm;
593 struct uci_optmap *om;
594 int ofs = (char *)field - (char *)section;
597 ucimap_foreach_option(sm, om) {
598 if (om->offset == ofs) {
599 SET_BIT(sd->cmap, i);
607 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
609 struct uci_sectionmap *sm = sd->sm;
610 struct uci_section *s = NULL;
611 struct uci_optmap *om;
612 struct uci_element *e;
617 uci_foreach_element(&p->sections, e) {
618 if (!strcmp(e->name, sd->section_name)) {
619 s = uci_to_section(e);
624 return UCI_ERR_NOTFOUND;
626 ucimap_foreach_option(sm, om) {
627 union ucimap_data *data;
632 if (ucimap_is_list(om->type))
635 data = ucimap_get_data(sd, om);
636 if (!TEST_BIT(sd->cmap, i - 1))
639 ucimap_fill_ptr(&ptr, s, om->name);
640 switch(om->type & UCIMAP_SUBTYPE) {
645 sprintf(buf, "%d", data->i);
649 sprintf(buf, "%d", !!data->b);
658 union ucimap_data tdata, *data;
660 data = ucimap_get_data(sd, om);
661 if (ucimap_is_custom(om->type)) {
662 tdata.s = (char *)data;
666 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
673 ret = uci_set(s->package->ctx, &ptr);
677 CLR_BIT(sd->cmap, i - 1);
684 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
686 struct uci_element *e;
687 struct list_head *p, *tmp;
690 INIT_LIST_HEAD(&map->fixup);
691 uci_foreach_element(&pkg->sections, e) {
692 struct uci_section *s = uci_to_section(e);
694 for (i = 0; i < map->n_sections; i++) {
695 struct uci_sectionmap *sm = map->sections[i];
696 struct ucimap_section_data *sd;
698 if (strcmp(s->type, map->sections[i]->type) != 0)
702 sd = sm->alloc(map, sm, s);
703 memset(sd, 0, sizeof(struct ucimap_section_data));
705 sd = malloc(sm->alloc_len);
706 memset(sd, 0, sm->alloc_len);
711 ucimap_parse_section(map, sm, sd, s);
716 list_for_each_safe(p, tmp, &map->fixup) {
717 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
718 ucimap_handle_fixup(map, f);
723 list_for_each_safe(p, tmp, &map->pending) {
724 struct ucimap_section_data *sd;
725 sd = list_entry(p, struct ucimap_section_data, list);
727 list_del_init(&sd->list);
728 ucimap_add_section(sd);