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