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