5 /* conffile.c - process the configuration file */
7 /* #define DUMP_PARSE_TREE */
13 #include "conffile_internal.h"
18 extern void init_builtin_modules(dict_t
*dict
);
20 static struct cloc no_loc
={"none",0};
23 struct atomlist
*next
;
35 struct searchlist
*next
;
40 struct searchlist
*search
;
41 struct entry
*entries
;
45 static struct atomlist
*atoms
=NULL
;
47 static void process_alist(dict_t
*context
, struct p_node
*c
);
48 static list_t
*process_invocation(dict_t
*context
, struct p_node
*i
);
50 static list_t
*dict_ilookup_primitive(dict_t
*dict
, atom_t key
)
53 for (i
=dict
->entries
; i
; i
=i
->next
) {
54 if (key
==i
->key
) return i
->val
;
59 static list_t
*dict_ilookup(dict_t
*dict
, atom_t key
)
64 v
=dict_ilookup_primitive(dict
, key
);
66 /* Check dictionaries in search path */
68 /* Check lexical parents */
69 for (d
=dict
; d
; d
=d
->parent
) {
70 v
=dict_ilookup_primitive(d
, key
);
76 static void dict_iadd(dict_t
*dict
, atom_t key
, list_t
*val
)
79 /* XXX May want to permit redefinition of keys in the future */
80 /* (although it could be very confusing) */
81 if (dict_ilookup_primitive(dict
, key
)) {
82 fatal("duplicate key \"%s\" in dictionary\n",key
);
84 e
=safe_malloc(sizeof(*e
),"dict_add");
85 e
->next
=dict
->entries
;
92 /***** Functions beyond this point are private to the config system *****/
94 static dict_t
*dict_new(dict_t
*parent
)
98 d
=safe_malloc(sizeof(*d
),"dict_new");
106 static struct p_node
*node_copy(struct p_node
*n
)
109 r
=safe_malloc(sizeof(*r
),"node_copy");
114 static struct p_node
*list_reverse(struct p_node
*list
)
116 struct p_node
*rl
=NULL
, *i
, *n
;
118 for (i
=list
; i
; i
=i
->r
) {
126 /* Since we use left-recursion in the parser for efficiency, sequences
127 end up "backwards" in the parse tree. Rather than have complicated
128 code for, eg. processing assignments in the right order, we reverse
129 these sequences here. */
130 static void ptree_mangle(struct p_node
*t
)
137 /* ASSERT !t->l || t->l->type==T_ALIST */
138 /* ASSERT !t->r || t->r->type==T_LISTITEM */
139 t
->l
=list_reverse(t
->l
);
140 t
->r
=list_reverse(t
->r
);
143 /* ASSERT t->l->type==T_KEY */
144 /* ASSERT t->r->type==T_LISTITEM */
145 t
->r
=list_reverse(t
->r
);
149 /* ASSERT t->l==NULL */
150 /* ASSERT t->r->type==T_PATHELEM */
151 t
->r
=list_reverse(t
->r
);
155 /* ASSERT t->r->type==T_LISTITEM */
156 t
->r
=list_reverse(t
->r
);
161 #ifdef DUMP_PARSE_TREE
162 /* Convert a node type to a string, for parse tree dump */
163 static string_t
ntype(uint32_t type
)
166 case T_STRING
: return "T_STRING";
167 case T_NUMBER
: return "T_NUMBER";
168 case T_KEY
: return "T_KEY";
169 case T_ASSIGNMENT
: return "T_ASSIGNMENT";
170 case T_LISTITEM
: return "T_LISTITEM";
171 case T_EXEC
: return "T_EXEC";
172 case T_PATHELEM
: return "T_PATHELEM";
173 case T_ABSPATH
: return "T_ABSPATH";
174 case T_RELPATH
: return "T_RELPATH";
175 case T_DICT
: return "T_DICT";
176 case T_ALIST
: return "T_ALIST";
177 case T_ERROR
: return "T_ERROR";
179 return "**unknown**";
182 static void ptree_indent(uint32_t amount
)
185 for (i
=0; i
<amount
; i
++) printf(" . ");
188 static void ptree_dump(struct p_node
*n
, uint32_t d
)
197 case T_STRING
: printf("T_STRING: \"%s\" (%s line %d)\n",
198 n
->data
.string
,n
->loc
.file
,n
->loc
.line
); break;
199 case T_NUMBER
: printf("T_NUMBER: %d (%s line %d)\n",
200 n
->data
.number
, n
->loc
.file
,n
->loc
.line
); break;
201 case T_KEY
: printf("T_KEY: %s (%s line %d)\n",
202 n
->data
.key
, n
->loc
.file
,n
->loc
.line
); break;
203 default: printf("**unknown primitive type**\n"); break;
206 printf("%s: (%s line %d)\n",ntype(n
->type
),n
->loc
.file
,n
->loc
.line
);
208 printf(" |-"); ptree_dump(n
->l
, d
+1);
210 printf(" +-"); ptree_dump(n
->r
, d
+1);
214 #endif /* DUMP_PARSE_TREE */
216 static dict_t
*dict_find_root(dict_t
*d
)
220 for (i
=d
; i
->parent
; i
=i
->parent
);
224 static list_t
*dict_lookup_path(dict_t
*context
, struct p_node
*p
)
229 /* ASSERT p->type==T_PATHELEM */
230 /* ASSERT p->l->type==T_KEY */
231 l
=dict_ilookup(context
, p
->l
->data
.key
);
233 cfgfatal(p
->loc
,"conffile","can't find key %s\n",
238 if (l
->item
->type
!= t_dict
) {
239 cfgfatal(p
->loc
,"conffile","path element \"%s\" "
240 "is not a dictionary\n",p
->l
->data
.key
);
242 i
=l
->item
->data
.dict
; /* First thing in list */
245 l
=dict_ilookup_primitive(i
, p
->l
->data
.key
);
247 cfgfatal(p
->loc
,"conffile","can't find key %s\n",
254 static item_t
*new_item(enum types type
, struct cloc loc
)
258 i
=safe_malloc(sizeof(*i
),"new_item");
264 static list_t
*process_item(dict_t
*context
, struct p_node
*i
)
270 item
=new_item(t_string
, i
->loc
);
271 item
->data
.string
=i
->data
.string
; /* XXX maybe strcpy */
274 item
=new_item(t_number
, i
->loc
);
275 item
->data
.number
=i
->data
.number
;
278 context
=dict_find_root(context
);
281 return dict_lookup_path(context
, i
->r
);
282 /* returns immediately */
285 item
=new_item(t_dict
, i
->loc
);
286 item
->data
.dict
=dict_new(context
);
287 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
288 process_alist(item
->data
.dict
, i
->l
);
291 return process_invocation(context
, i
);
292 /* returns immediately */
295 #ifdef DUMP_PARSE_TREE
297 fatal("process_item: invalid node type for a list item (%s)\n",
300 fatal("process_item: list item has invalid node type %d - recompile "
301 "with DUMP_PARSE_TREE defined in conffile.c for more "
302 "detailed debug output",i
->type
);
303 #endif /* DUMP_PARSE_TREE */
306 return list_append(NULL
,item
);
309 static list_t
*process_ilist(dict_t
*context
, struct p_node
*l
)
314 /* ASSERT l->type==T_LISTITEM */
318 for (i
=l
; i
; i
=i
->r
) {
319 r
=list_append_list(r
,process_item(context
,i
->l
));
324 static list_t
*process_invocation(dict_t
*context
, struct p_node
*i
)
330 /* ASSERT i->type==T_EXEC */
331 /* ASSERT i->r->type==T_LISTITEM */
332 /* XXX it might be null too */
333 cll
=process_item(context
,i
->l
);
335 if (cl
->type
!= t_closure
) {
336 cfgfatal(i
->l
->loc
,"conffile","only closures can be invoked\n");
338 if (!cl
->data
.closure
->apply
) {
339 cfgfatal(i
->l
->loc
,"conffile","this closure cannot be invoked\n");
341 args
=process_ilist(context
, i
->r
);
342 return cl
->data
.closure
->apply(cl
->data
.closure
, i
->loc
, context
, args
);
345 static void process_alist(dict_t
*context
, struct p_node
*c
)
351 if (!c
) return; /* NULL assignment lists are valid (empty dictionary) */
353 /* ASSERT c->type==T_ALIST */
354 if (c
->type
!=T_ALIST
) {
355 fatal("invalid node type in assignment list\n");
358 for (i
=c
; i
; i
=i
->r
) {
359 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
360 /* ASSERT i->l->l->type==T_KEY */
361 /* ASSERT i->l->r->type==T_LISTITEM */
363 l
=process_ilist(context
, i
->l
->r
);
364 dict_iadd(context
, k
, l
);
368 /* Take a list of items; turn any dictionaries in this list into lists */
369 static list_t
*makelist(closure_t
*self
, struct cloc loc
,
370 dict_t
*context
, list_t
*args
)
375 for (i
=args
; i
; i
=i
->next
) {
376 if (i
->item
->type
==t_dict
) {
378 for (e
=i
->item
->data
.dict
->entries
; e
; e
=e
->next
) {
379 r
=list_append_list(r
, e
->val
);
382 r
=list_append_list(r
, list_append(NULL
,i
->item
));
388 /* Take a list consisting of a closure and some other things. Apply the
389 closure to the other things, and return the resulting list */
390 static list_t
*map(closure_t
*self
, struct cloc loc
, dict_t
*context
,
398 ci
=list_elem(args
,0);
399 if (ci
&& ci
->type
==t_closure
) {
402 cfgfatal(loc
,"map","closure cannot be applied\n");
404 for (al
=args
->next
; al
; al
=al
->next
) {
405 /* Construct a single-element list */
408 /* Invoke the closure, append its result to the output */
409 r
=list_append_list(r
,cl
->apply(cl
,loc
,context
,&se
));
412 cfgfatal(loc
,"map","you must supply a closure as the "
418 /* Read a file and turn it into a string */
419 static list_t
*readfile(closure_t
*self
, struct cloc loc
,
420 dict_t
*context
, list_t
*args
)
429 cfgfatal(loc
,"readfile","you must supply a filename\n");
431 if (r
->type
!=t_string
) {
432 cfgfatal(loc
,"readfile","filename must be a string\n");
434 filename
=r
->data
.string
;
435 f
=fopen(filename
,"rb");
437 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
438 loc
.file
,loc
.line
, filename
);
440 if (fseek(f
, 0, SEEK_END
)!=0) {
441 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc
.file
,loc
.line
);
445 fatal_perror("readfile (%s:%d): ftell()",loc
.file
,loc
.line
);
447 if (fseek(f
, 0, SEEK_SET
)!=0) {
448 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc
.file
,loc
.line
);
450 r
=new_item(t_string
,loc
);
451 r
->data
.string
=safe_malloc(length
+1,"readfile");
452 if (fread(r
->data
.string
,length
,1,f
)!=1) {
453 fatal("readfile (%s:%d): fread: could not read all of file\n",
456 r
->data
.string
[length
]=0;
458 fatal_perror("readfile (%s:%d): fclose",loc
.file
,loc
.line
);
460 return list_append(NULL
,r
);
463 static dict_t
*process_config(struct p_node
*c
)
474 /* Predefined keys for boolean values */
475 i
=new_item(t_bool
,no_loc
);
477 false=list_append(NULL
,i
);
478 i
=new_item(t_bool
,no_loc
);
480 true=list_append(NULL
,i
);
481 dict_add(root
,"false",false);
482 dict_add(root
,"False",false);
483 dict_add(root
,"FALSE",false);
484 dict_add(root
,"no",false);
485 dict_add(root
,"No",false);
486 dict_add(root
,"NO",false);
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);
494 add_closure(root
,"makelist",makelist
);
495 add_closure(root
,"readfile",readfile
);
496 add_closure(root
,"map",map
);
498 init_builtin_modules(root
);
500 process_alist(context
, c
);
505 /***** Externally accessible functions */
507 atom_t
intern(string_t s
)
511 for (i
=atoms
; i
; i
=i
->next
) {
512 if (strcmp(i
->a
, s
)==0) break;
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");
525 list_t
*dict_lookup(dict_t
*dict
, string_t key
)
527 return dict_ilookup(dict
, intern(key
));
530 list_t
*dict_lookup_primitive(dict_t
*dict
, string_t key
)
532 return dict_ilookup_primitive(dict
, intern(key
));
535 void dict_add(dict_t
*dict
, string_t key
, list_t
*val
)
537 dict_iadd(dict
,intern(key
),val
);
540 string_t
*dict_keys(dict_t
*dict
)
544 r
=safe_malloc(sizeof(*r
)*(dict
->size
+1),"dict_keys");
545 for (i
=dict
->entries
, j
=r
; i
; i
=i
->next
, j
++) {
553 /* List-related functions */
555 list_t
*list_new(void)
560 uint32_t list_length(list_t
*a
)
564 for (i
=a
; i
; i
=i
->next
) l
++;
568 list_t
*list_copy(list_t
*a
)
570 list_t
*r
, *i
, *b
, *l
;
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
;
585 list_t
*list_append_list(list_t
*a
, list_t
*b
)
591 for (i
=a
; i
->next
; i
=i
->next
);
596 list_t
*list_append(list_t
*list
, item_t
*item
)
600 l
=safe_malloc(sizeof(*l
),"list_append");
604 return list_append_list(list
,l
);
607 item_t
*list_elem(list_t
*l
, uint32_t index
)
610 if (index
==0) return l
->item
;
611 return list_elem(l
->next
, index
-1);
614 list_t
*new_closure(closure_t
*cl
)
618 i
=new_item(t_closure
,no_loc
);
620 return list_append(NULL
,i
);
623 void add_closure(dict_t
*dict
, string_t name
, apply_fn apply
)
626 c
=safe_malloc(sizeof(*c
),"add_closure");
631 dict_add(dict
,name
,new_closure(c
));
634 void *find_cl_if(dict_t
*dict
, string_t name
, uint32_t type
,
635 bool_t fail_if_invalid
, string_t desc
, struct cloc loc
)
641 l
=dict_lookup(dict
,name
);
643 if (!fail_if_invalid
) return NULL
;
644 cfgfatal(loc
,desc
,"closure \"%s\" not found\n",name
);
647 if (i
->type
!=t_closure
) {
648 if (!fail_if_invalid
) return NULL
;
649 cfgfatal(loc
,desc
,"\"%s\" must be a closure\n",name
);
652 if (cl
->type
!=type
) {
653 if (!fail_if_invalid
) return NULL
;
654 cfgfatal(loc
,desc
,"\"%s\" is the wrong type of closure\n",name
);
656 return cl
->interface
;
659 /* Convenience functions for modules reading configuration dictionaries */
660 item_t
*dict_find_item(dict_t
*dict
, string_t key
, bool_t required
,
661 string_t desc
, struct cloc loc
)
666 l
=dict_lookup(dict
,key
);
668 if (!required
) return NULL
;
669 cfgfatal(loc
,desc
,"required parameter \"%s\" not found\n",key
);
675 string_t
dict_read_string(dict_t
*dict
, string_t key
, bool_t required
,
676 string_t desc
, struct cloc loc
)
681 i
=dict_find_item(dict
,key
,required
,desc
,loc
);
683 if (i
->type
!=t_string
) {
684 cfgfatal(loc
,desc
,"\"%s\" must be a string\n",key
);
690 uint32_t dict_read_number(dict_t
*dict
, string_t key
, bool_t required
,
691 string_t desc
, struct cloc loc
, uint32_t def
)
696 i
=dict_find_item(dict
,key
,required
,desc
,loc
);
698 if (i
->type
!=t_number
) {
699 cfgfatal(loc
,desc
,"\"%s\" must be a number\n",key
);
705 bool_t
dict_read_bool(dict_t
*dict
, string_t key
, bool_t required
,
706 string_t desc
, struct cloc loc
, bool_t def
)
711 i
=dict_find_item(dict
,key
,required
,desc
,loc
);
713 if (i
->type
!=t_bool
) {
714 cfgfatal(loc
,desc
,"\"%s\" must be a boolean\n",key
);
720 uint32_t string_to_word(string_t s
, struct cloc loc
,
721 struct flagstr
*f
, string_t desc
)
724 for (j
=f
; j
->name
; j
++)
725 if (strcmp(s
,j
->name
)==0)
727 cfgfatal(loc
,desc
,"option \"%s\" not known\n",s
);
731 uint32_t string_list_to_word(list_t
*l
, struct flagstr
*f
, string_t desc
)
737 for (i
=l
; i
; i
=i
->next
) {
738 if (i
->item
->type
!=t_string
) {
739 cfgfatal(i
->item
->loc
,desc
,"all elements of list must be "
742 for (j
=f
; j
->name
; j
++)
743 r
|=string_to_word(i
->item
->data
.string
,i
->item
->loc
,f
,desc
);
748 dict_t
*read_conffile(char *name
)
751 struct p_node
*config
;
753 if (strcmp(name
,"-")==0) {
756 conffile
=fopen(name
,"r");
758 fatal_perror("Cannot open configuration file \"%s\"",name
);
762 config
=parse_conffile(conffile
);
765 #ifdef DUMP_PARSE_TREE
766 printf("*** config file parse tree BEFORE MANGLE\n");
767 ptree_dump(config
,0);
768 #endif /* DUMP_PARSE_TREE */
769 /* The root of the configuration is a T_ALIST, which needs reversing
770 before we mangle because it isn't the child of a T_DICT. */
771 config
=list_reverse(config
);
772 ptree_mangle(config
);
773 #ifdef DUMP_PARSE_TREE
774 printf("\n\n*** config file parse tree AFTER MANGLE\n");
775 ptree_dump(config
,0);
776 #endif /* DUMP_PARSE_TREE */
777 return process_config(config
);