Import release 0.1.4
[secnet] / conffile.c
CommitLineData
2fe58dfd
SE
1/*
2 * $Log$
3 */
4
5/* conffile.c - process the configuration file */
6
7/* #define DUMP_PARSE_TREE */
8
8689b3a9 9#include "secnet.h"
2fe58dfd 10#include <stdio.h>
2fe58dfd 11#include <string.h>
2fe58dfd
SE
12#include "conffile.h"
13#include "conffile_internal.h"
14#include "util.h"
15#include "modules.h"
16
17static struct cloc no_loc={"none",0};
18
19struct atomlist {
20 struct atomlist *next;
21 atom_t a;
22};
23
24struct entry {
25 struct entry *next;
26 atom_t key;
27 list_t *val;
28};
29
30struct searchlist {
31 struct dict *d;
32 struct searchlist *next;
33};
34
35struct dict {
36 struct dict *parent;
37 struct searchlist *search;
38 struct entry *entries;
39 uint32_t size;
40};
41
42static struct atomlist *atoms=NULL;
43
44static void process_alist(dict_t *context, struct p_node *c);
45static list_t *process_invocation(dict_t *context, struct p_node *i);
46
47static 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
56static 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
73static 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
91static 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
103static 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
111static 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. */
127static 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 */
160static 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
179static void ptree_indent(uint32_t amount)
180{
181 uint32_t i;
182 for (i=0; i<amount; i++) printf(" . ");
183}
184
185static 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
213static 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
221static 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
251static 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
261static 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
306static 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
321static 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
339static 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 */
363static 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
9d3a4132
SE
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 */
384static 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
2fe58dfd
SE
412/* Read a file and turn it into a string */
413static 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
457static 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);
9d3a4132 490 add_closure(root,"map",map);
2fe58dfd
SE
491
492 init_builtin_modules(root);
493
494 process_alist(context, c);
495
496 return root;
497}
498
499/***** Externally accessible functions */
500
501atom_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
519list_t *dict_lookup(dict_t *dict, string_t key)
520{
521 return dict_ilookup(dict, intern(key));
522}
523
524list_t *dict_lookup_primitive(dict_t *dict, string_t key)
525{
526 return dict_ilookup_primitive(dict, intern(key));
527}
528
529void dict_add(dict_t *dict, string_t key, list_t *val)
530{
531 dict_iadd(dict,intern(key),val);
532}
533
534string_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
549list_t *list_new(void)
550{
551 return NULL;
552}
553
3454dce4
SE
554list_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
2fe58dfd
SE
571list_t *list_append_list(list_t *a, list_t *b)
572{
573 list_t *i;
574
3454dce4 575 b=list_copy(b);
2fe58dfd
SE
576 if (!a) return b;
577 for (i=a; i->next; i=i->next);
578 i->next=b;
579 return a;
580}
581
582list_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
593item_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
600list_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
609void 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
620void *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 */
646item_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
661string_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
676uint32_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
691bool_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
706static 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));
70dc107b 733 s.len=n;
2fe58dfd
SE
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
741uint32_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
762void 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
9d3a4132
SE
794uint32_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
2fe58dfd
SE
812dict_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}