chiark / gitweb /
f33354e6e075b36b38e2d20ad21c3c6ab9f2d5e1
[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     if (!cl->data.closure->apply) {
339         cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
340     }
341     args=process_ilist(context, i->r);
342     return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
343 }
344
345 static void process_alist(dict_t *context, struct p_node *c)
346 {
347     struct p_node *i;
348     atom_t k;
349     list_t *l;
350
351     if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
352
353     /* ASSERT c->type==T_ALIST */
354     if (c->type!=T_ALIST) {
355         fatal("invalid node type in assignment list\n");
356     }
357
358     for (i=c; i; i=i->r) {
359         /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
360         /* ASSERT i->l->l->type==T_KEY */
361         /* ASSERT i->l->r->type==T_LISTITEM */
362         k=i->l->l->data.key;
363         l=process_ilist(context, i->l->r);
364         dict_iadd(context, k, l);
365     }
366 }
367
368 /* Take a list of items; turn any dictionaries in this list into lists */
369 static list_t *makelist(closure_t *self, struct cloc loc,
370                         dict_t *context, list_t *args)
371 {
372     list_t *r=NULL, *i;
373     struct entry *e;
374     
375     for (i=args; i; i=i->next) {
376         if (i->item->type==t_dict) {
377             /* Convert */
378             for (e=i->item->data.dict->entries; e; e=e->next) {
379                 r=list_append_list(r, e->val);
380             }
381         } else {
382             r=list_append_list(r, list_append(NULL,i->item));
383         }
384     }
385     return r;
386 }
387
388 /* Take a list consisting of a closure and some other things. Apply the
389    closure to the other things, and return the resulting list */
390 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
391                    list_t *args)
392 {
393     list_t *r=NULL, *al;
394     item_t *ci;
395     closure_t *cl;
396     list_t se;
397     
398     ci=list_elem(args,0);
399     if (ci && ci->type==t_closure) {
400         cl=ci->data.closure;
401         if (!cl->apply) {
402             cfgfatal(loc,"map","closure cannot be applied\n");
403         }
404         for (al=args->next; al; al=al->next) {
405             /* Construct a single-element list */
406             se.next=NULL;
407             se.item=al->item;
408             /* Invoke the closure, append its result to the output */
409             r=list_append_list(r,cl->apply(cl,loc,context,&se));
410         }
411     } else {
412         cfgfatal(loc,"map","you must supply a closure as the "
413                  "first argument\n");
414     }
415     return r;
416 }
417
418 /* Read a file and turn it into a string */
419 static list_t *readfile(closure_t *self, struct cloc loc,
420                         dict_t *context, list_t *args)
421 {
422     FILE *f;
423     string_t filename;
424     long length;
425     item_t *r;
426
427     r=list_elem(args,0);
428     if (!r) {
429         cfgfatal(loc,"readfile","you must supply a filename\n");
430     }
431     if (r->type!=t_string) {
432         cfgfatal(loc,"readfile","filename must be a string\n");
433     }
434     filename=r->data.string;
435     f=fopen(filename,"rb");
436     if (!f) {
437         fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
438                      loc.file,loc.line, filename);
439     }
440     if (fseek(f, 0, SEEK_END)!=0) {
441         fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
442     }
443     length=ftell(f);
444     if (length<0) {
445         fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
446     }
447     if (fseek(f, 0, SEEK_SET)!=0) {
448         fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
449     }
450     r=new_item(t_string,loc);
451     r->data.string=safe_malloc(length+1,"readfile");
452     if (fread(r->data.string,length,1,f)!=1) {
453         fatal("readfile (%s:%d): fread: could not read all of file\n",
454               loc.file,loc.line);
455     }
456     r->data.string[length]=0;
457     if (fclose(f)!=0) {
458         fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
459     }
460     return list_append(NULL,r);
461 }
462     
463 static dict_t *process_config(struct p_node *c)
464 {
465     dict_t *root;
466     dict_t *context;
467     item_t *i;
468     list_t *false;
469     list_t *true;
470
471     root=dict_new(NULL);
472     context=root;
473
474     /* Predefined keys for boolean values */
475     i=new_item(t_bool,no_loc);
476     i->data.bool=False;
477     false=list_append(NULL,i);
478     i=new_item(t_bool,no_loc);
479     i->data.bool=True;
480     true=list_append(NULL,i);
481     dict_add(root,"false",false);
482     dict_add(root,"False",false);
483     dict_add(root,"FALSE",false);
484     dict_add(root,"no",false);
485     dict_add(root,"No",false);
486     dict_add(root,"NO",false);
487     dict_add(root,"true",true);
488     dict_add(root,"True",true);
489     dict_add(root,"TRUE",true);
490     dict_add(root,"yes",true);
491     dict_add(root,"Yes",true);
492     dict_add(root,"YES",true);
493
494     add_closure(root,"makelist",makelist);
495     add_closure(root,"readfile",readfile);
496     add_closure(root,"map",map);
497
498     init_builtin_modules(root);
499
500     process_alist(context, c);
501
502     return root;
503 }
504
505 /***** Externally accessible functions */
506
507 atom_t intern(string_t s)
508 {
509     struct atomlist *i;
510
511     for (i=atoms; i; i=i->next) {
512         if (strcmp(i->a, s)==0) break;
513     }
514
515     if (!i) {
516         /* Did't find it; create a new one */
517         i=safe_malloc(sizeof(*i),"intern: alloc list entry");
518         i->a=safe_strdup(s,"intern: alloc string");
519         i->next=atoms;
520         atoms=i;
521     }
522     return i->a;
523 }
524
525 list_t *dict_lookup(dict_t *dict, string_t key)
526 {
527     return dict_ilookup(dict, intern(key));
528 }
529
530 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
531 {
532     return dict_ilookup_primitive(dict, intern(key));
533 }
534
535 void dict_add(dict_t *dict, string_t key, list_t *val)
536 {
537     dict_iadd(dict,intern(key),val);
538 }
539
540 string_t *dict_keys(dict_t *dict)
541 {
542     atom_t *r, *j;
543     struct entry *i;
544     r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
545     for (i=dict->entries, j=r; i; i=i->next, j++) {
546         *j=i->key;
547     }
548     *j=NULL;
549     return r;
550 }
551
552
553 /* List-related functions */
554
555 list_t *list_new(void)
556 {
557     return NULL;
558 }
559
560 uint32_t list_length(list_t *a)
561 {
562     uint32_t l=0;
563     list_t *i;
564     for (i=a; i; i=i->next) l++;
565     return l;
566 }
567
568 list_t *list_copy(list_t *a)
569 {
570     list_t *r, *i, *b, *l;
571
572     if (!a) return NULL;
573     l=NULL;
574     r=NULL;
575     for (i=a; i; i=i->next) {
576         b=safe_malloc(sizeof(*b),"list_copy");
577         if (l) l->next=b; else r=b;
578         l=b;
579         b->item=i->item;
580         b->next=NULL;
581     }
582     return r;
583 }
584
585 list_t *list_append_list(list_t *a, list_t *b)
586 {
587     list_t *i;
588
589     b=list_copy(b);
590     if (!a) return b;
591     for (i=a; i->next; i=i->next);
592     i->next=b;
593     return a;
594 }
595
596 list_t *list_append(list_t *list, item_t *item)
597 {
598     list_t *l;
599
600     l=safe_malloc(sizeof(*l),"list_append");
601     l->item=item;
602     l->next=NULL;
603
604     return list_append_list(list,l);
605 }
606
607 item_t *list_elem(list_t *l, uint32_t index)
608 {
609     if (!l) return NULL;
610     if (index==0) return l->item;
611     return list_elem(l->next, index-1);
612 }
613
614 list_t *new_closure(closure_t *cl)
615 {
616     item_t *i;
617
618     i=new_item(t_closure,no_loc);
619     i->data.closure=cl;
620     return list_append(NULL,i);
621 }
622
623 void add_closure(dict_t *dict, string_t name, apply_fn apply)
624 {
625     closure_t *c;
626     c=safe_malloc(sizeof(*c),"add_closure");
627     c->description=name;
628     c->apply=apply;
629     c->interface=NULL;
630
631     dict_add(dict,name,new_closure(c));
632 }
633
634 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
635                  bool_t fail_if_invalid, string_t desc, struct cloc loc)
636 {
637     list_t *l;
638     item_t *i;
639     closure_t *cl;
640
641     l=dict_lookup(dict,name);
642     if (!l) {
643         if (!fail_if_invalid) return NULL;
644         cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
645     }
646     i=list_elem(l,0);
647     if (i->type!=t_closure) {
648         if (!fail_if_invalid) return NULL;
649         cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
650     }
651     cl=i->data.closure;
652     if (cl->type!=type) {
653         if (!fail_if_invalid) return NULL;
654         cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
655     }
656     return cl->interface;
657 }
658
659 /* Convenience functions for modules reading configuration dictionaries */
660 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
661                        string_t desc, struct cloc loc)
662 {
663     list_t *l;
664     item_t *i;
665
666     l=dict_lookup(dict,key);
667     if (!l) {
668         if (!required) return NULL;
669         cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
670     }
671     i=list_elem(l,0);
672     return i;
673 }
674
675 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
676                           string_t desc, struct cloc loc)
677 {
678     item_t *i;
679     string_t r;
680
681     i=dict_find_item(dict,key,required,desc,loc);
682     if (!i) return NULL;
683     if (i->type!=t_string) {
684         cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
685     }
686     r=i->data.string;
687     return r;
688 }
689
690 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
691                           string_t desc, struct cloc loc, uint32_t def)
692 {
693     item_t *i;
694     uint32_t r;
695
696     i=dict_find_item(dict,key,required,desc,loc);
697     if (!i) return def;
698     if (i->type!=t_number) {
699         cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
700     }
701     r=i->data.number;
702     return r;
703 }
704
705 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
706                       string_t desc, struct cloc loc, bool_t def)
707 {
708     item_t *i;
709     bool_t r;
710
711     i=dict_find_item(dict,key,required,desc,loc);
712     if (!i) return def;
713     if (i->type!=t_bool) {
714         cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
715     }
716     r=i->data.bool;
717     return r;
718 }
719
720 static struct subnet string_to_subnet(item_t *i, string_t desc)
721 {
722     struct subnet s;
723     uint32_t a, b, c, d, n;
724     uint32_t match;
725
726     /* i is not guaranteed to be a string */
727     if (i->type!=t_string) {
728         cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
729     }
730
731     if (strcmp(i->data.string,"default")==0) {
732         s.prefix=0;
733         s.mask=0;
734         s.len=0;
735         return s;
736     }
737
738     /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
739        NOT optional. The subnet mask is optional; if missing it is assumed
740        to be /32. */
741     match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
742     if (match<4) {
743         cfgfatal(i->loc,desc,"\"%s\" is not a valid "
744                  "subnet specification\n",i->data.string);
745     }
746     if (match<5) {
747         n=32;
748     }
749     if (a>255 || b>255 || c>255 || d>255 || n>32) {
750         cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
751     }
752     s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
753     s.mask=n?(~0UL << (32-n)):0;
754     s.len=n;
755     if (s.prefix & ~s.mask) {
756         cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
757                  "in mask\n",i->data.string);
758     }
759     return s;
760 }
761
762 uint32_t string_to_ipaddr(item_t *i, string_t desc)
763 {
764     uint32_t a, b, c, d;
765     uint32_t match;
766
767     /* i is not guaranteed to be a string */
768     if (i->type!=t_string) {
769         cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
770     }
771
772     match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
773     if (match<4) {
774         cfgfatal(i->loc,desc,"\"%s\" is not a valid "
775                  "IP address\n",i->data.string);
776     }
777     if (a>255 || b>255 || c>255 || d>255) {
778         cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
779     }
780     return (a<<24)|(b<<16)|(c<<8)|(d);
781 }
782
783 void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
784                            string_t desc, struct cloc loc,
785                            struct subnet_list *sl)
786 {
787     list_t *l, *li;
788     item_t *i;
789     uint32_t e=0;
790
791     sl->entries=0;
792     sl->list=NULL;
793     l=dict_lookup(dict, key);
794     if (!l) {
795         if (!required) return;
796         cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
797     }
798     /* Count the items in the list */
799     e=list_length(l);
800     if (e==0) return;
801     sl->entries=e;
802     sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
803     e=0;
804     /* Fill in the list */
805     for (li=l; li; li=li->next) {
806         i=li->item;
807         if (i->type!=t_string) {
808             cfgfatal(loc,desc,"parameter \"%s\": all elements must "
809                      "be strings\n",key);
810         }
811         sl->list[e++]=string_to_subnet(i,desc);
812     }
813 }
814
815 uint32_t string_to_word(string_t s, struct cloc loc,
816                         struct flagstr *f, string_t desc)
817 {
818     struct flagstr *j;
819     for (j=f; j->name; j++)
820         if (strcmp(s,j->name)==0)
821             return j->value;
822     cfgfatal(loc,desc,"option \"%s\" not known\n",s);
823     return 0;
824 }
825
826 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
827 {
828     list_t *i;
829     uint32_t r=0;
830     struct flagstr *j;
831
832     for (i=l; i; i=i->next) {
833         if (i->item->type!=t_string) {
834             cfgfatal(i->item->loc,desc,"all elements of list must be "
835                      "strings\n");
836         }
837         for (j=f; j->name; j++)
838             r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
839     }
840     return r;
841 }
842
843 dict_t *read_conffile(char *name)
844 {
845     FILE *conffile;
846     struct p_node *config;
847
848     if (strcmp(name,"-")==0) {
849         conffile=stdin;
850     } else {
851         conffile=fopen(name,"r");
852         if (!conffile)
853             fatal_perror("Cannot open configuration file \"%s\"",name);
854     }
855     config_lineno=1;
856     config_file=name;
857     config=parse_conffile(conffile);
858     fclose(conffile);
859
860 #ifdef DUMP_PARSE_TREE
861     printf("*** config file parse tree BEFORE MANGLE\n");
862     ptree_dump(config,0);
863 #endif /* DUMP_PARSE_TREE */
864     /* The root of the configuration is a T_ALIST, which needs reversing
865        before we mangle because it isn't the child of a T_DICT. */
866     config=list_reverse(config);
867     ptree_mangle(config);
868 #ifdef DUMP_PARSE_TREE
869     printf("\n\n*** config file parse tree AFTER MANGLE\n");
870     ptree_dump(config,0);
871 #endif /* DUMP_PARSE_TREE */
872     return process_config(config);
873 }