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