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