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 if (dict_ilookup_primitive(dict, key)) {
80 fatal("duplicate key \"%s\" in dictionary\n",key);
82 e=safe_malloc(sizeof(*e),"dict_add");
83 e->next=dict->entries;
90 /***** Functions beyond this point are private to the config system *****/
92 static dict_t *dict_new(dict_t *parent)
96 d=safe_malloc(sizeof(*d),"dict_new");
104 static struct p_node *node_copy(struct p_node *n)
107 r=safe_malloc(sizeof(*r),"node_copy");
112 static struct p_node *list_reverse(struct p_node *list)
114 struct p_node *rl=NULL, *i, *n;
116 for (i=list; i; i=i->r) {
124 /* Since we use left-recursion in the parser for efficiency, sequences
125 end up "backwards" in the parse tree. Rather than have complicated
126 code for, eg. processing assignments in the right order, we reverse
127 these sequences here. */
128 static void ptree_mangle(struct p_node *t)
135 /* ASSERT !t->l || t->l->type==T_ALIST */
136 /* ASSERT !t->r || t->r->type==T_LISTITEM */
137 t->l=list_reverse(t->l);
138 t->r=list_reverse(t->r);
141 /* ASSERT t->l->type==T_KEY */
142 /* ASSERT t->r->type==T_LISTITEM */
143 t->r=list_reverse(t->r);
147 /* ASSERT t->l==NULL */
148 /* ASSERT t->r->type==T_PATHELEM */
149 t->r=list_reverse(t->r);
153 /* ASSERT t->r->type==T_LISTITEM */
154 t->r=list_reverse(t->r);
159 #ifdef DUMP_PARSE_TREE
160 /* Convert a node type to a string, for parse tree dump */
161 static string_t ntype(uint32_t type)
164 case T_STRING: return "T_STRING";
165 case T_NUMBER: return "T_NUMBER";
166 case T_KEY: return "T_KEY";
167 case T_ASSIGNMENT: return "T_ASSIGNMENT";
168 case T_LISTITEM: return "T_LISTITEM";
169 case T_EXEC: return "T_EXEC";
170 case T_PATHELEM: return "T_PATHELEM";
171 case T_ABSPATH: return "T_ABSPATH";
172 case T_RELPATH: return "T_RELPATH";
173 case T_DICT: return "T_DICT";
174 case T_ALIST: return "T_ALIST";
175 case T_ERROR: return "T_ERROR";
177 return "**unknown**";
180 static void ptree_indent(uint32_t amount)
183 for (i=0; i<amount; i++) printf(" . ");
186 static void ptree_dump(struct p_node *n, uint32_t d)
195 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
196 n->data.string,n->loc.file,n->loc.line); break;
197 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
198 n->data.number, n->loc.file,n->loc.line); break;
199 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
200 n->data.key, n->loc.file,n->loc.line); break;
201 default: printf("**unknown primitive type**\n"); break;
204 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
206 printf(" |-"); ptree_dump(n->l, d+1);
208 printf(" +-"); ptree_dump(n->r, d+1);
212 #endif /* DUMP_PARSE_TREE */
214 static dict_t *dict_find_root(dict_t *d)
218 for (i=d; i->parent; i=i->parent);
222 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
227 /* ASSERT p->type==T_PATHELEM */
228 /* ASSERT p->l->type==T_KEY */
229 l=dict_ilookup(context, p->l->data.key);
231 cfgfatal(p->loc,"conffile","can't find key %s\n",
236 if (l->item->type != t_dict) {
237 cfgfatal(p->loc,"conffile","path element \"%s\" "
238 "is not a dictionary\n",p->l->data.key);
240 i=l->item->data.dict; /* First thing in list */
243 l=dict_ilookup_primitive(i, p->l->data.key);
245 cfgfatal(p->loc,"conffile","can't find key %s\n",
252 static item_t *new_item(enum types type, struct cloc loc)
256 i=safe_malloc(sizeof(*i),"new_item");
262 static list_t *process_item(dict_t *context, struct p_node *i)
268 item=new_item(t_string, i->loc);
269 item->data.string=i->data.string; /* XXX maybe strcpy */
272 item=new_item(t_number, i->loc);
273 item->data.number=i->data.number;
276 context=dict_find_root(context);
279 return dict_lookup_path(context, i->r);
280 /* returns immediately */
283 item=new_item(t_dict, i->loc);
284 item->data.dict=dict_new(context);
285 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
286 process_alist(item->data.dict, i->l);
289 return process_invocation(context, i);
290 /* returns immediately */
293 #ifdef DUMP_PARSE_TREE
295 fatal("process_item: invalid node type for a list item (%s)\n",
298 fatal("process_item: list item has invalid node type %d - recompile "
299 "with DUMP_PARSE_TREE defined in conffile.c for more "
300 "detailed debug output",i->type);
301 #endif /* DUMP_PARSE_TREE */
304 return list_append(NULL,item);
307 static list_t *process_ilist(dict_t *context, struct p_node *l)
312 /* ASSERT l->type==T_LISTITEM */
316 for (i=l; i; i=i->r) {
317 r=list_append_list(r,process_item(context,i->l));
322 static list_t *process_invocation(dict_t *context, struct p_node *i)
328 /* ASSERT i->type==T_EXEC */
329 /* ASSERT i->r->type==T_LISTITEM */
330 /* XXX it might be null too */
331 cll=process_item(context,i->l);
333 if (cl->type != t_closure) {
334 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
336 if (!cl->data.closure->apply) {
337 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
339 args=process_ilist(context, i->r);
340 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
343 static void process_alist(dict_t *context, struct p_node *c)
349 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
351 /* ASSERT c->type==T_ALIST */
352 if (c->type!=T_ALIST) {
353 fatal("invalid node type in assignment list\n");
356 for (i=c; i; i=i->r) {
357 /* ASSERT i->l && i->l->type==T_ASSIGNMENT */
358 /* ASSERT i->l->l->type==T_KEY */
359 /* ASSERT i->l->r->type==T_LISTITEM */
361 l=process_ilist(context, i->l->r);
362 dict_iadd(context, k, l);
366 /* Take a list of items; turn any dictionaries in this list into lists */
367 static list_t *makelist(closure_t *self, struct cloc loc,
368 dict_t *context, list_t *args)
373 for (i=args; i; i=i->next) {
374 if (i->item->type==t_dict) {
376 for (e=i->item->data.dict->entries; e; e=e->next) {
377 r=list_append_list(r, e->val);
380 r=list_append_list(r, list_append(NULL,i->item));
386 /* Take a list consisting of a closure and some other things. Apply the
387 closure to the other things, and return the resulting list */
388 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
396 ci=list_elem(args,0);
397 if (ci && ci->type==t_closure) {
400 cfgfatal(loc,"map","closure cannot be applied\n");
402 for (al=args->next; al; al=al->next) {
403 /* Construct a single-element list */
406 /* Invoke the closure, append its result to the output */
407 r=list_append_list(r,cl->apply(cl,loc,context,&se));
410 cfgfatal(loc,"map","you must supply a closure as the "
416 /* Read a file and turn it into a string */
417 static list_t *readfile(closure_t *self, struct cloc loc,
418 dict_t *context, list_t *args)
427 cfgfatal(loc,"readfile","you must supply a filename\n");
429 if (r->type!=t_string) {
430 cfgfatal(loc,"readfile","filename must be a string\n");
432 filename=r->data.string;
433 f=fopen(filename,"rb");
435 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
436 loc.file,loc.line, filename);
438 if (fseek(f, 0, SEEK_END)!=0) {
439 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
443 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
445 if (fseek(f, 0, SEEK_SET)!=0) {
446 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
448 r=new_item(t_string,loc);
449 r->data.string=safe_malloc(length+1,"readfile");
450 if (fread(r->data.string,length,1,f)!=1) {
451 fatal("readfile (%s:%d): fread: could not read all of file\n",
454 r->data.string[length]=0;
456 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
458 return list_append(NULL,r);
461 static dict_t *process_config(struct p_node *c)
472 /* Predefined keys for boolean values */
473 i=new_item(t_bool,no_loc);
475 false=list_append(NULL,i);
476 i=new_item(t_bool,no_loc);
478 true=list_append(NULL,i);
479 dict_add(root,"false",false);
480 dict_add(root,"False",false);
481 dict_add(root,"FALSE",false);
482 dict_add(root,"no",false);
483 dict_add(root,"No",false);
484 dict_add(root,"NO",false);
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);
492 add_closure(root,"makelist",makelist);
493 add_closure(root,"readfile",readfile);
494 add_closure(root,"map",map);
496 init_builtin_modules(root);
498 process_alist(context, c);
503 /***** Externally accessible functions */
505 atom_t intern(string_t s)
509 for (i=atoms; i; i=i->next) {
510 if (strcmp(i->a, s)==0) break;
514 /* Did't find it; create a new one */
515 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
516 i->a=safe_strdup(s,"intern: alloc string");
523 list_t *dict_lookup(dict_t *dict, string_t key)
525 return dict_ilookup(dict, intern(key));
528 list_t *dict_lookup_primitive(dict_t *dict, string_t key)
530 return dict_ilookup_primitive(dict, intern(key));
533 void dict_add(dict_t *dict, string_t key, list_t *val)
535 dict_iadd(dict,intern(key),val);
538 string_t *dict_keys(dict_t *dict)
542 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
543 for (i=dict->entries, j=r; i; i=i->next, j++) {
551 /* List-related functions */
553 list_t *list_new(void)
558 uint32_t list_length(list_t *a)
562 for (i=a; i; i=i->next) l++;
566 list_t *list_copy(list_t *a)
568 list_t *r, *i, *b, *l;
573 for (i=a; i; i=i->next) {
574 b=safe_malloc(sizeof(*b),"list_copy");
575 if (l) l->next=b; else r=b;
583 list_t *list_append_list(list_t *a, list_t *b)
589 for (i=a; i->next; i=i->next);
594 list_t *list_append(list_t *list, item_t *item)
598 l=safe_malloc(sizeof(*l),"list_append");
602 return list_append_list(list,l);
605 item_t *list_elem(list_t *l, uint32_t index)
608 if (index==0) return l->item;
609 return list_elem(l->next, index-1);
612 list_t *new_closure(closure_t *cl)
616 i=new_item(t_closure,no_loc);
618 return list_append(NULL,i);
621 void add_closure(dict_t *dict, string_t name, apply_fn apply)
624 c=safe_malloc(sizeof(*c),"add_closure");
629 dict_add(dict,name,new_closure(c));
632 void *find_cl_if(dict_t *dict, string_t name, uint32_t type,
633 bool_t fail_if_invalid, string_t desc, struct cloc loc)
639 l=dict_lookup(dict,name);
641 if (!fail_if_invalid) return NULL;
642 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
645 if (i->type!=t_closure) {
646 if (!fail_if_invalid) return NULL;
647 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
650 if (cl->type!=type) {
651 if (!fail_if_invalid) return NULL;
652 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
654 return cl->interface;
657 /* Convenience functions for modules reading configuration dictionaries */
658 item_t *dict_find_item(dict_t *dict, string_t key, bool_t required,
659 string_t desc, struct cloc loc)
664 l=dict_lookup(dict,key);
666 if (!required) return NULL;
667 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
673 string_t dict_read_string(dict_t *dict, string_t key, bool_t required,
674 string_t desc, struct cloc loc)
679 i=dict_find_item(dict,key,required,desc,loc);
681 if (i->type!=t_string) {
682 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
688 uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
689 string_t desc, struct cloc loc, uint32_t def)
694 i=dict_find_item(dict,key,required,desc,loc);
696 if (i->type!=t_number) {
697 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
703 bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
704 string_t desc, struct cloc loc, bool_t def)
709 i=dict_find_item(dict,key,required,desc,loc);
711 if (i->type!=t_bool) {
712 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
718 uint32_t string_to_word(string_t s, struct cloc loc,
719 struct flagstr *f, string_t desc)
722 for (j=f; j->name; j++)
723 if (strcmp(s,j->name)==0)
725 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
729 uint32_t string_list_to_word(list_t *l, struct flagstr *f, string_t desc)
735 for (i=l; i; i=i->next) {
736 if (i->item->type!=t_string) {
737 cfgfatal(i->item->loc,desc,"all elements of list must be "
740 for (j=f; j->name; j++)
741 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
746 dict_t *read_conffile(char *name)
749 struct p_node *config;
751 if (strcmp(name,"-")==0) {
754 conffile=fopen(name,"r");
756 fatal_perror("Cannot open configuration file \"%s\"",name);
760 config=parse_conffile(conffile);
763 #ifdef DUMP_PARSE_TREE
764 printf("*** config file parse tree BEFORE MANGLE\n");
765 ptree_dump(config,0);
766 #endif /* DUMP_PARSE_TREE */
767 /* The root of the configuration is a T_ALIST, which needs reversing
768 before we mangle because it isn't the child of a T_DICT. */
769 config=list_reverse(config);
770 ptree_mangle(config);
771 #ifdef DUMP_PARSE_TREE
772 printf("\n\n*** config file parse tree AFTER MANGLE\n");
773 ptree_dump(config,0);
774 #endif /* DUMP_PARSE_TREE */
775 return process_config(config);