2 * ucimap.c - Library for the Unified Configuration Interface
3 * Copyright (C) 2008-2009 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 Lesser General Public License version 2.1
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.
16 * This file contains ucimap, an API for mapping UCI to C data structures
29 #include "uci_internal.h"
35 struct ucimap_alloc_custom
{
37 struct uci_optmap
*om
;
42 struct ucimap_fixup
*next
;
43 struct uci_sectionmap
*sm
;
45 enum ucimap_type type
;
46 union ucimap_data
*data
;
49 #define ucimap_foreach_option(_sm, _o) \
50 if (!(_sm)->options_size) \
51 (_sm)->options_size = sizeof(struct uci_optmap); \
52 for (_o = &(_sm)->options[0]; \
53 ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
54 (_sm)->options_size * (_sm)->n_options); \
55 _o = (struct uci_optmap *) ((char *)(_o) + \
60 ucimap_is_alloc(enum ucimap_type type
)
62 switch(type
& UCIMAP_SUBTYPE
) {
71 ucimap_is_fixup(enum ucimap_type type
)
73 switch(type
& UCIMAP_SUBTYPE
) {
82 ucimap_is_simple(enum ucimap_type type
)
84 return ((type
& UCIMAP_TYPE
) == UCIMAP_SIMPLE
);
88 ucimap_is_list(enum ucimap_type type
)
90 return ((type
& UCIMAP_TYPE
) == UCIMAP_LIST
);
94 ucimap_is_list_auto(enum ucimap_type type
)
96 return ucimap_is_list(type
) && !!(type
& UCIMAP_LIST_AUTO
);
100 ucimap_is_custom(enum ucimap_type type
)
102 return ((type
& UCIMAP_SUBTYPE
) == UCIMAP_CUSTOM
);
106 ucimap_section_ptr(struct ucimap_section_data
*sd
)
108 return ((char *) sd
- sd
->sm
->smap_offset
);
111 static inline struct ucimap_section_data
*
112 ucimap_ptr_section(struct uci_sectionmap
*sm
, void *ptr
) {
113 ptr
= (char *) ptr
+ sm
->smap_offset
;
117 static inline union ucimap_data
*
118 ucimap_get_data(struct ucimap_section_data
*sd
, struct uci_optmap
*om
)
122 data
= (char *) ucimap_section_ptr(sd
) + om
->offset
;
127 ucimap_init(struct uci_map
*map
)
131 map
->fixup_tail
= &map
->fixup
;
132 map
->sdata_tail
= &map
->sdata
;
137 ucimap_add_alloc(struct ucimap_section_data
*sd
, void *ptr
)
139 struct ucimap_alloc
*a
= &sd
->allocmap
[sd
->allocmap_len
++];
144 ucimap_free_section(struct uci_map
*map
, struct ucimap_section_data
*sd
)
149 section
= ucimap_section_ptr(sd
);
154 sd
->sm
->free(map
, section
);
156 for (i
= 0; i
< sd
->allocmap_len
; i
++) {
157 free(sd
->allocmap
[i
].ptr
);
160 if (sd
->alloc_custom
) {
161 for (i
= 0; i
< sd
->alloc_custom_len
; i
++) {
162 struct ucimap_alloc_custom
*a
= &sd
->alloc_custom
[i
];
163 a
->om
->free(a
->section
, a
->om
, a
->ptr
);
165 free(sd
->alloc_custom
);
173 ucimap_cleanup(struct uci_map
*map
)
175 struct ucimap_section_data
*sd
;
177 for (sd
= map
->sdata
; sd
; sd
= sd
->next
) {
178 ucimap_free_section(map
, sd
);
183 ucimap_find_section(struct uci_map
*map
, struct ucimap_fixup
*f
)
185 struct ucimap_section_data
*sd
;
187 for (sd
= map
->sdata
; sd
; sd
= sd
->next
) {
190 if (strcmp(f
->name
, sd
->section_name
) != 0)
192 return ucimap_section_ptr(sd
);
194 for (sd
= map
->pending
; sd
; sd
= sd
->next
) {
197 if (strcmp(f
->name
, sd
->section_name
) != 0)
199 return ucimap_section_ptr(sd
);
204 static union ucimap_data
*
205 ucimap_list_append(struct ucimap_list
*list
)
207 if (unlikely(list
->size
<= list
->n_items
)) {
208 /* should not happen */
209 DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list
->size
);
212 return &list
->item
[list
->n_items
++];
217 ucimap_handle_fixup(struct uci_map
*map
, struct ucimap_fixup
*f
)
219 void *ptr
= ucimap_find_section(map
, f
);
220 struct ucimap_list
*list
;
221 union ucimap_data
*data
;
226 switch(f
->type
& UCIMAP_TYPE
) {
231 list
= f
->data
->list
;
232 data
= ucimap_list_append(f
->data
->list
);
243 ucimap_free_item(struct ucimap_section_data
*sd
, void *item
)
245 struct ucimap_alloc_custom
*ac
;
246 struct ucimap_alloc
*a
;
247 void *ptr
= *((void **) item
);
253 *((void **)item
) = NULL
;
254 for (i
= 0, a
= sd
->allocmap
; i
< sd
->allocmap_len
; i
++, a
++) {
258 if (i
!= sd
->allocmap_len
- 1)
259 a
->ptr
= sd
->allocmap
[sd
->allocmap_len
- 1].ptr
;
265 for (i
= 0, ac
= sd
->alloc_custom
; i
< sd
->alloc_custom_len
; i
++, ac
++) {
269 if (i
!= sd
->alloc_custom_len
- 1)
270 memcpy(ac
, &sd
->alloc_custom
[sd
->alloc_custom_len
- 1],
271 sizeof(struct ucimap_alloc_custom
));
273 ac
->om
->free(ac
->section
, ac
->om
, ac
->ptr
);
274 sd
->alloc_custom_len
--;
280 ucimap_resize_list(struct ucimap_section_data
*sd
, struct ucimap_list
**list
, int items
)
282 struct ucimap_list
*new;
283 struct ucimap_alloc
*a
;
285 int size
= sizeof(struct ucimap_list
) + items
* sizeof(union ucimap_data
);
288 new = calloc(1, size
);
290 ucimap_add_alloc(sd
, new);
294 for (i
= 0, a
= sd
->allocmap
; i
< sd
->allocmap_len
; i
++, a
++) {
303 if (items
> (*list
)->size
)
304 offset
= (items
- (*list
)->size
) * sizeof(union ucimap_data
);
306 a
->ptr
= realloc(a
->ptr
, size
);
308 memset((char *) a
->ptr
+ offset
, 0, size
- offset
);
318 ucimap_add_fixup(struct ucimap_section_data
*sd
, union ucimap_data
*data
, struct uci_optmap
*om
, const char *str
)
320 struct ucimap_fixup
*f
, tmp
;
321 struct uci_map
*map
= sd
->map
;
323 tmp
.sm
= om
->data
.sm
;
327 if (ucimap_handle_fixup(map
, &tmp
))
330 f
= malloc(sizeof(struct ucimap_fixup
));
334 memcpy(f
, &tmp
, sizeof(tmp
));
336 *map
->fixup_tail
= f
;
337 map
->fixup_tail
= &f
->next
;
341 ucimap_add_custom_alloc(struct ucimap_section_data
*sd
, struct uci_optmap
*om
, void *ptr
)
343 struct ucimap_alloc_custom
*a
= &sd
->alloc_custom
[sd
->alloc_custom_len
++];
345 a
->section
= ucimap_section_ptr(sd
);
351 ucimap_add_value(union ucimap_data
*data
, struct uci_optmap
*om
, struct ucimap_section_data
*sd
, const char *str
)
353 union ucimap_data tdata
= *data
;
359 if (ucimap_is_list(om
->type
) && !ucimap_is_fixup(om
->type
)) {
360 data
= ucimap_list_append(data
->list
);
365 switch(om
->type
& UCIMAP_SUBTYPE
) {
367 if ((om
->data
.s
.maxlen
> 0) &&
368 (strlen(str
) > om
->data
.s
.maxlen
))
373 ucimap_add_alloc(sd
, s
);
376 if (!strcmp(str
, "on"))
378 else if (!strcmp(str
, "1"))
380 else if (!strcmp(str
, "enabled"))
382 else if (!strcmp(str
, "off"))
384 else if (!strcmp(str
, "0"))
386 else if (!strcmp(str
, "disabled"))
394 lval
= strtol(str
, &eptr
, om
->data
.i
.base
);
395 if (lval
< INT_MIN
|| lval
> INT_MAX
)
398 if (!eptr
|| *eptr
== '\0')
399 tdata
.i
= (int) lval
;
404 ucimap_add_fixup(sd
, data
, om
, str
);
407 tdata
.s
= (char *) data
;
411 if (om
->parse(ucimap_section_ptr(sd
), om
, &tdata
, str
) < 0)
413 if (ucimap_is_custom(om
->type
) && om
->free
) {
414 if (tdata
.ptr
!= data
->ptr
)
415 ucimap_add_custom_alloc(sd
, om
, data
->ptr
);
418 if (ucimap_is_custom(om
->type
))
420 memcpy(data
, &tdata
, sizeof(union ucimap_data
));
425 ucimap_convert_list(union ucimap_data
*data
, struct uci_optmap
*om
, struct ucimap_section_data
*sd
, const char *str
)
433 ucimap_add_alloc(sd
, s
);
443 while (*s
&& !isspace(*s
))
451 ucimap_add_value(data
, om
, sd
, p
);
456 ucimap_parse_options(struct uci_map
*map
, struct uci_sectionmap
*sm
, struct ucimap_section_data
*sd
, struct uci_section
*s
)
458 struct uci_element
*e
, *l
;
459 struct uci_option
*o
;
460 union ucimap_data
*data
;
462 uci_foreach_element(&s
->options
, e
) {
463 struct uci_optmap
*om
= NULL
, *tmp
;
465 ucimap_foreach_option(sm
, tmp
) {
466 if (strcmp(e
->name
, tmp
->name
) == 0) {
474 data
= ucimap_get_data(sd
, om
);
475 o
= uci_to_option(e
);
476 if ((o
->type
== UCI_TYPE_STRING
) && ucimap_is_simple(om
->type
)) {
477 ucimap_add_value(data
, om
, sd
, o
->v
.string
);
478 } else if ((o
->type
== UCI_TYPE_LIST
) && ucimap_is_list(om
->type
)) {
479 uci_foreach_element(&o
->v
.list
, l
) {
480 ucimap_add_value(data
, om
, sd
, l
->name
);
482 } else if ((o
->type
== UCI_TYPE_STRING
) && ucimap_is_list_auto(om
->type
)) {
483 ucimap_convert_list(data
, om
, sd
, o
->v
.string
);
491 ucimap_add_section_list(struct uci_map
*map
, struct ucimap_section_data
*sd
)
493 sd
->ref
= map
->sdata_tail
;
495 map
->sdata_tail
= &sd
->next
;
499 ucimap_add_section(struct ucimap_section_data
*sd
)
501 struct uci_map
*map
= sd
->map
;
504 if (sd
->sm
->add(map
, ucimap_section_ptr(sd
)) < 0)
505 ucimap_free_section(map
, sd
);
507 ucimap_add_section_list(map
, sd
);
511 static const char *ucimap_type_names
[] = {
512 [UCIMAP_STRING
] = "string",
513 [UCIMAP_INT
] = "integer",
514 [UCIMAP_BOOL
] = "boolean",
515 [UCIMAP_SECTION
] = "section",
516 [UCIMAP_LIST
] = "list",
520 ucimap_get_type_name(int type
)
525 if (ucimap_is_list(type
))
526 return ucimap_type_names
[UCIMAP_LIST
];
528 name
= ucimap_type_names
[type
& UCIMAP_SUBTYPE
];
530 sprintf(buf
, "Unknown (%d)", type
& UCIMAP_SUBTYPE
);
539 ucimap_check_optmap_type(struct uci_sectionmap
*sm
, struct uci_optmap
*om
)
543 if (unlikely(sm
->type_name
!= om
->type_name
) &&
544 unlikely(strcmp(sm
->type_name
, om
->type_name
) != 0)) {
545 DPRINTF("Option '%s' of section type '%s' refereces unknown "
546 "section type '%s', should be '%s'.\n",
547 om
->name
, sm
->type
, om
->type_name
, sm
->type_name
);
551 if (om
->detected_type
< 0)
554 if (ucimap_is_custom(om
->type
))
557 if (ucimap_is_list(om
->type
) !=
558 ucimap_is_list(om
->detected_type
))
561 if (ucimap_is_list(om
->type
))
564 type
= om
->type
& UCIMAP_SUBTYPE
;
569 if (type
!= om
->detected_type
)
580 DPRINTF("Invalid type in option '%s' of section type '%s', "
581 "declared type is %s, detected type is %s\n",
583 ucimap_get_type_name(om
->type
),
584 ucimap_get_type_name(om
->detected_type
));
589 ucimap_count_alloc(struct uci_optmap
*om
, int *n_alloc
, int *n_custom
)
591 if (ucimap_is_alloc(om
->type
))
593 else if (ucimap_is_custom(om
->type
) && om
->free
)
598 ucimap_parse_section(struct uci_map
*map
, struct uci_sectionmap
*sm
, struct ucimap_section_data
*sd
, struct uci_section
*s
)
600 struct uci_optmap
*om
;
604 int n_alloc_custom
= 0;
610 ucimap_foreach_option(sm
, om
) {
611 if (!ucimap_check_optmap_type(sm
, om
))
614 if (ucimap_is_list(om
->type
)) {
615 union ucimap_data
*data
;
616 struct uci_element
*e
;
618 int n_elements_alloc
= 0;
619 int n_elements_custom
= 0;
622 data
= ucimap_get_data(sd
, om
);
623 uci_foreach_element(&s
->options
, e
) {
624 struct uci_option
*o
= uci_to_option(e
);
625 struct uci_element
*tmp
;
627 if (strcmp(e
->name
, om
->name
) != 0)
630 if (o
->type
== UCI_TYPE_LIST
) {
631 uci_foreach_element(&o
->v
.list
, tmp
) {
632 ucimap_count_alloc(om
, &n_elements_alloc
, &n_elements_custom
);
635 } else if ((o
->type
== UCI_TYPE_STRING
) &&
636 ucimap_is_list_auto(om
->type
)) {
637 const char *data
= o
->v
.string
;
639 while (isspace(*data
))
646 ucimap_count_alloc(om
, &n_elements_alloc
, &n_elements_custom
);
648 while (*data
&& !isspace(*data
))
652 /* for the duplicated data string */
658 /* add one more for the ucimap_list */
659 n_alloc
+= n_elements_alloc
+ 1;
660 n_alloc_custom
+= n_elements_custom
;
661 size
= sizeof(struct ucimap_list
) +
662 n_elements
* sizeof(union ucimap_data
);
664 data
->list
= malloc(size
);
668 memset(data
->list
, 0, size
);
669 data
->list
->size
= n_elements
;
671 ucimap_count_alloc(om
, &n_alloc
, &n_alloc_custom
);
675 sd
->allocmap
= calloc(n_alloc
, sizeof(struct ucimap_alloc
));
679 if (n_alloc_custom
> 0) {
680 sd
->alloc_custom
= calloc(n_alloc_custom
, sizeof(struct ucimap_alloc_custom
));
681 if (!sd
->alloc_custom
)
685 section_name
= strdup(s
->e
.name
);
689 sd
->section_name
= section_name
;
691 sd
->cmap
= calloc(1, BITFIELD_SIZE(sm
->n_options
));
695 ucimap_add_alloc(sd
, (void *)section_name
);
696 ucimap_add_alloc(sd
, (void *)sd
->cmap
);
697 ucimap_foreach_option(sm
, om
) {
698 if (!ucimap_is_list(om
->type
))
701 ucimap_add_alloc(sd
, ucimap_get_data(sd
, om
)->list
);
704 section
= ucimap_section_ptr(sd
);
705 err
= sm
->init(map
, section
, s
);
710 ucimap_add_section(sd
);
712 ucimap_add_section_list(map
, sd
);
715 err
= ucimap_parse_options(map
, sm
, sd
, s
);
728 ucimap_free_section(map
, sd
);
733 ucimap_fill_ptr(struct uci_ptr
*ptr
, struct uci_section
*s
, const char *option
)
735 struct uci_package
*p
= s
->package
;
737 memset(ptr
, 0, sizeof(struct uci_ptr
));
739 ptr
->package
= p
->e
.name
;
742 ptr
->section
= s
->e
.name
;
745 ptr
->option
= option
;
746 return uci_lookup_ptr(p
->ctx
, ptr
, NULL
, false);
750 ucimap_set_changed(struct ucimap_section_data
*sd
, void *field
)
752 void *section
= ucimap_section_ptr(sd
);
753 struct uci_sectionmap
*sm
= sd
->sm
;
754 struct uci_optmap
*om
;
755 int ofs
= (char *)field
- (char *)section
;
758 ucimap_foreach_option(sm
, om
) {
759 if (om
->offset
== ofs
) {
760 SET_BIT(sd
->cmap
, i
);
768 ucimap_data_to_string(struct ucimap_section_data
*sd
, struct uci_optmap
*om
, union ucimap_data
*data
)
773 switch(om
->type
& UCIMAP_SUBTYPE
) {
778 sprintf(buf
, "%d", data
->i
);
782 sprintf(buf
, "%d", !!data
->b
);
787 str
= (char *) ucimap_ptr_section(om
->data
.sm
, data
->ptr
)->section_name
;
798 union ucimap_data tdata
;
800 if (ucimap_is_custom(om
->type
)) {
801 tdata
.s
= (char *)data
;
805 if (om
->format(ucimap_section_ptr(sd
), om
, data
, &str
) < 0)
815 ucimap_store_section(struct uci_map
*map
, struct uci_package
*p
, struct ucimap_section_data
*sd
)
817 struct uci_sectionmap
*sm
= sd
->sm
;
818 struct uci_section
*s
= NULL
;
819 struct uci_optmap
*om
;
820 struct uci_element
*e
;
825 uci_foreach_element(&p
->sections
, e
) {
826 if (!strcmp(e
->name
, sd
->section_name
)) {
827 s
= uci_to_section(e
);
832 return UCI_ERR_NOTFOUND
;
834 ucimap_foreach_option(sm
, om
) {
835 union ucimap_data
*data
;
838 data
= ucimap_get_data(sd
, om
);
839 if (!TEST_BIT(sd
->cmap
, i
- 1))
842 ucimap_fill_ptr(&ptr
, s
, om
->name
);
843 if (ucimap_is_list(om
->type
)) {
844 struct ucimap_list
*list
= data
->list
;
848 for (j
= 0; j
< list
->n_items
; j
++) {
849 ptr
.value
= ucimap_data_to_string(sd
, om
, &list
->item
[j
]);
854 ret
= uci_set(s
->package
->ctx
, &ptr
);
857 ret
= uci_add_list(s
->package
->ctx
, &ptr
);
863 ptr
.value
= ucimap_data_to_string(sd
, om
, data
);
867 ret
= uci_set(s
->package
->ctx
, &ptr
);
872 CLR_BIT(sd
->cmap
, i
- 1);
879 ucimap_parse(struct uci_map
*map
, struct uci_package
*pkg
)
881 struct uci_element
*e
;
882 struct ucimap_section_data
*sd
, **sd_tail
;
883 struct ucimap_fixup
*f
;
886 sd_tail
= map
->sdata_tail
;
888 map
->sdata_tail
= &map
->pending
;
889 uci_foreach_element(&pkg
->sections
, e
) {
890 struct uci_section
*s
= uci_to_section(e
);
892 for (i
= 0; i
< map
->n_sections
; i
++) {
893 struct uci_sectionmap
*sm
= map
->sections
[i
];
894 struct ucimap_section_data
*sd
;
896 if (strcmp(s
->type
, map
->sections
[i
]->type
) != 0)
900 sd
= sm
->alloc(map
, sm
, s
);
901 memset(sd
, 0, sizeof(struct ucimap_section_data
));
903 sd
= malloc(sm
->alloc_len
);
904 memset(sd
, 0, sm
->alloc_len
);
909 ucimap_parse_section(map
, sm
, sd
, s
);
914 map
->sdata_tail
= sd_tail
;
919 struct ucimap_fixup
*next
= f
->next
;
920 ucimap_handle_fixup(map
, f
);
924 map
->fixup_tail
= &map
->fixup
;
929 struct ucimap_section_data
*next
= sd
->next
;
930 ucimap_add_section(sd
);