33c254894c57c328178ae06860326fd78da124bd
[project/uci.git] / ucimap.c
1 /*
2 * ucimap.c - Library for the Unified Configuration Interface
3 * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org>
4 *
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
8 *
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.
13 */
14
15 /*
16 * This file contains ucimap, an API for mapping UCI to C data structures
17 */
18
19 #include <strings.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include "ucimap.h"
29 #include "uci_internal.h"
30
31 struct ucimap_alloc {
32 void *ptr;
33 };
34
35 struct ucimap_alloc_custom {
36 void *section;
37 struct uci_optmap *om;
38 void *ptr;
39 };
40
41 struct ucimap_fixup {
42 struct ucimap_fixup *next;
43 struct uci_sectionmap *sm;
44 const char *name;
45 enum ucimap_type type;
46 union ucimap_data *data;
47 };
48
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) + \
56 (_sm)->options_size))
57
58
59 static inline bool
60 ucimap_is_alloc(enum ucimap_type type)
61 {
62 return (type & UCIMAP_SUBTYPE) == UCIMAP_STRING;
63 }
64
65 static inline bool
66 ucimap_is_fixup(enum ucimap_type type)
67 {
68 return (type & UCIMAP_SUBTYPE) == UCIMAP_SECTION;
69 }
70
71 static inline bool
72 ucimap_is_simple(enum ucimap_type type)
73 {
74 return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
75 }
76
77 static inline bool
78 ucimap_is_list(enum ucimap_type type)
79 {
80 return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
81 }
82
83 static inline bool
84 ucimap_is_list_auto(enum ucimap_type type)
85 {
86 return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
87 }
88
89 static inline bool
90 ucimap_is_custom(enum ucimap_type type)
91 {
92 return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
93 }
94
95 static inline void *
96 ucimap_section_ptr(struct ucimap_section_data *sd)
97 {
98 return ((char *) sd - sd->sm->smap_offset);
99 }
100
101 static inline struct ucimap_section_data *
102 ucimap_ptr_section(struct uci_sectionmap *sm, void *ptr) {
103 ptr = (char *) ptr + sm->smap_offset;
104 return ptr;
105 }
106
107 static inline union ucimap_data *
108 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
109 {
110 void *data;
111
112 data = (char *) ucimap_section_ptr(sd) + om->offset;
113 return data;
114 }
115
116 int
117 ucimap_init(struct uci_map *map)
118 {
119 map->fixup = NULL;
120 map->sdata = NULL;
121 map->fixup_tail = &map->fixup;
122 map->sdata_tail = &map->sdata;
123 return 0;
124 }
125
126 static void
127 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
128 {
129 struct ucimap_alloc *a = &sd->allocmap[sd->allocmap_len++];
130 a->ptr = ptr;
131 }
132
133 void
134 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
135 {
136 void *section;
137 int i;
138
139 section = ucimap_section_ptr(sd);
140 if (sd->ref)
141 *sd->ref = sd->next;
142
143 if (sd->sm->free)
144 sd->sm->free(map, section);
145
146 for (i = 0; i < sd->allocmap_len; i++) {
147 free(sd->allocmap[i].ptr);
148 }
149
150 if (sd->alloc_custom) {
151 for (i = 0; i < sd->alloc_custom_len; i++) {
152 struct ucimap_alloc_custom *a = &sd->alloc_custom[i];
153 a->om->free(a->section, a->om, a->ptr);
154 }
155 free(sd->alloc_custom);
156 }
157
158 free(sd->allocmap);
159 free(sd);
160 }
161
162 void
163 ucimap_cleanup(struct uci_map *map)
164 {
165 struct ucimap_section_data *sd;
166
167 for (sd = map->sdata; sd; sd = sd->next) {
168 ucimap_free_section(map, sd);
169 }
170 }
171
172 static void *
173 ucimap_find_section(struct uci_map *map, struct ucimap_fixup *f)
174 {
175 struct ucimap_section_data *sd;
176
177 for (sd = map->sdata; sd; sd = sd->next) {
178 if (sd->sm != f->sm)
179 continue;
180 if (strcmp(f->name, sd->section_name) != 0)
181 continue;
182 return ucimap_section_ptr(sd);
183 }
184 for (sd = map->pending; sd; sd = sd->next) {
185 if (sd->sm != f->sm)
186 continue;
187 if (strcmp(f->name, sd->section_name) != 0)
188 continue;
189 return ucimap_section_ptr(sd);
190 }
191 return NULL;
192 }
193
194 static union ucimap_data *
195 ucimap_list_append(struct ucimap_list *list)
196 {
197 if (unlikely(list->size <= list->n_items)) {
198 /* should not happen */
199 DPRINTF("ERROR: overflow while filling a list (size=%d)\n", list->size);
200 return NULL;
201 }
202 return &list->item[list->n_items++];
203 }
204
205
206 static bool
207 ucimap_handle_fixup(struct uci_map *map, struct ucimap_fixup *f)
208 {
209 void *ptr = ucimap_find_section(map, f);
210 union ucimap_data *data;
211
212 if (!ptr)
213 return false;
214
215 switch(f->type & UCIMAP_TYPE) {
216 case UCIMAP_SIMPLE:
217 f->data->ptr = ptr;
218 break;
219 case UCIMAP_LIST:
220 data = ucimap_list_append(f->data->list);
221 if (!data)
222 return false;
223
224 data->ptr = ptr;
225 break;
226 }
227 return true;
228 }
229
230 void
231 ucimap_free_item(struct ucimap_section_data *sd, void *item)
232 {
233 struct ucimap_alloc_custom *ac;
234 struct ucimap_alloc *a;
235 void *ptr = *((void **) item);
236 int i;
237
238 if (!ptr)
239 return;
240
241 *((void **)item) = NULL;
242 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
243 if (a->ptr != ptr)
244 continue;
245
246 if (i != sd->allocmap_len - 1)
247 a->ptr = sd->allocmap[sd->allocmap_len - 1].ptr;
248
249 sd->allocmap_len--;
250 return;
251 }
252
253 for (i = 0, ac = sd->alloc_custom; i < sd->alloc_custom_len; i++, ac++) {
254 if (ac->ptr != ptr)
255 continue;
256
257 if (i != sd->alloc_custom_len - 1)
258 memcpy(ac, &sd->alloc_custom[sd->alloc_custom_len - 1],
259 sizeof(struct ucimap_alloc_custom));
260
261 ac->om->free(ac->section, ac->om, ac->ptr);
262 sd->alloc_custom_len--;
263 return;
264 }
265 }
266
267 int
268 ucimap_resize_list(struct ucimap_section_data *sd, struct ucimap_list **list, int items)
269 {
270 struct ucimap_list *new;
271 struct ucimap_alloc *a;
272 int i, offset = 0;
273 int size = sizeof(struct ucimap_list) + items * sizeof(union ucimap_data);
274
275 if (!*list) {
276 new = calloc(1, size);
277
278 ucimap_add_alloc(sd, new);
279 goto set;
280 }
281
282 for (i = 0, a = sd->allocmap; i < sd->allocmap_len; i++, a++) {
283 if (a->ptr != *list)
284 continue;
285
286 goto realloc;
287 }
288 return -ENOENT;
289
290 realloc:
291 if (items > (*list)->size)
292 offset = (items - (*list)->size) * sizeof(union ucimap_data);
293
294 a->ptr = realloc(a->ptr, size);
295 if (offset)
296 memset((char *) a->ptr + offset, 0, size - offset);
297 new = a->ptr;
298
299 set:
300 new->size = items;
301 *list = new;
302 return 0;
303 }
304
305 static void
306 ucimap_add_fixup(struct ucimap_section_data *sd, union ucimap_data *data, struct uci_optmap *om, const char *str)
307 {
308 struct ucimap_fixup *f, tmp;
309 struct uci_map *map = sd->map;
310
311 tmp.sm = om->data.sm;
312 tmp.name = str;
313 tmp.type = om->type;
314 tmp.data = data;
315 if (ucimap_handle_fixup(map, &tmp))
316 return;
317
318 f = malloc(sizeof(struct ucimap_fixup));
319 if (!f)
320 return;
321
322 memcpy(f, &tmp, sizeof(tmp));
323 f->next = NULL;
324 *map->fixup_tail = f;
325 map->fixup_tail = &f->next;
326 }
327
328 static void
329 ucimap_add_custom_alloc(struct ucimap_section_data *sd, struct uci_optmap *om, void *ptr)
330 {
331 struct ucimap_alloc_custom *a = &sd->alloc_custom[sd->alloc_custom_len++];
332
333 a->section = ucimap_section_ptr(sd);
334 a->om = om;
335 a->ptr = ptr;
336 }
337
338 static void
339 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
340 {
341 union ucimap_data tdata = *data;
342 char *eptr = NULL;
343 long lval;
344 char *s;
345 int val;
346
347 if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type)) {
348 data = ucimap_list_append(data->list);
349 if (!data)
350 return;
351 }
352
353 switch(om->type & UCIMAP_SUBTYPE) {
354 case UCIMAP_STRING:
355 if ((om->data.s.maxlen > 0) &&
356 (strlen(str) > om->data.s.maxlen))
357 return;
358
359 s = strdup(str);
360 tdata.s = s;
361 ucimap_add_alloc(sd, s);
362 break;
363 case UCIMAP_BOOL:
364 if (!strcmp(str, "on"))
365 val = true;
366 else if (!strcmp(str, "1"))
367 val = true;
368 else if (!strcmp(str, "enabled"))
369 val = true;
370 else if (!strcmp(str, "off"))
371 val = false;
372 else if (!strcmp(str, "0"))
373 val = false;
374 else if (!strcmp(str, "disabled"))
375 val = false;
376 else
377 return;
378
379 tdata.b = val;
380 break;
381 case UCIMAP_INT:
382 lval = strtol(str, &eptr, om->data.i.base);
383 if (lval < INT_MIN || lval > INT_MAX)
384 return;
385
386 if (!eptr || *eptr == '\0')
387 tdata.i = (int) lval;
388 else
389 return;
390 break;
391 case UCIMAP_SECTION:
392 ucimap_add_fixup(sd, data, om, str);
393 return;
394 case UCIMAP_CUSTOM:
395 break;
396 }
397 if (om->parse) {
398 if (om->parse(ucimap_section_ptr(sd), om, data, str) < 0)
399 return;
400 if (ucimap_is_custom(om->type) && om->free) {
401 if (tdata.ptr != data->ptr)
402 ucimap_add_custom_alloc(sd, om, data->ptr);
403 }
404 }
405 if (ucimap_is_custom(om->type))
406 return;
407 memcpy(data, &tdata, sizeof(union ucimap_data));
408 }
409
410
411 static void
412 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
413 {
414 char *s, *p;
415
416 s = strdup(str);
417 if (!s)
418 return;
419
420 ucimap_add_alloc(sd, s);
421
422 do {
423 while (isspace(*s))
424 s++;
425
426 if (!*s)
427 break;
428
429 p = s;
430 while (*s && !isspace(*s))
431 s++;
432
433 if (isspace(*s)) {
434 *s = 0;
435 s++;
436 }
437
438 ucimap_add_value(data, om, sd, p);
439 } while (*s);
440 }
441
442 static int
443 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
444 {
445 struct uci_element *e, *l;
446 struct uci_option *o;
447 union ucimap_data *data;
448
449 uci_foreach_element(&s->options, e) {
450 struct uci_optmap *om = NULL, *tmp;
451
452 ucimap_foreach_option(sm, tmp) {
453 if (strcmp(e->name, tmp->name) == 0) {
454 om = tmp;
455 break;
456 }
457 }
458 if (!om)
459 continue;
460
461 data = ucimap_get_data(sd, om);
462 o = uci_to_option(e);
463 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
464 ucimap_add_value(data, om, sd, o->v.string);
465 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
466 uci_foreach_element(&o->v.list, l) {
467 ucimap_add_value(data, om, sd, l->name);
468 }
469 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
470 ucimap_convert_list(data, om, sd, o->v.string);
471 }
472 }
473
474 return 0;
475 }
476
477 static void
478 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
479 {
480 sd->ref = map->sdata_tail;
481 *sd->ref = sd;
482 map->sdata_tail = &sd->next;
483 }
484
485 static void
486 ucimap_add_section(struct ucimap_section_data *sd)
487 {
488 struct uci_map *map = sd->map;
489
490 sd->next = NULL;
491 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
492 ucimap_free_section(map, sd);
493 else
494 ucimap_add_section_list(map, sd);
495 }
496
497 #ifdef UCI_DEBUG
498 static const char *ucimap_type_names[] = {
499 [UCIMAP_STRING] = "string",
500 [UCIMAP_INT] = "integer",
501 [UCIMAP_BOOL] = "boolean",
502 [UCIMAP_SECTION] = "section",
503 [UCIMAP_LIST] = "list",
504 };
505
506 static const char *
507 ucimap_get_type_name(int type)
508 {
509 static char buf[32];
510 const char *name;
511
512 if (ucimap_is_list(type))
513 return ucimap_type_names[UCIMAP_LIST];
514
515 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
516 if (!name) {
517 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
518 name = buf;
519 }
520
521 return name;
522 }
523 #endif
524
525 static bool
526 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
527 {
528 unsigned int type;
529
530 if (unlikely(sm->type_name != om->type_name) &&
531 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
532 DPRINTF("Option '%s' of section type '%s' refereces unknown "
533 "section type '%s', should be '%s'.\n",
534 om->name, sm->type, om->type_name, sm->type_name);
535 return false;
536 }
537
538 if (om->detected_type < 0)
539 return true;
540
541 if (ucimap_is_custom(om->type))
542 return true;
543
544 if (ucimap_is_list(om->type) !=
545 ucimap_is_list(om->detected_type))
546 goto failed;
547
548 if (ucimap_is_list(om->type))
549 return true;
550
551 type = om->type & UCIMAP_SUBTYPE;
552 switch(type) {
553 case UCIMAP_STRING:
554 case UCIMAP_INT:
555 case UCIMAP_BOOL:
556 if (type != om->detected_type)
557 goto failed;
558 break;
559 case UCIMAP_SECTION:
560 goto failed;
561 default:
562 break;
563 }
564 return true;
565
566 failed:
567 DPRINTF("Invalid type in option '%s' of section type '%s', "
568 "declared type is %s, detected type is %s\n",
569 om->name, sm->type,
570 ucimap_get_type_name(om->type),
571 ucimap_get_type_name(om->detected_type));
572 return false;
573 }
574
575 static void
576 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
577 {
578 if (ucimap_is_alloc(om->type))
579 (*n_alloc)++;
580 else if (ucimap_is_custom(om->type) && om->free)
581 (*n_custom)++;
582 }
583
584 int
585 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
586 {
587 struct uci_optmap *om;
588 char *section_name;
589 void *section;
590 int n_alloc = 2;
591 int n_alloc_custom = 0;
592 int err;
593
594 sd->map = map;
595 sd->sm = sm;
596
597 ucimap_foreach_option(sm, om) {
598 if (!ucimap_check_optmap_type(sm, om))
599 continue;
600
601 if (ucimap_is_list(om->type)) {
602 union ucimap_data *data;
603 struct uci_element *e;
604 int n_elements = 0;
605 int n_elements_alloc = 0;
606 int n_elements_custom = 0;
607 int size;
608
609 data = ucimap_get_data(sd, om);
610 uci_foreach_element(&s->options, e) {
611 struct uci_option *o = uci_to_option(e);
612 struct uci_element *tmp;
613
614 if (strcmp(e->name, om->name) != 0)
615 continue;
616
617 if (o->type == UCI_TYPE_LIST) {
618 uci_foreach_element(&o->v.list, tmp) {
619 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
620 n_elements++;
621 }
622 } else if ((o->type == UCI_TYPE_STRING) &&
623 ucimap_is_list_auto(om->type)) {
624 const char *data = o->v.string;
625 do {
626 while (isspace(*data))
627 data++;
628
629 if (!*data)
630 break;
631
632 n_elements++;
633 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
634
635 while (*data && !isspace(*data))
636 data++;
637 } while (*data);
638
639 /* for the duplicated data string */
640 if (n_elements)
641 n_alloc++;
642 }
643 break;
644 }
645 /* add one more for the ucimap_list */
646 n_alloc += n_elements_alloc + 1;
647 n_alloc_custom += n_elements_custom;
648 size = sizeof(struct ucimap_list) +
649 n_elements * sizeof(union ucimap_data);
650
651 data->list = malloc(size);
652 if (!data->list)
653 goto error_mem;
654
655 memset(data->list, 0, size);
656 data->list->size = n_elements;
657 } else {
658 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
659 }
660 }
661
662 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
663 if (!sd->allocmap)
664 goto error_mem;
665
666 if (n_alloc_custom > 0) {
667 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
668 if (!sd->alloc_custom)
669 goto error_mem;
670 }
671
672 section_name = strdup(s->e.name);
673 if (!section_name)
674 goto error_mem;
675
676 sd->section_name = section_name;
677
678 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
679 if (!sd->cmap)
680 goto error_mem;
681
682 ucimap_add_alloc(sd, (void *)section_name);
683 ucimap_add_alloc(sd, (void *)sd->cmap);
684 ucimap_foreach_option(sm, om) {
685 if (!ucimap_is_list(om->type))
686 continue;
687
688 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
689 }
690
691 section = ucimap_section_ptr(sd);
692 err = sm->init(map, section, s);
693 if (err)
694 goto error;
695
696 if (map->parsed) {
697 ucimap_add_section(sd);
698 } else {
699 ucimap_add_section_list(map, sd);
700 }
701
702 err = ucimap_parse_options(map, sm, sd, s);
703 if (err)
704 goto error;
705
706 return 0;
707
708 error_mem:
709 if (sd->allocmap)
710 free(sd->allocmap);
711 free(sd);
712 return UCI_ERR_MEM;
713
714 error:
715 ucimap_free_section(map, sd);
716 return err;
717 }
718
719 static int
720 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
721 {
722 struct uci_package *p = s->package;
723
724 memset(ptr, 0, sizeof(struct uci_ptr));
725
726 ptr->package = p->e.name;
727 ptr->p = p;
728
729 ptr->section = s->e.name;
730 ptr->s = s;
731
732 ptr->option = option;
733 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
734 }
735
736 void
737 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
738 {
739 void *section = ucimap_section_ptr(sd);
740 struct uci_sectionmap *sm = sd->sm;
741 struct uci_optmap *om;
742 int ofs = (char *)field - (char *)section;
743 int i = 0;
744
745 ucimap_foreach_option(sm, om) {
746 if (om->offset == ofs) {
747 SET_BIT(sd->cmap, i);
748 break;
749 }
750 i++;
751 }
752 }
753
754 static char *
755 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
756 {
757 static char buf[32];
758 char *str = NULL;
759
760 switch(om->type & UCIMAP_SUBTYPE) {
761 case UCIMAP_STRING:
762 str = data->s;
763 break;
764 case UCIMAP_INT:
765 sprintf(buf, "%d", data->i);
766 str = buf;
767 break;
768 case UCIMAP_BOOL:
769 sprintf(buf, "%d", !!data->b);
770 str = buf;
771 break;
772 case UCIMAP_SECTION:
773 if (data->ptr)
774 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
775 else
776 str = "";
777 break;
778 case UCIMAP_CUSTOM:
779 break;
780 default:
781 return NULL;
782 }
783
784 if (om->format) {
785 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
786 return NULL;
787
788 if (!str)
789 str = "";
790 }
791 return str;
792 }
793
794 int
795 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
796 {
797 struct uci_sectionmap *sm = sd->sm;
798 struct uci_section *s = NULL;
799 struct uci_optmap *om;
800 struct uci_element *e;
801 struct uci_ptr ptr;
802 int i = 0;
803 int ret;
804
805 uci_foreach_element(&p->sections, e) {
806 if (!strcmp(e->name, sd->section_name)) {
807 s = uci_to_section(e);
808 break;
809 }
810 }
811 if (!s)
812 return UCI_ERR_NOTFOUND;
813
814 ucimap_foreach_option(sm, om) {
815 union ucimap_data *data;
816
817 i++;
818 data = ucimap_get_data(sd, om);
819 if (!TEST_BIT(sd->cmap, i - 1))
820 continue;
821
822 ucimap_fill_ptr(&ptr, s, om->name);
823 if (ucimap_is_list(om->type)) {
824 struct ucimap_list *list = data->list;
825 bool first = true;
826 int j;
827
828 for (j = 0; j < list->n_items; j++) {
829 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
830 if (!ptr.value)
831 continue;
832
833 if (first) {
834 ret = uci_set(s->package->ctx, &ptr);
835 first = false;
836 } else {
837 ret = uci_add_list(s->package->ctx, &ptr);
838 }
839 if (ret)
840 return ret;
841 }
842 } else {
843 ptr.value = ucimap_data_to_string(sd, om, data);
844 if (!ptr.value)
845 continue;
846
847 ret = uci_set(s->package->ctx, &ptr);
848 if (ret)
849 return ret;
850 }
851
852 CLR_BIT(sd->cmap, i - 1);
853 }
854
855 return 0;
856 }
857
858 void
859 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
860 {
861 struct uci_element *e;
862 struct ucimap_section_data *sd, **sd_tail;
863 struct ucimap_fixup *f;
864 int i;
865
866 sd_tail = map->sdata_tail;
867 map->parsed = false;
868 map->sdata_tail = &map->pending;
869 uci_foreach_element(&pkg->sections, e) {
870 struct uci_section *s = uci_to_section(e);
871
872 for (i = 0; i < map->n_sections; i++) {
873 struct uci_sectionmap *sm = map->sections[i];
874 struct ucimap_section_data *sd;
875
876 if (strcmp(s->type, map->sections[i]->type) != 0)
877 continue;
878
879 if (sm->alloc) {
880 sd = sm->alloc(map, sm, s);
881 memset(sd, 0, sizeof(struct ucimap_section_data));
882 } else {
883 sd = malloc(sm->alloc_len);
884 memset(sd, 0, sm->alloc_len);
885 sd = ucimap_ptr_section(sm, sd);
886 }
887 if (!sd)
888 continue;
889
890 ucimap_parse_section(map, sm, sd, s);
891 }
892 }
893 if (!map->parsed) {
894 map->parsed = true;
895 map->sdata_tail = sd_tail;
896 }
897
898 f = map->fixup;
899 while (f) {
900 struct ucimap_fixup *next = f->next;
901 ucimap_handle_fixup(map, f);
902 free(f);
903 f = next;
904 }
905 map->fixup_tail = &map->fixup;
906 map->fixup = NULL;
907
908 sd = map->pending;
909 while (sd) {
910 struct ucimap_section_data *next = sd->next;
911 ucimap_add_section(sd);
912 sd = next;
913 }
914 map->pending = NULL;
915 }