]> mj.ucw.cz Git - libucw.git/blob - ucw/conf-intr.c
Conf: Dynamic arrays re-implemented as gary's
[libucw.git] / ucw / conf-intr.c
1 /*
2  *      UCW Library -- Configuration files: interpreter
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2014 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 <ucw/lib.h>
12 #include <ucw/conf.h>
13 #include <ucw/getopt.h>
14 #include <ucw/conf-internal.h>
15 #include <ucw/clists.h>
16 #include <ucw/gary.h>
17
18 #include <string.h>
19 #include <stdio.h>
20
21 #define TRY(f)  do { char *_msg = f; if (_msg) return _msg; } while (0)
22
23 /* Register size of and parser for each basic type */
24
25 static char *
26 cf_parse_string(char *str, char **ptr)
27 {
28   *ptr = cf_strdup(str);
29   return NULL;
30 }
31
32 typedef char *cf_basic_parser(char *str, void *ptr);
33 static struct {
34   uns size;
35   void *parser;
36 } parsers[] = {
37   { sizeof(int), cf_parse_int },
38   { sizeof(u64), cf_parse_u64 },
39   { sizeof(double), cf_parse_double },
40   { sizeof(u32), cf_parse_ip },
41   { sizeof(char*), cf_parse_string },
42   { sizeof(int), NULL },                        // lookups are parsed extra
43   { 0, NULL },                                  // user-defined types are parsed extra
44 };
45
46 inline uns
47 cf_type_size(enum cf_type type, struct cf_user_type *utype)
48 {
49   if (type < CT_USER)
50     return parsers[type].size;
51   else
52     return utype->size;
53 }
54
55 static char *
56 cf_parse_lookup(char *str, int *ptr, const char * const *t)
57 {
58   const char * const *n = t;
59   uns total_len = 0;
60   while (*n && strcasecmp(*n, str)) {
61     total_len += strlen(*n) + 2;
62     n++;
63   }
64   if (*n) {
65     *ptr = n - t;
66     return NULL;
67   }
68   char *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
69   c += sprintf(err, "Invalid value %s, possible values are: ", str);
70   for (n=t; *n; n++)
71     c+= sprintf(c, "%s, ", *n);
72   if (*t)
73     c[-2] = 0;
74   *ptr = -1;
75   return err;
76 }
77
78 static char *
79 cf_parse_ary(uns number, char **pars, void *ptr, enum cf_type type, union cf_union *u)
80 {
81   for (uns i=0; i<number; i++)
82   {
83     char *msg;
84     uns size = cf_type_size(type, u->utype);
85     if (type < CT_LOOKUP)
86       msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size);
87     else if (type == CT_LOOKUP)
88       msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup);
89     else if (type == CT_USER)
90       msg = u->utype->parser(pars[i], ptr + i * size);
91     else
92       ASSERT(0);
93     if (msg)
94       return number > 1 ? cf_printf("Item %d: %s", i+1, msg) : msg;
95   }
96   return NULL;
97 }
98
99 /* Interpreter */
100
101 #define T(x) #x,
102 char *cf_op_names[] = { CF_OPERATIONS };
103 #undef T
104 char *cf_type_names[] = { "int", "u64", "double", "ip", "string", "lookup", "user" };
105
106 static char *
107 interpret_set_dynamic(struct cf_item *item, int number, char **pars, void **ptr)
108 {
109   enum cf_type type = item->type;
110   uns size = cf_type_size(type, item->u.utype);
111   cf_journal_block(ptr, sizeof(void*));
112   // boundary checks done by the caller
113   struct gary_allocator *a = gary_new_allocator_mp(cf_get_pool());      // FIXME: One copy should be enough
114   *ptr = gary_init(size, number, a);
115   return cf_parse_ary(number, pars, *ptr, type, &item->u);
116 }
117
118 static char *
119 interpret_add_dynamic(struct cf_item *item, int number, char **pars, int *processed, void **ptr, enum cf_operation op)
120 {
121   enum cf_type type = item->type;
122   void *old_p = *ptr;
123   uns size = cf_type_size(item->type, item->u.utype);
124   ASSERT(size >= sizeof(uns));
125   int old_nr = old_p ? GARY_SIZE(old_p) : 0;
126   int taken = MIN(number, ABS(item->number)-old_nr);
127   *processed = taken;
128   // stretch the dynamic array
129   struct gary_allocator *a = gary_new_allocator_mp(cf_get_pool());      // FIXME: One copy should be enough
130   void *new_p = gary_init(size, old_nr + taken, a);
131   cf_journal_block(ptr, sizeof(void*));
132   *ptr = new_p;
133   if (op == OP_APPEND) {
134     memcpy(new_p, old_p, old_nr * size);
135     return cf_parse_ary(taken, pars, new_p + old_nr * size, type, &item->u);
136   } else if (op == OP_PREPEND) {
137     memcpy(new_p + taken * size, old_p, old_nr * size);
138     return cf_parse_ary(taken, pars, new_p, type, &item->u);
139   } else
140     return cf_printf("Dynamic arrays do not support operation %s", cf_op_names[op]);
141 }
142
143 static char *interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uns allow_dynamic);
144
145 static char *
146 interpret_section(struct cf_section *sec, int number, char **pars, int *processed, void *ptr, uns allow_dynamic)
147 {
148   cf_add_dirty(sec, ptr);
149   *processed = 0;
150   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
151   {
152     int taken;
153     char *msg = interpret_set_item(ci, number, pars, &taken, ptr + (uintptr_t) ci->ptr, allow_dynamic && !ci[1].cls);
154     if (msg)
155       return cf_printf("Item %s: %s", ci->name, msg);
156     *processed += taken;
157     number -= taken;
158     pars += taken;
159     if (!number)                // stop parsing, because many parsers would otherwise complain that number==0
160       break;
161   }
162   return NULL;
163 }
164
165 static void
166 add_to_list(cnode *where, cnode *new_node, enum cf_operation op)
167 {
168   switch (op)
169   {
170     case OP_EDIT:               // editation has been done in-place
171       break;
172     case OP_REMOVE:
173       CF_JOURNAL_VAR(where->prev->next);
174       CF_JOURNAL_VAR(where->next->prev);
175       clist_remove(where);
176       break;
177     case OP_AFTER:              // implementation dependent (prepend_head = after(list)), and where==list, see clists.h:74
178     case OP_PREPEND:
179     case OP_COPY:
180       CF_JOURNAL_VAR(where->next->prev);
181       CF_JOURNAL_VAR(where->next);
182       clist_insert_after(new_node, where);
183       break;
184     case OP_BEFORE:             // implementation dependent (append_tail = before(list))
185     case OP_APPEND:
186     case OP_SET:
187       CF_JOURNAL_VAR(where->prev->next);
188       CF_JOURNAL_VAR(where->prev);
189       clist_insert_before(new_node, where);
190       break;
191     default:
192       ASSERT(0);
193   }
194 }
195
196 static char *
197 interpret_add_list(struct cf_item *item, int number, char **pars, int *processed, void *ptr, enum cf_operation op)
198 {
199   if (op >= OP_REMOVE)
200     return cf_printf("You have to open a block for operation %s", cf_op_names[op]);
201   if (!number)
202     return "Nothing to add to the list";
203   struct cf_section *sec = item->u.sec;
204   *processed = 0;
205   uns index = 0;
206   while (number > 0)
207   {
208     void *node = cf_malloc(sec->size);
209     cf_init_section(item->name, sec, node, 1);
210     add_to_list(ptr, node, op);
211     int taken;
212     /* If the node contains any dynamic attribute at the end, we suppress
213      * auto-repetition here and pass the flag inside instead.  */
214     index++;
215     char *msg = interpret_section(sec, number, pars, &taken, node, sec->flags & SEC_FLAG_DYNAMIC);
216     if (msg)
217       return sec->flags & SEC_FLAG_DYNAMIC ? msg : cf_printf("Node %d of list %s: %s", index, item->name, msg);
218     *processed += taken;
219     number -= taken;
220     pars += taken;
221     if (sec->flags & SEC_FLAG_DYNAMIC)
222       break;
223   }
224   return NULL;
225 }
226
227 static char *
228 interpret_add_bitmap(struct cf_item *item, int number, char **pars, int *processed, u32 *ptr, enum cf_operation op)
229 {
230   if (op == OP_PREPEND || op == OP_APPEND)
231     op = OP_SET;
232   if (op != OP_SET && op != OP_REMOVE)
233     return cf_printf("Cannot apply operation %s on a bitmap", cf_op_names[op]);
234   else if (item->type != CT_INT && item->type != CT_LOOKUP)
235     return cf_printf("Type %s cannot be used with bitmaps", cf_type_names[item->type]);
236   cf_journal_block(ptr, sizeof(u32));
237   for (int i=0; i<number; i++) {
238     uns idx;
239     if (item->type == CT_INT)
240       TRY( cf_parse_int(pars[i], &idx) );
241     else
242       TRY( cf_parse_lookup(pars[i], &idx, item->u.lookup) );
243     if (idx >= 32)
244       return "Bitmaps only have 32 bits";
245     if (op == OP_SET)
246       *ptr |= 1<<idx;
247     else
248       *ptr &= ~(1<<idx);
249   }
250   *processed = number;
251   return NULL;
252 }
253
254 static char *
255 interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uns allow_dynamic)
256 {
257   int taken;
258   switch (item->cls)
259   {
260     case CC_STATIC:
261       if (!number)
262         return "Missing value";
263       taken = MIN(number, item->number);
264       *processed = taken;
265       uns size = cf_type_size(item->type, item->u.utype);
266       cf_journal_block(ptr, taken * size);
267       return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
268     case CC_DYNAMIC:
269       if (!allow_dynamic)
270         return "Dynamic array cannot be used here";
271       taken = MIN(number, ABS(item->number));
272       *processed = taken;
273       return interpret_set_dynamic(item, taken, pars, ptr);
274     case CC_PARSER:
275       if (item->number < 0 && !allow_dynamic)
276         return "Parsers with variable number of parameters cannot be used here";
277       if (item->number > 0 && number < item->number)
278         return "Not enough parameters available for the parser";
279       taken = MIN(number, ABS(item->number));
280       *processed = taken;
281       for (int i=0; i<taken; i++)
282         pars[i] = cf_strdup(pars[i]);
283       return item->u.par(taken, pars, ptr);
284     case CC_SECTION:
285       return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
286     case CC_LIST:
287       if (!allow_dynamic)
288         return "Lists cannot be used here";
289       return interpret_add_list(item, number, pars, processed, ptr, OP_SET);
290     case CC_BITMAP:
291       if (!allow_dynamic)
292         return "Bitmaps cannot be used here";
293       return interpret_add_bitmap(item, number, pars, processed, ptr, OP_SET);
294     default:
295       ASSERT(0);
296   }
297 }
298
299 static char *
300 interpret_set_all(struct cf_item *item, void *ptr, enum cf_operation op)
301 {
302   if (item->cls == CC_BITMAP) {
303     cf_journal_block(ptr, sizeof(u32));
304     if (op == OP_CLEAR)
305       * (u32*) ptr = 0;
306     else
307       if (item->type == CT_INT)
308         * (u32*) ptr = ~0u;
309       else {
310         uns nr = -1;
311         while (item->u.lookup[++nr]);
312         * (u32*) ptr = ~0u >> (32-nr);
313       }
314     return NULL;
315   } else if (op != OP_CLEAR)
316     return "The item is not a bitmap";
317
318   if (item->cls == CC_LIST) {
319     cf_journal_block(ptr, sizeof(clist));
320     clist_init(ptr);
321   } else if (item->cls == CC_DYNAMIC) {
322     cf_journal_block(ptr, sizeof(void *));
323     * (void**) ptr = GARY_FOREVER_EMPTY;
324   } else if (item->cls == CC_STATIC && item->type == CT_STRING) {
325     cf_journal_block(ptr, item->number * sizeof(char*));
326     bzero(ptr, item->number * sizeof(char*));
327   } else
328     return "The item is not a list, dynamic array, bitmap, or string";
329   return NULL;
330 }
331
332 static int
333 cmp_items(void *i1, void *i2, struct cf_item *item)
334 {
335   ASSERT(item->cls == CC_STATIC);
336   i1 += (uintptr_t) item->ptr;
337   i2 += (uintptr_t) item->ptr;
338   if (item->type == CT_STRING)
339     return strcmp(* (char**) i1, * (char**) i2);
340   else                          // all numeric types
341     return memcmp(i1, i2, cf_type_size(item->type, item->u.utype));
342 }
343
344 static void *
345 find_list_node(clist *list, void *query, struct cf_section *sec, u32 mask)
346 {
347   CLIST_FOR_EACH(cnode *, n, *list)
348   {
349     uns found = 1;
350     for (uns i=0; i<32; i++)
351       if (mask & (1<<i))
352         if (cmp_items(n, query, sec->cfg+i))
353         {
354           found = 0;
355           break;
356         }
357     if (found)
358       return n;
359   }
360   return NULL;
361 }
362
363 static char *
364 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
365 {
366   uns nr = sec->flags & SEC_FLAG_NUMBER;
367   if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
368   {
369     uns i = item - sec->cfg;
370     if (i >= 32)
371       return "Cannot select list nodes by this attribute";
372     if (sec->cfg[i].cls != CC_STATIC)
373       return "Selection can only be done based on basic attributes";
374     *mask |= 1 << i;
375   }
376   return NULL;
377 }
378
379 static char *
380 opening_brace(struct cf_context *cc, struct cf_item *item, void *ptr, enum cf_operation op)
381 {
382   if (cc->stack_level >= MAX_STACK_SIZE-1)
383     return "Too many nested sections";
384   enum cf_operation pure_op = op & OP_MASK;
385   cc->stack[++cc->stack_level] = (struct item_stack) {
386     .sec = NULL,
387     .base_ptr = NULL,
388     .op = pure_op,
389     .list = NULL,
390     .mask = 0,
391     .item = NULL,
392   };
393   if (!item)                    // unknown is ignored; we just need to trace recursion
394     return NULL;
395   cc->stack[cc->stack_level].sec = item->u.sec;
396   if (item->cls == CC_SECTION)
397   {
398     if (pure_op != OP_SET)
399       return "Only SET operation can be used with a section";
400     cc->stack[cc->stack_level].base_ptr = ptr;
401     cc->stack[cc->stack_level].op = OP_EDIT | OP_2ND;   // this list operation does nothing
402   }
403   else if (item->cls == CC_LIST)
404   {
405     cc->stack[cc->stack_level].base_ptr = cf_malloc(item->u.sec->size);
406     cf_init_section(item->name, item->u.sec, cc->stack[cc->stack_level].base_ptr, 1);
407     cc->stack[cc->stack_level].list = ptr;
408     cc->stack[cc->stack_level].item = item;
409     if (pure_op == OP_ALL)
410       return "Operation ALL cannot be applied on lists";
411     else if (pure_op < OP_REMOVE) {
412       add_to_list(ptr, cc->stack[cc->stack_level].base_ptr, pure_op);
413       cc->stack[cc->stack_level].op |= OP_2ND;
414     } else
415       cc->stack[cc->stack_level].op |= OP_1ST;
416   }
417   else
418     return "Opening brace can only be used on sections and lists";
419   return NULL;
420 }
421
422 static char *
423 closing_brace(struct cf_context *cc, struct item_stack *st, enum cf_operation op, int number, char **pars)
424 {
425   if (st->op == OP_CLOSE)       // top-level
426     return "Unmatched } parenthesis";
427   if (!st->sec) {               // dummy run on unknown section
428     if (!(op & OP_OPEN))
429       cc->stack_level--;
430     return NULL;
431   }
432   enum cf_operation pure_op = st->op & OP_MASK;
433   if (st->op & OP_1ST)
434   {
435     st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
436     if (!st->list)
437       return "Cannot find a node matching the query";
438     if (pure_op != OP_REMOVE)
439     {
440       if (pure_op == OP_EDIT)
441         st->base_ptr = st->list;
442       else if (pure_op == OP_AFTER || pure_op == OP_BEFORE)
443         cf_init_section(st->item->name, st->sec, st->base_ptr, 1);
444       else if (pure_op == OP_COPY) {
445         if (st->sec->flags & SEC_FLAG_CANT_COPY)
446           return cf_printf("Item %s cannot be copied", st->item->name);
447         memcpy(st->base_ptr, st->list, st->sec->size);  // strings and dynamic arrays are shared
448         if (st->sec->copy)
449           TRY( st->sec->copy(st->base_ptr, st->list) );
450       } else
451         ASSERT(0);
452       if (op & OP_OPEN) {       // stay at the same recursion level
453         st->op = (st->op | OP_2ND) & ~OP_1ST;
454         add_to_list(st->list, st->base_ptr, pure_op);
455         return NULL;
456       }
457       int taken;                // parse parameters on 1 line immediately
458       TRY( interpret_section(st->sec, number, pars, &taken, st->base_ptr, 1) );
459       number -= taken;
460       pars += taken;
461       // and fall-thru to the 2nd phase
462     }
463     add_to_list(st->list, st->base_ptr, pure_op);
464   }
465   cc->stack_level--;
466   if (number)
467     return "No parameters expected after the }";
468   else if (op & OP_OPEN)
469     return "No { is expected";
470   else
471     return NULL;
472 }
473
474 static struct cf_item *
475 find_item(struct cf_section *curr_sec, const char *name, char **msg, void **ptr)
476 {
477   struct cf_context *cc = cf_get_context();
478   *msg = NULL;
479   if (name[0] == '^')                           // absolute name instead of relative
480     name++, curr_sec = &cc->sections, *ptr = NULL;
481   if (!curr_sec)                                // don't even search in an unknown section
482     return NULL;
483   while (1)
484   {
485     if (curr_sec != &cc->sections)
486       cf_add_dirty(curr_sec, *ptr);
487     char *c = strchr(name, '.');
488     if (c)
489       *c++ = 0;
490     struct cf_item *ci = cf_find_subitem(curr_sec, name);
491     if (!ci->cls)
492     {
493       if (!(curr_sec->flags & SEC_FLAG_UNKNOWN))        // ignore silently unknown top-level sections and unknown attributes in flagged sections
494         *msg = cf_printf("Unknown item %s", name);
495       return NULL;
496     }
497     *ptr += (uintptr_t) ci->ptr;
498     if (!c)
499       return ci;
500     if (ci->cls != CC_SECTION)
501     {
502       *msg = cf_printf("Item %s is not a section", name);
503       return NULL;
504     }
505     curr_sec = ci->u.sec;
506     name = c;
507   }
508 }
509
510 static char *
511 interpret_add(char *name, struct cf_item *item, int number, char **pars, int *takenp, void *ptr, enum cf_operation op)
512 {
513   switch (item->cls) {
514     case CC_DYNAMIC:
515       return interpret_add_dynamic(item, number, pars, takenp, ptr, op);
516     case CC_LIST:
517       return interpret_add_list(item, number, pars, takenp, ptr, op);
518     case CC_BITMAP:
519       return interpret_add_bitmap(item, number, pars, takenp, ptr, op);
520     default:
521       return cf_printf("Operation %s not supported on attribute %s", cf_op_names[op], name);
522   }
523 }
524
525 char *
526 cf_interpret_line(struct cf_context *cc, char *name, enum cf_operation op, int number, char **pars)
527 {
528   char *msg;
529   if ((op & OP_MASK) == OP_CLOSE)
530     return closing_brace(cc, cc->stack+cc->stack_level, op, number, pars);
531   void *ptr = cc->stack[cc->stack_level].base_ptr;
532   struct cf_item *item = find_item(cc->stack[cc->stack_level].sec, name, &msg, &ptr);
533   if (msg)
534     return msg;
535   if (cc->stack[cc->stack_level].op & OP_1ST)
536     TRY( record_selector(item, cc->stack[cc->stack_level].sec, &cc->stack[cc->stack_level].mask) );
537   if (op & OP_OPEN) {           // the operation will be performed after the closing brace
538     if (number)
539       return "Cannot open a block after a parameter has been passed on a line";
540     return opening_brace(cc, item, ptr, op);
541   }
542   if (!item)                    // ignored item in an unknown section
543     return NULL;
544   op &= OP_MASK;
545
546   int taken = 0;                // process as many parameters as possible
547   switch (op) {
548     case OP_CLEAR:
549     case OP_ALL:
550       msg = interpret_set_all(item, ptr, op);
551       break;
552     case OP_SET:
553       msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
554       break;
555     case OP_RESET:
556       msg = interpret_set_all(item, ptr, OP_CLEAR);
557       if (!msg)
558         msg = interpret_add(name, item, number, pars, &taken, ptr, OP_APPEND);
559       break;
560     default:
561       msg = interpret_add(name, item, number, pars, &taken, ptr, op);
562   }
563   if (msg)
564     return msg;
565   if (taken < number)
566     return cf_printf("Too many parameters: %d>%d", number, taken);
567
568   return NULL;
569 }
570
571 char *
572 cf_find_item(const char *name, struct cf_item *item)
573 {
574   struct cf_context *cc = cf_get_context();
575   char *msg;
576   void *ptr = NULL;
577   struct cf_item *ci = find_item(&cc->sections, name, &msg, &ptr);
578   if (msg)
579     return msg;
580   if (ci) {
581     *item = *ci;
582     item->ptr = ptr;
583   } else
584     bzero(item, sizeof(struct cf_item));
585   return NULL;
586 }
587
588 char *
589 cf_modify_item(struct cf_item *item, enum cf_operation op, int number, char **pars)
590 {
591   char *msg;
592   int taken = 0;
593   switch (op) {
594     case OP_SET:
595       msg = interpret_set_item(item, number, pars, &taken, item->ptr, 1);
596       break;
597     case OP_CLEAR:
598     case OP_ALL:
599       msg = interpret_set_all(item, item->ptr, op);
600       break;
601     case OP_APPEND:
602     case OP_PREPEND:
603       switch (item->cls) {
604         case CC_DYNAMIC:
605           msg = interpret_add_dynamic(item, number, pars, &taken, item->ptr, op);
606           break;
607         case CC_LIST:
608           msg = interpret_add_list(item, number, pars, &taken, item->ptr, op);
609           break;
610         case CC_BITMAP:
611           msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
612           break;
613         default:
614           return "The attribute does not support append/prepend";
615       }
616       break;
617     case OP_REMOVE:
618       if (item->cls == CC_BITMAP)
619         msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
620       else
621         return "Only applicable on bitmaps";
622       break;
623     default:
624       return "Unsupported operation";
625   }
626   if (msg)
627     return msg;
628   if (taken < number)
629     return "Too many parameters";
630   return NULL;
631 }
632
633 void
634 cf_init_stack(struct cf_context *cc)
635 {
636   if (!cc->sections_initialized++) {
637     cc->sections.flags |= SEC_FLAG_UNKNOWN;
638     cc->sections.size = 0;                      // size of allocated array used to be stored here
639     cf_init_section(NULL, &cc->sections, NULL, 0);
640   }
641   cc->stack_level = 0;
642   cc->stack[0] = (struct item_stack) {
643     .sec = &cc->sections,
644     .base_ptr = NULL,
645     .op = OP_CLOSE,
646     .list = NULL,
647     .mask = 0,
648     .item = NULL
649   };
650 }
651
652 int
653 cf_done_stack(struct cf_context *cc)
654 {
655   return (cc->stack_level > 0);
656 }