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 }