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