]> mj.ucw.cz Git - libucw.git/blob - lib/conf-intr.c
conf: clearing a dynamic array allocates a zero-length array
[libucw.git] / lib / conf-intr.c
1 /*
2  *      UCW Library -- Configuration files: interpreter
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/conf.h"
13 #include "lib/getopt.h"
14 #include "lib/conf-internal.h"
15 #include "lib/clists.h"
16
17 #include <string.h>
18 #include <stdio.h>
19
20 #define TRY(f)  do { byte *_msg = f; if (_msg) return _msg; } while (0)
21
22 /* Register size of and parser for each basic type */
23
24 static byte *
25 cf_parse_string(byte *str, byte **ptr)
26 {
27   *ptr = cf_strdup(str);
28   return NULL;
29 }
30
31 typedef byte *cf_basic_parser(byte *str, void *ptr);
32 static struct {
33   uns size;
34   void *parser;
35 } parsers[] = {
36   { sizeof(int), cf_parse_int },
37   { sizeof(u64), cf_parse_u64 },
38   { sizeof(double), cf_parse_double },
39   { sizeof(u32), cf_parse_ip },
40   { sizeof(byte*), cf_parse_string },
41   { sizeof(int), NULL },                        // lookups are parsed extra
42   { 0, NULL },                                  // user-defined types are parsed extra
43 };
44
45 inline uns
46 cf_type_size(enum cf_type type, struct cf_user_type *utype)
47 {
48   if (type < CT_USER)
49     return parsers[type].size;
50   else
51     return utype->size;
52 }
53
54 static byte *
55 cf_parse_lookup(byte *str, int *ptr, byte **t)
56 {
57   byte **n = t;
58   uns total_len = 0;
59   while (*n && strcasecmp(*n, str)) {
60     total_len += strlen(*n) + 2;
61     n++;
62   }
63   if (*n) {
64     *ptr = n - t;
65     return NULL;
66   }
67   byte *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
68   c += sprintf(err, "Invalid value %s, possible values are: ", str);
69   for (n=t; *n; n++)
70     c+= sprintf(c, "%s, ", *n);
71   if (*t)
72     c[-2] = 0;
73   *ptr = -1;
74   return err;
75 }
76
77 static byte *
78 cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type, union cf_union *u)
79 {
80   for (uns i=0; i<number; i++)
81   {
82     byte *msg;
83     uns size = cf_type_size(type, u->utype);
84     if (type < CT_LOOKUP)
85       msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size);
86     else if (type == CT_LOOKUP)
87       msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup);
88     else if (type == CT_USER)
89       msg = u->utype->parser(pars[i], ptr + i * size);
90     else
91       ASSERT(0);
92     if (msg)
93       return number > 1 ? cf_printf("Item %d: %s", i+1, msg) : msg;
94   }
95   return NULL;
96 }
97
98 /* Interpreter */
99
100 #define T(x) #x,
101 byte *cf_op_names[] = { CF_OPERATIONS };
102 #undef T
103
104 static byte *
105 interpret_set_dynamic(struct cf_item *item, int number, byte **pars, void **ptr)
106 {
107   enum cf_type type = item->type;
108   cf_journal_block(ptr, sizeof(void*));
109   // boundary checks done by the caller
110   uns size = cf_type_size(item->type, item->u.utype);
111   ASSERT(size >= sizeof(uns));
112   *ptr = cf_malloc((number+1) * size) + size;
113   * (uns*) (*ptr - size) = number;
114   return cf_parse_ary(number, pars, *ptr, type, &item->u);
115 }
116
117 static byte *
118 interpret_add_dynamic(struct cf_item *item, int number, byte **pars, int *processed, void **ptr, enum cf_operation op)
119 {
120   enum cf_type type = item->type;
121   void *old_p = *ptr;
122   uns size = cf_type_size(item->type, item->u.utype);
123   ASSERT(size >= sizeof(uns));
124   int old_nr = old_p ? * (int*) (old_p - size) : 0;
125   int taken = MIN(number, ABS(item->number)-old_nr);
126   *processed = taken;
127   // stretch the dynamic array
128   void *new_p = cf_malloc((old_nr + taken + 1) * size) + size;
129   * (uns*) (new_p - size) = old_nr + taken;
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 byte *interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic);
143
144 static byte *
145 interpret_section(struct cf_section *sec, int number, byte **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     byte *msg = interpret_set_item(ci, number, pars, &taken, ptr + (addr_int_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:               // edition 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 dependend (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 dependend (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 byte *
196 interpret_add_list(struct cf_item *item, int number, byte **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     byte *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 byte *
227 interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
228 {
229   int taken;
230   switch (item->cls)
231   {
232     case CC_STATIC:
233       if (!number)
234         return "Missing value";
235       taken = MIN(number, item->number);
236       *processed = taken;
237       uns size = cf_type_size(item->type, item->u.utype);
238       cf_journal_block(ptr, taken * size);
239       return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
240     case CC_DYNAMIC:
241       if (!allow_dynamic)
242         return "Dynamic array cannot be used here";
243       taken = MIN(number, ABS(item->number));
244       *processed = taken;
245       return interpret_set_dynamic(item, taken, pars, ptr);
246     case CC_PARSER:
247       if (item->number < 0 && !allow_dynamic)
248         return "Parsers with variable number of parameters cannot be used here";
249       if (item->number > 0 && number < item->number)
250         return "Not enough parameters available for the parser";
251       taken = MIN(number, ABS(item->number));
252       *processed = taken;
253       for (int i=0; i<taken; i++)
254         pars[i] = cf_strdup(pars[i]);
255       return item->u.par(taken, pars, ptr);
256     case CC_SECTION:
257       return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
258     case CC_LIST:
259       if (!allow_dynamic)
260         return "Lists cannot be used here";
261       return interpret_add_list(item, number, pars, processed, ptr, OP_SET);
262     default:
263       ASSERT(0);
264   }
265 }
266
267 byte *
268 cf_interpret_clear(struct cf_item *item, void *ptr)
269 {
270   if (item->cls == CC_LIST) {
271     cf_journal_block(ptr, sizeof(clist));
272     clist_init(ptr);
273   } else if (item->cls == CC_DYNAMIC) {
274     cf_journal_block(ptr, sizeof(void *));
275     uns size = cf_type_size(item->type, item->u.utype);
276     static u64 zero = 0;
277     if (size <= sizeof(zero))
278       *(void**)ptr = (&zero) + 1;
279     else
280       *(void**)ptr = cf_malloc_zero(size) + size;
281   } else if (item->cls == CC_STATIC && item->type == CT_STRING) {
282     cf_journal_block(ptr, item->number * sizeof(byte*));
283     bzero(ptr, item->number * sizeof(byte*));
284   } else
285     return "The item is not a list, dynamic array, or string";
286   return NULL;
287 }
288
289 static int
290 cmp_items(void *i1, void *i2, struct cf_item *item)
291 {
292   ASSERT(item->cls == CC_STATIC);
293   i1 += (addr_int_t) item->ptr;
294   i2 += (addr_int_t) item->ptr;
295   if (item->type == CT_STRING)
296     return strcmp(* (byte**) i1, * (byte**) i2);
297   else                          // all numeric types
298     return memcmp(i1, i2, cf_type_size(item->type, item->u.utype));
299 }
300
301 static void *
302 find_list_node(clist *list, void *query, struct cf_section *sec, u32 mask)
303 {
304   CLIST_FOR_EACH(cnode *, n, *list)
305   {
306     uns found = 1;
307     for (uns i=0; i<32; i++)
308       if (mask & (1<<i))
309         if (cmp_items(n, query, sec->cfg+i))
310         {
311           found = 0;
312           break;
313         }
314     if (found)
315       return n;
316   }
317   return NULL;
318 }
319
320 static byte *
321 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
322 {
323   uns nr = sec->flags & SEC_FLAG_NUMBER;
324   if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
325   {
326     uns i = item - sec->cfg;
327     if (i >= 32)
328       return "Cannot select list nodes by this attribute";
329     if (sec->cfg[i].cls != CC_STATIC)
330       return "Selection can only be done based on basic attributes";
331     *mask |= 1 << i;
332   }
333   return NULL;
334 }
335
336 #define MAX_STACK_SIZE  10
337 static struct item_stack {
338   struct cf_section *sec;       // nested section
339   void *base_ptr;               // because original pointers are often relative
340   enum cf_operation op;         // it is performed when a closing brace is encountered
341   void *list;                   // list the operations should be done on
342   u32 mask;                     // bit array of selectors searching in a list
343   struct cf_item *item;         // cf_item of the list
344 } stack[MAX_STACK_SIZE];
345 static uns level;
346
347 static byte *
348 opening_brace(struct cf_item *item, void *ptr, enum cf_operation op)
349 {
350   if (level >= MAX_STACK_SIZE-1)
351     return "Too many nested sections";
352   stack[++level] = (struct item_stack) {
353     .sec = NULL,
354     .base_ptr = NULL,
355     .op = op & OP_MASK,
356     .list = NULL,
357     .mask = 0,
358     .item = NULL,
359   };
360   if (!item)                    // unknown is ignored; we just need to trace recursion
361     return NULL;
362   stack[level].sec = item->u.sec;
363   if (item->cls == CC_SECTION)
364   {
365     stack[level].base_ptr = ptr;
366     stack[level].op = OP_EDIT | OP_2ND; // this list operation does nothing
367   }
368   else if (item->cls == CC_LIST)
369   {
370     stack[level].base_ptr = cf_malloc(item->u.sec->size);
371     cf_init_section(item->name, item->u.sec, stack[level].base_ptr, 1);
372     stack[level].list = ptr;
373     stack[level].item = item;
374     if ((op & OP_MASK) < OP_REMOVE) {
375       add_to_list(ptr, stack[level].base_ptr, op & OP_MASK);
376       stack[level].op |= OP_2ND;
377     } else
378       stack[level].op |= OP_1ST;
379   }
380   else
381     return "Opening brace can only be used on sections and lists";
382   return NULL;
383 }
384
385 static byte *
386 closing_brace(struct item_stack *st, enum cf_operation op, int number, byte **pars)
387 {
388   if (st->op == OP_CLOSE)       // top-level
389     return "Unmatched } parenthesis";
390   if (!st->sec) {               // dummy run on unknown section
391     if (!(op & OP_OPEN))
392       level--;
393     return NULL;
394   }
395   enum cf_operation pure_op = st->op & OP_MASK;
396   if (st->op & OP_1ST)
397   {
398     st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
399     if (!st->list)
400       return "Cannot find a node matching the query";
401     if (pure_op != OP_REMOVE)
402     {
403       if (pure_op == OP_EDIT)
404         st->base_ptr = st->list;
405       else if (pure_op == OP_AFTER || pure_op == OP_BEFORE)
406         cf_init_section(st->item->name, st->sec, st->base_ptr, 1);
407       else if (pure_op == OP_COPY) {
408         if (st->sec->flags & SEC_FLAG_CANT_COPY)
409           return cf_printf("Item %s cannot be copied", st->item->name);
410         memcpy(st->base_ptr, st->list, st->sec->size);  // strings and dynamic arrays are shared
411         if (st->sec->copy)
412           TRY( st->sec->copy(st->base_ptr, st->list) );
413       } else
414         ASSERT(0);
415       if (op & OP_OPEN) {       // stay at the same recursion level
416         st->op = (st->op | OP_2ND) & ~OP_1ST;
417         add_to_list(st->list, st->base_ptr, pure_op);
418         return NULL;
419       }
420       int taken;                // parse parameters on 1 line immediately
421       TRY( interpret_section(st->sec, number, pars, &taken, st->base_ptr, 1) );
422       number -= taken;
423       pars += taken;
424       // and fall-thru to the 2nd phase
425     }
426     add_to_list(st->list, st->base_ptr, pure_op);
427   }
428   level--;
429   if (number)
430     return "No parameters expected after the }";
431   else if (op & OP_OPEN)
432     return "No { is expected";
433   else
434     return NULL;
435 }
436
437 static struct cf_item *
438 find_item(struct cf_section *curr_sec, byte *name, byte **msg, void **ptr)
439 {
440   *msg = NULL;
441   if (name[0] == '^')                           // absolute name instead of relative
442     name++, curr_sec = &cf_sections, *ptr = NULL;
443   if (!curr_sec)                                // don't even search in an unknown section
444     return NULL;
445   while (1)
446   {
447     if (curr_sec != &cf_sections)
448       cf_add_dirty(curr_sec, *ptr);
449     byte *c = strchr(name, '.');
450     if (c)
451       *c++ = 0;
452     struct cf_item *ci = cf_find_subitem(curr_sec, name);
453     if (!ci->cls)
454     {
455       if (!(curr_sec->flags & SEC_FLAG_UNKNOWN))        // ignore silently unknown top-level sections and unknown attributes in flagged sections
456         *msg = cf_printf("Unknown item %s", name);
457       return NULL;
458     }
459     *ptr += (addr_int_t) ci->ptr;
460     if (!c)
461       return ci;
462     if (ci->cls != CC_SECTION)
463     {
464       *msg = cf_printf("Item %s is not a section", name);
465       return NULL;
466     }
467     curr_sec = ci->u.sec;
468     name = c;
469   }
470 }
471
472 byte *
473 cf_interpret_line(byte *name, enum cf_operation op, int number, byte **pars)
474 {
475   byte *msg;
476   if ((op & OP_MASK) == OP_CLOSE)
477     return closing_brace(stack+level, op, number, pars);
478   void *ptr = stack[level].base_ptr;
479   struct cf_item *item = find_item(stack[level].sec, name, &msg, &ptr);
480   if (msg)
481     return msg;
482   if (stack[level].op & OP_1ST)
483     TRY( record_selector(item, stack[level].sec, &stack[level].mask) );
484   if (op & OP_OPEN) {           // the operation will be performed after the closing brace
485     if (number)
486       return "Cannot open a block after a parameter has been passed on a line";
487     return opening_brace(item, ptr, op);
488   }
489   if (!item)                    // ignored item in an unknown section
490     return NULL;
491   op &= OP_MASK;
492
493   int taken;                    // process as many parameters as possible
494   if (op == OP_CLEAR)
495     taken = 0, msg = cf_interpret_clear(item, ptr);
496   else if (op == OP_SET)
497     msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
498   else if (item->cls == CC_DYNAMIC)
499     msg = interpret_add_dynamic(item, number, pars, &taken, ptr, op);
500   else if (item->cls == CC_LIST)
501     msg = interpret_add_list(item, number, pars, &taken, ptr, op);
502   else
503     return cf_printf("Operation %s not supported on attribute %s", cf_op_names[op], name);
504   if (msg)
505     return msg;
506   if (taken < number)
507     return cf_printf("Too many parameters: %d>%d", number, taken);
508
509   return NULL;
510 }
511
512 byte *
513 cf_find_item(byte *name, struct cf_item *item)
514 {
515   byte *msg;
516   void *ptr;
517   struct cf_item *ci = find_item(&cf_sections, name, &msg, &ptr);
518   if (msg)
519     return msg;
520   if (ci) {
521     *item = *ci;
522     item->ptr = ptr;
523   } else
524     bzero(item, sizeof(struct cf_item));
525   return NULL;
526 }
527
528 byte *
529 cf_write_item(struct cf_item *item, enum cf_operation op, int number, byte **pars)
530 {
531   byte *msg;
532   int taken;
533   switch (op) {
534     case OP_SET:
535       msg = interpret_set_item(item, number, pars, &taken, item->ptr, 1);
536       break;
537     case OP_CLEAR:
538       taken = 0;
539       msg = cf_interpret_clear(item, item->ptr);
540       break;
541     case OP_APPEND:
542     case OP_PREPEND:
543       if (item->cls == CC_DYNAMIC)
544         msg = interpret_add_dynamic(item, number, pars, &taken, item->ptr, op);
545       else if (item->cls == CC_LIST)
546         msg = interpret_add_list(item, number, pars, &taken, item->ptr, op);
547       else
548         return "The attribute class does not support append/prepend";
549       break;
550     default:
551       return "Unsupported operation";
552   }
553   if (msg)
554     return msg;
555   if (taken < number)
556     return "Too many parameters";
557   return NULL;
558 }
559
560 void
561 cf_init_stack(void)
562 {
563   static uns initialized = 0;
564   if (!initialized++) {
565     cf_sections.flags |= SEC_FLAG_UNKNOWN;
566     cf_sections.size = 0;                       // size of allocated array used to be stored here
567     cf_init_section(NULL, &cf_sections, NULL, 0);
568   }
569   level = 0;
570   stack[0] = (struct item_stack) {
571     .sec = &cf_sections,
572     .base_ptr = NULL,
573     .op = OP_CLOSE,
574     .list = NULL,
575     .mask = 0,
576     .item = NULL
577   };
578 }
579
580 int
581 cf_check_stack(void)
582 {
583   if (level > 0) {
584     log(L_ERROR, "Unterminated block");
585     return 1;
586   }
587   return 0;
588 }
589