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