]> mj.ucw.cz Git - libucw.git/blob - lib/conf2.c
conf2: implemented all list operations in the interpreter
[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 #define SEC_FLAG_DYNAMIC 0x80000000     // contains a dynamic attribute
144 #define SEC_FLAG_NUMBER 0x7fffffff      // number of entries
145
146 static struct cf_section sections;      // root section
147
148 static struct cf_item *
149 find_subitem(struct cf_section *sec, byte *name)
150 {
151   struct cf_item *ci = sec->cfg;
152   for (; ci->cls; ci++)
153     if (!strcasecmp(ci->name, name))
154       return ci;
155   return ci;
156 }
157
158 static void
159 inspect_section(struct cf_section *sec)
160 {
161   sec->flags = 0;
162   struct cf_item *ci;
163   for (ci=sec->cfg; ci->cls; ci++)
164     if (ci->cls == CC_SECTION) {
165       inspect_section(ci->u.sec);
166       sec->flags |= ci->u.sec->flags & SEC_FLAG_DYNAMIC;
167     } else if (ci->cls == CC_LIST) {
168       inspect_section(ci->u.sec);
169       sec->flags |= SEC_FLAG_DYNAMIC;
170     } else if (ci->cls == CC_DYNAMIC || ci->cls == CC_PARSER && ci->number < 0)
171       sec->flags |= SEC_FLAG_DYNAMIC;
172   sec->flags |= ci - sec->cfg;          // record the number of entries
173 }
174
175 void
176 cf_declare_section(byte *name, struct cf_section *sec)
177 {
178   if (!sections.cfg)
179   {
180     sections.size = 50;
181     sections.cfg = xmalloc_zero(sections.size * sizeof(struct cf_item));
182   }
183   struct cf_item *ci = find_subitem(&sections, name);
184   if (ci->cls)
185     die("Cannot register section %s twice", name);
186   ci->cls = CC_SECTION;
187   ci->name = name;
188   ci->number = 1;
189   ci->ptr = NULL;
190   ci->u.sec = sec;
191   inspect_section(sec);
192   ci++;
193   if (ci - sections.cfg >= (int) sections.size)
194   {
195     sections.cfg = xrealloc(sections.cfg, 2*sections.size * sizeof(struct cf_item));
196     bzero(sections.cfg + sections.size, sections.size * sizeof(struct cf_item));
197     sections.size *= 2;
198   }
199 }
200
201 void
202 cf_init_section(byte *name, struct cf_section *sec, void *ptr)
203 {
204   if (sec->size)
205     bzero(ptr, sec->size);
206   for (uns i=0; sec->cfg[i].cls; i++)
207     if (sec->cfg[i].cls == CC_SECTION)
208       cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr);
209     else if (sec->cfg[i].cls == CC_LIST)
210       clist_init(sec->cfg[i].ptr);
211   byte *msg = sec->init(ptr);
212   if (msg)
213     die("Cannot initialize section %s: %s", name, msg);
214 }
215
216 static void
217 global_init(void)
218 {
219   for (struct cf_item *ci=sections.cfg; ci->cls; ci++)
220     cf_init_section(ci->name, ci->u.sec, NULL);
221 }
222
223 static struct cf_item *
224 find_item(struct cf_section *curr_sec, byte *name, byte **msg)
225 {
226   *msg = NULL;
227   if (name[0] == '^')                           // absolute name instead of relative
228     name++, curr_sec = &sections;
229   if (!curr_sec)                                // don't even search in an unknown section
230     return NULL;
231   while (1)
232   {
233     byte *c = strchr(name, '.');
234     if (c)
235       *c++ = 0;
236     struct cf_item *ci = find_subitem(curr_sec, name);
237     if (!ci->cls)
238     {
239       if (curr_sec != &sections)                // ignore silently unknown top-level sections
240         *msg = cf_printf("Unknown item %s", name);
241       return NULL;
242     }
243     if (!c)
244       return ci;
245     if (ci->cls != CC_SECTION)
246     {
247       *msg = cf_printf("Item %s is not a section", name);
248       return NULL;
249     }
250     curr_sec = ci->u.sec;
251     name = c;
252   }
253 }
254
255 /* Safe loading and reloading */
256
257 byte *cf_def_file = DEFAULT_CONFIG;
258
259 #ifndef DEFAULT_CONFIG
260 #define DEFAULT_CONFIG NULL
261 #endif
262
263 byte *cfdeffile = DEFAULT_CONFIG;
264
265 static byte *load_file(byte *file);
266 static byte *load_string(byte *string);
267
268 byte *
269 cf_reload(byte *file)
270 {
271   journal_swap();
272   struct journal_item *oldj = journal_new_section(1);
273   byte *msg = load_file(file);
274   if (!msg)
275   {
276     for (struct old_pools *p=pools; p; p=pools)
277     {
278       pools = p->prev;
279       mp_delete(p->pool);
280     }
281     journal_commit_section(1, NULL);
282   }
283   else
284   {
285     journal_rollback_section(1, oldj, msg);
286     journal_swap();
287   }
288   return msg;
289 }
290
291 byte *
292 cf_load(byte *file)
293 {
294   struct journal_item *oldj = journal_new_section(1);
295   byte *msg = load_file(file);
296   if (!msg)
297     journal_commit_section(1, oldj);
298   else
299     journal_rollback_section(1, oldj, msg);
300   return msg;
301 }
302
303 byte *
304 cf_set(byte *string)
305 {
306   struct journal_item *oldj = journal_new_section(0);
307   byte *msg = load_string(string);
308   if (!msg)
309     journal_commit_section(0, oldj);
310   else
311     journal_rollback_section(0, oldj, msg);
312   return msg;
313 }
314
315 /* Parsers for standard types */
316
317 struct unit {
318   uns name;                     // one-letter name of the unit
319   uns num, den;                 // fraction
320 };
321
322 static const struct unit units[] = {
323   { 'd', 86400, 1 },
324   { 'h', 3600, 1 },
325   { 'k', 1000, 1 },
326   { 'm', 1000000, 1 },
327   { 'g', 1000000000, 1 },
328   { 'K', 1024, 1 },
329   { 'M', 1048576, 1 },
330   { 'G', 1073741824, 1 },
331   { '%', 1, 100 },
332   { 0, 0, 0 }
333 };
334
335 static const struct unit *
336 lookup_unit(byte *value, byte *end, byte **msg)
337 {
338   if (end && *end) {
339     if (end == value || end[1] || *end >= '0' && *end <= '9')
340       *msg = "Invalid number";
341     else {
342       for (const struct unit *u=units; u->name; u++)
343         if (u->name == *end)
344           return u;
345       *msg = "Invalid unit";
346     }
347   }
348   return NULL;
349 }
350
351 static char cf_rngerr[] = "Number out of range";
352
353 byte *
354 cf_parse_int(byte *str, int *ptr)
355 {
356   byte *msg = NULL;
357   if (!*str)
358     msg = "Missing number";
359   else {
360     const struct unit *u;
361     char *end;
362     errno = 0;
363     uns x = strtoul(str, &end, 0);
364     if (errno == ERANGE)
365       msg = cf_rngerr;
366     else if (u = lookup_unit(str, end, &msg)) {
367       u64 y = (u64)x * u->num;
368       if (y % u->den)
369         msg = "Number is not an integer";
370       else {
371         y /= u->den;
372         if (y > 0xffffffff)
373           msg = cf_rngerr;
374         *ptr = y;
375       }
376     } else
377       *ptr = x;
378   }
379   return msg;
380 }
381
382 byte *
383 cf_parse_u64(byte *str, u64 *ptr)
384 {
385   byte *msg = NULL;
386   if (!*str)
387     msg = "Missing number";
388   else {
389     const struct unit *u;
390     char *end;
391     errno = 0;
392     u64 x = strtoull(str, &end, 0);
393     if (errno == ERANGE)
394       msg = cf_rngerr;
395     else if (u = lookup_unit(str, end, &msg)) {
396       if (x > ~(u64)0 / u->num)
397         msg = "Number out of range";
398       else {
399         x *= u->num;
400         if (x % u->den)
401           msg = "Number is not an integer";
402         else
403           *ptr = x / u->den;
404       }
405     } else
406       *ptr = x;
407   }
408   return msg;
409 }
410
411 byte *
412 cf_parse_double(byte *str, double *ptr)
413 {
414   byte *msg = NULL;
415   if (!*str)
416     msg = "Missing number";
417   else {
418     const struct unit *u;
419     char *end;
420     errno = 0;
421     double x = strtoul(str, &end, 0);
422     if (errno == ERANGE)
423       msg = cf_rngerr;
424     else if (u = lookup_unit(str, end, &msg))
425       *ptr = x * u->num / u->den;
426     else
427       *ptr = x;
428   }
429   return msg;
430 }
431
432 static byte *
433 cf_parse_string(byte *str, byte **ptr)
434 {
435   *ptr = cf_strdup(str);
436   return NULL;
437 }
438
439 /* Register size of and parser for each basic type */
440
441 typedef byte *cf_basic_parser(byte *str, void *ptr);
442 static struct {
443   uns size;
444   void *parser;
445 } parsers[] = {
446   { sizeof(int), cf_parse_int },
447   { sizeof(u64), cf_parse_u64 },
448   { sizeof(double), cf_parse_double },
449   { sizeof(byte*), cf_parse_string }
450 };
451
452 static byte *
453 cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type)
454 {
455   for (uns i=0; i<number; i++)
456   {
457     byte *msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * parsers[type].size);
458     if (msg)
459       return cf_printf("Cannot parse item %d: %s", i+1, msg);
460   }
461   return NULL;
462 }
463
464 /* Interpreter */
465
466 enum operation {
467   OP_CLOSE,                     // closing brace finishes previous block
468   OP_CLEAR,                     // list
469   OP_SET,                       // basic attribute (static, dynamic, parsed), section, list
470   OP_APPEND,                    // dynamic array, list
471   OP_PREPEND,                   // dynamic array, list
472   OP_REMOVE,                    // list operations with search follow:
473   OP_EDIT,
474   OP_AFTER,
475   OP_BEFORE
476 };
477 #define OP_MASK 0xff            // only get the operation
478 #define OP_OPEN 0x100           // here we only get an opening brace instead of parameters
479 #define OP_1ST 0x200            // in the 1st phase selectors are recorded into the mask
480 #define OP_2ND 0x400            // in the 2nd phase real data are entered
481
482 static byte *
483 interpret_set_dynamic(struct cf_item *item, int number, byte **pars, void **ptr)
484 {
485   enum cf_type type = item->u.type;
486   cf_journal_block(ptr, sizeof(void*));
487   // boundary checks done by the caller
488   *ptr = cf_malloc((number+1) * parsers[type].size) + parsers[type].size;
489   * (uns*) (*ptr - parsers[type].size) = number;
490   return cf_parse_ary(number, pars, *ptr, type);
491 }
492
493 static byte *
494 interpret_add_dynamic(struct cf_item *item, int number, byte **pars, int *processed, void **ptr, enum operation op)
495 {
496   enum cf_type type = item->u.type;
497   void *old_p = *ptr;
498   int old_nr = * (int*) (old_p - parsers[type].size);
499   int taken = MIN(number, item->number-old_nr);
500   *processed = taken;
501   // stretch the dynamic array
502   void *new_p = cf_malloc((old_nr + taken + 1) * parsers[type].size) + parsers[type].size;
503   * (uns*) (new_p - parsers[type].size) = old_nr + taken;
504   cf_journal_block(ptr, sizeof(void*));
505   *ptr = new_p;
506   if (op == OP_APPEND)
507   {
508     memcpy(new_p, old_p, old_nr * parsers[type].size);
509     return cf_parse_ary(taken, pars, new_p + old_nr * parsers[type].size, type);
510   }
511   else if (op == OP_PREPEND)
512   {
513     memcpy(new_p + taken * parsers[type].size, old_p, old_nr * parsers[type].size);
514     return cf_parse_ary(taken, pars, new_p, type);
515   }
516   else
517     return cf_printf("Dynamic arrays do not support operation %d", op);
518 }
519
520 static byte *interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic);
521
522 static byte *
523 interpret_section(struct cf_section *sec, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
524 {
525   *processed = 0;
526   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
527   {
528     int taken;
529     byte *msg = interpret_set_item(ci, number, pars, &taken, ptr + (addr_int_t) ci->ptr, allow_dynamic && !ci[1].cls);
530     if (msg)
531       return cf_printf("Item %s: %s", ci->name, msg);
532     *processed += taken;
533     number -= taken;
534     pars += taken;
535     if (!number)                // stop parsing, because many parsers would otherwise complain that number==0
536       break;
537   }
538   return NULL;
539 }
540
541 static void
542 add_to_list(void *list, struct cnode *node, enum operation op)
543 {
544   cf_journal_block(list, sizeof(struct clist));         //FIXME: we should journal the nodes of the list instead
545   switch (op)
546   {
547     case OP_SET:
548     case OP_APPEND:
549       clist_add_tail(list, node);
550       break;
551     case OP_PREPEND:
552       clist_add_head(list, node);
553       break;
554     case OP_REMOVE:
555       clist_remove(list);
556       break;
557     case OP_EDIT:               // edition has been done in-place
558       break;
559     case OP_AFTER:              // here the pointer list is actually a pointer to another node
560       clist_insert_after(node, list);
561       break;
562     case OP_BEFORE:
563       clist_insert_before(node, list);
564       break;
565     default:
566       ASSERT(0);
567   }
568 }
569
570 static byte *
571 interpret_add_list(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, enum operation op)
572 {
573   struct cf_section *sec = item->u.sec;
574   /* If the node contains any dynamic attribute at the end, we suppress
575    * auto-repetition here and pass it inside instead.  We also suppress it when
576    * the operation works on a found node.  */
577   uns only_1_node = (sec->flags & SEC_FLAG_DYNAMIC) || (op >= OP_EDIT);
578   *processed = 0;
579   while (number > 0)
580   {
581     void *node = cf_malloc(sec->size);
582     cf_init_section(item->name, sec, node);
583     add_to_list(ptr, node, op);
584     int taken;
585     byte *msg = interpret_section(sec, number, pars, &taken, node, only_1_node);
586     if (msg)
587       return msg;
588     *processed += taken;
589     number -= taken;
590     pars += taken;
591     if (only_1_node)
592       break;
593   }
594   return NULL;
595 }
596
597 static byte *
598 interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
599 {
600   int taken;
601   switch (item->cls)
602   {
603     case CC_STATIC:
604       taken = MIN(number, item->number);
605       *processed = taken;
606       cf_journal_block(ptr, taken * parsers[item->u.type].size);
607       return cf_parse_ary(taken, pars, ptr, item->u.type);
608     case CC_DYNAMIC:
609       if (!allow_dynamic)
610         return "Dynamic array cannot be used here";
611       taken = MIN(number, item->number);
612       *processed = taken;
613       return interpret_set_dynamic(item, taken, pars, ptr);
614     case CC_PARSER:
615       if (item->number < 0 && !allow_dynamic)
616         return "Parsers with variable number of parameters cannot be used here";
617       if (item->number > 0 && number < item->number)
618         return "Not enough parameters available for the parser";
619       taken = MIN(number, ABS(item->number));
620       *processed = taken;
621       for (int i=0; i<taken; i++)
622         pars[i] = cf_strdup(pars[i]);
623       return item->u.par(taken, pars, ptr);
624     case CC_SECTION:
625       return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
626     case CC_LIST:
627       if (!allow_dynamic)
628         return "Lists cannot be used here";
629       return interpret_add_list(item, number, pars, ptr, processed, OP_SET);
630     default:
631       ASSERT(0);
632   }
633 }
634
635 static byte *
636 interpret_clear(struct cf_item *item, void *ptr)
637 {
638   if (item->cls == CC_LIST) {
639     cf_journal_block(ptr, sizeof(struct clist));
640     clist_init(ptr);
641   } else if (item->cls == CC_DYNAMIC) {
642     cf_journal_block(ptr, sizeof(void *));
643     * (void**) ptr = NULL;
644   } else
645     return "The item is not a list or a dynamic array";
646   return NULL;
647 }
648
649 static int
650 cmp_items(void *i1, void *i2, struct cf_item *item)
651 {
652   ASSERT(item->cls == CC_STATIC);
653   i1 += (addr_int_t) item->ptr;
654   i2 += (addr_int_t) item->ptr;
655   if (item->u.type == CT_STRING)
656     return strcmp(* (byte**) i1, * (byte**) i2);
657   else                          // all numeric types
658     return memcmp(i1, i2, parsers[item->u.type].size);
659 }
660
661 static void *
662 find_list_node(struct clist *list, void *query, struct cf_section *sec, u32 mask)
663 {
664   struct cnode *n;
665   CLIST_WALK(n, *list)
666   {
667     uns found = 1;
668     for (uns i=0; i<32; i++)
669       if (mask & (1<<i))
670         if (cmp_items(n, query, sec->cfg+i))
671         {
672           found = 0;
673           break;
674         }
675     if (found)
676       return n;
677   }
678   return NULL;
679 }
680
681 static byte *
682 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
683 {
684   uns nr = sec->flags & SEC_FLAG_NUMBER;
685   if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
686   {
687     uns i = item - sec->cfg;
688     if (i >= 32)
689       return "Cannot select list nodes by this attribute";
690     if (sec->cfg[i].cls != CC_STATIC)
691       return "Selection can only be done based on basic attributes";
692     *mask |= 1 << i;
693   }
694   return NULL;
695 }
696
697 #define MAX_STACK_SIZE  100
698 static struct item_stack {
699   struct cf_section *sec;       // nested section
700   void *base_ptr;               // because original pointers are often relative
701   enum operation op;            // it is performed when a closing brace is encountered
702   void *list;                   // list the operations should be done on
703   u32 mask;                     // bit array of selectors searching in a list
704   struct cf_item *item;         // cf_item of the list
705 } stack[MAX_STACK_SIZE];
706 static uns level;
707
708 static byte *
709 opening_brace(struct cf_item *item, void *ptr, enum operation op)
710 {
711   if (level >= MAX_STACK_SIZE-1)
712     return "Too many nested sections";
713   stack[++level] = (struct item_stack) {
714     .sec = NULL,
715     .base_ptr = NULL,
716     .op = op,
717     .list = NULL,
718     .mask = 0,
719     .item = NULL,
720   };
721   if (!item)                    // unknown is ignored; we just need to trace recursion
722     return NULL;
723   stack[level].sec = item->u.sec;
724   if (item->cls == CC_SECTION)
725   {
726     stack[level].base_ptr = ptr;
727     stack[level].op = OP_EDIT | OP_2ND; // this list operation does nothing
728   }
729   else if (item->cls == CC_LIST)
730   {
731     stack[level].base_ptr = cf_malloc(item->u.sec->size);
732     cf_init_section(item->name, item->u.sec, stack[level].base_ptr);
733     stack[level].list = ptr;
734     stack[level].item = item;
735     stack[level].op |= op < OP_REMOVE ? OP_2ND : OP_1ST;
736   }
737   else
738     return "Opening brace can only be used on sections and lists";
739   return NULL;
740 }
741
742 static uns
743 closing_brace(struct item_stack *st, int number, byte **msg)
744 {
745   *msg = NULL;
746   if (!st->sec) {               // dummy run on unknown section
747     level--;
748     return 1;
749   }
750   if (st->op == OP_CLOSE) {     // top-level
751     *msg = "Unmatched } parenthese";
752     return 1;
753   }
754   if (st->op & OP_2ND)          // finished the 2nd phase; only for lists
755   {
756 list_op:
757     add_to_list(st->list, st->base_ptr, st->op & OP_MASK);
758     level--;
759     if (number)
760       *msg = "No parameters expected after the }";
761     else if (st->op & OP_OPEN)
762       *msg = "No { is expected";
763     return 1;
764   } else {                      // constructed the query
765     st->op = (st->op | OP_2ND) & ~OP_1ST;
766     st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
767     if (!st->list) {
768       *msg = "Cannot find a node matching the query";
769       return 1;
770     }
771     enum operation op = st->op & OP_MASK;
772     if (op == OP_REMOVE)
773       goto list_op;
774     else if (op == OP_EDIT)
775       st->base_ptr = st->list;
776     else if (op == OP_AFTER || op == OP_BEFORE)
777       cf_init_section(st->item->name, st->sec, st->base_ptr);
778     else
779       ASSERT(0);
780   }
781   if (st->op & OP_OPEN)         // stay in the same recursion level
782     return 1;
783   return 0;                     // continue with immediate data
784 }
785
786 static byte *
787 interpret_line(byte *name, enum operation op, int number, byte **pars)
788 {
789   byte *msg;
790   struct cf_item *item;
791   void *ptr;
792   if (op == OP_CLOSE) {
793     if (closing_brace(stack+level, number, &msg))
794       return NULL;
795     op = stack[level].op;       // operation: edit, after, or before
796     item = stack[level].item;
797     ptr = stack[level].base_ptr;
798
799   } else {
800     item = find_item(stack[level].sec, name, &msg);
801     if (msg)
802       return msg;
803     if (stack[level].op & OP_1ST) {
804       msg = record_selector(item, stack[level].sec, &stack[level].mask);
805       if (msg)
806         return msg;
807     }
808     ptr = stack[level].base_ptr + (addr_int_t) item->ptr;
809     if (op & OP_OPEN)           // the operation will be performed after the closing brace
810       return opening_brace(item, ptr, op);
811     if (!item)                  // ignored item in an unknown section
812       return NULL;
813   }
814
815   int taken;                    // process as many parameters as possible
816   op &= OP_MASK;
817   if (op == OP_CLEAR)
818     taken = 0, msg = interpret_clear(item, ptr);
819   else if (op == OP_SET)
820     msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
821   else if (item->cls == CC_DYNAMIC)
822     msg = interpret_add_dynamic(item, number, pars, &taken, ptr, op);
823   else if (item->cls == CC_LIST)
824     msg = interpret_add_list(item, number, pars, &taken, ptr, op);
825   else
826     return cf_printf("Operation %d not supported on attribute class %d", op, item->cls);
827   if (msg)
828     return msg;
829   if (taken < number)
830     return cf_printf("Too many parameters: %d>%d", number, taken);
831   return NULL;
832 }
833