]> mj.ucw.cz Git - libucw.git/blob - lib/conf2.c
the init-hook of the main section inserts a few nodes into the link-list
[libucw.git] / lib / conf2.c
1 /*
2  *      UCW Library -- Reading of configuration files
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2006 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "lib/lib.h"
12 #include "lib/conf2.h"
13 #include "lib/mempool.h"
14 #include "lib/clists.h"
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19
20 /* Memory allocation */
21
22 struct mempool *cf_pool;        // current pool for loading new configuration
23 static struct old_pools {
24   struct old_pools *prev;
25   struct mempool *pool;
26 } *pools;                       // link-list of older cf_pool's
27
28 void *
29 cf_malloc(uns size)
30 {
31   return mp_alloc(cf_pool, size);
32 }
33
34 void *
35 cf_malloc_zero(uns size)
36 {
37   return mp_alloc_zero(cf_pool, size);
38 }
39
40 byte *
41 cf_strdup(byte *s)
42 {
43   return mp_strdup(cf_pool, s);
44 }
45
46 byte *
47 cf_printf(char *fmt, ...)
48 {
49   va_list args;
50   va_start(args, fmt);
51   byte *res = mp_vprintf(cf_pool, fmt, args);
52   va_end(args);
53   return res;
54 }
55
56 /* Undo journal */
57
58 uns cf_need_journal;            // some programs do not need journal
59 static struct journal_item {
60   struct journal_item *prev;
61   byte *ptr;
62   uns len;
63   byte copy[0];
64 } *journal;
65
66 void
67 cf_journal_block(void *ptr, uns len)
68 {
69   if (!cf_need_journal)
70     return;
71   struct journal_item *ji = cf_malloc(sizeof(struct journal_item) + len);
72   ji->prev = journal;
73   ji->ptr = ptr;
74   ji->len = len;
75   memcpy(ji->copy, ptr, len);
76   journal = ji;
77 }
78
79 static void
80 journal_swap(void)
81   // swaps the contents of the memory and the journal, and reverses the list
82 {
83   struct journal_item *curr, *prev, *next;
84   for (next=NULL, curr=journal; curr; next=curr, curr=prev)
85   {
86     prev = curr->prev;
87     curr->prev = next;
88     for (uns i=0; i<curr->len; i++)
89     {
90       byte x = curr->copy[i];
91       curr->copy[i] = curr->ptr[i];
92       curr->ptr[i] = x;
93     }
94   }
95   journal = next;
96 }
97
98 static struct journal_item *
99 journal_new_section(uns new_pool)
100 {
101   if (new_pool)
102     cf_pool = mp_new(1<<14);
103   struct journal_item *oldj = journal;
104   journal = NULL;
105   return oldj;
106 }
107
108 static void
109 journal_commit_section(uns new_pool, struct journal_item *oldj)
110 {
111   if (new_pool)
112   {
113     struct old_pools *p = cf_malloc(sizeof(struct old_pools));
114     p->prev = pools;
115     p->pool = cf_pool;
116     pools = p;
117   }
118   if (oldj)
119   {
120     struct journal_item **j = &journal;
121     while (*j)
122       j = &(*j)->prev;
123     *j = oldj;
124   }
125 }
126
127 static void
128 journal_rollback_section(uns new_pool, struct journal_item *oldj, byte *msg)
129 {
130   if (!cf_need_journal)
131     die("Cannot rollback the configuration, because the journal is disabled.  Error: %s", msg);
132   journal_swap();
133   journal = oldj;
134   if (new_pool)
135   {
136     mp_delete(cf_pool);
137     cf_pool = pools ? pools->pool : NULL;
138   }
139 }
140
141 /* Initialization */
142
143 static struct section {
144   struct section *prev;
145   byte *name;
146   struct cf_section *sec;
147 } *sections;
148
149 void
150 cf_declare_section(byte *name, struct cf_section *sec)
151 {
152   struct section *s = sections;
153   for (; s; s=s->prev)
154     if (!strcasecmp(s->name, name))
155       die("Cannot register cf_section %s twice", name);
156   s = xmalloc(sizeof(struct section));
157   s->prev = sections;
158   s->name = name;
159   s->sec = sec;
160   sections = s;
161 }
162
163 void
164 cf_init_section(byte *name, struct cf_section *sec, void *ptr)
165 {
166   if (sec->size)
167     bzero(ptr, sec->size);
168   for (uns i=0; sec->cfg[i].cls; i++)
169     if (sec->cfg[i].cls == CC_SECTION)
170       cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr);
171     else if (sec->cfg[i].cls == CC_LIST)
172       clist_init(sec->cfg[i].ptr);
173   byte *msg = sec->init(ptr);
174   if (msg)
175     die("Cannot initialize section %s: %s", name, msg);
176 }
177
178 static void
179 global_init(void)
180 {
181   for (struct section *s=sections; s; s=s->prev)
182     cf_init_section(s->name, s->sec, NULL);
183 }
184
185 /* Safe loading and reloading */
186
187 byte *cf_def_file = DEFAULT_CONFIG;
188
189 static byte *load_file(byte *file);
190 static byte *load_string(byte *string);
191
192 byte *
193 cf_reload(byte *file)
194 {
195   journal_swap();
196   struct journal_item *oldj = journal_new_section(1);
197   byte *msg = load_file(file);
198   if (!msg)
199   {
200     for (struct old_pools *p=pools; p; p=pools)
201     {
202       pools = p->prev;
203       mp_delete(p->pool);
204     }
205     journal_commit_section(1, NULL);
206   }
207   else
208   {
209     journal_rollback_section(1, oldj, msg);
210     journal_swap();
211   }
212   return msg;
213 }
214
215 byte *
216 cf_load(byte *file)
217 {
218   struct journal_item *oldj = journal_new_section(1);
219   byte *msg = load_file(file);
220   if (!msg)
221     journal_commit_section(1, oldj);
222   else
223     journal_rollback_section(1, oldj, msg);
224   return msg;
225 }
226
227 byte *
228 cf_set(byte *string)
229 {
230   struct journal_item *oldj = journal_new_section(0);
231   byte *msg = load_string(string);
232   if (!msg)
233     journal_commit_section(0, oldj);
234   else
235     journal_rollback_section(0, oldj, msg);
236   return msg;
237 }
238
239 /* Parsers for standard types */
240
241 struct unit {
242   uns name;                     // one-letter name of the unit
243   uns num, den;                 // fraction
244 };
245
246 static const struct unit units[] = {
247   { 'd', 86400, 1 },
248   { 'h', 3600, 1 },
249   { 'k', 1000, 1 },
250   { 'm', 1000000, 1 },
251   { 'g', 1000000000, 1 },
252   { 'K', 1024, 1 },
253   { 'M', 1048576, 1 },
254   { 'G', 1073741824, 1 },
255   { '%', 1, 100 },
256   { 0, 0, 0 }
257 };
258
259 static const struct unit *
260 lookup_unit(byte *value, byte *end, byte **msg)
261 {
262   if (end && *end) {
263     if (end == value || end[1] || *end >= '0' && *end <= '9')
264       *msg = "Invalid number";
265     else {
266       for (const struct unit *u=units; u->name; u++)
267         if (u->name == *end)
268           return u;
269       *msg = "Invalid unit";
270     }
271   }
272   return NULL;
273 }
274
275 static char cf_rngerr[] = "Number out of range";
276
277 byte *
278 cf_parse_int(uns number, byte **pars, int *ptr)
279 {
280   for (uns i=0; i<number; i++)
281   {
282     byte *msg = NULL;
283     if (!*pars[i])
284       msg = "Missing number";
285     else {
286       const struct unit *u;
287       char *end;
288       errno = 0;
289       uns x = strtoul(pars[i], &end, 0);
290       if (errno == ERANGE)
291         msg = cf_rngerr;
292       else if (u = lookup_unit(pars[i], end, &msg)) {
293         u64 y = (u64)x * u->num;
294         if (y % u->den)
295           msg = "Number is not an integer";
296         else {
297           y /= u->den;
298           if (y > 0xffffffff)
299             msg = cf_rngerr;
300           ptr[i] = y;
301         }
302       } else
303         ptr[i] = x;
304     }
305     if (msg)
306       return number==1 ? msg : cf_printf("Item #%d: %s", i+1, msg);
307   }
308   return NULL;
309 }
310
311 byte *
312 cf_parse_u64(uns number, byte **pars, u64 *ptr)
313 {
314   for (uns i=0; i<number; i++)
315   {
316     byte *msg = NULL;
317     if (!*pars[i])
318       msg = "Missing number";
319     else {
320       const struct unit *u;
321       char *end;
322       errno = 0;
323       u64 x = strtoull(pars[i], &end, 0);
324       if (errno == ERANGE)
325         msg = cf_rngerr;
326       else if (u = lookup_unit(pars[i], end, &msg)) {
327         if (x > ~(u64)0 / u->num)
328           msg = "Number out of range";
329         else {
330           x *= u->num;
331           if (x % u->den)
332             msg = "Number is not an integer";
333           else
334             ptr[i] = x / u->den;
335         }
336       } else
337         ptr[i] = x;
338     }
339     if (msg)
340       return number==1 ? msg : cf_printf("Item #%d: %s", i+1, msg);
341   }
342   return NULL;
343 }
344
345 byte *
346 cf_parse_double(uns number, byte **pars, double *ptr)
347 {
348   for (uns i=0; i<number; i++)
349   {
350     byte *msg = NULL;
351     if (!*pars[i])
352       msg = "Missing number";
353     else {
354       const struct unit *u;
355       char *end;
356       errno = 0;
357       double x = strtoul(pars[i], &end, 0);
358       if (errno == ERANGE)
359         msg = cf_rngerr;
360       else if (u = lookup_unit(pars[i], end, &msg))
361         ptr[i] = x * u->num / u->den;
362       else
363         ptr[i] = x;
364     }
365     if (msg)
366       return number==1 ? msg : cf_printf("Item #%d: %s", i+1, msg);
367   }
368   return NULL;
369 }
370
371 static byte *
372 cf_parse_string(uns number, byte **pars, byte **ptr)
373 {
374   for (uns i=0; i<number; i++)
375     ptr[i] = cf_strdup(pars[i]);
376   return NULL;
377 }
378
379 /* Register size of and parser for each basic type */
380 static struct {
381   uns size;
382   void *parser;
383 } parsers[] = {
384   { sizeof(int), cf_parse_int },
385   { sizeof(u64), cf_parse_u64 },
386   { sizeof(double), cf_parse_double },
387   { sizeof(byte*), cf_parse_string }
388 };
389
390 static byte *
391 cf_parse_dyn(uns number, byte **pars, void **ptr, enum cf_type type)
392 {
393   cf_journal_block(ptr, sizeof(void*));
394   *ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
395   * (uns*) (*ptr - parsers[type].size) = number;
396   return ((cf_parser*) parsers[type].parser) (number, pars, *ptr);
397 }
398