]> mj.ucw.cz Git - libucw.git/blob - lib/conf2.c
The --dumpconfig option stops the program after dumping.
[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/conf.h"
13 #include "lib/getopt.h"
14 #include "lib/mempool.h"
15 #include "lib/clists.h"
16 #include "lib/fastbuf.h"
17 #include "lib/chartype.h"
18 #include "lib/lfs.h"
19 #include "lib/stkstring.h"
20 #include "lib/binsearch.h"
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <fcntl.h>
27
28 #define TRY(f)  do { byte *_msg = f; if (_msg) return _msg; } while (0)
29
30 /* Memory allocation */
31
32 struct mempool *cf_pool;        // current pool for loading new configuration
33 static struct old_pools {
34   struct old_pools *prev;
35   struct mempool *pool;
36 } *pools;                       // link-list of older cf_pool's
37
38 void *
39 cf_malloc(uns size)
40 {
41   return mp_alloc(cf_pool, size);
42 }
43
44 void *
45 cf_malloc_zero(uns size)
46 {
47   return mp_alloc_zero(cf_pool, size);
48 }
49
50 byte *
51 cf_strdup(byte *s)
52 {
53   return mp_strdup(cf_pool, s);
54 }
55
56 byte *
57 cf_printf(char *fmt, ...)
58 {
59   va_list args;
60   va_start(args, fmt);
61   byte *res = mp_vprintf(cf_pool, fmt, args);
62   va_end(args);
63   return res;
64 }
65
66 /* Undo journal */
67
68 uns cf_need_journal = 1;        // some programs do not need journal
69 static struct cf_journal_item {
70   struct cf_journal_item *prev;
71   byte *ptr;
72   uns len;
73   byte copy[0];
74 } *journal;
75
76 void
77 cf_journal_block(void *ptr, uns len)
78 {
79   if (!cf_need_journal)
80     return;
81   struct cf_journal_item *ji = cf_malloc(sizeof(struct cf_journal_item) + len);
82   ji->prev = journal;
83   ji->ptr = ptr;
84   ji->len = len;
85   memcpy(ji->copy, ptr, len);
86   journal = ji;
87 }
88
89 static void
90 journal_swap(void)
91   // swaps the contents of the memory and the journal, and reverses the list
92 {
93   struct cf_journal_item *curr, *prev, *next;
94   for (next=NULL, curr=journal; curr; next=curr, curr=prev)
95   {
96     prev = curr->prev;
97     curr->prev = next;
98     for (uns i=0; i<curr->len; i++)
99     {
100       byte x = curr->copy[i];
101       curr->copy[i] = curr->ptr[i];
102       curr->ptr[i] = x;
103     }
104   }
105   journal = next;
106 }
107
108 struct cf_journal_item *
109 cf_journal_new_transaction(uns new_pool)
110 {
111   if (new_pool)
112     cf_pool = mp_new(1<<10);
113   struct cf_journal_item *oldj = journal;
114   journal = NULL;
115   return oldj;
116 }
117
118 void
119 cf_journal_commit_transaction(uns new_pool, struct cf_journal_item *oldj)
120 {
121   if (new_pool)
122   {
123     struct old_pools *p = cf_malloc(sizeof(struct old_pools));
124     p->prev = pools;
125     p->pool = cf_pool;
126     pools = p;
127   }
128   if (oldj)
129   {
130     struct cf_journal_item **j = &journal;
131     while (*j)
132       j = &(*j)->prev;
133     *j = oldj;
134   }
135 }
136
137 void
138 cf_journal_rollback_transaction(uns new_pool, struct cf_journal_item *oldj)
139 {
140   if (!cf_need_journal)
141     die("Cannot rollback the configuration, because the journal is disabled.");
142   journal_swap();
143   journal = oldj;
144   if (new_pool)
145   {
146     mp_delete(cf_pool);
147     cf_pool = pools ? pools->pool : NULL;
148   }
149 }
150
151 /* Dirty sections */
152
153 struct dirty_section {
154   struct cf_section *sec;
155   void *ptr;
156 };
157 #define GBUF_TYPE       struct dirty_section
158 #define GBUF_PREFIX(x)  dirtsec_##x
159 #include "lib/gbuf.h"
160 static dirtsec_t dirty;
161 static uns dirties;
162 static uns everything_committed;                // after the 1st load, this flag is set on
163
164 static void
165 add_dirty(struct cf_section *sec, void *ptr)
166 {
167   dirtsec_grow(&dirty, dirties+1);
168   struct dirty_section *dest = dirty.ptr + dirties;
169   if (dirties && dest[-1].sec == sec && dest[-1].ptr == ptr)
170     return;
171   dest->sec = sec;
172   dest->ptr = ptr;
173   dirties++;
174 }
175
176 #define ASORT_PREFIX(x) dirtsec_##x
177 #define ASORT_KEY_TYPE  struct dirty_section
178 #define ASORT_ELT(i)    dirty.ptr[i]
179 #define ASORT_LT(x,y)   x.sec < y.sec || x.sec == y.sec && x.ptr < y.ptr
180 #include "lib/arraysort.h"
181
182 static void
183 sort_dirty(void)
184 {
185   if (dirties <= 1)
186     return;
187   dirtsec_sort(dirties);
188   struct dirty_section *read = dirty.ptr + 1, *write = dirty.ptr + 1, *limit = dirty.ptr + dirties;
189   while (read < limit) {
190     if (read->sec != read[-1].sec || read->ptr != read[-1].ptr) {
191       if (read != write)
192         *write = *read;
193       write++;
194     }
195     read++;
196   }
197   dirties = write - dirty.ptr;
198 }
199
200 /* Initialization */
201
202 #define SEC_FLAG_DYNAMIC        0x80000000      // contains a dynamic attribute
203 #define SEC_FLAG_UNKNOWN        0x40000000      // ignore unknown entriies
204 #define SEC_FLAG_CANT_COPY      0x20000000      // contains lists or parsers
205 #define SEC_FLAG_NUMBER         0x0fffffff      // number of entries
206
207 static struct cf_section sections;      // root section
208
209 static struct cf_item *
210 find_subitem(struct cf_section *sec, byte *name)
211 {
212   struct cf_item *ci = sec->cfg;
213   for (; ci->cls; ci++)
214     if (!strcasecmp(ci->name, name))
215       return ci;
216   return ci;
217 }
218
219 static void
220 inspect_section(struct cf_section *sec)
221 {
222   sec->flags = 0;
223   struct cf_item *ci;
224   for (ci=sec->cfg; ci->cls; ci++)
225     if (ci->cls == CC_SECTION) {
226       inspect_section(ci->u.sec);
227       sec->flags |= ci->u.sec->flags & (SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY);
228     } else if (ci->cls == CC_LIST) {
229       inspect_section(ci->u.sec);
230       sec->flags |= SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY;
231     } else if (ci->cls == CC_DYNAMIC)
232       sec->flags |= SEC_FLAG_DYNAMIC;
233     else if (ci->cls == CC_PARSER) {
234       sec->flags |= SEC_FLAG_CANT_COPY;
235       if (ci->number < 0)
236         sec->flags |= SEC_FLAG_DYNAMIC;
237     }
238   if (sec->copy)
239     sec->flags &= ~SEC_FLAG_CANT_COPY;
240   sec->flags |= ci - sec->cfg;          // record the number of entries
241 }
242
243 void
244 cf_declare_section(byte *name, struct cf_section *sec, uns allow_unknown)
245 {
246   if (!sections.cfg)
247   {
248     sections.size = 50;
249     sections.cfg = xmalloc_zero(sections.size * sizeof(struct cf_item));
250   }
251   struct cf_item *ci = find_subitem(&sections, name);
252   if (ci->cls)
253     die("Cannot register section %s twice", name);
254   ci->cls = CC_SECTION;
255   ci->name = name;
256   ci->number = 1;
257   ci->ptr = NULL;
258   ci->u.sec = sec;
259   inspect_section(sec);
260   if (allow_unknown)
261     sec->flags |= SEC_FLAG_UNKNOWN;
262   ci++;
263   if (ci - sections.cfg >= (int) sections.size)
264   {
265     sections.cfg = xrealloc(sections.cfg, 2*sections.size * sizeof(struct cf_item));
266     bzero(sections.cfg + sections.size, sections.size * sizeof(struct cf_item));
267     sections.size *= 2;
268   }
269 }
270
271 void
272 cf_init_section(byte *name, struct cf_section *sec, void *ptr, uns do_bzero)
273 {
274   if (do_bzero) {
275     ASSERT(sec->size);
276     bzero(ptr, sec->size);
277   }
278   for (uns i=0; sec->cfg[i].cls; i++)
279     if (sec->cfg[i].cls == CC_SECTION)
280       cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr, 0);
281     else if (sec->cfg[i].cls == CC_LIST)
282       clist_init(ptr + (addr_int_t) sec->cfg[i].ptr);
283   if (sec->init) {
284     byte *msg = sec->init(ptr);
285     if (msg)
286       die("Cannot initialize section %s: %s", name, msg);
287   }
288 }
289
290 static void
291 global_init(void)
292 {
293   static uns initialized = 0;
294   if (initialized++)
295     return;
296   sections.flags |= SEC_FLAG_UNKNOWN;
297   sections.size = 0;                    // size of allocated array used to be stored here
298   cf_init_section(NULL, &sections, NULL, 0);
299 }
300
301 static byte *
302 commit_section(struct cf_section *sec, void *ptr, uns commit_all)
303 {
304   struct cf_item *ci;
305   byte *err;
306   for (ci=sec->cfg; ci->cls; ci++)
307     if (ci->cls == CC_SECTION) {
308       if ((err = commit_section(ci->u.sec, ptr + (addr_int_t) ci->ptr, commit_all))) {
309         log(L_ERROR, "Cannot commit section %s: %s", ci->name, err);
310         return "commit of a subsection failed";
311       }
312     } else if (ci->cls == CC_LIST) {
313       uns idx = 0;
314       CLIST_FOR_EACH(cnode *, n, * (clist*) (ptr + (addr_int_t) ci->ptr))
315         if (idx++, err = commit_section(ci->u.sec, n, commit_all)) {
316           log(L_ERROR, "Cannot commit node #%d of list %s: %s", idx, ci->name, err);
317           return "commit of a list failed";
318         }
319     }
320   if (sec->commit) {
321     /* We have to process the whole tree of sections even if just a few changes
322      * have been made, because there are dependencies between commit-hooks and
323      * hence we need to call them in a fixed order.  */
324 #define ARY_LT_X(ary,i,x) ary[i].sec < x.sec || ary[i].sec == x.sec && ary[i].ptr < x.ptr
325     struct dirty_section comp = { sec, ptr };
326     uns pos = BIN_SEARCH_FIRST_GE_CMP(dirty.ptr, dirties, comp, ARY_LT_X);
327
328     if (commit_all
329         || (pos < dirties && dirty.ptr[pos].sec == sec && dirty.ptr[pos].ptr == ptr)) {
330       return sec->commit(ptr);
331     }
332   }
333   return 0;
334 }
335
336 static struct cf_item *
337 find_item(struct cf_section *curr_sec, byte *name, byte **msg, void **ptr)
338 {
339   *msg = NULL;
340   if (name[0] == '^')                           // absolute name instead of relative
341     name++, curr_sec = &sections, *ptr = NULL;
342   if (!curr_sec)                                // don't even search in an unknown section
343     return NULL;
344   while (1)
345   {
346     if (curr_sec != &sections)
347       add_dirty(curr_sec, *ptr);
348     byte *c = strchr(name, '.');
349     if (c)
350       *c++ = 0;
351     struct cf_item *ci = find_subitem(curr_sec, name);
352     if (!ci->cls)
353     {
354       if (!(curr_sec->flags & SEC_FLAG_UNKNOWN))        // ignore silently unknown top-level sections and unknown attributes in flagged sections
355         *msg = cf_printf("Unknown item %s", name);
356       return NULL;
357     }
358     *ptr += (addr_int_t) ci->ptr;
359     if (!c)
360       return ci;
361     if (ci->cls != CC_SECTION)
362     {
363       *msg = cf_printf("Item %s is not a section", name);
364       return NULL;
365     }
366     curr_sec = ci->u.sec;
367     name = c;
368   }
369 }
370
371 byte *
372 cf_find_item(byte *name, struct cf_item *item)
373 {
374   byte *msg;
375   void *ptr;
376   struct cf_item *ci = find_item(&sections, name, &msg, &ptr);
377   if (msg)
378     return msg;
379   if (ci) {
380     *item = *ci;
381     item->ptr = ptr;
382   } else
383     bzero(item, sizeof(struct cf_item));
384   return NULL;
385 }
386
387 /* Safe loading and reloading */
388
389 static int load_file(byte *file);
390 static int load_string(byte *string);
391
392 int
393 cf_reload(byte *file)
394 {
395   journal_swap();
396   struct cf_journal_item *oldj = cf_journal_new_transaction(1);
397   uns ec = everything_committed;
398   everything_committed = 0;
399   int err = load_file(file);
400   if (!err)
401   {
402     for (struct old_pools *p=pools; p; p=pools)
403     {
404       pools = p->prev;
405       mp_delete(p->pool);
406     }
407     cf_journal_commit_transaction(1, NULL);
408   }
409   else
410   {
411     everything_committed = ec;
412     cf_journal_rollback_transaction(1, oldj);
413     journal_swap();
414   }
415   return err;
416 }
417
418 int
419 cf_load(byte *file)
420 {
421   struct cf_journal_item *oldj = cf_journal_new_transaction(1);
422   int err = load_file(file);
423   if (!err)
424     cf_journal_commit_transaction(1, oldj);
425   else
426     cf_journal_rollback_transaction(1, oldj);
427   return err;
428 }
429
430 int
431 cf_set(byte *string)
432 {
433   struct cf_journal_item *oldj = cf_journal_new_transaction(0);
434   int err = load_string(string);
435   if (!err)
436     cf_journal_commit_transaction(0, oldj);
437   else
438     cf_journal_rollback_transaction(0, oldj);
439   return err;
440 }
441
442 /* Parsers for standard types */
443
444 struct unit {
445   uns name;                     // one-letter name of the unit
446   uns num, den;                 // fraction
447 };
448
449 static const struct unit units[] = {
450   { 'd', 86400, 1 },
451   { 'h', 3600, 1 },
452   { 'k', 1000, 1 },
453   { 'm', 1000000, 1 },
454   { 'g', 1000000000, 1 },
455   { 'K', 1024, 1 },
456   { 'M', 1048576, 1 },
457   { 'G', 1073741824, 1 },
458   { '%', 1, 100 },
459   { 0, 0, 0 }
460 };
461
462 static const struct unit *
463 lookup_unit(byte *value, byte *end, byte **msg)
464 {
465   if (end && *end) {
466     if (end == value || end[1] || *end >= '0' && *end <= '9')
467       *msg = "Invalid number";
468     else {
469       for (const struct unit *u=units; u->name; u++)
470         if (u->name == *end)
471           return u;
472       *msg = "Invalid unit";
473     }
474   }
475   return NULL;
476 }
477
478 static char cf_rngerr[] = "Number out of range";
479
480 byte *
481 cf_parse_int(byte *str, int *ptr)
482 {
483   byte *msg = NULL;
484   if (!*str)
485     msg = "Missing number";
486   else {
487     const struct unit *u;
488     char *end;
489     errno = 0;
490     uns x = strtoul(str, &end, 0);
491     if (errno == ERANGE)
492       msg = cf_rngerr;
493     else if (u = lookup_unit(str, end, &msg)) {
494       u64 y = (u64)x * u->num;
495       if (y % u->den)
496         msg = "Number is not an integer";
497       else {
498         y /= u->den;
499         if (y > 0xffffffff)
500           msg = cf_rngerr;
501         *ptr = y;
502       }
503     } else
504       *ptr = x;
505   }
506   return msg;
507 }
508
509 byte *
510 cf_parse_u64(byte *str, u64 *ptr)
511 {
512   byte *msg = NULL;
513   if (!*str)
514     msg = "Missing number";
515   else {
516     const struct unit *u;
517     char *end;
518     errno = 0;
519     u64 x = strtoull(str, &end, 0);
520     if (errno == ERANGE)
521       msg = cf_rngerr;
522     else if (u = lookup_unit(str, end, &msg)) {
523       if (x > ~(u64)0 / u->num)
524         msg = "Number out of range";
525       else {
526         x *= u->num;
527         if (x % u->den)
528           msg = "Number is not an integer";
529         else
530           *ptr = x / u->den;
531       }
532     } else
533       *ptr = x;
534   }
535   return msg;
536 }
537
538 byte *
539 cf_parse_double(byte *str, double *ptr)
540 {
541   byte *msg = NULL;
542   if (!*str)
543     msg = "Missing number";
544   else {
545     const struct unit *u;
546     double x;
547     uns read_chars;
548     if (sscanf(str, "%lf%n", &x, &read_chars) != 1)
549       msg = "Invalid number";
550     else if (u = lookup_unit(str, str + read_chars, &msg))
551       *ptr = x * u->num / u->den;
552     else
553       *ptr = x;
554   }
555   return msg;
556 }
557
558 byte *
559 cf_parse_ip(byte *p, u32 *varp)
560 {
561   if (!*p)
562     return "Missing IP address";
563   uns x = 0;
564   char *p2;
565   if (*p == '0' && (p[1] | 32) == 'x' && Cxdigit(p[2])) {
566     errno = 0;
567     x = strtoul(p, &p2, 16);
568     if (errno == ERANGE || x > 0xffffffff)
569       goto error;
570     p = p2;
571   }
572   else
573     for (uns i = 0; i < 4; i++) {
574       if (i) {
575         if (*p++ != '.')
576           goto error;
577       }
578       if (!Cdigit(*p))
579         goto error;
580       errno = 0;
581       uns y = strtoul(p, &p2, 10);
582       if (errno == ERANGE || p2 == (char*) p || y > 255)
583         goto error;
584       p = p2;
585       x = (x << 8) + y;
586     }
587   *varp = x;
588   return *p ? "Trailing characters" : NULL;
589 error:
590   return "Invalid IP address";
591 }
592
593 static byte *
594 cf_parse_string(byte *str, byte **ptr)
595 {
596   *ptr = cf_strdup(str);
597   return NULL;
598 }
599
600 static byte *
601 cf_parse_lookup(byte *str, int *ptr, byte **t)
602 {
603   byte **n = t;
604   uns total_len = 0;
605   while (*n && strcasecmp(*n, str)) {
606     total_len += strlen(*n) + 2;
607     n++;
608   }
609   if (*n) {
610     *ptr = n - t;
611     return NULL;
612   }
613   byte *err = cf_malloc(total_len + strlen(str) + 60), *c = err;
614   c += sprintf(err, "Invalid value %s, possible values are: ", str);
615   for (n=t; *n; n++)
616     c+= sprintf(c, "%s, ", *n);
617   if (*t)
618     c[-2] = 0;
619   *ptr = -1;
620   return err;
621 }
622
623 /* Register size of and parser for each basic type */
624
625 typedef byte *cf_basic_parser(byte *str, void *ptr);
626 static struct {
627   uns size;
628   void *parser;
629 } parsers[] = {
630   { sizeof(int), cf_parse_int },
631   { sizeof(u64), cf_parse_u64 },
632   { sizeof(double), cf_parse_double },
633   { sizeof(u32), cf_parse_ip },
634   { sizeof(byte*), cf_parse_string },
635   { sizeof(int), NULL },                        // lookups are parsed extra
636   { 0, NULL },                                  // user-defined types are parsed extra
637 };
638
639 static inline uns
640 type_size(enum cf_type type, struct cf_user_type *utype)
641 {
642   if (type < CT_USER)
643     return parsers[type].size;
644   else
645     return utype->size;
646 }
647
648 static byte *
649 cf_parse_ary(uns number, byte **pars, void *ptr, enum cf_type type, union cf_union *u)
650 {
651   for (uns i=0; i<number; i++)
652   {
653     byte *msg;
654     uns size = type_size(type, u->utype);
655     if (type < CT_LOOKUP)
656       msg = ((cf_basic_parser*) parsers[type].parser) (pars[i], ptr + i * size);
657     else if (type == CT_LOOKUP)
658       msg = cf_parse_lookup(pars[i], ptr + i * size, u->lookup);
659     else if (type == CT_USER)
660       msg = u->utype->parser(pars[i], ptr + i * size);
661     else
662       ASSERT(0);
663     if (msg)
664       return cf_printf("Cannot parse item %d: %s", i+1, msg);
665   }
666   return NULL;
667 }
668
669 /* Interpreter */
670
671 #define T(x) #x,
672 static byte *op_names[] = { CF_OPERATIONS };
673 #undef T
674
675 #define OP_MASK 0xff            // only get the operation
676 #define OP_OPEN 0x100           // here we only get an opening brace instead of parameters
677 #define OP_1ST 0x200            // in the 1st phase selectors are recorded into the mask
678 #define OP_2ND 0x400            // in the 2nd phase real data are entered
679
680 static byte *
681 interpret_set_dynamic(struct cf_item *item, int number, byte **pars, void **ptr)
682 {
683   enum cf_type type = item->type;
684   cf_journal_block(ptr, sizeof(void*));
685   // boundary checks done by the caller
686   uns size = type_size(item->type, item->u.utype);
687   ASSERT(size >= sizeof(uns));
688   *ptr = cf_malloc((number+1) * size) + size;
689   * (uns*) (*ptr - size) = number;
690   return cf_parse_ary(number, pars, *ptr, type, &item->u);
691 }
692
693 static byte *
694 interpret_add_dynamic(struct cf_item *item, int number, byte **pars, int *processed, void **ptr, enum cf_operation op)
695 {
696   enum cf_type type = item->type;
697   void *old_p = *ptr;
698   uns size = type_size(item->type, item->u.utype);
699   ASSERT(size >= sizeof(uns));
700   int old_nr = old_p ? * (int*) (old_p - size) : 0;
701   int taken = MIN(number, ABS(item->number)-old_nr);
702   *processed = taken;
703   // stretch the dynamic array
704   void *new_p = cf_malloc((old_nr + taken + 1) * size) + size;
705   * (uns*) (new_p - size) = old_nr + taken;
706   cf_journal_block(ptr, sizeof(void*));
707   *ptr = new_p;
708   if (op == OP_APPEND) {
709     memcpy(new_p, old_p, old_nr * size);
710     return cf_parse_ary(taken, pars, new_p + old_nr * size, type, &item->u);
711   } else if (op == OP_PREPEND) {
712     memcpy(new_p + taken * size, old_p, old_nr * size);
713     return cf_parse_ary(taken, pars, new_p, type, &item->u);
714   } else
715     return cf_printf("Dynamic arrays do not support operation %s", op_names[op]);
716 }
717
718 static byte *interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic);
719
720 static byte *
721 interpret_section(struct cf_section *sec, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
722 {
723   add_dirty(sec, ptr);
724   *processed = 0;
725   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
726   {
727     int taken;
728     byte *msg = interpret_set_item(ci, number, pars, &taken, ptr + (addr_int_t) ci->ptr, allow_dynamic && !ci[1].cls);
729     if (msg)
730       return cf_printf("Item %s: %s", ci->name, msg);
731     *processed += taken;
732     number -= taken;
733     pars += taken;
734     if (!number)                // stop parsing, because many parsers would otherwise complain that number==0
735       break;
736   }
737   return NULL;
738 }
739
740 static void
741 add_to_list(cnode *where, cnode *new_node, enum cf_operation op)
742 {
743   switch (op)
744   {
745     case OP_EDIT:               // edition has been done in-place
746       break;
747     case OP_REMOVE:
748       CF_JOURNAL_VAR(where->prev->next);
749       CF_JOURNAL_VAR(where->next->prev);
750       clist_remove(where);
751       break;
752     case OP_AFTER:              // implementation dependend (prepend_head = after(list)), and where==list, see clists.h:74
753     case OP_PREPEND:
754     case OP_COPY:
755       CF_JOURNAL_VAR(where->next->prev);
756       CF_JOURNAL_VAR(where->next);
757       clist_insert_after(new_node, where);
758       break;
759     case OP_BEFORE:             // implementation dependend (append_tail = before(list))
760     case OP_APPEND:
761     case OP_SET:
762       CF_JOURNAL_VAR(where->prev->next);
763       CF_JOURNAL_VAR(where->prev);
764       clist_insert_before(new_node, where);
765       break;
766     default:
767       ASSERT(0);
768   }
769 }
770
771 static byte *
772 interpret_add_list(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, enum cf_operation op)
773 {
774   if (op >= OP_REMOVE)
775     return cf_printf("You have to open a block for operation %s", op_names[op]);
776   if (!number)
777     return "Nothing to add to the list";
778   struct cf_section *sec = item->u.sec;
779   *processed = 0;
780   while (number > 0)
781   {
782     void *node = cf_malloc(sec->size);
783     cf_init_section(item->name, sec, node, 1);
784     add_to_list(ptr, node, op);
785     int taken;
786     /* If the node contains any dynamic attribute at the end, we suppress
787      * auto-repetition here and pass the flag inside instead.  */
788     TRY( interpret_section(sec, number, pars, &taken, node, sec->flags & SEC_FLAG_DYNAMIC) );
789     *processed += taken;
790     number -= taken;
791     pars += taken;
792     if (sec->flags & SEC_FLAG_DYNAMIC)
793       break;
794   }
795   return NULL;
796 }
797
798 static byte *
799 interpret_set_item(struct cf_item *item, int number, byte **pars, int *processed, void *ptr, uns allow_dynamic)
800 {
801   int taken;
802   switch (item->cls)
803   {
804     case CC_STATIC:
805       if (!number)
806         return "Missing value";
807       taken = MIN(number, item->number);
808       *processed = taken;
809       uns size = type_size(item->type, item->u.utype);
810       cf_journal_block(ptr, taken * size);
811       return cf_parse_ary(taken, pars, ptr, item->type, &item->u);
812     case CC_DYNAMIC:
813       if (!allow_dynamic)
814         return "Dynamic array cannot be used here";
815       taken = MIN(number, ABS(item->number));
816       *processed = taken;
817       return interpret_set_dynamic(item, taken, pars, ptr);
818     case CC_PARSER:
819       if (item->number < 0 && !allow_dynamic)
820         return "Parsers with variable number of parameters cannot be used here";
821       if (item->number > 0 && number < item->number)
822         return "Not enough parameters available for the parser";
823       taken = MIN(number, ABS(item->number));
824       *processed = taken;
825       for (int i=0; i<taken; i++)
826         pars[i] = cf_strdup(pars[i]);
827       return item->u.par(taken, pars, ptr);
828     case CC_SECTION:
829       return interpret_section(item->u.sec, number, pars, processed, ptr, allow_dynamic);
830     case CC_LIST:
831       if (!allow_dynamic)
832         return "Lists cannot be used here";
833       return interpret_add_list(item, number, pars, processed, ptr, OP_SET);
834     default:
835       ASSERT(0);
836   }
837 }
838
839 static byte *
840 interpret_clear(struct cf_item *item, void *ptr)
841 {
842   if (item->cls == CC_LIST) {
843     cf_journal_block(ptr, sizeof(clist));
844     clist_init(ptr);
845   } else if (item->cls == CC_DYNAMIC) {
846     cf_journal_block(ptr, sizeof(void *));
847     * (void**) ptr = NULL;
848   } else
849     return "The item is not a list or a dynamic array";
850   return NULL;
851 }
852
853 static int
854 cmp_items(void *i1, void *i2, struct cf_item *item)
855 {
856   ASSERT(item->cls == CC_STATIC);
857   i1 += (addr_int_t) item->ptr;
858   i2 += (addr_int_t) item->ptr;
859   if (item->type == CT_STRING)
860     return strcmp(* (byte**) i1, * (byte**) i2);
861   else                          // all numeric types
862     return memcmp(i1, i2, type_size(item->type, item->u.utype));
863 }
864
865 static void *
866 find_list_node(clist *list, void *query, struct cf_section *sec, u32 mask)
867 {
868   CLIST_FOR_EACH(cnode *, n, *list)
869   {
870     uns found = 1;
871     for (uns i=0; i<32; i++)
872       if (mask & (1<<i))
873         if (cmp_items(n, query, sec->cfg+i))
874         {
875           found = 0;
876           break;
877         }
878     if (found)
879       return n;
880   }
881   return NULL;
882 }
883
884 static byte *
885 record_selector(struct cf_item *item, struct cf_section *sec, u32 *mask)
886 {
887   uns nr = sec->flags & SEC_FLAG_NUMBER;
888   if (item >= sec->cfg && item < sec->cfg + nr) // setting an attribute relative to this section
889   {
890     uns i = item - sec->cfg;
891     if (i >= 32)
892       return "Cannot select list nodes by this attribute";
893     if (sec->cfg[i].cls != CC_STATIC)
894       return "Selection can only be done based on basic attributes";
895     *mask |= 1 << i;
896   }
897   return NULL;
898 }
899
900 #define MAX_STACK_SIZE  100
901 static struct item_stack {
902   struct cf_section *sec;       // nested section
903   void *base_ptr;               // because original pointers are often relative
904   enum cf_operation op;         // it is performed when a closing brace is encountered
905   void *list;                   // list the operations should be done on
906   u32 mask;                     // bit array of selectors searching in a list
907   struct cf_item *item;         // cf_item of the list
908 } stack[MAX_STACK_SIZE];
909 static uns level;
910
911 static byte *
912 opening_brace(struct cf_item *item, void *ptr, enum cf_operation op)
913 {
914   if (level >= MAX_STACK_SIZE-1)
915     return "Too many nested sections";
916   stack[++level] = (struct item_stack) {
917     .sec = NULL,
918     .base_ptr = NULL,
919     .op = op & OP_MASK,
920     .list = NULL,
921     .mask = 0,
922     .item = NULL,
923   };
924   if (!item)                    // unknown is ignored; we just need to trace recursion
925     return NULL;
926   stack[level].sec = item->u.sec;
927   if (item->cls == CC_SECTION)
928   {
929     stack[level].base_ptr = ptr;
930     stack[level].op = OP_EDIT | OP_2ND; // this list operation does nothing
931   }
932   else if (item->cls == CC_LIST)
933   {
934     stack[level].base_ptr = cf_malloc(item->u.sec->size);
935     cf_init_section(item->name, item->u.sec, stack[level].base_ptr, 1);
936     stack[level].list = ptr;
937     stack[level].item = item;
938     if ((op & OP_MASK) < OP_REMOVE) {
939       add_to_list(ptr, stack[level].base_ptr, op & OP_MASK);
940       stack[level].op |= OP_2ND;
941     } else
942       stack[level].op |= OP_1ST;
943   }
944   else
945     return "Opening brace can only be used on sections and lists";
946   return NULL;
947 }
948
949 static byte *
950 closing_brace(struct item_stack *st, enum cf_operation op, int number, byte **pars)
951 {
952   if (st->op == OP_CLOSE)       // top-level
953     return "Unmatched } parenthesis";
954   if (!st->sec) {               // dummy run on unknown section
955     if (!(op & OP_OPEN))
956       level--;
957     return NULL;
958   }
959   enum cf_operation pure_op = st->op & OP_MASK;
960   if (st->op & OP_1ST)
961   {
962     st->list = find_list_node(st->list, st->base_ptr, st->sec, st->mask);
963     if (!st->list)
964       return "Cannot find a node matching the query";
965     if (pure_op != OP_REMOVE)
966     {
967       if (pure_op == OP_EDIT)
968         st->base_ptr = st->list;
969       else if (pure_op == OP_AFTER || pure_op == OP_BEFORE)
970         cf_init_section(st->item->name, st->sec, st->base_ptr, 1);
971       else if (pure_op == OP_COPY) {
972         if (st->sec->flags & SEC_FLAG_CANT_COPY)
973           return cf_printf("Item %s cannot be copied", st->item->name);
974         memcpy(st->base_ptr, st->list, st->sec->size);  // strings and dynamic arrays are shared
975         if (st->sec->copy)
976           TRY( st->sec->copy(st->base_ptr, st->list) );
977       } else
978         ASSERT(0);
979       if (op & OP_OPEN) {       // stay at the same recursion level
980         st->op = (st->op | OP_2ND) & ~OP_1ST;
981         add_to_list(st->list, st->base_ptr, pure_op);
982         return NULL;
983       }
984       int taken;                // parse parameters on 1 line immediately
985       TRY( interpret_section(st->sec, number, pars, &taken, st->base_ptr, 1) );
986       number -= taken;
987       pars += taken;
988       // and fall-thru to the 2nd phase
989     }
990     add_to_list(st->list, st->base_ptr, pure_op);
991   }
992   level--;
993   if (number)
994     return "No parameters expected after the }";
995   else if (op & OP_OPEN)
996     return "No { is expected";
997   else
998     return NULL;
999 }
1000
1001 static byte *
1002 interpret_line(byte *name, enum cf_operation op, int number, byte **pars)
1003 {
1004   byte *msg;
1005   if ((op & OP_MASK) == OP_CLOSE)
1006     return closing_brace(stack+level, op, number, pars);
1007   void *ptr = stack[level].base_ptr;
1008   struct cf_item *item = find_item(stack[level].sec, name, &msg, &ptr);
1009   if (msg)
1010     return msg;
1011   if (stack[level].op & OP_1ST)
1012     TRY( record_selector(item, stack[level].sec, &stack[level].mask) );
1013   if (op & OP_OPEN) {           // the operation will be performed after the closing brace
1014     if (number)
1015       return "Cannot open a block after a parameter has been passed on a line";
1016     return opening_brace(item, ptr, op);
1017   }
1018   if (!item)                    // ignored item in an unknown section
1019     return NULL;
1020   op &= OP_MASK;
1021
1022   int taken;                    // process as many parameters as possible
1023   if (op == OP_CLEAR)
1024     taken = 0, msg = interpret_clear(item, ptr);
1025   else if (op == OP_SET)
1026     msg = interpret_set_item(item, number, pars, &taken, ptr, 1);
1027   else if (item->cls == CC_DYNAMIC)
1028     msg = interpret_add_dynamic(item, number, pars, &taken, ptr, op);
1029   else if (item->cls == CC_LIST)
1030     msg = interpret_add_list(item, number, pars, &taken, ptr, op);
1031   else
1032     return cf_printf("Operation %s not supported on attribute %s", op_names[op], name);
1033   if (msg)
1034     return msg;
1035   if (taken < number)
1036     return cf_printf("Too many parameters: %d>%d", number, taken);
1037
1038   return NULL;
1039 }
1040
1041 byte *
1042 cf_write_item(struct cf_item *item, enum cf_operation op, int number, byte **pars)
1043 {
1044   byte *msg;
1045   int taken;
1046   switch (op) {
1047     case OP_SET:
1048       msg = interpret_set_item(item, number, pars, &taken, item->ptr, 1);
1049       break;
1050     case OP_CLEAR:
1051       taken = 0;
1052       msg = interpret_clear(item, item->ptr);
1053       break;
1054     case OP_APPEND:
1055     case OP_PREPEND:
1056       if (item->cls == CC_DYNAMIC)
1057         msg = interpret_add_dynamic(item, number, pars, &taken, item->ptr, op);
1058       else if (item->cls == CC_LIST)
1059         msg = interpret_add_list(item, number, pars, &taken, item->ptr, op);
1060       else
1061         return "The attribute class does not support append/prepend";
1062       break;
1063     default:
1064       return "Unsupported operation";
1065   }
1066   if (msg)
1067     return msg;
1068   if (taken < number)
1069     return "Too many parameters";
1070   return NULL;
1071 }
1072
1073 static void
1074 init_stack(void)
1075 {
1076   global_init();
1077   level = 0;
1078   stack[0] = (struct item_stack) {
1079     .sec = &sections,
1080     .base_ptr = NULL,
1081     .op = OP_CLOSE,
1082     .list = NULL,
1083     .mask = 0,
1084     .item = NULL
1085   };
1086 }
1087
1088 static uns postpone_commit;                     // only for cf_getopt()
1089
1090 static int
1091 done_stack(void)
1092 {
1093   if (level > 0) {
1094     log(L_ERROR, "Unterminated block");
1095     return 1;
1096   }
1097   sort_dirty();
1098   if (postpone_commit)
1099     return 0;
1100   if (commit_section(&sections, NULL, !everything_committed))
1101     return 1;
1102   everything_committed = 1;
1103   dirties = 0;
1104   return 0;
1105 }
1106
1107 static void
1108 final_commit(void)
1109 {
1110   if (postpone_commit) {
1111     postpone_commit = 0;
1112     if (done_stack())
1113       die("Cannot commit after the initialization");
1114   }
1115 }
1116
1117 /* Text file parser */
1118
1119 static byte *name_parse_fb;
1120 static struct fastbuf *parse_fb;
1121 static uns line_num;
1122
1123 #define MAX_LINE        4096
1124 static byte line_buf[MAX_LINE];
1125 static byte *line = line_buf;
1126
1127 #include "lib/bbuf.h"
1128 static bb_t copy_buf;
1129 static uns copied;
1130
1131 #define GBUF_TYPE       uns
1132 #define GBUF_PREFIX(x)  split_##x
1133 #include "lib/gbuf.h"
1134 static split_t word_buf;
1135 static uns words;
1136 static uns ends_by_brace;               // the line is ended by "{"
1137
1138 static int
1139 get_line(void)
1140 {
1141   if (!bgets(parse_fb, line_buf, MAX_LINE))
1142     return 0;
1143   line_num++;
1144   line = line_buf;
1145   while (Cblank(*line))
1146     line++;
1147   return 1;
1148 }
1149
1150 static void
1151 append(byte *start, byte *end)
1152 {
1153   uns len = end - start;
1154   bb_grow(&copy_buf, copied + len + 1);
1155   memcpy(copy_buf.ptr + copied, start, len);
1156   copied += len + 1;
1157   copy_buf.ptr[copied-1] = 0;
1158 }
1159
1160 #define CONTROL_CHAR(x) (x == '{' || x == '}' || x == ';')
1161   // these characters separate words like blanks
1162
1163 static byte *
1164 get_word(uns is_command_name)
1165 {
1166   if (*line == '\'') {
1167     line++;
1168     while (1) {
1169       byte *start = line;
1170       while (*line && *line != '\'')
1171         line++;
1172       append(start, line);
1173       if (*line)
1174         break;
1175       copy_buf.ptr[copied-1] = '\n';
1176       if (!get_line())
1177         return "Unterminated apostrophe word at the end";
1178     }
1179     line++;
1180
1181   } else if (*line == '"') {
1182     line++;
1183     uns start_copy = copied;
1184     while (1) {
1185       byte *start = line;
1186       uns escape = 0;
1187       while (*line) {
1188         if (*line == '"' && !escape)
1189           break;
1190         else if (*line == '\\')
1191           escape ^= 1;
1192         else
1193           escape = 0;
1194         line++;
1195       }
1196       append(start, line);
1197       if (*line)
1198         break;
1199       if (!escape)
1200         copy_buf.ptr[copied-1] = '\n';
1201       else // merge two lines
1202         copied -= 2;
1203       if (!get_line())
1204         return "Unterminated quoted word at the end";
1205     }
1206     line++;
1207
1208     byte *tmp = stk_str_unesc(copy_buf.ptr + start_copy);
1209     uns l = strlen(tmp);
1210     bb_grow(&copy_buf, start_copy + l + 1);
1211     strcpy(copy_buf.ptr + start_copy, tmp);
1212     copied = start_copy + l + 1;
1213
1214   } else {
1215     // promised that *line is non-null and non-blank
1216     byte *start = line;
1217     while (*line && !Cblank(*line) && !CONTROL_CHAR(*line)
1218         && (*line != '=' || !is_command_name))
1219       line++;
1220     if (*line == '=') {                         // nice for setting from a command-line
1221       if (line == start)
1222         return "Assignment without a variable";
1223       *line = ' ';
1224     }
1225     if (line == start)                          // already the first char is control
1226       line++;
1227     append(start, line);
1228   }
1229   while (Cblank(*line))
1230     line++;
1231   return NULL;
1232 }
1233
1234 static byte *
1235 get_token(uns is_command_name, byte **msg)
1236 {
1237   *msg = NULL;
1238   while (1) {
1239     if (!*line || *line == '#') {
1240       if (!is_command_name || !get_line())
1241         return NULL;
1242     } else if (*line == ';') {
1243       *msg = get_word(0);
1244       if (!is_command_name || *msg)
1245         return NULL;
1246     } else if (*line == '\\' && !line[1]) {
1247       if (!get_line()) {
1248         *msg = "Last line ends by a backslash";
1249         return NULL;
1250       }
1251       if (!*line || *line == '#')
1252         log(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb, line_num);
1253     } else {
1254       split_grow(&word_buf, words+1);
1255       uns start = copied;
1256       word_buf.ptr[words++] = copied;
1257       *msg = get_word(is_command_name);
1258       return *msg ? NULL : copy_buf.ptr + start;
1259     }
1260   }
1261 }
1262
1263 static byte *
1264 split_command(void)
1265 {
1266   words = copied = ends_by_brace = 0;
1267   byte *msg, *start_word;
1268   if (!(start_word = get_token(1, &msg)))
1269     return msg;
1270   if (*start_word == '{')                       // only one opening brace
1271     return "Unexpected opening brace";
1272   while (*line != '}')                          // stays for the next time
1273   {
1274     if (!(start_word = get_token(0, &msg)))
1275       return msg;
1276     if (*start_word == '{') {
1277       words--;                                  // discard the brace
1278       ends_by_brace = 1;
1279       break;
1280     }
1281   }
1282   return NULL;
1283 }
1284
1285 /* Parsing multiple files */
1286
1287 static byte *
1288 parse_fastbuf(byte *name_fb, struct fastbuf *fb, uns depth)
1289 {
1290   byte *msg;
1291   name_parse_fb = name_fb;
1292   parse_fb = fb;
1293   line_num = 0;
1294   line = line_buf;
1295   *line = 0;
1296   while (1)
1297   {
1298     msg = split_command();
1299     if (msg)
1300       goto error;
1301     if (!words)
1302       return NULL;
1303     byte *name = copy_buf.ptr + word_buf.ptr[0];
1304     byte *pars[words-1];
1305     for (uns i=1; i<words; i++)
1306       pars[i-1] = copy_buf.ptr + word_buf.ptr[i];
1307     if (!strcasecmp(name, "include"))
1308     {
1309       if (words != 2)
1310         msg = "Expecting one filename";
1311       else if (depth > 8)
1312         msg = "Too many nested files";
1313       else if (*line && *line != '#')           // because the contents of line_buf is not re-entrant and will be cleared
1314         msg = "The input command must be the last one on a line";
1315       if (msg)
1316         goto error;
1317       struct fastbuf *new_fb = bopen_try(pars[0], O_RDONLY, 1<<14);
1318       if (!new_fb) {
1319         msg = cf_printf("Cannot open file %s: %m", pars[0]);
1320         goto error;
1321       }
1322       uns ll = line_num;
1323       msg = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1);
1324       line_num = ll;
1325       bclose(new_fb);
1326       if (msg)
1327         goto error;
1328       parse_fb = fb;
1329       continue;
1330     }
1331     enum cf_operation op;
1332     byte *c = strchr(name, ':');
1333     if (!c)
1334       op = strcmp(name, "}") ? OP_SET : OP_CLOSE;
1335     else {
1336       *c++ = 0;
1337       switch (Clocase(*c)) {
1338         case 's': op = OP_SET; break;
1339         case 'c': op = Clocase(c[1]) == 'l' ? OP_CLEAR: OP_COPY; break;
1340         case 'a': op = Clocase(c[1]) == 'p' ? OP_APPEND : OP_AFTER; break;
1341         case 'p': op = OP_PREPEND; break;
1342         case 'r': op = OP_REMOVE; break;
1343         case 'e': op = OP_EDIT; break;
1344         case 'b': op = OP_BEFORE; break;
1345         default: op = OP_SET; break;
1346       }
1347       if (strcasecmp(c, op_names[op])) {
1348         msg = cf_printf("Unknown operation %s", c);
1349         goto error;
1350       }
1351     }
1352     if (ends_by_brace)
1353       op |= OP_OPEN;
1354     msg = interpret_line(name, op, words-1, pars);
1355     if (msg)
1356       goto error;
1357   }
1358 error:
1359   log(L_ERROR, "File %s, line %d: %s", name_fb, line_num, msg);
1360   return "included from here";
1361 }
1362
1363 #ifndef DEFAULT_CONFIG
1364 #define DEFAULT_CONFIG NULL
1365 #endif
1366 byte *cf_def_file = DEFAULT_CONFIG;
1367
1368 static int
1369 load_file(byte *file)
1370 {
1371   init_stack();
1372   struct fastbuf *fb = bopen_try(file, O_RDONLY, 1<<14);
1373   if (!fb) {
1374     log(L_ERROR, "Cannot open %s: %m", file);
1375     return 1;
1376   }
1377   byte *msg = parse_fastbuf(file, fb, 0);
1378   bclose(fb);
1379   int err = !!msg || done_stack();
1380   if (!err)
1381     cf_def_file = NULL;
1382   return err;
1383 }
1384
1385 static int
1386 load_string(byte *string)
1387 {
1388   init_stack();
1389   struct fastbuf fb;
1390   fbbuf_init_read(&fb, string, strlen(string), 0);
1391   byte *msg = parse_fastbuf("memory string", &fb, 0);
1392   return !!msg || done_stack();
1393 }
1394
1395 /* Command-line parser */
1396
1397 static void
1398 load_default(void)
1399 {
1400   if (cf_def_file)
1401     if (cf_load(cf_def_file))
1402       die("Cannot load default config %s", cf_def_file);
1403 }
1404
1405 int
1406 cf_getopt(int argc, char * const argv[], const char *short_opts, const struct option *long_opts, int *long_index)
1407 {
1408   static int other_options = 0;
1409   while (1) {
1410     int res = getopt_long (argc, argv, short_opts, long_opts, long_index);
1411     if (res == 'S' || res == 'C' || res == 0x64436667)
1412     {
1413       if (other_options)
1414         die("The -S and -C options must precede all other arguments");
1415       if (res == 'S') {
1416         postpone_commit = 1;
1417         load_default();
1418         if (cf_set(optarg))
1419           die("Cannot set %s", optarg);
1420       } else if (res == 'C') {
1421         postpone_commit = 1;
1422         if (cf_load(optarg))
1423           die("Cannot load config file %s", optarg);
1424       }
1425 #ifdef CONFIG_DEBUG
1426       else {   /* --dumpconfig */
1427         load_default();
1428         final_commit();
1429         struct fastbuf *b = bfdopen(1, 4096);
1430         cf_dump_sections(b);
1431         bclose(b);
1432         exit(0);
1433       }
1434 #endif
1435     } else {
1436       /* unhandled option or end of options */
1437       if (res != ':' && res != '?')
1438         load_default();
1439       final_commit();
1440       other_options++;
1441       return res;
1442     }
1443   }
1444 }
1445
1446 /* Debug dumping */
1447
1448 static void
1449 spaces(struct fastbuf *fb, uns nr)
1450 {
1451   for (uns i=0; i<nr; i++)
1452     bputs(fb, "  ");
1453 }
1454
1455 static void
1456 dump_basic(struct fastbuf *fb, void *ptr, enum cf_type type, union cf_union *u)
1457 {
1458   switch (type) {
1459     case CT_INT:        bprintf(fb, "%d ", *(uns*)ptr); break;
1460     case CT_U64:        bprintf(fb, "%llu ", *(u64*)ptr); break;
1461     case CT_DOUBLE:     bprintf(fb, "%lg ", *(double*)ptr); break;
1462     case CT_IP:         bprintf(fb, "%08x ", *(uns*)ptr); break;
1463     case CT_STRING:
1464       if (*(byte**)ptr)
1465         bprintf(fb, "'%s' ", *(byte**)ptr);
1466       else
1467         bprintf(fb, "NULL ");
1468       break;
1469     case CT_LOOKUP:     bprintf(fb, "%s ", *(int*)ptr >= 0 ? u->lookup[ *(int*)ptr ] : (byte*) "???"); break;
1470     case CT_USER:
1471       if (u->utype->dumper)
1472         u->utype->dumper(fb, ptr);
1473       else
1474         bprintf(fb, "??? ");
1475       break;
1476   }
1477 }
1478
1479 static void dump_section(struct fastbuf *fb, struct cf_section *sec, int level, void *ptr);
1480
1481 static byte *class_names[] = { "end", "static", "dynamic", "parser", "section", "list" };
1482 static byte *type_names[] = { "int", "u64", "double", "ip", "string", "lookup", "user" };
1483
1484 static void
1485 dump_item(struct fastbuf *fb, struct cf_item *item, int level, void *ptr)
1486 {
1487   ptr += (addr_int_t) item->ptr;
1488   enum cf_type type = item->type;
1489   uns size = type_size(item->type, item->u.utype);
1490   int i;
1491   spaces(fb, level);
1492   bprintf(fb, "%s: C%s #", item->name, class_names[item->cls]);
1493   if (item->number == CF_ANY_NUM)
1494     bputs(fb, "any ");
1495   else
1496     bprintf(fb, "%d ", item->number);
1497   if (item->cls == CC_STATIC || item->cls == CC_DYNAMIC) {
1498     bprintf(fb, "T%s ", type_names[type]);
1499     if (item->type == CT_USER)
1500       bprintf(fb, "U%s S%d ", item->u.utype->name, size);
1501   }
1502   if (item->cls == CC_STATIC) {
1503     for (i=0; i<item->number; i++)
1504       dump_basic(fb, ptr + i * size, type, &item->u);
1505   } else if (item->cls == CC_DYNAMIC) {
1506     ptr = * (void**) ptr;
1507     if (ptr) {
1508       int real_nr = * (int*) (ptr - size);
1509       bprintf(fb, "N%d ", real_nr);
1510       for (i=0; i<real_nr; i++)
1511         dump_basic(fb, ptr + i * size, type, &item->u);
1512     } else
1513       bprintf(fb, "NULL ");
1514   }
1515   bputc(fb, '\n');
1516   if (item->cls == CC_SECTION)
1517     dump_section(fb, item->u.sec, level+1, ptr);
1518   else if (item->cls == CC_LIST) {
1519     uns idx = 0;
1520     CLIST_FOR_EACH(cnode *, n, * (clist*) ptr) {
1521       spaces(fb, level+1);
1522       bprintf(fb, "item %d\n", ++idx);
1523       dump_section(fb, item->u.sec, level+2, n);
1524     }
1525   }
1526 }
1527
1528 static void
1529 dump_section(struct fastbuf *fb, struct cf_section *sec, int level, void *ptr)
1530 {
1531   spaces(fb, level);
1532   bprintf(fb, "S%d F%x:\n", sec->size, sec->flags);
1533   for (struct cf_item *item=sec->cfg; item->cls; item++)
1534     dump_item(fb, item, level, ptr);
1535 }
1536
1537 void
1538 cf_dump_sections(struct fastbuf *fb)
1539 {
1540   dump_section(fb, &sections, 0, NULL);
1541 }
1542
1543 /* TODO
1544  * - more space efficient journal
1545  */