Import release 0.1.2
[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
554list_t *list_append_list(list_t *a, list_t *b)
555{
556 list_t *i;
557
558 if (!a) return b;
559 for (i=a; i->next; i=i->next);
560 i->next=b;
561 return a;
562}
563
564list_t *list_append(list_t *list, item_t *item)
565{
566 list_t *l;
567
568 l=safe_malloc(sizeof(*l),"list_append");
569 l->item=item;
570 l->next=NULL;
571
572 return list_append_list(list,l);
573}
574
575item_t *list_elem(list_t *l, uint32_t index)
576{
577 if (!l) return NULL;
578 if (index==0) return l->item;
579 return list_elem(l->next, index-1);
580}
581
582list_t *new_closure(closure_t *cl)
583{
584 item_t *i;
585
586 i=new_item(t_closure,no_loc);
587 i->data.closure=cl;
588 return list_append(NULL,i);
589}
590
591void add_closure(dict_t *dict, string_t name, apply_fn apply)
592{
593 closure_t *c;
594 c=safe_malloc(sizeof(*c),"add_closure");
595 c->description=name;
596 c->apply=apply;
597 c->interface=NULL;
598
599 dict_add(dict,name,new_closure(c));
600}
601
602void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
603 bool_t fail_if_invalid, string_t desc, struct cloc loc)
604{
605 list_t *l;
606 item_t *i;
607 closure_t *cl;
608
609 l=dict_lookup(dict,name);
610 if (!l) {
611 if (!fail_if_invalid) return NULL;
612 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
613 }
614 i=list_elem(l,0);
615 if (i->type!=t_closure) {
616 if (!fail_if_invalid) return NULL;
617 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
618 }
619 cl=i->data.closure;
620 if (cl->type!=type) {
621 if (!fail_if_invalid) return NULL;
622 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
623 }
624 return cl->interface;
625}
626
627/* Convenience functions for modules reading configuration dictionaries */
628item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
629 string_t desc, struct cloc loc)
630{
631 list_t *l;
632 item_t *i;
633
634 l=dict_lookup(dict,key);
635 if (!l) {
636 if (!required) return NULL;
637 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
638 }
639 i=list_elem(l,0);
640 return i;
641}
642
643string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
644 string_t desc, struct cloc loc)
645{
646 item_t *i;
647 string_t r;
648
649 i=dict_find_item(dict,key,required,desc,loc);
650 if (!i) return NULL;
651 if (i->type!=t_string) {
652 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
653 }
654 r=i->data.string;
655 return r;
656}
657
658uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
659 string_t desc, struct cloc loc, uint32_t def)
660{
661 item_t *i;
662 uint32_t r;
663
664 i=dict_find_item(dict,key,required,desc,loc);
665 if (!i) return def;
666 if (i->type!=t_number) {
667 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
668 }
669 r=i->data.number;
670 return r;
671}
672
673bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
674 string_t desc, struct cloc loc, bool_t def)
675{
676 item_t *i;
677 bool_t r;
678
679 i=dict_find_item(dict,key,required,desc,loc);
680 if (!i) return def;
681 if (i->type!=t_bool) {
682 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
683 }
684 r=i->data.bool;
685 return r;
686}
687
688static struct subnet string_to_subnet(item_t *i, string_t desc)
689{
690 struct subnet s;
691 uint32_t a, b, c, d, n;
692 uint32_t match;
693
694 /* i is not guaranteed to be a string */
695 if (i->type!=t_string) {
696 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
697 }
698
699 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
700 NOT optional. The subnet mask is optional; if missing it is assumed
701 to be /32. */
702 match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
703 if (match<4) {
704 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
705 "subnet specification\n",i->data.string);
706 }
707 if (match<5) {
708 n=32;
709 }
710 if (a>255 || b>255 || c>255 || d>255 || n>32) {
711 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
712 }
713 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
714 s.mask=(~0UL << (32-n));
70dc107b 715 s.len=n;
2fe58dfd
SE
716 if (s.prefix & ~s.mask) {
717 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
718 "in mask\n",i->data.string);
719 }
720 return s;
721}
722
723uint32_t string_to_ipaddr(item_t *i, string_t desc)
724{
725 uint32_t a, b, c, d;
726 uint32_t match;
727
728 /* i is not guaranteed to be a string */
729 if (i->type!=t_string) {
730 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
731 }
732
733 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
734 if (match<4) {
735 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
736 "IP address\n",i->data.string);
737 }
738 if (a>255 || b>255 || c>255 || d>255) {
739 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
740 }
741 return (a<<24)|(b<<16)|(c<<8)|(d);
742}
743
744void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
745 string_t desc, struct cloc loc,
746 struct subnet_list *sl)
747{
748 list_t *l, *li;
749 item_t *i;
750 uint32_t e=0;
751
752 sl->entries=0;
753 sl->list=NULL;
754 l=dict_lookup(dict, key);
755 if (!l) {
756 if (!required) return;
757 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
758 }
759 /* Count the items in the list */
760 for (li=l; li; li=li->next) e++;
761 if (e==0) return;
762 sl->entries=e;
763 sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
764 e=0;
765 /* Fill in the list */
766 for (li=l; li; li=li->next) {
767 i=li->item;
768 if (i->type!=t_string) {
769 cfgfatal(loc,desc,"parameter \"%s\": all elements must "
770 "be strings\n",key);
771 }
772 sl->list[e++]=string_to_subnet(i,desc);
773 }
774}
775
9d3a4132
SE
776uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
777{
778 list_t *i;
779 uint32_t r=0;
780 struct flagstr *j;
781
782 for (i=l; i; i=i->next) {
783 if (i->item->type!=t_string) {
784 cfgfatal(i->item->loc,desc,"all elements of list must be "
785 "strings\n");
786 }
787 for (j=f; j->name; j++)
788 if (strcmp(i->item->data.string,j->name)==0)
789 r|=j->value;
790 }
791 return r;
792}
793
2fe58dfd
SE
794dict_t *read_conffile(char *name)
795{
796 FILE *conffile;
797 struct p_node *config;
798
799 if (strcmp(name,"-")==0) {
800 conffile=stdin;
801 } else {
802 conffile=fopen(name,"r");
803 if (!conffile)
804 fatal_perror("Cannot open configuration file \"%s\"",name);
805 }
806 config_lineno=1;
807 config_file=name;
808 config=parse_conffile(conffile);
809 fclose(conffile);
810
811#ifdef DUMP_PARSE_TREE
812 printf("*** config file parse tree BEFORE MANGLE\n");
813 ptree_dump(config,0);
814#endif /* DUMP_PARSE_TREE */
815 /* The root of the configuration is a T_ALIST, which needs reversing
816 before we mangle because it isn't the child of a T_DICT. */
817 config=list_reverse(config);
818 ptree_mangle(config);
819#ifdef DUMP_PARSE_TREE
820 printf("\n\n*** config file parse tree AFTER MANGLE\n");
821 ptree_dump(config,0);
822#endif /* DUMP_PARSE_TREE */
823 return process_config(config);
824}