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