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