1 /* conffile.c - process the configuration file */
3 /* #define DUMP_PARSE_TREE */
9 #include "conffile_internal.h"
14 extern void init_builtin_modules(dict_t *dict);
16 static struct cloc no_loc={"none",0};
19 struct atomlist *next;
31 struct searchlist *next;
36 struct searchlist *search;
37 struct entry *entries;
41 static struct atomlist *atoms=NULL;
43 static void process_alist(dict_t *context, struct p_node *c);
44 static list_t *process_invocation(dict_t *context, struct p_node *i);
46 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
49 for (i=dict->entries; i; i=i->next) {
50 if (key==i->key) return i->val;
55 static list_t *dict_ilookup(dict_t *dict, atom_t key)
60 v=dict_ilookup_primitive(dict, key);
62 /* Check dictionaries in search path */
64 /* Check lexical parents */
65 for (d=dict; d; d=d->parent) {
66 v=dict_ilookup_primitive(d, key);
72 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
75 if (dict_ilookup_primitive(dict, key)) {
76 fatal("duplicate key \"%s\" in dictionary",key);
78 e=safe_malloc(sizeof(*e),"dict_add");
79 e->next=dict->entries;
86 /***** Functions beyond this point are private to the config system *****/
88 static dict_t *dict_new(dict_t *parent)
92 d=safe_malloc(sizeof(*d),"dict_new");
100 static struct p_node *node_copy(struct p_node *n)
103 r=safe_malloc(sizeof(*r),"node_copy");
108 static struct p_node *list_reverse(struct p_node *list)
110 struct p_node *rl=NULL, *i, *n;
112 for (i=list; i; i=i->r) {
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. */
124 static void ptree_mangle(struct p_node *t)
131 ASSERT(!t->l || t->l->type==T_ALIST);
132 ASSERT(!t->r || t->r->type==T_LISTITEM);
133 t->l=list_reverse(t->l);
134 t->r=list_reverse(t->r);
137 ASSERT(t->l->type==T_KEY);
138 ASSERT(t->r->type==T_LISTITEM);
139 t->r=list_reverse(t->r);
144 ASSERT(t->r->type==T_PATHELEM);
145 t->r=list_reverse(t->r);
149 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
150 t->r=list_reverse(t->r);
155 #ifdef DUMP_PARSE_TREE
156 /* Convert a node type to a string, for parse tree dump */
157 static string_t ntype(uint32_t 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";
173 return "**unknown**";
176 static void ptree_indent(uint32_t amount)
179 for (i=0; i<amount; i++) printf(" . ");
182 static void ptree_dump(struct p_node *n, uint32_t d)
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;
200 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
202 printf(" |-"); ptree_dump(n->l, d+1);
204 printf(" +-"); ptree_dump(n->r, d+1);
208 #endif /* DUMP_PARSE_TREE */
210 static dict_t *dict_find_root(dict_t *d)
214 for (i=d; i->parent; i=i->parent);
218 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
223 ASSERT(p->type==T_PATHELEM);
224 ASSERT(p->l->type==T_KEY);
225 l=dict_ilookup(context, p->l->data.key);
227 cfgfatal(p->loc,"conffile","can't find key %s\n",
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);
236 i=l->item->data.dict; /* First thing in list */
239 l=dict_ilookup_primitive(i, p->l->data.key);
241 cfgfatal(p->loc,"conffile","can't find key %s\n",
248 static item_t *new_item(enum types type, struct cloc loc)
252 i=safe_malloc(sizeof(*i),"new_item");
258 static list_t *process_item(dict_t *context, struct p_node *i)
264 item=new_item(t_string, i->loc);
265 item->data.string=i->data.string; /* XXX maybe strcpy */
268 item=new_item(t_number, i->loc);
269 item->data.number=i->data.number;
272 context=dict_find_root(context);
275 return dict_lookup_path(context, i->r);
276 /* returns immediately */
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);
285 return process_invocation(context, i);
286 /* returns immediately */
289 #ifdef DUMP_PARSE_TREE
291 fatal("process_item: invalid node type for a list item (%s)",
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 */
300 return list_append(NULL,item);
303 static list_t *process_ilist(dict_t *context, struct p_node *l)
308 ASSERT(!l || l->type==T_LISTITEM);
312 for (i=l; i; i=i->r) {
313 r=list_append_list(r,process_item(context,i->l));
318 static list_t *process_invocation(dict_t *context, struct p_node *i)
324 ASSERT(i->type==T_EXEC);
325 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
326 cll=process_item(context,i->l);
328 if (cl->type != t_closure) {
329 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
331 if (!cl->data.closure->apply) {
332 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
334 args=process_ilist(context, i->r);
335 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
338 static void process_alist(dict_t *context, struct p_node *c)
344 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
346 ASSERT(c->type==T_ALIST);
347 if (c->type!=T_ALIST) {
348 fatal("invalid node type in assignment list");
351 for (i=c; i; i=i->r) {
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);
356 l=process_ilist(context, i->l->r);
357 dict_iadd(context, k, l);
361 /* Take a list of items; turn any dictionaries in this list into lists */
362 static list_t *makelist(closure_t *self, struct cloc loc,
363 dict_t *context, list_t *args)
368 for (i=args; i; i=i->next) {
369 if (i->item->type==t_dict) {
371 for (e=i->item->data.dict->entries; e; e=e->next) {
372 r=list_append_list(r, e->val);
375 r=list_append_list(r, list_append(NULL,i->item));
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 */
383 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
391 ci=list_elem(args,0);
392 if (ci && ci->type==t_closure) {
395 cfgfatal(loc,"map","closure cannot be applied\n");
397 for (al=args->next; al; al=al->next) {
398 /* Construct a single-element list */
401 /* Invoke the closure, append its result to the output */
402 r=list_append_list(r,cl->apply(cl,loc,context,&se));
405 cfgfatal(loc,"map","you must supply a closure as the "
411 /* Read a file and turn it into a string */
412 static list_t *readfile(closure_t *self, struct cloc loc,
413 dict_t *context, list_t *args)
422 cfgfatal(loc,"readfile","you must supply a filename\n");
424 if (r->type!=t_string) {
425 cfgfatal(loc,"readfile","filename must be a string\n");
427 filename=r->data.string;
428 f=fopen(filename,"rb");
430 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
431 loc.file,loc.line, filename);
433 if (fseek(f, 0, SEEK_END)!=0) {
434 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
438 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
440 if (fseek(f, 0, SEEK_SET)!=0) {
441 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
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) {
446 (ferror(f) ? fatal_perror : fatal)
447 ("readfile (%s:%d): fread: could not read all of file",
450 r->data.string[length]=0;
452 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
454 return list_append(NULL,r);
457 static dict_t *process_config(struct p_node *c)
468 /* Predefined keys for boolean values */
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) */
472 i=new_item(t_bool,no_loc);
474 false=list_append(NULL,i);
475 i=new_item(t_bool,no_loc);
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);
484 dict_add(root,"nowise",false);
485 dict_add(root,"Nowise",false);
486 dict_add(root,"NOWISE",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);
493 dict_add(root,"verily",true);
494 dict_add(root,"Verily",true);
495 dict_add(root,"VERILY",true);
497 add_closure(root,"makelist",makelist);
498 add_closure(root,"readfile",readfile);
499 add_closure(root,"map",map);
501 init_builtin_modules(root);
503 process_alist(context, c);
508 /***** Externally accessible functions */
510 atom_t intern(cstring_t s)
514 for (i=atoms; i; i=i->next) {
515 if (strcmp(i->a, s)==0) break;
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");
528 list_t *dict_lookup(dict_t *dict, cstring_t key)
530 return dict_ilookup(dict, intern(key));
533 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
535 return dict_ilookup_primitive(dict, intern(key));
538 void dict_add(dict_t *dict, cstring_t key, list_t *val)
540 dict_iadd(dict,intern(key),val);
543 cstring_t *dict_keys(dict_t *dict)
547 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
548 for (i=dict->entries, j=r; i; i=i->next, j++) {
556 /* List-related functions */
558 list_t *list_new(void)
563 uint32_t list_length(list_t *a)
567 for (i=a; i; i=i->next) l++;
571 list_t *list_copy(list_t *a)
573 list_t *r, *i, *b, *l;
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;
588 list_t *list_append_list(list_t *a, list_t *b)
594 for (i=a; i->next; i=i->next);
599 list_t *list_append(list_t *list, item_t *item)
603 l=safe_malloc(sizeof(*l),"list_append");
607 return list_append_list(list,l);
610 item_t *list_elem(list_t *l, uint32_t index)
613 if (index==0) return l->item;
614 return list_elem(l->next, index-1);
617 list_t *new_closure(closure_t *cl)
621 i=new_item(t_closure,no_loc);
623 return list_append(NULL,i);
626 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
629 c=safe_malloc(sizeof(*c),"add_closure");
635 dict_add(dict,name,new_closure(c));
638 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
639 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
645 l=dict_lookup(dict,name);
647 if (!fail_if_invalid) return NULL;
648 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
651 if (i->type!=t_closure) {
652 if (!fail_if_invalid) return NULL;
653 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
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);
660 return cl->interface;
663 /* Convenience functions for modules reading configuration dictionaries */
664 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
665 cstring_t desc, struct cloc loc)
670 l=dict_lookup(dict,key);
672 if (!required) return NULL;
673 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
679 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
680 cstring_t desc, struct cloc loc)
685 i=dict_find_item(dict,key,required,desc,loc);
687 if (i->type!=t_string) {
688 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
694 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
695 cstring_t desc, struct cloc loc, uint32_t def)
700 i=dict_find_item(dict,key,required,desc,loc);
702 if (i->type!=t_number) {
703 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
709 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
710 cstring_t desc, struct cloc loc, bool_t def)
715 i=dict_find_item(dict,key,required,desc,loc);
717 if (i->type!=t_bool) {
718 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
724 uint32_t string_to_word(cstring_t s, struct cloc loc,
725 struct flagstr *f, cstring_t desc)
728 for (j=f; j->name; j++)
729 if (strcmp(s,j->name)==0)
731 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
735 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
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 "
746 for (j=f; j->name; j++)
747 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
752 dict_t *read_conffile(const char *name)
755 struct p_node *config;
757 if (strcmp(name,"-")==0) {
760 conffile=fopen(name,"r");
762 fatal_perror("Cannot open configuration file \"%s\"",name);
766 config=parse_conffile(conffile);
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);