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->sdata);
113 INIT_LIST_HEAD(&map->fixup);
118 ucimap_free_item(struct uci_alloc *a)
120 switch(a->type & UCIMAP_TYPE) {
129 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
131 struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
132 a->type = UCIMAP_SIMPLE;
137 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
142 section = ucimap_section_ptr(sd);
143 if (!list_empty(&sd->list))
147 sd->sm->free(map, section);
149 for (i = 0; i < sd->allocmap_len; i++) {
150 ucimap_free_item(&sd->allocmap[i]);
158 ucimap_cleanup(struct uci_map *map)
160 struct list_head *ptr, *tmp;
162 list_for_each_safe(ptr, tmp, &map->sdata) {
163 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
164 ucimap_free_section(map, sd);
169 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
171 struct ucimap_section_data *sd;
174 list_for_each(p, &map->sdata) {
175 sd = list_entry(p, struct ucimap_section_data, list);
178 if (strcmp(f->name, sd->section_name) != 0)
180 return ucimap_section_ptr(sd);
186 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
188 void *ptr = ucimap_find_section(map, f);
189 struct ucimap_list *list;
194 switch(f->type & UCIMAP_TYPE) {
199 list = f->data->list;
200 list->item[list->n_items++].ptr = ptr;
207 ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
209 struct uci_fixup *f, tmp;
211 INIT_LIST_HEAD(&tmp.list);
212 tmp.sm = om->data.sm;
216 if (ucimap_handle_fixup(map, &tmp))
219 f = malloc(sizeof(struct uci_fixup));
223 memcpy(f, &tmp, sizeof(tmp));
224 list_add_tail(&f->list, &map->fixup);
228 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
230 union ucimap_data tdata = *data;
236 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
237 data = &data->list->item[data->list->n_items++];
239 switch(om->type & UCIMAP_SUBTYPE) {
241 if ((om->data.s.maxlen > 0) &&
242 (strlen(str) > om->data.s.maxlen))
247 ucimap_add_alloc(sd, s);
250 if (!strcmp(str, "on"))
252 else if (!strcmp(str, "1"))
254 else if (!strcmp(str, "enabled"))
256 else if (!strcmp(str, "off"))
258 else if (!strcmp(str, "0"))
260 else if (!strcmp(str, "disabled"))
268 lval = strtol(str, &eptr, om->data.i.base);
269 if (lval < INT_MIN || lval > INT_MAX)
272 if (!eptr || *eptr == '\0')
273 tdata.i = (int) lval;
278 ucimap_add_fixup(sd->map, data, om, str);
281 tdata.s = (char *) data;
285 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
288 if (ucimap_is_custom(om->type))
290 memcpy(data, &tdata, sizeof(union ucimap_data));
295 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
303 ucimap_add_alloc(sd, s);
313 while (*s && !isspace(*s))
321 ucimap_add_value(data, om, sd, p);
326 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
328 struct uci_element *e, *l;
329 struct uci_option *o;
330 union ucimap_data *data;
332 uci_foreach_element(&s->options, e) {
333 struct uci_optmap *om = NULL, *tmp;
335 ucimap_foreach_option(sm, tmp) {
336 if (strcmp(e->name, tmp->name) == 0) {
344 data = ucimap_get_data(sd, om);
345 o = uci_to_option(e);
346 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
347 ucimap_add_value(data, om, sd, o->v.string);
348 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
349 uci_foreach_element(&o->v.list, l) {
350 ucimap_add_value(data, om, sd, l->name);
352 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
353 ucimap_convert_list(data, om, sd, o->v.string);
362 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
364 struct uci_optmap *om;
370 INIT_LIST_HEAD(&sd->list);
374 ucimap_foreach_option(sm, om) {
375 if (ucimap_is_list(om->type)) {
376 union ucimap_data *data;
377 struct uci_element *e;
381 data = ucimap_get_data(sd, om);
382 uci_foreach_element(&s->options, e) {
383 struct uci_option *o = uci_to_option(e);
384 struct uci_element *tmp;
386 if (strcmp(e->name, om->name) != 0)
389 if (o->type == UCI_TYPE_LIST) {
390 uci_foreach_element(&o->v.list, tmp) {
393 } else if ((o->type == UCI_TYPE_STRING) &&
394 ucimap_is_list_auto(om->type)) {
395 const char *data = o->v.string;
397 while (isspace(*data))
405 while (*data && !isspace(*data))
409 /* for the duplicated data string */
415 /* add one more for the ucimap_list */
416 n_alloc += n_elements + 1;
417 size = sizeof(struct ucimap_list) +
418 n_elements * sizeof(union ucimap_data);
419 data->list = malloc(size);
420 memset(data->list, 0, size);
421 } else if (ucimap_is_alloc(om->type)) {
426 sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
430 section_name = strdup(s->e.name);
434 sd->section_name = section_name;
436 sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
440 memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
441 ucimap_add_alloc(sd, (void *)section_name);
442 ucimap_add_alloc(sd, (void *)sd->cmap);
443 ucimap_foreach_option(sm, om) {
444 if (!ucimap_is_list(om->type))
447 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
450 section = ucimap_section_ptr(sd);
451 err = sm->init(map, section, s);
455 list_add_tail(&sd->list, &map->sdata);
456 err = ucimap_parse_options(map, sm, sd, s);
469 ucimap_free_section(map, sd);
474 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
476 struct uci_package *p = s->package;
478 memset(ptr, 0, sizeof(struct uci_ptr));
480 ptr->package = p->e.name;
483 ptr->section = s->e.name;
486 ptr->option = option;
487 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
491 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
493 void *section = ucimap_section_ptr(sd);
494 struct uci_sectionmap *sm = sd->sm;
495 struct uci_optmap *om;
496 int ofs = (char *)field - (char *)section;
499 ucimap_foreach_option(sm, om) {
500 if (om->offset == ofs) {
501 SET_BIT(sd->cmap, i);
509 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
511 struct uci_sectionmap *sm = sd->sm;
512 struct uci_section *s = NULL;
513 struct uci_optmap *om;
514 struct uci_element *e;
519 uci_foreach_element(&p->sections, e) {
520 if (!strcmp(e->name, sd->section_name)) {
521 s = uci_to_section(e);
526 return UCI_ERR_NOTFOUND;
528 ucimap_foreach_option(sm, om) {
529 union ucimap_data *data;
534 if (ucimap_is_list(om->type))
537 data = ucimap_get_data(sd, om);
538 if (!TEST_BIT(sd->cmap, i - 1))
541 ucimap_fill_ptr(&ptr, s, om->name);
542 switch(om->type & UCIMAP_SUBTYPE) {
547 sprintf(buf, "%d", data->i);
551 sprintf(buf, "%d", !!data->b);
560 union ucimap_data tdata, *data;
562 data = ucimap_get_data(sd, om);
563 if (ucimap_is_custom(om->type)) {
564 tdata.s = (char *)data;
568 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
575 ret = uci_set(s->package->ctx, &ptr);
579 CLR_BIT(sd->cmap, i - 1);
586 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
588 struct uci_element *e;
589 struct list_head *p, *tmp;
592 INIT_LIST_HEAD(&map->fixup);
593 uci_foreach_element(&pkg->sections, e) {
594 struct uci_section *s = uci_to_section(e);
596 for (i = 0; i < map->n_sections; i++) {
597 struct uci_sectionmap *sm = map->sections[i];
598 struct ucimap_section_data *sd;
600 if (strcmp(s->type, map->sections[i]->type) != 0)
604 sd = sm->alloc(map, sm, s);
605 memset(sd, 0, sizeof(struct ucimap_section_data));
607 sd = malloc(sm->alloc_len);
608 memset(sd, 0, sm->alloc_len);
613 ucimap_parse_section(map, sm, sd, s);
616 list_for_each_safe(p, tmp, &map->fixup) {
617 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
618 ucimap_handle_fixup(map, f);
622 list_for_each_safe(p, tmp, &map->sdata) {
623 struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list);
629 section = ucimap_section_ptr(sd);
630 if (sd->sm->add(map, section) != 0)
631 ucimap_free_section(map, sd);