add libucimap
[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 <string.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include "ucimap.h"
19
20
21 int
22 ucimap_init(struct uci_map *map)
23 {
24         INIT_LIST_HEAD(&map->sdata);
25         return 0;
26 }
27
28 static void
29 ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd)
30 {
31         void *section = sd;
32         int i;
33
34         section = (char *) section + sizeof(struct uci_sectmap_data);
35         if (!list_empty(&sd->list))
36                 list_del(&sd->list);
37
38         if (sd->sm->free_section)
39                 sd->sm->free_section(map, section);
40
41         for (i = 0; i < sd->allocmap_len; i++) {
42                 free(sd->allocmap[i]);
43         }
44
45         free(sd->allocmap);
46         free(sd);
47 }
48
49 void
50 ucimap_cleanup(struct uci_map *map)
51 {
52         struct list_head *ptr, *tmp;
53
54         list_for_each_safe(ptr, tmp, &map->sdata) {
55                 struct uci_sectmap_data *sd = list_entry(ptr, struct uci_sectmap_data, list);
56                 ucimap_free_section(map, sd);
57         }
58 }
59
60
61 static int
62 ucimap_parse_options(struct uci_map *map, struct uci_sectmap *sm, struct uci_sectmap_data *sd, struct uci_section *s)
63 {
64         struct uci_element *e;
65         struct uci_option *o;
66         void *section = sd;
67         int i;
68
69         section = (unsigned char *) section + sizeof(struct uci_sectmap_data);
70         uci_foreach_element(&s->options, e) {
71                 struct uci_optmap *om = NULL;
72
73                 for (i = 0; i < sm->n_options; i++) {
74                         if (strcmp(e->name, sm->options[i].name) == 0) {
75                                 om = &sm->options[i];
76                                 break;
77                         }
78                 }
79                 if (!om)
80                         goto next_element;
81
82                 o = uci_to_option(e);
83                 if(o->type != UCI_TYPE_STRING)
84                         goto next_element;
85
86                 switch(om->type) {
87                 case UCIMAP_STRING: {
88                         char **ptr;
89                         if ((om->data.s.maxlen > 0) &&
90                                 (strlen(o->v.string) > om->data.s.maxlen))
91                                 goto next_element;
92
93                         ptr = (char **) ((char *) section + om->offset);
94                         *ptr = strdup(o->v.string);
95                         sd->allocmap[sd->allocmap_len++] = *ptr;
96                         } break;
97                 case UCIMAP_BOOL: {
98                         bool *ptr = (bool *)((char *)section + om->offset);
99                         if (strcmp(o->v.string, "on"))
100                                 *ptr = true;
101                         else if (strcmp(o->v.string, "1"))
102                                 *ptr = true;
103                         else if (strcmp(o->v.string, "enabled"))
104                                 *ptr = true;
105                         else
106                                 *ptr = false;
107                         } break;
108                 case UCIMAP_INT: {
109                         int *ptr = (int *)((char *)section + om->offset);
110                         char *eptr = NULL;
111                         int val;
112
113                         val = strtol(o->v.string, &eptr, om->data.i.base);
114                         if (!eptr || *eptr == '\0')
115                                 *ptr = val;
116                         } break;
117                 }
118 next_element:
119                 continue;
120         }
121
122         return 0;
123 }
124
125
126 static int
127 ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_section *s)
128 {
129         struct uci_sectmap_data *sd = NULL;
130         void *section = NULL;
131         int err;
132
133         sd = malloc(sm->alloc_len + sizeof(struct uci_sectmap_data));
134         if (!sd)
135                 return UCI_ERR_MEM;
136
137         memset(sd, 0, sm->alloc_len + sizeof(struct uci_sectmap_data));
138         INIT_LIST_HEAD(&sd->list);
139
140         sd->sm = sm;
141         sd->allocmap = malloc(sm->n_options * sizeof(void *));
142         if (!sd->allocmap)
143                 goto error_mem;
144
145         sd->section_name = strdup(s->e.name);
146         if (!sd->section_name)
147                 goto error_mem;
148
149         sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
150         if (!sd->cmap)
151                 goto error_mem;
152
153         memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
154         sd->allocmap[sd->allocmap_len++] = (void *)sd->section_name;
155         sd->allocmap[sd->allocmap_len++] = (void *)sd->cmap;
156
157         section = (char *)sd + sizeof(struct uci_sectmap_data);
158
159         err = sm->init_section(map, section, s);
160         if (err)
161                 goto error;
162
163         list_add(&sd->list, &map->sdata);
164         err = ucimap_parse_options(map, sm, sd, s);
165         if (err)
166                 goto error;
167
168         err = sm->add_section(map, section);
169         if (err)
170                 goto error;
171
172         return 0;
173
174 error_mem:
175         if (sd->allocmap)
176                 free(sd->allocmap);
177         free(sd);
178         return UCI_ERR_MEM;
179
180 error:
181         ucimap_free_section(map, sd);
182         return err;
183 }
184
185 static int
186 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
187 {
188         struct uci_package *p = s->package;
189
190         memset(ptr, 0, sizeof(struct uci_ptr));
191
192         ptr->package = p->e.name;
193         ptr->p = p;
194
195         ptr->section = s->e.name;
196         ptr->s = s;
197
198         ptr->option = option;
199         return uci_lookup_ptr(p->ctx, ptr, NULL, false);
200 }
201
202 void
203 ucimap_set_changed(void *section, void *field)
204 {
205         char *sptr = (char *)section - sizeof(struct uci_sectmap_data);
206         struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr;
207         struct uci_sectmap *sm = sd->sm;
208         int ofs = (char *)field - (char *)section;
209         int i;
210
211         for (i = 0; i < sm->n_options; i++) {
212                 if (sm->options[i].offset == ofs) {
213                         SET_BIT(sd->cmap, i);
214                         break;
215                 }
216         }
217 }
218
219 int
220 ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section)
221 {
222         char *sptr = (char *)section - sizeof(struct uci_sectmap_data);
223         struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr;
224         struct uci_sectmap *sm = sd->sm;
225         struct uci_section *s = NULL;
226         struct uci_element *e;
227         struct uci_ptr ptr;
228         int i, ret;
229
230         uci_foreach_element(&p->sections, e) {
231                 if (!strcmp(e->name, sd->section_name)) {
232                         s = uci_to_section(e);
233                         break;
234                 }
235         }
236         if (!s)
237                 return UCI_ERR_NOTFOUND;
238
239         for (i = 0; i < sm->n_options; i++) {
240                 struct uci_optmap *om = &sm->options[i];
241                 static char buf[32];
242                 const char *str = NULL;
243                 void *p = (char *)section + om->offset;
244
245                 if (!TEST_BIT(sd->cmap, i))
246                         continue;
247
248                 ucimap_fill_ptr(&ptr, s, om->name);
249                 switch(om->type) {
250                 case UCIMAP_STRING:
251                         str = *((char **) p);
252                         break;
253                 case UCIMAP_INT:
254                         sprintf(buf, "%d", *((int *) p));
255                         str = buf;
256                         break;
257                 case UCIMAP_BOOL:
258                         sprintf(buf, "%d", !!*((bool *)p));
259                         str = buf;
260                         break;
261                 }
262                 ptr.value = str;
263
264                 ret = uci_set(s->package->ctx, &ptr);
265                 if (ret)
266                         return ret;
267
268                 CLR_BIT(sd->cmap, i);
269         }
270
271         return 0;
272 }
273
274
275 void
276 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
277 {
278         struct uci_element *e;
279         int i;
280
281         uci_foreach_element(&pkg->sections, e) {
282                 struct uci_section *s = uci_to_section(e);
283
284                 for (i = 0; i < map->n_sections; i++) {
285                         if (strcmp(s->type, map->sections[i].type) != 0)
286                                 continue;
287                         ucimap_parse_section(map, &map->sections[i], s);
288                 }
289         }
290 }