]> mj.ucw.cz Git - libucw.git/blob - lib/conf2.c
conf2: resolve an issue concerning variable numbers of parameters
[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 cf_section sections;      // root section
144
145 static struct cf_item *
146 find_subitem(struct cf_section *sec, byte *name)
147 {
148   struct cf_item *ci = sec->cfg;
149   for (; ci->cls; ci++)
150     if (!strcasecmp(ci->name, name))
151       return ci;
152   return ci;
153 }
154
155 void
156 cf_declare_section(byte *name, struct cf_section *sec)
157 {
158   if (!sections.cfg)
159   {
160     sections.size = 50;
161     sections.cfg = xmalloc_zero(sections.size * sizeof(struct cf_item));
162   }
163   struct cf_item *ci = find_subitem(&sections, name);
164   if (ci->cls)
165     die("Cannot register section %s twice", name);
166   ci->cls = CC_SECTION;
167   ci->name = name;
168   ci->number = 1;
169   ci->ptr = NULL;
170   ci->u.sec = sec;
171   ci++;
172   if (ci - sections.cfg >= (int) sections.size)
173   {
174     sections.cfg = xrealloc(sections.cfg, 2*sections.size * sizeof(struct cf_item));
175     bzero(sections.cfg + sections.size, sections.size * sizeof(struct cf_item));
176     sections.size *= 2;
177   }
178 }
179
180 void
181 cf_init_section(byte *name, struct cf_section *sec, void *ptr)
182 {
183   if (sec->size)
184     bzero(ptr, sec->size);
185   for (uns i=0; sec->cfg[i].cls; i++)
186     if (sec->cfg[i].cls == CC_SECTION)
187       cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr);
188     else if (sec->cfg[i].cls == CC_LIST)
189       clist_init(sec->cfg[i].ptr);
190   byte *msg = sec->init(ptr);
191   if (msg)
192     die("Cannot initialize section %s: %s", name, msg);
193 }
194
195 static void
196 global_init(void)
197 {
198   for (struct cf_item *ci=sections.cfg; ci->cls; ci++)
199     cf_init_section(ci->name, ci->u.sec, NULL);
200 }
201
202 static struct cf_item *
203 find_item(struct cf_section *curr_sec, byte *name, byte **msg)
204 {
205   *msg = NULL;
206   if (name[0] == '.')
207     name++;
208   else if (strchr(name, '.'))
209     curr_sec = &sections;
210   if (!curr_sec)
211     return NULL;
212   byte *c;
213   while ((c = strchr(name, '.')))
214   {
215     *c++ = 0;
216     struct cf_item *ci = find_subitem(curr_sec, name);
217     if (ci->cls != CC_SECTION)
218     {
219       *msg = cf_printf("Item %s %s", name, !ci->cls ? "does not exist" : "is not a subsection");
220       return NULL;
221     }
222     curr_sec = ci->u.sec;
223     name = c;
224   }
225   struct cf_item *ci = find_subitem(curr_sec, name);
226   if (!ci->cls)
227   {
228     *msg = "Unknown item";
229     return NULL;
230   }
231   return ci;
232 }
233
234 /* Safe loading and reloading */
235
236 byte *cf_def_file = DEFAULT_CONFIG;
237
238 #ifndef DEFAULT_CONFIG
239 #define DEFAULT_CONFIG NULL
240 #endif
241
242 byte *cfdeffile = DEFAULT_CONFIG;
243
244 static byte *load_file(byte *file);
245 static byte *load_string(byte *string);
246
247 byte *
248 cf_reload(byte *file)
249 {
250   journal_swap();
251   struct journal_item *oldj = journal_new_section(1);
252   byte *msg = load_file(file);
253   if (!msg)
254   {
255     for (struct old_pools *p=pools; p; p=pools)
256     {
257       pools = p->prev;
258       mp_delete(p->pool);
259     }
260     journal_commit_section(1, NULL);
261   }
262   else
263   {
264     journal_rollback_section(1, oldj, msg);
265     journal_swap();
266   }
267   return msg;
268 }
269
270 byte *
271 cf_load(byte *file)
272 {
273   struct journal_item *oldj = journal_new_section(1);
274   byte *msg = load_file(file);
275   if (!msg)
276     journal_commit_section(1, oldj);
277   else
278     journal_rollback_section(1, oldj, msg);
279   return msg;
280 }
281
282 byte *
283 cf_set(byte *string)
284 {
285   struct journal_item *oldj = journal_new_section(0);
286   byte *msg = load_string(string);
287   if (!msg)
288     journal_commit_section(0, oldj);
289   else
290     journal_rollback_section(0, oldj, msg);
291   return msg;
292 }
293
294 /* Parsers for standard types */
295
296 struct unit {
297   uns name;                     // one-letter name of the unit
298   uns num, den;                 // fraction
299 };
300
301 static const struct unit units[] = {
302   { 'd', 86400, 1 },
303   { 'h', 3600, 1 },
304   { 'k', 1000, 1 },
305   { 'm', 1000000, 1 },
306   { 'g', 1000000000, 1 },
307   { 'K', 1024, 1 },
308   { 'M', 1048576, 1 },
309   { 'G', 1073741824, 1 },
310   { '%', 1, 100 },
311   { 0, 0, 0 }
312 };
313
314 static const struct unit *
315 lookup_unit(byte *value, byte *end, byte **msg)
316 {
317   if (end && *end) {
318     if (end == value || end[1] || *end >= '0' && *end <= '9')
319       *msg = "Invalid number";
320     else {
321       for (const struct unit *u=units; u->name; u++)
322         if (u->name == *end)
323           return u;
324       *msg = "Invalid unit";
325     }
326   }
327   return NULL;
328 }
329
330 static char cf_rngerr[] = "Number out of range";
331
332 byte *
333 cf_parse_int(byte *str, int *ptr)
334 {
335   byte *msg = NULL;
336   if (!*str)
337     msg = "Missing number";
338   else {
339     const struct unit *u;
340     char *end;
341     errno = 0;
342     uns x = strtoul(str, &end, 0);
343     if (errno == ERANGE)
344       msg = cf_rngerr;
345     else if (u = lookup_unit(str, end, &msg)) {
346       u64 y = (u64)x * u->num;
347       if (y % u->den)
348         msg = "Number is not an integer";
349       else {
350         y /= u->den;
351         if (y > 0xffffffff)
352           msg = cf_rngerr;
353         *ptr = y;
354       }
355     } else
356       *ptr = x;
357   }
358   return msg;
359 }
360
361 byte *
362 cf_parse_u64(byte *str, u64 *ptr)
363 {
364   byte *msg = NULL;
365   if (!*str)
366     msg = "Missing number";
367   else {
368     const struct unit *u;
369     char *end;
370     errno = 0;
371     u64 x = strtoull(str, &end, 0);
372     if (errno == ERANGE)
373       msg = cf_rngerr;
374     else if (u = lookup_unit(str, end, &msg)) {
375       if (x > ~(u64)0 / u->num)
376         msg = "Number out of range";
377       else {
378         x *= u->num;
379         if (x % u->den)
380           msg = "Number is not an integer";
381         else
382           *ptr = x / u->den;
383       }
384     } else
385       *ptr = x;
386   }
387   return msg;
388 }
389
390 byte *
391 cf_parse_double(byte *str, double *ptr)
392 {
393   byte *msg = NULL;
394   if (!*str)
395     msg = "Missing number";
396   else {
397     const struct unit *u;
398     char *end;
399     errno = 0;
400     double x = strtoul(str, &end, 0);
401     if (errno == ERANGE)
402       msg = cf_rngerr;
403     else if (u = lookup_unit(str, end, &msg))
404       *ptr = x * u->num / u->den;
405     else
406       *ptr = x;
407   }
408   return msg;
409 }
410
411 static byte *
412 cf_parse_string(byte *str, byte **ptr)
413 {
414   *ptr = cf_strdup(str);
415   return NULL;
416 }
417
418 /* Register size of and parser for each basic type */
419
420 typedef byte *cf_basic_parser(byte *str, void *ptr);
421 static struct {
422   uns size;
423   void *parser;
424 } parsers[] = {
425   { sizeof(int), cf_parse_int },
426   { sizeof(u64), cf_parse_u64 },
427   { sizeof(double), cf_parse_double },
428   { sizeof(byte*), cf_parse_string }
429 };
430
431 static byte *
432 cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type)
433 {
434   for (uns i=0; i<number; i++)
435   {
436     byte *msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * parsers[type].size);
437     if (msg)
438       return cf_printf("Cannot parse item %d: %s", i+1, msg);
439   }
440   return NULL;
441 }
442
443 /* Interpreter */
444
445 enum operation {
446   OP_CLEAR,                     // list
447   OP_SET,                       // basic attribute (static, dynamic, parsed), section, list
448   OP_APPEND,                    // dynamic array, list
449   OP_PREPEND,                   // dynamic array, list
450   OP_REMOVE,                    // list
451   OP_OPEN = 0x80                // here we only have an opening brace
452 };
453
454 #define MAX_STACK_SIZE  100
455 static struct item_stack {
456   struct cf_section *sec;       // nested section
457   void *base_ptr;               // because original pointers are often relative
458   enum operation op;            // it is performed when a closing brace is encountered
459 } stack[MAX_STACK_SIZE];
460 static uns level;
461
462 static byte *
463 interpret_set_dynamic(struct cf_item *item, int number, byte **pars, void **ptr)
464 {
465   enum cf_type type = item->u.type;
466   cf_journal_block(ptr, sizeof(void*));
467   // boundary checks done by the caller
468   *ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
469   * (uns*) (*ptr - parsers[type].size) = number;
470   return cf_parse_ary(number, pars, *ptr, type);
471 }
472
473 static byte *
474 interpret_add_dynamic(struct cf_item *item, int number, byte **pars, int *processed, void **ptr, enum operation op)
475 {
476   enum cf_type type = item->u.type;
477   void *old_p = *ptr;
478   int old_nr = * (int*) (old_p - parsers[type].size);
479   int taken = MIN(number, item->number-old_nr);
480   *processed = taken;
481   // stretch the dynamic array
482   void *new_p = cf_malloc((old_nr + taken + 1) * parsers[type].size) + parsers[type].size;
483   * (uns*) (new_p - parsers[type].size) = old_nr + taken;
484   cf_journal_block(ptr, sizeof(void*));
485   *ptr = new_p;
486   if (op == OP_APPEND)
487   {
488     memcpy(new_p, old_p, old_nr * parsers[type].size);
489     return cf_parse_ary(taken, pars, new_p + old_nr * parsers[type].size, type);
490   }
491   else if (op == OP_PREPEND)
492   {
493     memcpy(new_p + taken * parsers[type].size, old_p, old_nr * parsers[type].size);
494     return cf_parse_ary(taken, pars, new_p, type);
495   }
496   else
497     ASSERT(0);
498 }
499
500 static byte *interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic);
501
502 static byte *
503 interpret_subsection(struct cf_section *sec, int number, byte **pars, int *processed, uns allow_dynamic, void *ptr)
504 {
505   *processed = 0;
506   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
507   {
508     int taken;
509     byte *msg = interpret_set_item(ci, number, pars, &taken, ptr + (addr_int_t) ci->ptr, allow_dynamic && !ci[1].cls);
510     if (msg)
511       return cf_printf("Item %s: %s", ci->name, msg);
512     *processed += taken;
513     number -= taken;
514     pars += taken;
515   }
516   return NULL;
517 }
518
519 static void
520 add_to_list(struct clist *list, struct cnode *node, enum operation op)
521 {
522   cf_journal_block(list, sizeof(struct clist));
523   if (op == OP_APPEND)
524     clist_add_tail(list, node);
525   else if (op == OP_PREPEND)
526     clist_add_head(list, node);
527   else
528     ASSERT(0);
529 }
530
531 static byte *
532 interpret_add_list(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, enum operation op)
533 {
534   struct cf_item *ci = item;
535   uns only_1_item = 0;
536   do
537     for (ci=ci->u.sec->cfg; ci->cls; ci++);
538   while ((--ci)->cls == CC_SECTION);
539   if (ci->cls == CC_DYNAMIC || ci->cls == CC_LIST
540       || ci->cls == CC_PARSER && ci->number < 0)
541     only_1_item = 1;
542   /* If the node contains any dynamic attribute at the end, we suppress
543    * repetition here and pass it instead inside.  */
544   struct cf_section *sec = item->u.sec;
545   *processed = 0;
546   while (number > 0)
547   {
548     void *node = cf_malloc(sec->size);
549     cf_init_section(item->name, sec, node);
550     add_to_list(ptr, node, op);
551     int taken;
552     byte *msg = interpret_subsection(sec, number, pars, &taken, only_1_item, node);
553     if (msg)
554       return msg;
555     *processed += taken;
556     number -= taken;
557     pars += taken;
558     if (only_1_item)
559       break;
560   }
561   return NULL;
562 }
563
564 static byte *
565 interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
566 {
567   int taken;
568   switch (item->cls)
569   {
570     case CC_STATIC:
571       taken = MIN(number, item->number);
572       *processed = taken;
573       cf_journal_block(ptr, taken * parsers[item->u.type].size);
574       return cf_parse_ary(taken, pars, ptr, item->u.type);
575     case CC_DYNAMIC:
576       if (!allow_dynamic)
577         return "Dynamic array cannot be used here";
578       taken = MIN(number, item->number);
579       *processed = taken;
580       return interpret_set_dynamic(item, taken, pars, ptr);
581     case CC_PARSER:
582       if (item->number < 0 && !allow_dynamic)
583         return "Parsers with variable number of parameters cannot be used here";
584       if (item->number > 0 && number < item->number)
585         return "Not enough parameters available for the parser";
586       taken = MIN(number, ABS(item->number));
587       *processed = taken;
588       for (int i=0; i<taken; i++)
589         pars[i] = cf_strdup(pars[i]);
590       return item->u.par(taken, pars, ptr);
591     case CC_SECTION:
592       return interpret_subsection(item->u.sec, number, pars, processed, allow_dynamic, ptr);
593     case CC_LIST:
594       if (!allow_dynamic)
595         return "Lists cannot be used here";
596       return interpret_add_list(item, number, pars, ptr, processed, OP_APPEND);
597     default:
598       ASSERT(0);
599   }
600 }
601
602 static byte *
603 increase_stack(struct cf_item *item, enum operation op)
604 {
605   if (level >= MAX_STACK_SIZE-1)
606     return "Too many nested sections";
607   ++level;
608   if (item)                     // fill in the base pointer
609   {
610     if (item->cls == CC_SECTION)
611       stack[level].base_ptr = stack[level].base_ptr + (addr_int_t) item->ptr;
612     else if (item->cls != CC_LIST)
613     {
614       stack[level].base_ptr = cf_malloc(item->u.sec->size);
615       cf_init_section(item->name, item->u.sec, stack[level].base_ptr);
616     }
617     else
618       return "Opening brace can only be used on sections and lists";
619     stack[level].sec = item->u.sec;
620   }
621   else                          // unknown is also handled here, since we need to trace recursion
622   {
623     stack[level].base_ptr = NULL;
624     stack[level].sec = NULL;
625   }
626   stack[level].op = op;
627   return NULL;
628 }
629
630 static byte *
631 interpret_line(byte *name, enum operation op, int number, byte **pars)
632 {
633   byte *msg;
634   struct cf_item *item = find_item(stack[level].sec, name, &msg);
635   if (op & OP_OPEN)             // the operation will be performed after the closing brace
636     return increase_stack(item, op) ? : msg;
637   if (!item)
638     return msg;
639
640   void *ptr = stack[level].base_ptr + (addr_int_t) item->ptr;
641   if (op == OP_CLEAR)
642   {
643     if (item->cls != CC_LIST)
644       return "The item is not a list";
645     cf_journal_block(ptr, sizeof(struct clist));
646     clist_init(ptr);
647     return NULL;
648   }
649   int taken;                    // process as many parameters as possible
650   if (op == OP_SET)
651     msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
652   else if (item->cls == CC_DYNAMIC)
653     msg = interpret_add_dynamic(item, number, pars, &taken, ptr, op);
654   else if (item->cls == CC_LIST)
655     msg = interpret_add_list(item, number, pars, &taken, ptr, op);
656   else
657     return cf_printf("Operation %d not supported for class %d", op, item->cls);
658   if (msg)
659     return msg;
660   if (taken < number)
661     return cf_printf("Too many parameters: %d>%d", number, taken);
662   return NULL;
663 }
664