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