2 * UCW Library -- Configuration files: interpreter
4 * (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5 * (c) 2003--2014 Martin Mares <mj@ucw.cz>
6 * (c) 2014 Pavel Charvat <pchar@ucw.cz>
8 * This software may be freely distributed and used according to the terms
9 * of the GNU Lesser General Public License.
14 #include <ucw/getopt.h>
15 #include <ucw/conf-internal.h>
16 #include <ucw/clists.h>
18 #include <ucw/mempool.h>
19 #include <ucw/xtypes.h>
24 #define TRY(f) do { char *_msg = f; if (_msg) return _msg; } while (0)
26 /* Register size of and parser for each basic type */
29 cf_parse_string(char *str, char **ptr)
31 *ptr = cf_strdup(str);
35 typedef char *cf_basic_parser(char *str, void *ptr);
40 { sizeof(int), cf_parse_int },
41 { sizeof(u64), cf_parse_u64 },
42 { sizeof(double), cf_parse_double },
43 { sizeof(u32), cf_parse_ip },
44 { sizeof(char*), cf_parse_string },
45 { sizeof(int), NULL }, // lookups are parsed extra
46 { 0, NULL }, // user-defined types are parsed extra
50 cf_type_size(enum cf_type type, const union cf_union *u)
55 return u->utype->size;
57 return u->xtype->size;
59 ASSERT(type < ARRAY_SIZE(parsers) - 1);
60 return parsers[type].size;
65 cf_parse_lookup(char *str, int *ptr, const char * const *t)
67 const char * const *n = t;
69 while (*n && strcasecmp(*n, str)) {
70 total_len += strlen(*n) + 2;
77 char *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
78 c += sprintf(err, "Invalid value %s, possible values are: ", str);
80 c+= sprintf(c, "%s, ", *n);
88 cf_parse_ary(uint number, char **pars, void *ptr, enum cf_type type, union cf_union *u)
90 for (uint i=0; i<number; i++)
93 uint size = cf_type_size(type, u);
95 msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size);
96 else if (type == CT_LOOKUP)
97 msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup);
98 else if (type == CT_USER)
99 msg = u->utype->parser(pars[i], ptr + i * size);
100 else if (type == CT_XTYPE)
101 msg = (char *)u->xtype->parse(pars[i], ptr + i * size, cf_get_pool());
105 return number > 1 ? cf_printf("Item %d: %s", i+1, msg) : msg;
113 char *cf_op_names[] = { CF_OPERATIONS };
115 char *cf_type_names[] = { "int", "u64", "double", "ip", "string", "lookup", "user" };
118 interpret_set_dynamic(struct cf_item *item, int number, char **pars, void **ptr)
120 enum cf_type type = item->type;
121 uint size = cf_type_size(type, &item->u);
122 cf_journal_block(ptr, sizeof(void*));
123 // boundary checks done by the caller
124 *ptr = gary_init(size, number, mp_get_allocator(cf_get_pool()));
125 return cf_parse_ary(number, pars, *ptr, type, &item->u);
129 interpret_add_dynamic(struct cf_item *item, int number, char **pars, int *processed, void **ptr, enum cf_operation op)
131 enum cf_type type = item->type;
133 uint size = cf_type_size(item->type, &item->u);
134 ASSERT(size >= sizeof(uint));
135 int old_nr = old_p ? GARY_SIZE(old_p) : 0;
136 int taken = MIN(number, ABS(item->number)-old_nr);
138 // stretch the dynamic array
139 void *new_p = gary_init(size, old_nr + taken, mp_get_allocator(cf_get_pool()));
140 cf_journal_block(ptr, sizeof(void*));
142 if (op == OP_APPEND) {
143 memcpy(new_p, old_p, old_nr * size);
144 return cf_parse_ary(taken, pars, new_p + old_nr * size, type, &item->u);
145 } else if (op == OP_PREPEND) {
146 memcpy(new_p + taken * size, old_p, old_nr * size);
147 return cf_parse_ary(taken, pars, new_p, type, &item->u);
149 return cf_printf("Dynamic arrays do not support operation %s", cf_op_names[op]);
152 static char *interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uint allow_dynamic);
155 interpret_section(struct cf_section *sec, int number, char **pars, int *processed, void *ptr, uint allow_dynamic)
157 cf_add_dirty(sec, ptr);
159 for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
162 char *msg = interpret_set_item(ci, number, pars, &taken, ptr + (uintptr_t) ci->ptr, allow_dynamic && !ci[1].cls);
164 return cf_printf("Item %s: %s", ci->name, msg);
168 if (!number) // stop parsing, because many parsers would otherwise complain that number==0
175 add_to_list(cnode *where, cnode *new_node, enum cf_operation op)
179 case OP_EDIT: // editation has been done in-place
182 CF_JOURNAL_VAR(where->prev->next);
183 CF_JOURNAL_VAR(where->next->prev);
186 case OP_AFTER: // implementation dependent (prepend_head = after(list)), and where==list, see clists.h:74
189 CF_JOURNAL_VAR(where->next->prev);
190 CF_JOURNAL_VAR(where->next);
191 clist_insert_after(new_node, where);
193 case OP_BEFORE: // implementation dependent (append_tail = before(list))
196 CF_JOURNAL_VAR(where->prev->next);
197 CF_JOURNAL_VAR(where->prev);
198 clist_insert_before(new_node, where);
206 interpret_add_list(struct cf_item *item, int number, char **pars, int *processed, void *ptr, enum cf_operation op)
209 return cf_printf("You have to open a block for operation %s", cf_op_names[op]);
211 return "Nothing to add to the list";
212 struct cf_section *sec = item->u.sec;
217 void *node = cf_malloc(sec->size);
218 cf_init_section(item->name, sec, node, 1);
219 add_to_list(ptr, node, op);
221 /* If the node contains any dynamic attribute at the end, we suppress
222 * auto-repetition here and pass the flag inside instead. */
224 char *msg = interpret_section(sec, number, pars, &taken, node, sec->flags & SEC_FLAG_DYNAMIC);
226 return sec->flags & SEC_FLAG_DYNAMIC ? msg : cf_printf("Node %d of list %s: %s", index, item->name, msg);
230 if (sec->flags & SEC_FLAG_DYNAMIC)
237 interpret_add_bitmap(struct cf_item *item, int number, char **pars, int *processed, u32 *ptr, enum cf_operation op)
239 if (op == OP_PREPEND || op == OP_APPEND)
241 if (op != OP_SET && op != OP_REMOVE)
242 return cf_printf("Cannot apply operation %s on a bitmap", cf_op_names[op]);
243 else if (item->type != CT_INT && item->type != CT_LOOKUP)
244 return cf_printf("Type %s cannot be used with bitmaps", cf_type_names[item->type]);
245 cf_journal_block(ptr, sizeof(u32));
246 for (int i=0; i<number; i++) {
248 if (item->type == CT_INT)
249 TRY( cf_parse_int(pars[i], &idx) );
251 TRY( cf_parse_lookup(pars[i], &idx, item->u.lookup) );
253 return "Bitmaps only have 32 bits";
264 interpret_set_item(struct cf_item *item, int number, char **pars, int *processed, void *ptr, uint allow_dynamic)
271 return "Missing value";
272 taken = MIN(number, item->number);
274 uint size = cf_type_size(item->type, &item->u);
275 cf_journal_block(ptr, taken * size);
276 return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
279 return "Dynamic array cannot be used here";
280 taken = MIN(number, ABS(item->number));
282 return interpret_set_dynamic(item, taken, pars, ptr);
284 if (item->number < 0 && !allow_dynamic)
285 return "Parsers with variable number of parameters cannot be used here";
286 if (item->number > 0 && number < item->number)
287 return "Not enough parameters available for the parser";
288 taken = MIN(number, ABS(item->number));
290 for (int i=0; i<taken; i++)
291 pars[i] = cf_strdup(pars[i]);
292 return item->u.par(taken, pars, ptr);
294 return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
297 return "Lists cannot be used here";
298 return interpret_add_list(item, number, pars, processed, ptr, OP_SET);
301 return "Bitmaps cannot be used here";
302 return interpret_add_bitmap(item, number, pars, processed, ptr, OP_SET);
309 interpret_set_all(struct cf_item *item, void *ptr, enum cf_operation op)
311 if (item->cls == CC_BITMAP) {
312 cf_journal_block(ptr, sizeof(u32));
316 if (item->type == CT_INT)
320 while (item->u.lookup[++nr]);
321 * (u32*) ptr = ~0u >> (32-nr);
324 } else if (op != OP_CLEAR)
325 return "The item is not a bitmap";
327 if (item->cls == CC_LIST) {
328 cf_journal_block(ptr, sizeof(clist));
330 } else if (item->cls == CC_DYNAMIC) {
331 cf_journal_block(ptr, sizeof(void *));
332 * (void**) ptr = GARY_FOREVER_EMPTY;
333 } else if (item->cls == CC_STATIC && item->type == CT_STRING) {
334 cf_journal_block(ptr, item->number * sizeof(char*));
335 bzero(ptr, item->number * sizeof(char*));
337 return "The item is not a list, dynamic array, bitmap, or string";
342 cmp_items(void *i1, void *i2, struct cf_item *item)
344 ASSERT(item->cls == CC_STATIC);
345 i1 += (uintptr_t) item->ptr;
346 i2 += (uintptr_t) item->ptr;
347 if (item->type == CT_STRING)
348 return strcmp(* (char**) i1, * (char**) i2);
349 else // all numeric types
350 return memcmp(i1, i2, cf_type_size(item->type, &item->u));
354 find_list_node(clist *list, void *query, struct cf_section *sec, u32 mask)
356 CLIST_FOR_EACH(cnode *, n, *list)
359 for (uint i=0; i<32; i++)
361 if (cmp_items(n, query, sec->cfg+i))
373 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
375 uint nr = sec->flags & SEC_FLAG_NUMBER;
376 if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
378 uint i = item - sec->cfg;
380 return "Cannot select list nodes by this attribute";
381 if (sec->cfg[i].cls != CC_STATIC)
382 return "Selection can only be done based on basic attributes";
389 opening_brace(struct cf_context *cc, struct cf_item *item, void *ptr, enum cf_operation op)
391 if (cc->stack_level >= MAX_STACK_SIZE-1)
392 return "Too many nested sections";
393 enum cf_operation pure_op = op & OP_MASK;
394 cc->stack[++cc->stack_level] = (struct item_stack) {
402 if (!item) // unknown is ignored; we just need to trace recursion
404 cc->stack[cc->stack_level].sec = item->u.sec;
405 if (item->cls == CC_SECTION)
407 if (pure_op != OP_SET)
408 return "Only SET operation can be used with a section";
409 cc->stack[cc->stack_level].base_ptr = ptr;
410 cc->stack[cc->stack_level].op = OP_EDIT | OP_2ND; // this list operation does nothing
412 else if (item->cls == CC_LIST)
414 cc->stack[cc->stack_level].base_ptr = cf_malloc(item->u.sec->size);
415 cf_init_section(item->name, item->u.sec, cc->stack[cc->stack_level].base_ptr, 1);
416 cc->stack[cc->stack_level].list = ptr;
417 cc->stack[cc->stack_level].item = item;
418 if (pure_op == OP_ALL)
419 return "Operation ALL cannot be applied on lists";
420 else if (pure_op < OP_REMOVE) {
421 add_to_list(ptr, cc->stack[cc->stack_level].base_ptr, pure_op);
422 cc->stack[cc->stack_level].op |= OP_2ND;
424 cc->stack[cc->stack_level].op |= OP_1ST;
427 return "Opening brace can only be used on sections and lists";
432 closing_brace(struct cf_context *cc, struct item_stack *st, enum cf_operation op, int number, char **pars)
434 if (st->op == OP_CLOSE) // top-level
435 return "Unmatched } parenthesis";
436 if (!st->sec) { // dummy run on unknown section
441 enum cf_operation pure_op = st->op & OP_MASK;
444 st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
446 return "Cannot find a node matching the query";
447 if (pure_op != OP_REMOVE)
449 if (pure_op == OP_EDIT)
450 st->base_ptr = st->list;
451 else if (pure_op == OP_AFTER || pure_op == OP_BEFORE)
452 cf_init_section(st->item->name, st->sec, st->base_ptr, 1);
453 else if (pure_op == OP_COPY) {
454 if (st->sec->flags & SEC_FLAG_CANT_COPY)
455 return cf_printf("Item %s cannot be copied", st->item->name);
456 memcpy(st->base_ptr, st->list, st->sec->size); // strings and dynamic arrays are shared
458 TRY( st->sec->copy(st->base_ptr, st->list) );
461 if (op & OP_OPEN) { // stay at the same recursion level
462 st->op = (st->op | OP_2ND) & ~OP_1ST;
463 add_to_list(st->list, st->base_ptr, pure_op);
466 int taken; // parse parameters on 1 line immediately
467 TRY( interpret_section(st->sec, number, pars, &taken, st->base_ptr, 1) );
470 // and fall-thru to the 2nd phase
472 add_to_list(st->list, st->base_ptr, pure_op);
476 return "No parameters expected after the }";
477 else if (op & OP_OPEN)
478 return "No { is expected";
483 static struct cf_item *
484 find_item(struct cf_section *curr_sec, const char *name, char **msg, void **ptr)
486 struct cf_context *cc = cf_get_context();
488 if (name[0] == '^') // absolute name instead of relative
489 name++, curr_sec = &cc->sections, *ptr = NULL;
490 if (!curr_sec) // don't even search in an unknown section
494 if (curr_sec != &cc->sections)
495 cf_add_dirty(curr_sec, *ptr);
496 char *c = strchr(name, '.');
499 struct cf_item *ci = cf_find_subitem(curr_sec, name);
502 if (!(curr_sec->flags & SEC_FLAG_UNKNOWN)) // ignore silently unknown top-level sections and unknown attributes in flagged sections
503 *msg = cf_printf("Unknown item %s", name);
506 *ptr += (uintptr_t) ci->ptr;
509 if (ci->cls != CC_SECTION)
511 *msg = cf_printf("Item %s is not a section", name);
514 curr_sec = ci->u.sec;
520 interpret_add(char *name, struct cf_item *item, int number, char **pars, int *takenp, void *ptr, enum cf_operation op)
524 return interpret_add_dynamic(item, number, pars, takenp, ptr, op);
526 return interpret_add_list(item, number, pars, takenp, ptr, op);
528 return interpret_add_bitmap(item, number, pars, takenp, ptr, op);
530 return cf_printf("Operation %s not supported on attribute %s", cf_op_names[op], name);
535 cf_interpret_line(struct cf_context *cc, char *name, enum cf_operation op, int number, char **pars)
538 if ((op & OP_MASK) == OP_CLOSE)
539 return closing_brace(cc, cc->stack+cc->stack_level, op, number, pars);
540 void *ptr = cc->stack[cc->stack_level].base_ptr;
541 struct cf_item *item = find_item(cc->stack[cc->stack_level].sec, name, &msg, &ptr);
544 if (cc->stack[cc->stack_level].op & OP_1ST)
545 TRY( record_selector(item, cc->stack[cc->stack_level].sec, &cc->stack[cc->stack_level].mask) );
546 if (op & OP_OPEN) { // the operation will be performed after the closing brace
548 return "Cannot open a block after a parameter has been passed on a line";
549 return opening_brace(cc, item, ptr, op);
551 if (!item) // ignored item in an unknown section
555 int taken = 0; // process as many parameters as possible
559 msg = interpret_set_all(item, ptr, op);
562 msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
565 msg = interpret_set_all(item, ptr, OP_CLEAR);
567 msg = interpret_add(name, item, number, pars, &taken, ptr, OP_APPEND);
570 msg = interpret_add(name, item, number, pars, &taken, ptr, op);
575 return cf_printf("Too many parameters: %d>%d", number, taken);
581 cf_find_item(const char *name, struct cf_item *item)
583 struct cf_context *cc = cf_get_context();
586 struct cf_item *ci = find_item(&cc->sections, name, &msg, &ptr);
593 bzero(item, sizeof(struct cf_item));
598 cf_modify_item(struct cf_item *item, enum cf_operation op, int number, char **pars)
604 msg = interpret_set_item(item, number, pars, &taken, item->ptr, 1);
608 msg = interpret_set_all(item, item->ptr, op);
614 msg = interpret_add_dynamic(item, number, pars, &taken, item->ptr, op);
617 msg = interpret_add_list(item, number, pars, &taken, item->ptr, op);
620 msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
623 return "The attribute does not support append/prepend";
627 if (item->cls == CC_BITMAP)
628 msg = interpret_add_bitmap(item, number, pars, &taken, item->ptr, op);
630 return "Only applicable on bitmaps";
633 return "Unsupported operation";
638 return "Too many parameters";
643 cf_init_stack(struct cf_context *cc)
645 if (!cc->sections_initialized++) {
646 cc->sections.flags |= SEC_FLAG_UNKNOWN;
647 cc->sections.size = 0; // size of allocated array used to be stored here
648 cf_init_section(NULL, &cc->sections, NULL, 0);
651 cc->stack[0] = (struct item_stack) {
652 .sec = &cc->sections,
662 cf_done_stack(struct cf_context *cc)
664 return (cc->stack_level > 0);