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