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