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_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
173 f = malloc(sizeof(struct uci_fixup));
177 INIT_LIST_HEAD(&f->list);
182 list_add_tail(&f->list, &map->fixup);
186 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
188 union ucimap_data tdata = *data;
194 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
195 data = &data->list->item[data->list->n_items++];
197 switch(om->type & UCIMAP_SUBTYPE) {
199 if ((om->data.s.maxlen > 0) &&
200 (strlen(str) > om->data.s.maxlen))
205 ucimap_add_alloc(sd, s);
208 if (!strcmp(str, "on"))
210 else if (!strcmp(str, "1"))
212 else if (!strcmp(str, "enabled"))
214 else if (!strcmp(str, "off"))
216 else if (!strcmp(str, "0"))
218 else if (!strcmp(str, "disabled"))
226 lval = strtol(str, &eptr, om->data.i.base);
227 if (lval < INT_MIN || lval > INT_MAX)
230 if (!eptr || *eptr == '\0')
231 tdata.i = (int) lval;
236 ucimap_add_fixup(sd->map, data, om, str);
239 tdata.s = (char *) data;
243 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
246 if (ucimap_is_custom(om->type))
248 memcpy(data, &tdata, sizeof(union ucimap_data));
253 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
261 ucimap_add_alloc(sd, s);
271 while (*s && !isspace(*s))
279 ucimap_add_value(data, om, sd, p);
284 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
286 struct uci_element *e, *l;
287 struct uci_option *o;
288 union ucimap_data *data;
290 uci_foreach_element(&s->options, e) {
291 struct uci_optmap *om = NULL, *tmp;
293 ucimap_foreach_option(sm, tmp) {
294 if (strcmp(e->name, tmp->name) == 0) {
302 data = ucimap_get_data(sd, om);
303 o = uci_to_option(e);
304 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
305 ucimap_add_value(data, om, sd, o->v.string);
306 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
307 uci_foreach_element(&o->v.list, l) {
308 ucimap_add_value(data, om, sd, l->name);
310 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
311 ucimap_convert_list(data, om, sd, o->v.string);
320 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_section *s)
322 struct ucimap_section_data *sd = NULL;
323 struct uci_optmap *om;
330 sd = sm->alloc(map, sm, s);
331 memset(sd, 0, sizeof(struct ucimap_section_data));
333 sd = malloc(sm->alloc_len);
334 memset(sd, 0, sm->alloc_len);
340 INIT_LIST_HEAD(&sd->list);
344 ucimap_foreach_option(sm, om) {
345 if (ucimap_is_list(om->type)) {
346 union ucimap_data *data;
347 struct uci_element *e;
351 data = ucimap_get_data(sd, om);
352 uci_foreach_element(&s->options, e) {
353 struct uci_option *o = uci_to_option(e);
354 struct uci_element *tmp;
356 if (strcmp(e->name, om->name) != 0)
359 if (o->type == UCI_TYPE_LIST) {
360 uci_foreach_element(&o->v.list, tmp) {
363 } else if ((o->type == UCI_TYPE_STRING) &&
364 ucimap_is_list_auto(om->type)) {
365 const char *data = o->v.string;
367 while (isspace(*data))
375 while (*data && !isspace(*data))
379 /* for the duplicated data string */
385 /* add one more for the ucimap_list */
386 n_alloc += n_elements + 1;
387 size = sizeof(struct ucimap_list) +
388 n_elements * sizeof(union ucimap_data);
389 data->list = malloc(size);
390 memset(data->list, 0, size);
391 } else if (ucimap_is_alloc(om->type)) {
396 sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
400 section_name = strdup(s->e.name);
404 sd->section_name = section_name;
406 sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
410 memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
411 ucimap_add_alloc(sd, (void *)section_name);
412 ucimap_add_alloc(sd, (void *)sd->cmap);
413 ucimap_foreach_option(sm, om) {
414 if (!ucimap_is_list(om->type))
417 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
420 section = ucimap_section_ptr(sd);
421 err = sm->init(map, section, s);
425 list_add(&sd->list, &map->sdata);
426 err = ucimap_parse_options(map, sm, sd, s);
439 ucimap_free_section(map, sd);
444 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
446 struct uci_package *p = s->package;
448 memset(ptr, 0, sizeof(struct uci_ptr));
450 ptr->package = p->e.name;
453 ptr->section = s->e.name;
456 ptr->option = option;
457 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
461 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
463 void *section = ucimap_section_ptr(sd);
464 struct uci_sectionmap *sm = sd->sm;
465 struct uci_optmap *om;
466 int ofs = (char *)field - (char *)section;
469 ucimap_foreach_option(sm, om) {
470 if (om->offset == ofs) {
471 SET_BIT(sd->cmap, i);
479 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
481 struct uci_sectionmap *sm = sd->sm;
482 struct uci_section *s = NULL;
483 struct uci_optmap *om;
484 struct uci_element *e;
489 uci_foreach_element(&p->sections, e) {
490 if (!strcmp(e->name, sd->section_name)) {
491 s = uci_to_section(e);
496 return UCI_ERR_NOTFOUND;
498 ucimap_foreach_option(sm, om) {
499 union ucimap_data *data;
504 if (ucimap_is_list(om->type))
507 data = ucimap_get_data(sd, om);
508 if (!TEST_BIT(sd->cmap, i - 1))
511 ucimap_fill_ptr(&ptr, s, om->name);
512 switch(om->type & UCIMAP_SUBTYPE) {
517 sprintf(buf, "%d", data->i);
521 sprintf(buf, "%d", !!data->b);
530 union ucimap_data tdata, *data;
532 data = ucimap_get_data(sd, om);
533 if (ucimap_is_custom(om->type)) {
534 tdata.s = (char *)data;
538 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
545 ret = uci_set(s->package->ctx, &ptr);
549 CLR_BIT(sd->cmap, i - 1);
556 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
558 struct ucimap_section_data *sd;
561 list_for_each(p, &map->sdata) {
562 sd = list_entry(p, struct ucimap_section_data, list);
565 if (strcmp(f->name, sd->section_name) != 0)
567 return ucimap_section_ptr(sd);
573 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
575 struct uci_element *e;
576 struct list_head *p, *tmp;
579 INIT_LIST_HEAD(&map->fixup);
580 uci_foreach_element(&pkg->sections, e) {
581 struct uci_section *s = uci_to_section(e);
583 for (i = 0; i < map->n_sections; i++) {
584 if (strcmp(s->type, map->sections[i]->type) != 0)
586 ucimap_parse_section(map, map->sections[i], s);
589 list_for_each_safe(p, tmp, &map->fixup) {
590 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
591 void *ptr = ucimap_find_section(map, f);
592 struct ucimap_list *list;
597 switch(f->type & UCIMAP_TYPE) {
599 f->data->section = ptr;
602 list = f->data->list;
603 list->item[list->n_items++].section = ptr;
608 list_for_each_safe(p, tmp, &map->sdata) {
609 struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list);
615 section = ucimap_section_ptr(sd);
616 if (sd->sm->add(map, section) != 0)
617 ucimap_free_section(map, sd);