0d16bb469d672fa8ab0f787524d3b1f9a1f93d12
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 #include "uci_internal.h"
30 struct uci_alloc_custom
{
32 struct uci_optmap
*om
;
37 struct list_head list
;
38 struct uci_sectionmap
*sm
;
40 enum ucimap_type type
;
41 union ucimap_data
*data
;
44 #define ucimap_foreach_option(_sm, _o) \
45 if (!(_sm)->options_size) \
46 (_sm)->options_size = sizeof(struct uci_optmap); \
47 for (_o = &(_sm)->options[0]; \
48 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
49 (_sm)->options_size * (_sm)->n_options); \
50 _o = (struct uci_optmap *) ((char *)(_o) + \
55 ucimap_is_alloc(enum ucimap_type type
)
57 switch(type
& UCIMAP_SUBTYPE
) {
66 ucimap_is_fixup(enum ucimap_type type
)
68 switch(type
& UCIMAP_SUBTYPE
) {
77 ucimap_is_simple(enum ucimap_type type
)
79 return ((type
& UCIMAP_TYPE
) == UCIMAP_SIMPLE
);
83 ucimap_is_list(enum ucimap_type type
)
85 return ((type
& UCIMAP_TYPE
) == UCIMAP_LIST
);
89 ucimap_is_list_auto(enum ucimap_type type
)
91 return ucimap_is_list(type
) && !!(type
& UCIMAP_LIST_AUTO
);
95 ucimap_is_custom(enum ucimap_type type
)
97 return ((type
& UCIMAP_SUBTYPE
) == UCIMAP_CUSTOM
);
101 ucimap_section_ptr(struct ucimap_section_data
*sd
)
103 return ((char *) sd
- sd
->sm
->smap_offset
);
106 static inline union ucimap_data
*
107 ucimap_get_data(struct ucimap_section_data
*sd
, struct uci_optmap
*om
)
111 data
= (char *) ucimap_section_ptr(sd
) + om
->offset
;
116 ucimap_init(struct uci_map
*map
)
118 INIT_LIST_HEAD(&map
->pending
);
119 INIT_LIST_HEAD(&map
->sdata
);
120 INIT_LIST_HEAD(&map
->fixup
);
125 ucimap_add_alloc(struct ucimap_section_data
*sd
, void *ptr
)
127 struct uci_alloc
*a
= &sd
->allocmap
[sd
->allocmap_len
++];
132 ucimap_free_section(struct uci_map
*map
, struct ucimap_section_data
*sd
)
137 section
= ucimap_section_ptr(sd
);
138 if (!list_empty(&sd
->list
))
142 sd
->sm
->free(map
, section
);
144 for (i
= 0; i
< sd
->allocmap_len
; i
++) {
145 free(sd
->allocmap
[i
].ptr
);
148 if (sd
->alloc_custom
) {
149 for (i
= 0; i
< sd
->alloc_custom_len
; i
++) {
150 struct uci_alloc_custom
*a
= &sd
->alloc_custom
[i
];
151 a
->om
->free(a
->section
, a
->om
, a
->ptr
);
153 free(sd
->alloc_custom
);
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_free_item(struct ucimap_section_data
*sd
, void *item
)
220 struct uci_alloc_custom
*ac
;
222 void *ptr
= *((void **) item
);
228 *((void **)item
) = NULL
;
229 for (i
= 0, a
= sd
->allocmap
; i
< sd
->allocmap_len
; i
++, a
++) {
233 if (i
!= sd
->allocmap_len
- 1)
234 a
->ptr
= sd
->allocmap
[sd
->allocmap_len
- 1].ptr
;
240 for (i
= 0, ac
= sd
->alloc_custom
; i
< sd
->alloc_custom_len
; i
++, ac
++) {
244 if (i
!= sd
->alloc_custom_len
- 1)
245 memcpy(ac
, &sd
->alloc_custom
[sd
->alloc_custom_len
- 1],
246 sizeof(struct uci_alloc_custom
));
248 ac
->om
->free(ac
->section
, ac
->om
, ac
->ptr
);
249 sd
->alloc_custom_len
--;
255 ucimap_resize_list(struct ucimap_section_data
*sd
, struct ucimap_list
**list
, int items
)
257 struct ucimap_list
*new;
260 int size
= sizeof(struct ucimap_list
) + items
* sizeof(union ucimap_data
);
263 new = calloc(1, size
);
265 ucimap_add_alloc(sd
, new);
269 for (i
= 0, a
= sd
->allocmap
; i
< sd
->allocmap_len
; i
++, a
++) {
278 if (items
> (*list
)->size
)
279 offset
= (items
- (*list
)->size
) * sizeof(union ucimap_data
);
281 a
->ptr
= realloc(a
->ptr
, size
);
283 memset((char *) a
->ptr
+ offset
, 0, size
- offset
);
293 ucimap_add_fixup(struct ucimap_section_data
*sd
, union ucimap_data
*data
, struct uci_optmap
*om
, const char *str
)
295 struct uci_fixup
*f
, tmp
;
296 struct uci_map
*map
= sd
->map
;
298 INIT_LIST_HEAD(&tmp
.list
);
299 tmp
.sm
= om
->data
.sm
;
303 if (ucimap_handle_fixup(map
, &tmp
))
306 f
= malloc(sizeof(struct uci_fixup
));
310 memcpy(f
, &tmp
, sizeof(tmp
));
311 list_add_tail(&f
->list
, &map
->fixup
);
315 ucimap_add_custom_alloc(struct ucimap_section_data
*sd
, struct uci_optmap
*om
, void *ptr
)
317 struct uci_alloc_custom
*a
= &sd
->alloc_custom
[sd
->alloc_custom_len
++];
319 a
->section
= ucimap_section_ptr(sd
);
325 ucimap_add_value(union ucimap_data
*data
, struct uci_optmap
*om
, struct ucimap_section_data
*sd
, const char *str
)
327 union ucimap_data tdata
= *data
;
333 if (ucimap_is_list(om
->type
) && !ucimap_is_fixup(om
->type
)) {
334 if (unlikely(data
->list
->size
<= data
->list
->n_items
)) {
335 /* should not happen */
336 DPRINTF("ERROR: overflow while filling a list\n");
340 data
= &data
->list
->item
[data
->list
->n_items
++];
343 switch(om
->type
& UCIMAP_SUBTYPE
) {
345 if ((om
->data
.s
.maxlen
> 0) &&
346 (strlen(str
) > om
->data
.s
.maxlen
))
351 ucimap_add_alloc(sd
, s
);
354 if (!strcmp(str
, "on"))
356 else if (!strcmp(str
, "1"))
358 else if (!strcmp(str
, "enabled"))
360 else if (!strcmp(str
, "off"))
362 else if (!strcmp(str
, "0"))
364 else if (!strcmp(str
, "disabled"))
372 lval
= strtol(str
, &eptr
, om
->data
.i
.base
);
373 if (lval
< INT_MIN
|| lval
> INT_MAX
)
376 if (!eptr
|| *eptr
== '\0')
377 tdata
.i
= (int) lval
;
382 ucimap_add_fixup(sd
, data
, om
, str
);
385 tdata
.s
= (char *) data
;
389 if (om
->parse(ucimap_section_ptr(sd
), om
, &tdata
, str
) < 0)
391 if (ucimap_is_custom(om
->type
) && om
->free
) {
392 if (tdata
.ptr
!= data
->ptr
)
393 ucimap_add_custom_alloc(sd
, om
, data
->ptr
);
396 if (ucimap_is_custom(om
->type
))
398 memcpy(data
, &tdata
, sizeof(union ucimap_data
));
403 ucimap_convert_list(union ucimap_data
*data
, struct uci_optmap
*om
, struct ucimap_section_data
*sd
, const char *str
)
411 ucimap_add_alloc(sd
, s
);
421 while (*s
&& !isspace(*s
))
429 ucimap_add_value(data
, om
, sd
, p
);
434 ucimap_parse_options(struct uci_map
*map
, struct uci_sectionmap
*sm
, struct ucimap_section_data
*sd
, struct uci_section
*s
)
436 struct uci_element
*e
, *l
;
437 struct uci_option
*o
;
438 union ucimap_data
*data
;
440 uci_foreach_element(&s
->options
, e
) {
441 struct uci_optmap
*om
= NULL
, *tmp
;
443 ucimap_foreach_option(sm
, tmp
) {
444 if (strcmp(e
->name
, tmp
->name
) == 0) {
452 data
= ucimap_get_data(sd
, om
);
453 o
= uci_to_option(e
);
454 if ((o
->type
== UCI_TYPE_STRING
) && ucimap_is_simple(om
->type
)) {
455 ucimap_add_value(data
, om
, sd
, o
->v
.string
);
456 } else if ((o
->type
== UCI_TYPE_LIST
) && ucimap_is_list(om
->type
)) {
457 uci_foreach_element(&o
->v
.list
, l
) {
458 ucimap_add_value(data
, om
, sd
, l
->name
);
460 } else if ((o
->type
== UCI_TYPE_STRING
) && ucimap_is_list_auto(om
->type
)) {
461 ucimap_convert_list(data
, om
, sd
, o
->v
.string
);
469 ucimap_add_section(struct ucimap_section_data
*sd
)
471 struct uci_map
*map
= sd
->map
;
473 if (sd
->sm
->add(map
, ucimap_section_ptr(sd
)) < 0)
474 ucimap_free_section(map
, sd
);
476 list_add_tail(&sd
->list
, &map
->sdata
);
479 static const char *ucimap_type_names
[] = {
480 [UCIMAP_STRING
] = "string",
481 [UCIMAP_INT
] = "integer",
482 [UCIMAP_BOOL
] = "boolean",
483 [UCIMAP_SECTION
] = "section",
484 [UCIMAP_LIST
] = "list",
487 static inline const char *
488 ucimap_get_type_name(int type
)
493 if (ucimap_is_list(type
))
494 return ucimap_type_names
[UCIMAP_LIST
];
496 name
= ucimap_type_names
[type
& UCIMAP_SUBTYPE
];
498 sprintf(buf
, "Unknown (%d)", type
& UCIMAP_SUBTYPE
);
506 ucimap_check_optmap_type(struct uci_sectionmap
*sm
, struct uci_optmap
*om
)
510 if (unlikely(sm
->type_name
!= om
->type_name
) &&
511 unlikely(strcmp(sm
->type_name
, om
->type_name
) != 0)) {
512 DPRINTF("Option '%s' of section type '%s' refereces unknown "
513 "section type '%s', should be '%s'.\n",
514 om
->name
, sm
->type
, om
->type_name
, sm
->type_name
);
518 if (om
->detected_type
< 0)
521 if (ucimap_is_custom(om
->type
))
524 if (ucimap_is_list(om
->type
) !=
525 ucimap_is_list(om
->detected_type
))
528 if (ucimap_is_list(om
->type
))
531 type
= om
->type
& UCIMAP_SUBTYPE
;
536 if (type
!= om
->detected_type
)
547 DPRINTF("Invalid type in option '%s' of section type '%s', "
548 "declared type is %s, detected type is %s\n",
550 ucimap_get_type_name(om
->type
),
551 ucimap_get_type_name(om
->detected_type
));
556 ucimap_count_alloc(struct uci_optmap
*om
, int *n_alloc
, int *n_custom
)
558 if (ucimap_is_alloc(om
->type
))
560 else if (ucimap_is_custom(om
->type
) && om
->free
)
565 ucimap_parse_section(struct uci_map
*map
, struct uci_sectionmap
*sm
, struct ucimap_section_data
*sd
, struct uci_section
*s
)
567 struct uci_optmap
*om
;
571 int n_alloc_custom
= 0;
574 INIT_LIST_HEAD(&sd
->list
);
578 ucimap_foreach_option(sm
, om
) {
579 if (!ucimap_check_optmap_type(sm
, om
))
582 if (ucimap_is_list(om
->type
)) {
583 union ucimap_data
*data
;
584 struct uci_element
*e
;
586 int n_elements_custom
= 0;
589 data
= ucimap_get_data(sd
, om
);
590 uci_foreach_element(&s
->options
, e
) {
591 struct uci_option
*o
= uci_to_option(e
);
592 struct uci_element
*tmp
;
594 if (strcmp(e
->name
, om
->name
) != 0)
597 if (o
->type
== UCI_TYPE_LIST
) {
598 uci_foreach_element(&o
->v
.list
, tmp
) {
599 ucimap_count_alloc(om
, &n_elements
, &n_elements_custom
);
601 } else if ((o
->type
== UCI_TYPE_STRING
) &&
602 ucimap_is_list_auto(om
->type
)) {
603 const char *data
= o
->v
.string
;
605 while (isspace(*data
))
612 ucimap_count_alloc(om
, &n_elements
, &n_elements_custom
);
614 while (*data
&& !isspace(*data
))
618 /* for the duplicated data string */
624 /* add one more for the ucimap_list */
625 n_alloc
+= n_elements
+ 1;
626 n_alloc_custom
+= n_elements_custom
;
627 size
= sizeof(struct ucimap_list
) +
628 n_elements
* sizeof(union ucimap_data
);
630 data
->list
= malloc(size
);
634 data
->list
->size
= n_elements
;
635 memset(data
->list
, 0, size
);
637 ucimap_count_alloc(om
, &n_alloc
, &n_alloc_custom
);
641 sd
->allocmap
= calloc(n_alloc
, sizeof(struct uci_alloc
));
645 if (n_alloc_custom
> 0) {
646 sd
->alloc_custom
= calloc(n_alloc_custom
, sizeof(struct uci_alloc_custom
));
647 if (!sd
->alloc_custom
)
651 section_name
= strdup(s
->e
.name
);
655 sd
->section_name
= section_name
;
657 sd
->cmap
= calloc(1, BITFIELD_SIZE(sm
->n_options
));
661 ucimap_add_alloc(sd
, (void *)section_name
);
662 ucimap_add_alloc(sd
, (void *)sd
->cmap
);
663 ucimap_foreach_option(sm
, om
) {
664 if (!ucimap_is_list(om
->type
))
667 ucimap_add_alloc(sd
, ucimap_get_data(sd
, om
)->list
);
670 section
= ucimap_section_ptr(sd
);
671 err
= sm
->init(map
, section
, s
);
676 ucimap_add_section(sd
);
678 list_add_tail(&sd
->list
, &map
->pending
);
681 err
= ucimap_parse_options(map
, sm
, sd
, s
);
694 ucimap_free_section(map
, sd
);
699 ucimap_fill_ptr(struct uci_ptr
*ptr
, struct uci_section
*s
, const char *option
)
701 struct uci_package
*p
= s
->package
;
703 memset(ptr
, 0, sizeof(struct uci_ptr
));
705 ptr
->package
= p
->e
.name
;
708 ptr
->section
= s
->e
.name
;
711 ptr
->option
= option
;
712 return uci_lookup_ptr(p
->ctx
, ptr
, NULL
, false);
716 ucimap_set_changed(struct ucimap_section_data
*sd
, void *field
)
718 void *section
= ucimap_section_ptr(sd
);
719 struct uci_sectionmap
*sm
= sd
->sm
;
720 struct uci_optmap
*om
;
721 int ofs
= (char *)field
- (char *)section
;
724 ucimap_foreach_option(sm
, om
) {
725 if (om
->offset
== ofs
) {
726 SET_BIT(sd
->cmap
, i
);
734 ucimap_store_section(struct uci_map
*map
, struct uci_package
*p
, struct ucimap_section_data
*sd
)
736 struct uci_sectionmap
*sm
= sd
->sm
;
737 struct uci_section
*s
= NULL
;
738 struct uci_optmap
*om
;
739 struct uci_element
*e
;
744 uci_foreach_element(&p
->sections
, e
) {
745 if (!strcmp(e
->name
, sd
->section_name
)) {
746 s
= uci_to_section(e
);
751 return UCI_ERR_NOTFOUND
;
753 ucimap_foreach_option(sm
, om
) {
754 union ucimap_data
*data
;
759 if (ucimap_is_list(om
->type
))
762 data
= ucimap_get_data(sd
, om
);
763 if (!TEST_BIT(sd
->cmap
, i
- 1))
766 ucimap_fill_ptr(&ptr
, s
, om
->name
);
767 switch(om
->type
& UCIMAP_SUBTYPE
) {
772 sprintf(buf
, "%d", data
->i
);
776 sprintf(buf
, "%d", !!data
->b
);
785 union ucimap_data tdata
, *data
;
787 data
= ucimap_get_data(sd
, om
);
788 if (ucimap_is_custom(om
->type
)) {
789 tdata
.s
= (char *)data
;
793 if (om
->format(ucimap_section_ptr(sd
), om
, data
, &str
) < 0)
803 ret
= uci_set(s
->package
->ctx
, &ptr
);
807 CLR_BIT(sd
->cmap
, i
- 1);
814 ucimap_parse(struct uci_map
*map
, struct uci_package
*pkg
)
816 struct uci_element
*e
;
817 struct list_head
*p
, *tmp
;
820 INIT_LIST_HEAD(&map
->fixup
);
821 uci_foreach_element(&pkg
->sections
, e
) {
822 struct uci_section
*s
= uci_to_section(e
);
824 for (i
= 0; i
< map
->n_sections
; i
++) {
825 struct uci_sectionmap
*sm
= map
->sections
[i
];
826 struct ucimap_section_data
*sd
;
828 if (strcmp(s
->type
, map
->sections
[i
]->type
) != 0)
832 sd
= sm
->alloc(map
, sm
, s
);
833 memset(sd
, 0, sizeof(struct ucimap_section_data
));
835 sd
= malloc(sm
->alloc_len
);
836 memset(sd
, 0, sm
->alloc_len
);
841 ucimap_parse_section(map
, sm
, sd
, s
);
846 list_for_each_safe(p
, tmp
, &map
->fixup
) {
847 struct uci_fixup
*f
= list_entry(p
, struct uci_fixup
, list
);
848 ucimap_handle_fixup(map
, f
);
853 list_for_each_safe(p
, tmp
, &map
->pending
) {
854 struct ucimap_section_data
*sd
;
855 sd
= list_entry(p
, struct ucimap_section_data
, list
);
857 list_del_init(&sd
->list
);
858 ucimap_add_section(sd
);