ucimap: fix list size initialization, validate list size on fixups as well
[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 tdata.s = (char *) data;
408 break;
409 }
410 if (om->parse) {
411 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
412 return;
413 if (ucimap_is_custom(om->type) && om->free) {
414 if (tdata.ptr != data->ptr)
415 ucimap_add_custom_alloc(sd, om, data->ptr);
416 }
417 }
418 if (ucimap_is_custom(om->type))
419 return;
420 memcpy(data, &tdata, sizeof(union ucimap_data));
421 }
422
423
424 static void
425 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
426 {
427 char *s, *p;
428
429 s = strdup(str);
430 if (!s)
431 return;
432
433 ucimap_add_alloc(sd, s);
434
435 do {
436 while (isspace(*s))
437 s++;
438
439 if (!*s)
440 break;
441
442 p = s;
443 while (*s && !isspace(*s))
444 s++;
445
446 if (isspace(*s)) {
447 *s = 0;
448 s++;
449 }
450
451 ucimap_add_value(data, om, sd, p);
452 } while (*s);
453 }
454
455 static int
456 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
457 {
458 struct uci_element *e, *l;
459 struct uci_option *o;
460 union ucimap_data *data;
461
462 uci_foreach_element(&s->options, e) {
463 struct uci_optmap *om = NULL, *tmp;
464
465 ucimap_foreach_option(sm, tmp) {
466 if (strcmp(e->name, tmp->name) == 0) {
467 om = tmp;
468 break;
469 }
470 }
471 if (!om)
472 continue;
473
474 data = ucimap_get_data(sd, om);
475 o = uci_to_option(e);
476 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
477 ucimap_add_value(data, om, sd, o->v.string);
478 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
479 uci_foreach_element(&o->v.list, l) {
480 ucimap_add_value(data, om, sd, l->name);
481 }
482 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
483 ucimap_convert_list(data, om, sd, o->v.string);
484 }
485 }
486
487 return 0;
488 }
489
490 static void
491 ucimap_add_section_list(struct uci_map *map, struct ucimap_section_data *sd)
492 {
493 sd->ref = map->sdata_tail;
494 *sd->ref = sd;
495 map->sdata_tail = &sd->next;
496 }
497
498 static void
499 ucimap_add_section(struct ucimap_section_data *sd)
500 {
501 struct uci_map *map = sd->map;
502
503 sd->next = NULL;
504 if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
505 ucimap_free_section(map, sd);
506 else
507 ucimap_add_section_list(map, sd);
508 }
509
510 #ifdef UCI_DEBUG
511 static const char *ucimap_type_names[] = {
512 [UCIMAP_STRING] = "string",
513 [UCIMAP_INT] = "integer",
514 [UCIMAP_BOOL] = "boolean",
515 [UCIMAP_SECTION] = "section",
516 [UCIMAP_LIST] = "list",
517 };
518
519 static const char *
520 ucimap_get_type_name(int type)
521 {
522 static char buf[32];
523 const char *name;
524
525 if (ucimap_is_list(type))
526 return ucimap_type_names[UCIMAP_LIST];
527
528 name = ucimap_type_names[type & UCIMAP_SUBTYPE];
529 if (!name) {
530 sprintf(buf, "Unknown (%d)", type & UCIMAP_SUBTYPE);
531 name = buf;
532 }
533
534 return name;
535 }
536 #endif
537
538 static bool
539 ucimap_check_optmap_type(struct uci_sectionmap *sm, struct uci_optmap *om)
540 {
541 unsigned int type;
542
543 if (unlikely(sm->type_name != om->type_name) &&
544 unlikely(strcmp(sm->type_name, om->type_name) != 0)) {
545 DPRINTF("Option '%s' of section type '%s' refereces unknown "
546 "section type '%s', should be '%s'.\n",
547 om->name, sm->type, om->type_name, sm->type_name);
548 return false;
549 }
550
551 if (om->detected_type < 0)
552 return true;
553
554 if (ucimap_is_custom(om->type))
555 return true;
556
557 if (ucimap_is_list(om->type) !=
558 ucimap_is_list(om->detected_type))
559 goto failed;
560
561 if (ucimap_is_list(om->type))
562 return true;
563
564 type = om->type & UCIMAP_SUBTYPE;
565 switch(type) {
566 case UCIMAP_STRING:
567 case UCIMAP_INT:
568 case UCIMAP_BOOL:
569 if (type != om->detected_type)
570 goto failed;
571 break;
572 case UCIMAP_SECTION:
573 goto failed;
574 default:
575 break;
576 }
577 return true;
578
579 failed:
580 DPRINTF("Invalid type in option '%s' of section type '%s', "
581 "declared type is %s, detected type is %s\n",
582 om->name, sm->type,
583 ucimap_get_type_name(om->type),
584 ucimap_get_type_name(om->detected_type));
585 return false;
586 }
587
588 static void
589 ucimap_count_alloc(struct uci_optmap *om, int *n_alloc, int *n_custom)
590 {
591 if (ucimap_is_alloc(om->type))
592 (*n_alloc)++;
593 else if (ucimap_is_custom(om->type) && om->free)
594 (*n_custom)++;
595 }
596
597 int
598 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
599 {
600 struct uci_optmap *om;
601 char *section_name;
602 void *section;
603 int n_alloc = 2;
604 int n_alloc_custom = 0;
605 int err;
606
607 sd->map = map;
608 sd->sm = sm;
609
610 ucimap_foreach_option(sm, om) {
611 if (!ucimap_check_optmap_type(sm, om))
612 continue;
613
614 if (ucimap_is_list(om->type)) {
615 union ucimap_data *data;
616 struct uci_element *e;
617 int n_elements = 0;
618 int n_elements_alloc = 0;
619 int n_elements_custom = 0;
620 int size;
621
622 data = ucimap_get_data(sd, om);
623 uci_foreach_element(&s->options, e) {
624 struct uci_option *o = uci_to_option(e);
625 struct uci_element *tmp;
626
627 if (strcmp(e->name, om->name) != 0)
628 continue;
629
630 if (o->type == UCI_TYPE_LIST) {
631 uci_foreach_element(&o->v.list, tmp) {
632 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
633 n_elements++;
634 }
635 } else if ((o->type == UCI_TYPE_STRING) &&
636 ucimap_is_list_auto(om->type)) {
637 const char *data = o->v.string;
638 do {
639 while (isspace(*data))
640 data++;
641
642 if (!*data)
643 break;
644
645 n_elements++;
646 ucimap_count_alloc(om, &n_elements_alloc, &n_elements_custom);
647
648 while (*data && !isspace(*data))
649 data++;
650 } while (*data);
651
652 /* for the duplicated data string */
653 if (n_elements)
654 n_alloc++;
655 }
656 break;
657 }
658 /* add one more for the ucimap_list */
659 n_alloc += n_elements_alloc + 1;
660 n_alloc_custom += n_elements_custom;
661 size = sizeof(struct ucimap_list) +
662 n_elements * sizeof(union ucimap_data);
663
664 data->list = malloc(size);
665 if (!data->list)
666 goto error_mem;
667
668 memset(data->list, 0, size);
669 data->list->size = n_elements;
670 } else {
671 ucimap_count_alloc(om, &n_alloc, &n_alloc_custom);
672 }
673 }
674
675 sd->allocmap = calloc(n_alloc, sizeof(struct ucimap_alloc));
676 if (!sd->allocmap)
677 goto error_mem;
678
679 if (n_alloc_custom > 0) {
680 sd->alloc_custom = calloc(n_alloc_custom, sizeof(struct ucimap_alloc_custom));
681 if (!sd->alloc_custom)
682 goto error_mem;
683 }
684
685 section_name = strdup(s->e.name);
686 if (!section_name)
687 goto error_mem;
688
689 sd->section_name = section_name;
690
691 sd->cmap = calloc(1, BITFIELD_SIZE(sm->n_options));
692 if (!sd->cmap)
693 goto error_mem;
694
695 ucimap_add_alloc(sd, (void *)section_name);
696 ucimap_add_alloc(sd, (void *)sd->cmap);
697 ucimap_foreach_option(sm, om) {
698 if (!ucimap_is_list(om->type))
699 continue;
700
701 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
702 }
703
704 section = ucimap_section_ptr(sd);
705 err = sm->init(map, section, s);
706 if (err)
707 goto error;
708
709 if (map->parsed) {
710 ucimap_add_section(sd);
711 } else {
712 ucimap_add_section_list(map, sd);
713 }
714
715 err = ucimap_parse_options(map, sm, sd, s);
716 if (err)
717 goto error;
718
719 return 0;
720
721 error_mem:
722 if (sd->allocmap)
723 free(sd->allocmap);
724 free(sd);
725 return UCI_ERR_MEM;
726
727 error:
728 ucimap_free_section(map, sd);
729 return err;
730 }
731
732 static int
733 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
734 {
735 struct uci_package *p = s->package;
736
737 memset(ptr, 0, sizeof(struct uci_ptr));
738
739 ptr->package = p->e.name;
740 ptr->p = p;
741
742 ptr->section = s->e.name;
743 ptr->s = s;
744
745 ptr->option = option;
746 return uci_lookup_ptr(p->ctx, ptr, NULL, false);
747 }
748
749 void
750 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
751 {
752 void *section = ucimap_section_ptr(sd);
753 struct uci_sectionmap *sm = sd->sm;
754 struct uci_optmap *om;
755 int ofs = (char *)field - (char *)section;
756 int i = 0;
757
758 ucimap_foreach_option(sm, om) {
759 if (om->offset == ofs) {
760 SET_BIT(sd->cmap, i);
761 break;
762 }
763 i++;
764 }
765 }
766
767 static char *
768 ucimap_data_to_string(struct ucimap_section_data *sd, struct uci_optmap *om, union ucimap_data *data)
769 {
770 static char buf[32];
771 char *str = NULL;
772
773 switch(om->type & UCIMAP_SUBTYPE) {
774 case UCIMAP_STRING:
775 str = data->s;
776 break;
777 case UCIMAP_INT:
778 sprintf(buf, "%d", data->i);
779 str = buf;
780 break;
781 case UCIMAP_BOOL:
782 sprintf(buf, "%d", !!data->b);
783 str = buf;
784 break;
785 case UCIMAP_SECTION:
786 if (data->ptr)
787 str = (char *) ucimap_ptr_section(om->data.sm, data->ptr)->section_name;
788 else
789 str = "";
790 break;
791 case UCIMAP_CUSTOM:
792 break;
793 default:
794 return NULL;
795 }
796
797 if (om->format) {
798 union ucimap_data tdata;
799
800 if (ucimap_is_custom(om->type)) {
801 tdata.s = (char *)data;
802 data = &tdata;
803 }
804
805 if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
806 return NULL;
807
808 if (!str)
809 str = "";
810 }
811 return str;
812 }
813
814 int
815 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
816 {
817 struct uci_sectionmap *sm = sd->sm;
818 struct uci_section *s = NULL;
819 struct uci_optmap *om;
820 struct uci_element *e;
821 struct uci_ptr ptr;
822 int i = 0;
823 int ret;
824
825 uci_foreach_element(&p->sections, e) {
826 if (!strcmp(e->name, sd->section_name)) {
827 s = uci_to_section(e);
828 break;
829 }
830 }
831 if (!s)
832 return UCI_ERR_NOTFOUND;
833
834 ucimap_foreach_option(sm, om) {
835 union ucimap_data *data;
836
837 i++;
838 data = ucimap_get_data(sd, om);
839 if (!TEST_BIT(sd->cmap, i - 1))
840 continue;
841
842 ucimap_fill_ptr(&ptr, s, om->name);
843 if (ucimap_is_list(om->type)) {
844 struct ucimap_list *list = data->list;
845 bool first = true;
846 int j;
847
848 for (j = 0; j < list->n_items; j++) {
849 ptr.value = ucimap_data_to_string(sd, om, &list->item[j]);
850 if (!ptr.value)
851 continue;
852
853 if (first) {
854 ret = uci_set(s->package->ctx, &ptr);
855 first = false;
856 } else {
857 ret = uci_add_list(s->package->ctx, &ptr);
858 }
859 if (ret)
860 return ret;
861 }
862 } else {
863 ptr.value = ucimap_data_to_string(sd, om, data);
864 if (!ptr.value)
865 continue;
866
867 ret = uci_set(s->package->ctx, &ptr);
868 if (ret)
869 return ret;
870 }
871
872 CLR_BIT(sd->cmap, i - 1);
873 }
874
875 return 0;
876 }
877
878 void
879 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
880 {
881 struct uci_element *e;
882 struct ucimap_section_data *sd, **sd_tail;
883 struct ucimap_fixup *f;
884 int i;
885
886 sd_tail = map->sdata_tail;
887 map->parsed = false;
888 map->sdata_tail = &map->pending;
889 uci_foreach_element(&pkg->sections, e) {
890 struct uci_section *s = uci_to_section(e);
891
892 for (i = 0; i < map->n_sections; i++) {
893 struct uci_sectionmap *sm = map->sections[i];
894 struct ucimap_section_data *sd;
895
896 if (strcmp(s->type, map->sections[i]->type) != 0)
897 continue;
898
899 if (sm->alloc) {
900 sd = sm->alloc(map, sm, s);
901 memset(sd, 0, sizeof(struct ucimap_section_data));
902 } else {
903 sd = malloc(sm->alloc_len);
904 memset(sd, 0, sm->alloc_len);
905 }
906 if (!sd)
907 continue;
908
909 ucimap_parse_section(map, sm, sd, s);
910 }
911 }
912 if (!map->parsed) {
913 map->parsed = true;
914 map->sdata_tail = sd_tail;
915 }
916
917 f = map->fixup;
918 while (f) {
919 struct ucimap_fixup *next = f->next;
920 ucimap_handle_fixup(map, f);
921 free(f);
922 f = next;
923 }
924 map->fixup_tail = &map->fixup;
925 map->fixup = NULL;
926
927 sd = map->pending;
928 while (sd) {
929 struct ucimap_section_data *next = sd->next;
930 ucimap_add_section(sd);
931 sd = next;
932 }
933 map->pending = NULL;
934 }