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 enum ucimap_type type;
31 struct list_head list;
32 struct uci_sectionmap *sm;
34 enum ucimap_type type;
35 union ucimap_data *data;
38 #define ucimap_foreach_option(_sm, _o) \
39 if (!(_sm)->options_size) \
40 (_sm)->options_size = sizeof(struct uci_optmap); \
41 for (_o = &(_sm)->options[0]; \
42 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
43 (_sm)->options_size * (_sm)->n_options); \
44 _o = (struct uci_optmap *) ((char *)(_o) + \
49 ucimap_is_alloc(enum ucimap_type type)
51 switch(type & UCIMAP_SUBTYPE) {
60 ucimap_is_fixup(enum ucimap_type type)
62 switch(type & UCIMAP_SUBTYPE) {
71 ucimap_is_simple(enum ucimap_type type)
73 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
77 ucimap_is_list(enum ucimap_type type)
79 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
83 ucimap_is_list_auto(enum ucimap_type type)
85 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
89 ucimap_is_custom(enum ucimap_type type)
91 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
95 ucimap_section_ptr(struct ucimap_section_data *sd)
97 return ((char *) sd - sd->sm->smap_offset);
100 static inline union ucimap_data *
101 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
105 data = (char *) ucimap_section_ptr(sd) + om->offset;
110 ucimap_init(struct uci_map *map)
112 INIT_LIST_HEAD(&map->pending);
113 INIT_LIST_HEAD(&map->sdata);
114 INIT_LIST_HEAD(&map->fixup);
119 ucimap_free_item(struct uci_alloc *a)
121 switch(a->type & UCIMAP_TYPE) {
130 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
132 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
133 a->type = UCIMAP_SIMPLE;
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 ucimap_free_item(&sd->allocmap[i]);
159 ucimap_cleanup(struct uci_map *map)
161 struct list_head *ptr, *tmp;
163 list_for_each_safe(ptr, tmp, &map->sdata) {
164 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
165 ucimap_free_section(map, sd);
170 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
172 struct ucimap_section_data *sd;
175 list_for_each(p, &map->sdata) {
176 sd = list_entry(p, struct ucimap_section_data, list);
179 if (strcmp(f->name, sd->section_name) != 0)
181 return ucimap_section_ptr(sd);
183 list_for_each(p, &map->pending) {
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);
195 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
197 void *ptr = ucimap_find_section(map, f);
198 struct ucimap_list *list;
203 switch(f->type & UCIMAP_TYPE) {
208 list = f->data->list;
209 list->item[list->n_items++].ptr = ptr;
216 ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
218 struct uci_fixup *f, tmp;
220 INIT_LIST_HEAD(&tmp.list);
221 tmp.sm = om->data.sm;
225 if (ucimap_handle_fixup(map, &tmp))
228 f = malloc(sizeof(struct uci_fixup));
232 memcpy(f, &tmp, sizeof(tmp));
233 list_add_tail(&f->list, &map->fixup);
237 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
239 union ucimap_data tdata = *data;
245 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
246 data = &data->list->item[data->list->n_items++];
248 switch(om->type & UCIMAP_SUBTYPE) {
250 if ((om->data.s.maxlen > 0) &&
251 (strlen(str) > om->data.s.maxlen))
256 ucimap_add_alloc(sd, s);
259 if (!strcmp(str, "on"))
261 else if (!strcmp(str, "1"))
263 else if (!strcmp(str, "enabled"))
265 else if (!strcmp(str, "off"))
267 else if (!strcmp(str, "0"))
269 else if (!strcmp(str, "disabled"))
277 lval = strtol(str, &eptr, om->data.i.base);
278 if (lval < INT_MIN || lval > INT_MAX)
281 if (!eptr || *eptr == '\0')
282 tdata.i = (int) lval;
287 ucimap_add_fixup(sd->map, data, om, str);
290 tdata.s = (char *) data;
294 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
297 if (ucimap_is_custom(om->type))
299 memcpy(data, &tdata, sizeof(union ucimap_data));
304 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
312 ucimap_add_alloc(sd, s);
322 while (*s && !isspace(*s))
330 ucimap_add_value(data, om, sd, p);
335 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
337 struct uci_element *e, *l;
338 struct uci_option *o;
339 union ucimap_data *data;
341 uci_foreach_element(&s->options, e) {
342 struct uci_optmap *om = NULL, *tmp;
344 ucimap_foreach_option(sm, tmp) {
345 if (strcmp(e->name, tmp->name) == 0) {
353 data = ucimap_get_data(sd, om);
354 o = uci_to_option(e);
355 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
356 ucimap_add_value(data, om, sd, o->v.string);
357 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
358 uci_foreach_element(&o->v.list, l) {
359 ucimap_add_value(data, om, sd, l->name);
361 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
362 ucimap_convert_list(data, om, sd, o->v.string);
370 ucimap_add_section(struct ucimap_section_data *sd)
372 struct uci_map *map = sd->map;
374 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
375 ucimap_free_section(map, sd);
377 list_add_tail(&sd->list, &map->sdata);
382 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
384 struct uci_optmap *om;
390 INIT_LIST_HEAD(&sd->list);
394 ucimap_foreach_option(sm, om) {
395 if (ucimap_is_list(om->type)) {
396 union ucimap_data *data;
397 struct uci_element *e;
401 data = ucimap_get_data(sd, om);
402 uci_foreach_element(&s->options, e) {
403 struct uci_option *o = uci_to_option(e);
404 struct uci_element *tmp;
406 if (strcmp(e->name, om->name) != 0)
409 if (o->type == UCI_TYPE_LIST) {
410 uci_foreach_element(&o->v.list, tmp) {
413 } else if ((o->type == UCI_TYPE_STRING) &&
414 ucimap_is_list_auto(om->type)) {
415 const char *data = o->v.string;
417 while (isspace(*data))
425 while (*data && !isspace(*data))
429 /* for the duplicated data string */
435 /* add one more for the ucimap_list */
436 n_alloc += n_elements + 1;
437 size = sizeof(struct ucimap_list) +
438 n_elements * sizeof(union ucimap_data);
439 data->list = malloc(size);
440 memset(data->list, 0, size);
441 } else if (ucimap_is_alloc(om->type)) {
446 sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
450 section_name = strdup(s->e.name);
454 sd->section_name = section_name;
456 sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
460 memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
461 ucimap_add_alloc(sd, (void *)section_name);
462 ucimap_add_alloc(sd, (void *)sd->cmap);
463 ucimap_foreach_option(sm, om) {
464 if (!ucimap_is_list(om->type))
467 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
470 section = ucimap_section_ptr(sd);
471 err = sm->init(map, section, s);
476 ucimap_add_section(sd);
478 list_add_tail(&sd->list, &map->pending);
481 err = ucimap_parse_options(map, sm, sd, s);
494 ucimap_free_section(map, sd);
499 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
501 struct uci_package *p = s->package;
503 memset(ptr, 0, sizeof(struct uci_ptr));
505 ptr->package = p->e.name;
508 ptr->section = s->e.name;
511 ptr->option = option;
512 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
516 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
518 void *section = ucimap_section_ptr(sd);
519 struct uci_sectionmap *sm = sd->sm;
520 struct uci_optmap *om;
521 int ofs = (char *)field - (char *)section;
524 ucimap_foreach_option(sm, om) {
525 if (om->offset == ofs) {
526 SET_BIT(sd->cmap, i);
534 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
536 struct uci_sectionmap *sm = sd->sm;
537 struct uci_section *s = NULL;
538 struct uci_optmap *om;
539 struct uci_element *e;
544 uci_foreach_element(&p->sections, e) {
545 if (!strcmp(e->name, sd->section_name)) {
546 s = uci_to_section(e);
551 return UCI_ERR_NOTFOUND;
553 ucimap_foreach_option(sm, om) {
554 union ucimap_data *data;
559 if (ucimap_is_list(om->type))
562 data = ucimap_get_data(sd, om);
563 if (!TEST_BIT(sd->cmap, i - 1))
566 ucimap_fill_ptr(&ptr, s, om->name);
567 switch(om->type & UCIMAP_SUBTYPE) {
572 sprintf(buf, "%d", data->i);
576 sprintf(buf, "%d", !!data->b);
585 union ucimap_data tdata, *data;
587 data = ucimap_get_data(sd, om);
588 if (ucimap_is_custom(om->type)) {
589 tdata.s = (char *)data;
593 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
600 ret = uci_set(s->package->ctx, &ptr);
604 CLR_BIT(sd->cmap, i - 1);
611 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
613 struct uci_element *e;
614 struct list_head *p, *tmp;
617 INIT_LIST_HEAD(&map->fixup);
618 uci_foreach_element(&pkg->sections, e) {
619 struct uci_section *s = uci_to_section(e);
621 for (i = 0; i < map->n_sections; i++) {
622 struct uci_sectionmap *sm = map->sections[i];
623 struct ucimap_section_data *sd;
625 if (strcmp(s->type, map->sections[i]->type) != 0)
629 sd = sm->alloc(map, sm, s);
630 memset(sd, 0, sizeof(struct ucimap_section_data));
632 sd = malloc(sm->alloc_len);
633 memset(sd, 0, sm->alloc_len);
638 ucimap_parse_section(map, sm, sd, s);
643 list_for_each_safe(p, tmp, &map->fixup) {
644 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
645 ucimap_handle_fixup(map, f);
650 list_for_each_safe(p, tmp, &map->pending) {
651 struct ucimap_section_data *sd;
652 sd = list_entry(p, struct ucimap_section_data, list);
654 list_del_init(&sd->list);
655 ucimap_add_section(sd);