build ucimap-example in make all
[project/uci.git] / ucimap.c
1 /*
2  * ucimap - library for mapping uci sections into data structures
3  * Copyright (C) 2008 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 General Public License version 2
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 #include <strings.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <limits.h>
20 #include <ctype.h>
21 #include "ucimap.h"
22
23 struct uci_alloc {
24         enum ucimap_type type;
25         union {
26                 void **ptr;
27         } data;
28 };
29
30 struct uci_fixup {
31         struct list_head list;
32         struct uci_sectionmap *sm;
33         const char *name;
34         enum ucimap_type type;
35         union ucimap_data *data;
36 };
37
38 #define ucimap_foreach_option(_sm, _o) \
39         if (!(_sm)->options_size) \
40                 (_sm)->options_size = sizeof(struct uci_optmap); \
41         for (_o = &(_sm)->options[0]; \
42                  ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
43                         (_sm)->options_size * (_sm)->n_options); \
44                  _o = (struct uci_optmap *) ((char *)(_o) + \
45                         (_sm)->options_size))
46
47
48 static inline bool
49 ucimap_is_alloc(enum ucimap_type type)
50 {
51         switch(type & UCIMAP_SUBTYPE) {
52         case UCIMAP_STRING:
53                 return true;
54         default:
55                 return false;
56         }
57 }
58
59 static inline bool
60 ucimap_is_fixup(enum ucimap_type type)
61 {
62         switch(type & UCIMAP_SUBTYPE) {
63         case UCIMAP_SECTION:
64                 return true;
65         default:
66                 return false;
67         }
68 }
69
70 static inline bool
71 ucimap_is_simple(enum ucimap_type type)
72 {
73         return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
74 }
75
76 static inline bool
77 ucimap_is_list(enum ucimap_type type)
78 {
79         return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
80 }
81
82 static inline bool
83 ucimap_is_list_auto(enum ucimap_type type)
84 {
85         return ucimap_is_list(type) && !!(type & UCIMAP_LIST_AUTO);
86 }
87
88 static inline bool
89 ucimap_is_custom(enum ucimap_type type)
90 {
91         return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
92 }
93
94 static inline void *
95 ucimap_section_ptr(struct ucimap_section_data *sd)
96 {
97         return ((char *) sd - sd->sm->smap_offset);
98 }
99
100 static inline union ucimap_data *
101 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
102 {
103         void *data;
104
105         data = (char *) ucimap_section_ptr(sd) + om->offset;
106         return data;
107 }
108
109 int
110 ucimap_init(struct uci_map *map)
111 {
112         INIT_LIST_HEAD(&map->pending);
113         INIT_LIST_HEAD(&map->sdata);
114         INIT_LIST_HEAD(&map->fixup);
115         return 0;
116 }
117
118 static void
119 ucimap_free_item(struct uci_alloc *a)
120 {
121         switch(a->type & UCIMAP_TYPE) {
122         case UCIMAP_SIMPLE:
123         case UCIMAP_LIST:
124                 free(a->data.ptr);
125                 break;
126         }
127 }
128
129 static void
130 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
131 {
132         struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
133         a->type = UCIMAP_SIMPLE;
134         a->data.ptr = ptr;
135 }
136
137 static void
138 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
139 {
140         void *section;
141         int i;
142
143         section = ucimap_section_ptr(sd);
144         if (!list_empty(&sd->list))
145                 list_del(&sd->list);
146
147         if (sd->sm->free)
148                 sd->sm->free(map, section);
149
150         for (i = 0; i < sd->allocmap_len; i++) {
151                 ucimap_free_item(&sd->allocmap[i]);
152         }
153
154         free(sd->allocmap);
155         free(sd);
156 }
157
158 void
159 ucimap_cleanup(struct uci_map *map)
160 {
161         struct list_head *ptr, *tmp;
162
163         list_for_each_safe(ptr, tmp, &map->sdata) {
164                 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
165                 ucimap_free_section(map, sd);
166         }
167 }
168
169 static void *
170 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
171 {
172         struct ucimap_section_data *sd;
173         struct list_head *p;
174
175         list_for_each(p, &map->sdata) {
176                 sd = list_entry(p, struct ucimap_section_data, list);
177                 if (sd->sm != f->sm)
178                         continue;
179                 if (strcmp(f->name, sd->section_name) != 0)
180                         continue;
181                 return ucimap_section_ptr(sd);
182         }
183         list_for_each(p, &map->pending) {
184                 sd = list_entry(p, struct ucimap_section_data, list);
185                 if (sd->sm != f->sm)
186                         continue;
187                 if (strcmp(f->name, sd->section_name) != 0)
188                         continue;
189                 return ucimap_section_ptr(sd);
190         }
191         return NULL;
192 }
193
194 static bool
195 ucimap_handle_fixup(struct uci_map *map, struct uci_fixup *f)
196 {
197         void *ptr = ucimap_find_section(map, f);
198         struct ucimap_list *list;
199
200         if (!ptr)
201                 return false;
202
203         switch(f->type & UCIMAP_TYPE) {
204         case UCIMAP_SIMPLE:
205                 f->data->ptr = ptr;
206                 break;
207         case UCIMAP_LIST:
208                 list = f->data->list;
209                 list->item[list->n_items++].ptr = ptr;
210                 break;
211         }
212         return true;
213 }
214
215 static void
216 ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
217 {
218         struct uci_fixup *f, tmp;
219
220         INIT_LIST_HEAD(&tmp.list);
221         tmp.sm = om->data.sm;
222         tmp.name = str;
223         tmp.type = om->type;
224         tmp.data = data;
225         if (ucimap_handle_fixup(map, &tmp))
226                 return;
227
228         f = malloc(sizeof(struct uci_fixup));
229         if (!f)
230                 return;
231
232         memcpy(f, &tmp, sizeof(tmp));
233         list_add_tail(&f->list, &map->fixup);
234 }
235
236 static void
237 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
238 {
239         union ucimap_data tdata = *data;
240         char *eptr = NULL;
241         long lval;
242         char *s;
243         int val;
244
245         if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
246                 data = &data->list->item[data->list->n_items++];
247
248         switch(om->type & UCIMAP_SUBTYPE) {
249         case UCIMAP_STRING:
250                 if ((om->data.s.maxlen > 0) &&
251                         (strlen(str) > om->data.s.maxlen))
252                         return;
253
254                 s = strdup(str);
255                 tdata.s = s;
256                 ucimap_add_alloc(sd, s);
257                 break;
258         case UCIMAP_BOOL:
259                 if (!strcmp(str, "on"))
260                         val = true;
261                 else if (!strcmp(str, "1"))
262                         val = true;
263                 else if (!strcmp(str, "enabled"))
264                         val = true;
265                 else if (!strcmp(str, "off"))
266                         val = false;
267                 else if (!strcmp(str, "0"))
268                         val = false;
269                 else if (!strcmp(str, "disabled"))
270                         val = false;
271                 else
272                         return;
273
274                 tdata.b = val;
275                 break;
276         case UCIMAP_INT:
277                 lval = strtol(str, &eptr, om->data.i.base);
278                 if (lval < INT_MIN || lval > INT_MAX)
279                         return;
280
281                 if (!eptr || *eptr == '\0')
282                         tdata.i = (int) lval;
283                 else
284                         return;
285                 break;
286         case UCIMAP_SECTION:
287                 ucimap_add_fixup(sd->map, data, om, str);
288                 return;
289         case UCIMAP_CUSTOM:
290                 tdata.s = (char *) data;
291                 break;
292         }
293         if (om->parse) {
294                 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
295                         return;
296         }
297         if (ucimap_is_custom(om->type))
298                 return;
299         memcpy(data, &tdata, sizeof(union ucimap_data));
300 }
301
302
303 static void
304 ucimap_convert_list(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
305 {
306         char *s, *p;
307
308         s = strdup(str);
309         if (!s)
310                 return;
311
312         ucimap_add_alloc(sd, s);
313
314         do {
315                 while (isspace(*s))
316                         s++;
317
318                 if (!*s)
319                         break;
320
321                 p = s;
322                 while (*s && !isspace(*s))
323                         s++;
324
325                 if (isspace(*s)) {
326                         *s = 0;
327                         s++;
328                 }
329
330                 ucimap_add_value(data, om, sd, p);
331         } while (*s);
332 }
333
334 static int
335 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
336 {
337         struct uci_element *e, *l;
338         struct uci_option *o;
339         union ucimap_data *data;
340
341         uci_foreach_element(&s->options, e) {
342                 struct uci_optmap *om = NULL, *tmp;
343
344                 ucimap_foreach_option(sm, tmp) {
345                         if (strcmp(e->name, tmp->name) == 0) {
346                                 om = tmp;
347                                 break;
348                         }
349                 }
350                 if (!om)
351                         continue;
352
353                 data = ucimap_get_data(sd, om);
354                 o = uci_to_option(e);
355                 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
356                         ucimap_add_value(data, om, sd, o->v.string);
357                 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
358                         uci_foreach_element(&o->v.list, l) {
359                                 ucimap_add_value(data, om, sd, l->name);
360                         }
361                 } else if ((o->type == UCI_TYPE_STRING) && ucimap_is_list_auto(om->type)) {
362                         ucimap_convert_list(data, om, sd, o->v.string);
363                 }
364         }
365
366         return 0;
367 }
368
369 static void
370 ucimap_add_section(struct ucimap_section_data *sd)
371 {
372         struct uci_map *map = sd->map;
373
374         if (sd->sm->add(map, ucimap_section_ptr(sd)) < 0)
375                 ucimap_free_section(map, sd);
376         else
377                 list_add_tail(&sd->list, &map->sdata);
378 }
379
380
381 int
382 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
383 {
384         struct uci_optmap *om;
385         char *section_name;
386         void *section;
387         int n_alloc = 2;
388         int err;
389
390         INIT_LIST_HEAD(&sd->list);
391         sd->map = map;
392         sd->sm = sm;
393
394         ucimap_foreach_option(sm, om) {
395                 if (ucimap_is_list(om->type)) {
396                         union ucimap_data *data;
397                         struct uci_element *e;
398                         int n_elements = 0;
399                         int size;
400
401                         data = ucimap_get_data(sd, om);
402                         uci_foreach_element(&s->options, e) {
403                                 struct uci_option *o = uci_to_option(e);
404                                 struct uci_element *tmp;
405
406                                 if (strcmp(e->name, om->name) != 0)
407                                         continue;
408
409                                 if (o->type == UCI_TYPE_LIST) {
410                                         uci_foreach_element(&o->v.list, tmp) {
411                                                 n_elements++;
412                                         }
413                                 } else if ((o->type == UCI_TYPE_STRING) &&
414                                            ucimap_is_list_auto(om->type)) {
415                                         const char *data = o->v.string;
416                                         do {
417                                                 while (isspace(*data))
418                                                         data++;
419
420                                                 if (!*data)
421                                                         break;
422
423                                                 n_elements++;
424
425                                                 while (*data && !isspace(*data))
426                                                         data++;
427                                         } while (*data);
428
429                                         /* for the duplicated data string */
430                                         if (n_elements > 0)
431                                                 n_alloc++;
432                                 }
433                                 break;
434                         }
435                         /* add one more for the ucimap_list */
436                         n_alloc += n_elements + 1;
437                         size = sizeof(struct ucimap_list) +
438                                 n_elements * sizeof(union ucimap_data);
439                         data->list = malloc(size);
440                         memset(data->list, 0, size);
441                 } else if (ucimap_is_alloc(om->type)) {
442                         n_alloc++;
443                 }
444         }
445
446         sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
447         if (!sd->allocmap)
448                 goto error_mem;
449
450         section_name = strdup(s->e.name);
451         if (!section_name)
452                 goto error_mem;
453
454         sd->section_name = section_name;
455
456         sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
457         if (!sd->cmap)
458                 goto error_mem;
459
460         memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
461         ucimap_add_alloc(sd, (void *)section_name);
462         ucimap_add_alloc(sd, (void *)sd->cmap);
463         ucimap_foreach_option(sm, om) {
464                 if (!ucimap_is_list(om->type))
465                         continue;
466
467                 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
468         }
469
470         section = ucimap_section_ptr(sd);
471         err = sm->init(map, section, s);
472         if (err)
473                 goto error;
474
475         if (map->parsed) {
476                 ucimap_add_section(sd);
477         } else {
478                 list_add_tail(&sd->list, &map->pending);
479         }
480
481         err = ucimap_parse_options(map, sm, sd, s);
482         if (err)
483                 goto error;
484
485         return 0;
486
487 error_mem:
488         if (sd->allocmap)
489                 free(sd->allocmap);
490         free(sd);
491         return UCI_ERR_MEM;
492
493 error:
494         ucimap_free_section(map, sd);
495         return err;
496 }
497
498 static int
499 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
500 {
501         struct uci_package *p = s->package;
502
503         memset(ptr, 0, sizeof(struct uci_ptr));
504
505         ptr->package = p->e.name;
506         ptr->p = p;
507
508         ptr->section = s->e.name;
509         ptr->s = s;
510
511         ptr->option = option;
512         return uci_lookup_ptr(p->ctx, ptr, NULL, false);
513 }
514
515 void
516 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
517 {
518         void *section = ucimap_section_ptr(sd);
519         struct uci_sectionmap *sm = sd->sm;
520         struct uci_optmap *om;
521         int ofs = (char *)field - (char *)section;
522         int i = 0;
523
524         ucimap_foreach_option(sm, om) {
525                 if (om->offset == ofs) {
526                         SET_BIT(sd->cmap, i);
527                         break;
528                 }
529                 i++;
530         }
531 }
532
533 int
534 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
535 {
536         struct uci_sectionmap *sm = sd->sm;
537         struct uci_section *s = NULL;
538         struct uci_optmap *om;
539         struct uci_element *e;
540         struct uci_ptr ptr;
541         int i = 0;
542         int ret;
543
544         uci_foreach_element(&p->sections, e) {
545                 if (!strcmp(e->name, sd->section_name)) {
546                         s = uci_to_section(e);
547                         break;
548                 }
549         }
550         if (!s)
551                 return UCI_ERR_NOTFOUND;
552
553         ucimap_foreach_option(sm, om) {
554                 union ucimap_data *data;
555                 static char buf[32];
556                 char *str = NULL;
557
558                 i++;
559                 if (ucimap_is_list(om->type))
560                         continue;
561
562                 data = ucimap_get_data(sd, om);
563                 if (!TEST_BIT(sd->cmap, i - 1))
564                         continue;
565
566                 ucimap_fill_ptr(&ptr, s, om->name);
567                 switch(om->type & UCIMAP_SUBTYPE) {
568                 case UCIMAP_STRING:
569                         str = data->s;
570                         break;
571                 case UCIMAP_INT:
572                         sprintf(buf, "%d", data->i);
573                         str = buf;
574                         break;
575                 case UCIMAP_BOOL:
576                         sprintf(buf, "%d", !!data->b);
577                         str = buf;
578                         break;
579                 case UCIMAP_CUSTOM:
580                         break;
581                 default:
582                         continue;
583                 }
584                 if (om->format) {
585                         union ucimap_data tdata, *data;
586
587                         data = ucimap_get_data(sd, om);
588                         if (ucimap_is_custom(om->type)) {
589                                 tdata.s = (char *)data;
590                                 data = &tdata;
591                         }
592
593                         if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
594                                 continue;
595                 }
596                 if (!str)
597                         continue;
598                 ptr.value = str;
599
600                 ret = uci_set(s->package->ctx, &ptr);
601                 if (ret)
602                         return ret;
603
604                 CLR_BIT(sd->cmap, i - 1);
605         }
606
607         return 0;
608 }
609
610 void
611 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
612 {
613         struct uci_element *e;
614         struct list_head *p, *tmp;
615         int i;
616
617         INIT_LIST_HEAD(&map->fixup);
618         uci_foreach_element(&pkg->sections, e) {
619                 struct uci_section *s = uci_to_section(e);
620
621                 for (i = 0; i < map->n_sections; i++) {
622                         struct uci_sectionmap *sm = map->sections[i];
623                         struct ucimap_section_data *sd;
624
625                         if (strcmp(s->type, map->sections[i]->type) != 0)
626                                 continue;
627
628                         if (sm->alloc) {
629                                 sd = sm->alloc(map, sm, s);
630                                 memset(sd, 0, sizeof(struct ucimap_section_data));
631                         } else {
632                                 sd = malloc(sm->alloc_len);
633                                 memset(sd, 0, sm->alloc_len);
634                         }
635                         if (!sd)
636                                 continue;
637
638                         ucimap_parse_section(map, sm, sd, s);
639                 }
640         }
641         map->parsed = true;
642
643         list_for_each_safe(p, tmp, &map->fixup) {
644                 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
645                 ucimap_handle_fixup(map, f);
646                 list_del(&f->list);
647                 free(f);
648         }
649
650         list_for_each_safe(p, tmp, &map->pending) {
651                 struct ucimap_section_data *sd;
652                 sd = list_entry(p, struct ucimap_section_data, list);
653
654                 list_del_init(&sd->list);
655                 ucimap_add_section(sd);
656         }
657 }