chiark / gitweb /
Import release 0.1.3
[secnet.git] / conffile.c
1 /*
2  * $Log$
3  */
4
5 /* conffile.c - process the configuration file */
6
7 /* #define DUMP_PARSE_TREE */
8
9 #include "secnet.h"
10 #include <stdio.h>
11 #include <string.h>
12 #include "conffile.h"
13 #include "conffile_internal.h"
14 #include "util.h"
15 #include "modules.h"
16
17 static struct cloc no_loc={"none",0};
18
19 struct atomlist {
20     struct atomlist *next;
21     atom_t a;
22 };
23
24 struct entry {
25     struct entry *next;
26     atom_t key;
27     list_t *val;
28 };
29
30 struct searchlist {
31     struct dict *d;
32     struct searchlist *next;
33 };
34
35 struct dict {
36     struct dict *parent;
37     struct searchlist *search;
38     struct entry *entries;
39     uint32_t size;
40 };
41
42 static struct atomlist *atoms=NULL;
43
44 static void process_alist(dict_t *context, struct p_node *c);
45 static list_t *process_invocation(dict_t *context, struct p_node *i);
46
47 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
48 {
49     struct entry *i;
50     for (i=dict->entries; i; i=i->next) {
51         if (key==i->key) return i->val;
52     }
53     return NULL;
54 }
55
56 static list_t *dict_ilookup(dict_t *dict, atom_t key)
57 {
58     dict_t *d;
59     list_t *v;
60
61     v=dict_ilookup_primitive(dict, key);
62     if (v) return v;
63     /* Check dictionaries in search path */
64 /* XXX */
65     /* Check lexical parents */
66     for (d=dict; d; d=d->parent) {
67         v=dict_ilookup_primitive(d, key);
68         if (v) return v;
69     }
70     return NULL;
71 }
72
73 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
74 {
75     struct entry *e;
76     /* XXX May want to permit redefinition of keys in the future */
77     /* (although it could be very confusing) */
78     if (dict_ilookup_primitive(dict, key)) {
79         fatal("duplicate key \"%s\" in dictionary\n",key);
80     }
81     e=safe_malloc(sizeof(*e),"dict_add");
82     e->next=dict->entries;
83     e->key=key;
84     e->val=val;
85     dict->entries=e;
86     dict->size++;
87 }
88
89 /***** Functions beyond this point are private to the config system *****/
90
91 static dict_t *dict_new(dict_t *parent)
92 {
93     dict_t *d;
94
95     d=safe_malloc(sizeof(*d),"dict_new");
96     d->parent=parent;
97     d->search=NULL;
98     d->entries=NULL;
99     d->size=0;
100     return d;
101 }
102
103 static struct p_node *node_copy(struct p_node *n)
104 {
105     struct p_node *r;
106     r=safe_malloc(sizeof(*r),"node_copy");
107     *r=*n;
108     return r;
109 }
110
111 static struct p_node *list_reverse(struct p_node *list)
112 {
113     struct p_node *rl=NULL, *i, *n;
114
115     for (i=list; i; i=i->r) {
116         n=node_copy(i);
117         n->r=rl;
118         rl=n;
119     }
120     return rl;
121 }
122
123 /* Since we use left-recursion in the parser for efficiency, sequences
124    end up "backwards" in the parse tree. Rather than have complicated
125    code for, eg. processing assignments in the right order, we reverse
126    these sequences here. */
127 static void ptree_mangle(struct p_node *t)
128 {
129     if (!t) return;
130     ptree_mangle(t->l);
131     ptree_mangle(t->r);
132     switch (t->type) {
133     case T_DICT:
134         /* ASSERT !t->l || t->l->type==T_ALIST */
135         /* ASSERT !t->r || t->r->type==T_LISTITEM */
136         t->l=list_reverse(t->l);
137         t->r=list_reverse(t->r);
138         break;
139     case T_ASSIGNMENT:
140         /* ASSERT t->l->type==T_KEY */
141         /* ASSERT t->r->type==T_LISTITEM */
142         t->r=list_reverse(t->r);
143         break;
144     case T_ABSPATH:
145     case T_RELPATH:
146         /* ASSERT t->l==NULL */
147         /* ASSERT t->r->type==T_PATHELEM */
148         t->r=list_reverse(t->r);
149         break;
150     case T_EXEC:
151         /* ASSERT t->l */
152         /* ASSERT t->r->type==T_LISTITEM */
153         t->r=list_reverse(t->r);
154         break;
155     }
156 }
157
158 #ifdef DUMP_PARSE_TREE
159 /* Convert a node type to a string, for parse tree dump */
160 static string_t ntype(uint32_t type)
161 {
162     switch(type) {
163     case T_STRING:     return "T_STRING";
164     case T_NUMBER:     return "T_NUMBER";
165     case T_KEY:        return "T_KEY";
166     case T_ASSIGNMENT: return "T_ASSIGNMENT";
167     case T_LISTITEM:   return "T_LISTITEM";
168     case T_EXEC:       return "T_EXEC";
169     case T_PATHELEM:   return "T_PATHELEM";
170     case T_ABSPATH:    return "T_ABSPATH";
171     case T_RELPATH:    return "T_RELPATH";
172     case T_DICT:       return "T_DICT";
173     case T_ALIST:      return "T_ALIST";
174     case T_ERROR:      return "T_ERROR";
175     }
176     return "**unknown**";
177 }
178
179 static void ptree_indent(uint32_t amount)
180 {
181     uint32_t i;
182     for (i=0; i<amount; i++) printf("  . ");
183 }
184
185 static void ptree_dump(struct p_node *n, uint32_t d)
186 {
187     if (!n) {
188         printf("NULL\n");
189         return;
190     }
191     
192     if (n->type<10) {
193         switch(n->type) {
194         case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
195                               n->data.string,n->loc.file,n->loc.line); break;
196         case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
197                               n->data.number, n->loc.file,n->loc.line); break;
198         case T_KEY:    printf("T_KEY:    %s (%s line %d)\n",
199                               n->data.key, n->loc.file,n->loc.line); break;
200         default:       printf("**unknown primitive type**\n"); break;
201         }
202     } else {
203         printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
204         ptree_indent(d);
205         printf("  |-"); ptree_dump(n->l, d+1);
206         ptree_indent(d);
207         printf("  +-"); ptree_dump(n->r, d+1);
208     }
209 }
210
211 #endif /* DUMP_PARSE_TREE */
212
213 static dict_t *dict_find_root(dict_t *d)
214 {
215     dict_t *i;
216
217     for (i=d; i->parent; i=i->parent);
218     return i;
219 }
220
221 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
222 {
223     dict_t *i;
224     list_t *l;
225
226     /* ASSERT p->type==T_PATHELEM */
227     /* ASSERT p->l->type==T_KEY */
228     l=dict_ilookup(context, p->l->data.key);
229     if (!l) {
230         cfgfatal(p->loc,"conffile","can't find key %s\n",
231                  p->l->data.key);
232     }
233
234     while (p->r) {
235         if (l->item->type != t_dict) {
236             cfgfatal(p->loc,"conffile","path element \"%s\" "
237                      "is not a dictionary\n",p->l->data.key);
238         }
239         i=l->item->data.dict; /* First thing in list */
240
241         p=p->r;
242         l=dict_ilookup_primitive(i, p->l->data.key);
243         if (!l) {
244             cfgfatal(p->loc,"conffile","can't find key %s\n",
245                      p->l->data.key);
246         }
247     }
248     return l;
249 }
250
251 static item_t *new_item(enum types type, struct cloc loc)
252 {
253     item_t *i;
254
255     i=safe_malloc(sizeof(*i),"new_item");
256     i->type=type;
257     i->loc=loc;
258     return i;
259 }
260
261 static list_t *process_item(dict_t *context, struct p_node *i)
262 {
263     item_t *item=NULL;
264
265     switch (i->type) {
266     case T_STRING:
267         item=new_item(t_string, i->loc);
268         item->data.string=i->data.string; /* XXX maybe strcpy */
269         break;
270     case T_NUMBER:
271         item=new_item(t_number, i->loc);
272         item->data.number=i->data.number;
273         break;
274     case T_ABSPATH:
275         context=dict_find_root(context);
276         /* falls through */
277     case T_RELPATH:
278         return dict_lookup_path(context, i->r);
279         /* returns immediately */
280         break;
281     case T_DICT:
282         item=new_item(t_dict, i->loc);
283         item->data.dict=dict_new(context);
284 /* XXX  dict_add_searchpath(context,process_ilist(context, i->r)); */
285         process_alist(item->data.dict, i->l);
286         break;
287     case T_EXEC:
288         return process_invocation(context, i);
289         /* returns immediately */
290         break;
291     default:
292 #ifdef DUMP_PARSE_TREE
293         ptree_dump(i,0);
294         fatal("process_item: invalid node type for a list item (%s)\n",
295               ntype(i->type));
296 #else
297         fatal("process_item: list item has invalid node type %d - recompile "
298               "with DUMP_PARSE_TREE defined in conffile.c for more "
299               "detailed debug output",i->type);
300 #endif /* DUMP_PARSE_TREE */
301         break;
302     }
303     return list_append(NULL,item);
304 }
305
306 static list_t *process_ilist(dict_t *context, struct p_node *l)
307 {
308     struct p_node *i;
309     list_t *r;
310
311     /* ASSERT l->type==T_LISTITEM */
312
313     r=list_new();
314
315     for (i=l; i; i=i->r) {
316         r=list_append_list(r,process_item(context,i->l));
317     }
318     return r;
319 }
320         
321 static list_t *process_invocation(dict_t *context, struct p_node *i)
322 {
323     list_t *cll;
324     item_t *cl;
325     list_t *args;
326
327     /* ASSERT i->type==T_EXEC */
328     /* ASSERT i->r->type==T_LISTITEM */
329     /* XXX it might be null too */
330     cll=process_item(context,i->l);
331     cl=cll->item;
332     if (cl->type != t_closure) {
333         cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
334     }
335     args=process_ilist(context, i->r);
336     return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
337 }
338
339 static void process_alist(dict_t *context, struct p_node *c)
340 {
341     struct p_node *i;
342     atom_t k;
343     list_t *l;
344
345     if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
346
347     /* ASSERT c->type==T_ALIST */
348     if (c->type!=T_ALIST) {
349         fatal("invalid node type in assignment list\n");
350     }
351
352     for (i=c; i; i=i->r) {
353         /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
354         /* ASSERT i->l->l->type==T_KEY */
355         /* ASSERT i->l->r->type==T_LISTITEM */
356         k=i->l->l->data.key;
357         l=process_ilist(context, i->l->r);
358         dict_iadd(context, k, l);
359     }
360 }
361
362 /* Take a list of items; turn any dictionaries in this list into lists */
363 static list_t *makelist(closure_t *self, struct cloc loc,
364                         dict_t *context, list_t *args)
365 {
366     list_t *r=NULL, *i;
367     struct entry *e;
368     
369     for (i=args; i; i=i->next) {
370         if (i->item->type==t_dict) {
371             /* Convert */
372             for (e=i->item->data.dict->entries; e; e=e->next) {
373                 r=list_append_list(r, e->val);
374             }
375         } else {
376             r=list_append_list(r, list_append(NULL,i->item));
377         }
378     }
379     return r;
380 }
381
382 /* Take a list consisting of a closure and some other things. Apply the
383    closure to the other things, and return the resulting list */
384 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
385                    list_t *args)
386 {
387     list_t *r=NULL, *al;
388     item_t *ci;
389     closure_t *cl;
390     list_t se;
391     
392     ci=list_elem(args,0);
393     if (ci && ci->type==t_closure) {
394         cl=ci->data.closure;
395         if (!cl->apply) {
396             cfgfatal(loc,"map","closure cannot be applied\n");
397         }
398         for (al=args->next; al; al=al->next) {
399             /* Construct a single-element list */
400             se.next=NULL;
401             se.item=al->item;
402             /* Invoke the closure, append its result to the output */
403             r=list_append_list(r,cl->apply(cl,loc,context,&se));
404         }
405     } else {
406         cfgfatal(loc,"map","you must supply a closure as the "
407                  "first argument\n");
408     }
409     return r;
410 }
411
412 /* Read a file and turn it into a string */
413 static list_t *readfile(closure_t *self, struct cloc loc,
414                         dict_t *context, list_t *args)
415 {
416     FILE *f;
417     string_t filename;
418     long length;
419     item_t *r;
420
421     r=list_elem(args,0);
422     if (!r) {
423         cfgfatal(loc,"readfile","you must supply a filename\n");
424     }
425     if (r->type!=t_string) {
426         cfgfatal(loc,"readfile","filename must be a string\n");
427     }
428     filename=r->data.string;
429     f=fopen(filename,"rb");
430     if (!f) {
431         fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
432                      loc.file,loc.line, filename);
433     }
434     if (fseek(f, 0, SEEK_END)!=0) {
435         fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
436     }
437     length=ftell(f);
438     if (length<0) {
439         fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
440     }
441     if (fseek(f, 0, SEEK_SET)!=0) {
442         fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
443     }
444     r=new_item(t_string,loc);
445     r->data.string=safe_malloc(length+1,"readfile");
446     if (fread(r->data.string,length,1,f)!=1) {
447         fatal("readfile (%s:%d): fread: could not read all of file\n",
448               loc.file,loc.line);
449     }
450     r->data.string[length]=0;
451     if (fclose(f)!=0) {
452         fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
453     }
454     return list_append(NULL,r);
455 }
456     
457 static dict_t *process_config(struct p_node *c)
458 {
459     dict_t *root;
460     dict_t *context;
461     item_t *i;
462     list_t *false;
463     list_t *true;
464
465     root=dict_new(NULL);
466     context=root;
467
468     /* Predefined keys for boolean values */
469     i=new_item(t_bool,no_loc);
470     i->data.bool=False;
471     false=list_append(NULL,i);
472     i=new_item(t_bool,no_loc);
473     i->data.bool=True;
474     true=list_append(NULL,i);
475     dict_add(root,"false",false);
476     dict_add(root,"False",false);
477     dict_add(root,"FALSE",false);
478     dict_add(root,"no",false);
479     dict_add(root,"No",false);
480     dict_add(root,"NO",false);
481     dict_add(root,"true",true);
482     dict_add(root,"True",true);
483     dict_add(root,"TRUE",true);
484     dict_add(root,"yes",true);
485     dict_add(root,"Yes",true);
486     dict_add(root,"YES",true);
487
488     add_closure(root,"makelist",makelist);
489     add_closure(root,"readfile",readfile);
490     add_closure(root,"map",map);
491
492     init_builtin_modules(root);
493
494     process_alist(context, c);
495
496     return root;
497 }
498
499 /***** Externally accessible functions */
500
501 atom_t intern(string_t s)
502 {
503     struct atomlist *i;
504
505     for (i=atoms; i; i=i->next) {
506         if (strcmp(i->a, s)==0) break;
507     }
508
509     if (!i) {
510         /* Did't find it; create a new one */
511         i=safe_malloc(sizeof(*i),"intern: alloc list entry");
512         i->a=safe_strdup(s,"intern: alloc string");
513         i->next=atoms;
514         atoms=i;
515     }
516     return i->a;
517 }
518
519 list_t *dict_lookup(dict_t *dict, string_t key)
520 {
521     return dict_ilookup(dict, intern(key));
522 }
523
524 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
525 {
526     return dict_ilookup_primitive(dict, intern(key));
527 }
528
529 void dict_add(dict_t *dict, string_t key, list_t *val)
530 {
531     dict_iadd(dict,intern(key),val);
532 }
533
534 string_t *dict_keys(dict_t *dict)
535 {
536     atom_t *r, *j;
537     struct entry *i;
538     r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
539     for (i=dict->entries, j=r; i; i=i->next, j++) {
540         *j=i->key;
541     }
542     *j=NULL;
543     return r;
544 }
545
546
547 /* List-related functions */
548
549 list_t *list_new(void)
550 {
551     return NULL;
552 }
553
554 list_t *list_copy(list_t *a)
555 {
556     list_t *r, *i, *b, *l;
557
558     if (!a) return NULL;
559     l=NULL;
560     r=NULL;
561     for (i=a; i; i=i->next) {
562         b=safe_malloc(sizeof(*b),"list_copy");
563         if (l) l->next=b; else r=b;
564         l=b;
565         b->item=i->item;
566         b->next=NULL;
567     }
568     return r;
569 }
570
571 list_t *list_append_list(list_t *a, list_t *b)
572 {
573     list_t *i;
574
575     b=list_copy(b);
576     if (!a) return b;
577     for (i=a; i->next; i=i->next);
578     i->next=b;
579     return a;
580 }
581
582 list_t *list_append(list_t *list, item_t *item)
583 {
584     list_t *l;
585
586     l=safe_malloc(sizeof(*l),"list_append");
587     l->item=item;
588     l->next=NULL;
589
590     return list_append_list(list,l);
591 }
592
593 item_t *list_elem(list_t *l, uint32_t index)
594 {
595     if (!l) return NULL;
596     if (index==0) return l->item;
597     return list_elem(l->next, index-1);
598 }
599
600 list_t *new_closure(closure_t *cl)
601 {
602     item_t *i;
603
604     i=new_item(t_closure,no_loc);
605     i->data.closure=cl;
606     return list_append(NULL,i);
607 }
608
609 void add_closure(dict_t *dict, string_t name, apply_fn apply)
610 {
611     closure_t *c;
612     c=safe_malloc(sizeof(*c),"add_closure");
613     c->description=name;
614     c->apply=apply;
615     c->interface=NULL;
616
617     dict_add(dict,name,new_closure(c));
618 }
619
620 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
621                  bool_t fail_if_invalid, string_t desc, struct cloc loc)
622 {
623     list_t *l;
624     item_t *i;
625     closure_t *cl;
626
627     l=dict_lookup(dict,name);
628     if (!l) {
629         if (!fail_if_invalid) return NULL;
630         cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
631     }
632     i=list_elem(l,0);
633     if (i->type!=t_closure) {
634         if (!fail_if_invalid) return NULL;
635         cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
636     }
637     cl=i->data.closure;
638     if (cl->type!=type) {
639         if (!fail_if_invalid) return NULL;
640         cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
641     }
642     return cl->interface;
643 }
644
645 /* Convenience functions for modules reading configuration dictionaries */
646 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
647                        string_t desc, struct cloc loc)
648 {
649     list_t *l;
650     item_t *i;
651
652     l=dict_lookup(dict,key);
653     if (!l) {
654         if (!required) return NULL;
655         cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
656     }
657     i=list_elem(l,0);
658     return i;
659 }
660
661 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
662                           string_t desc, struct cloc loc)
663 {
664     item_t *i;
665     string_t r;
666
667     i=dict_find_item(dict,key,required,desc,loc);
668     if (!i) return NULL;
669     if (i->type!=t_string) {
670         cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
671     }
672     r=i->data.string;
673     return r;
674 }
675
676 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
677                           string_t desc, struct cloc loc, uint32_t def)
678 {
679     item_t *i;
680     uint32_t r;
681
682     i=dict_find_item(dict,key,required,desc,loc);
683     if (!i) return def;
684     if (i->type!=t_number) {
685         cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
686     }
687     r=i->data.number;
688     return r;
689 }
690
691 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
692                       string_t desc, struct cloc loc, bool_t def)
693 {
694     item_t *i;
695     bool_t r;
696
697     i=dict_find_item(dict,key,required,desc,loc);
698     if (!i) return def;
699     if (i->type!=t_bool) {
700         cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
701     }
702     r=i->data.bool;
703     return r;
704 }
705
706 static struct subnet string_to_subnet(item_t *i, string_t desc)
707 {
708     struct subnet s;
709     uint32_t a, b, c, d, n;
710     uint32_t match;
711
712     /* i is not guaranteed to be a string */
713     if (i->type!=t_string) {
714         cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
715     }
716
717     /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
718        NOT optional. The subnet mask is optional; if missing it is assumed
719        to be /32. */
720     match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
721     if (match<4) {
722         cfgfatal(i->loc,desc,"\"%s\" is not a valid "
723                  "subnet specification\n",i->data.string);
724     }
725     if (match<5) {
726         n=32;
727     }
728     if (a>255 || b>255 || c>255 || d>255 || n>32) {
729         cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
730     }
731     s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
732     s.mask=(~0UL << (32-n));
733     s.len=n;
734     if (s.prefix & ~s.mask) {
735         cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
736                  "in mask\n",i->data.string);
737     }
738     return s;
739 }
740
741 uint32_t string_to_ipaddr(item_t *i, string_t desc)
742 {
743     uint32_t a, b, c, d;
744     uint32_t match;
745
746     /* i is not guaranteed to be a string */
747     if (i->type!=t_string) {
748         cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
749     }
750
751     match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
752     if (match<4) {
753         cfgfatal(i->loc,desc,"\"%s\" is not a valid "
754                  "IP address\n",i->data.string);
755     }
756     if (a>255 || b>255 || c>255 || d>255) {
757         cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
758     }
759     return (a<<24)|(b<<16)|(c<<8)|(d);
760 }
761
762 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
763                            string_t desc, struct cloc loc,
764                            struct subnet_list *sl)
765 {
766     list_t *l, *li;
767     item_t *i;
768     uint32_t e=0;
769
770     sl->entries=0;
771     sl->list=NULL;
772     l=dict_lookup(dict, key);
773     if (!l) {
774         if (!required) return;
775         cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
776     }
777     /* Count the items in the list */
778     for (li=l; li; li=li->next) e++;
779     if (e==0) return;
780     sl->entries=e;
781     sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
782     e=0;
783     /* Fill in the list */
784     for (li=l; li; li=li->next) {
785         i=li->item;
786         if (i->type!=t_string) {
787             cfgfatal(loc,desc,"parameter \"%s\": all elements must "
788                      "be strings\n",key);
789         }
790         sl->list[e++]=string_to_subnet(i,desc);
791     }
792 }
793
794 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
795 {
796     list_t *i;
797     uint32_t r=0;
798     struct flagstr *j;
799
800     for (i=l; i; i=i->next) {
801         if (i->item->type!=t_string) {
802             cfgfatal(i->item->loc,desc,"all elements of list must be "
803                      "strings\n");
804         }
805         for (j=f; j->name; j++)
806             if (strcmp(i->item->data.string,j->name)==0)
807                 r|=j->value;
808     }
809     return r;
810 }
811
812 dict_t *read_conffile(char *name)
813 {
814     FILE *conffile;
815     struct p_node *config;
816
817     if (strcmp(name,"-")==0) {
818         conffile=stdin;
819     } else {
820         conffile=fopen(name,"r");
821         if (!conffile)
822             fatal_perror("Cannot open configuration file \"%s\"",name);
823     }
824     config_lineno=1;
825     config_file=name;
826     config=parse_conffile(conffile);
827     fclose(conffile);
828
829 #ifdef DUMP_PARSE_TREE
830     printf("*** config file parse tree BEFORE MANGLE\n");
831     ptree_dump(config,0);
832 #endif /* DUMP_PARSE_TREE */
833     /* The root of the configuration is a T_ALIST, which needs reversing
834        before we mangle because it isn't the child of a T_DICT. */
835     config=list_reverse(config);
836     ptree_mangle(config);
837 #ifdef DUMP_PARSE_TREE
838     printf("\n\n*** config file parse tree AFTER MANGLE\n");
839     ptree_dump(config,0);
840 #endif /* DUMP_PARSE_TREE */
841     return process_config(config);
842 }