1 /* conffile.c - process the configuration file */
3 /* #define DUMP_PARSE_TREE */
9 #include "conffile_internal.h"
10 #include "conffile.yy.h"
14 static struct cloc no_loc={"none",0};
17 struct atomlist *next;
29 struct searchlist *next;
34 struct searchlist *search;
35 struct entry *entries;
39 static struct atomlist *atoms=NULL;
41 static void process_alist(dict_t *context, struct p_node *c);
42 static list_t *process_invocation(dict_t *context, struct p_node *i);
44 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
47 for (i=dict->entries; i; i=i->next) {
48 if (key==i->key) return i->val;
53 static list_t *dict_ilookup(dict_t *dict, atom_t key)
58 v=dict_ilookup_primitive(dict, key);
60 /* Check dictionaries in search path */
62 /* Check lexical parents */
63 for (d=dict; d; d=d->parent) {
64 v=dict_ilookup_primitive(d, key);
70 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
73 if (dict_ilookup_primitive(dict, key)) {
74 fatal("duplicate key \"%s\" in dictionary",key);
76 e=safe_malloc(sizeof(*e),"dict_add");
77 e->next=dict->entries;
84 /***** Functions beyond this point are private to the config system *****/
86 static dict_t *dict_new(dict_t *parent)
90 d=safe_malloc(sizeof(*d),"dict_new");
98 static struct p_node *node_copy(struct p_node *n)
101 r=safe_malloc(sizeof(*r),"node_copy");
106 static struct p_node *list_reverse(struct p_node *list)
108 struct p_node *rl=NULL, *i, *n;
110 for (i=list; i; i=i->r) {
118 /* Since we use left-recursion in the parser for efficiency, sequences
119 end up "backwards" in the parse tree. Rather than have complicated
120 code for, eg. processing assignments in the right order, we reverse
121 these sequences here. */
122 static void ptree_mangle(struct p_node *t)
129 ASSERT(!t->l || t->l->type==T_ALIST);
130 ASSERT(!t->r || t->r->type==T_LISTITEM);
131 t->l=list_reverse(t->l);
132 t->r=list_reverse(t->r);
135 ASSERT(t->l->type==T_KEY);
136 ASSERT(t->r->type==T_LISTITEM);
137 t->r=list_reverse(t->r);
142 ASSERT(t->r->type==T_PATHELEM);
143 t->r=list_reverse(t->r);
147 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
148 t->r=list_reverse(t->r);
153 #ifdef DUMP_PARSE_TREE
154 /* Convert a node type to a string, for parse tree dump */
155 static string_t ntype(uint32_t type)
158 case T_STRING: return "T_STRING";
159 case T_NUMBER: return "T_NUMBER";
160 case T_KEY: return "T_KEY";
161 case T_ASSIGNMENT: return "T_ASSIGNMENT";
162 case T_LISTITEM: return "T_LISTITEM";
163 case T_EXEC: return "T_EXEC";
164 case T_PATHELEM: return "T_PATHELEM";
165 case T_ABSPATH: return "T_ABSPATH";
166 case T_RELPATH: return "T_RELPATH";
167 case T_DICT: return "T_DICT";
168 case T_ALIST: return "T_ALIST";
169 case T_ERROR: return "T_ERROR";
171 return "**unknown**";
174 static void ptree_indent(uint32_t amount)
177 for (i=0; i<amount; i++) printf(" . ");
180 static void ptree_dump(struct p_node *n, uint32_t d)
189 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
190 n->data.string,n->loc.file,n->loc.line); break;
191 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
192 n->data.number, n->loc.file,n->loc.line); break;
193 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
194 n->data.key, n->loc.file,n->loc.line); break;
195 default: printf("**unknown primitive type**\n"); break;
198 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
200 printf(" |-"); ptree_dump(n->l, d+1);
202 printf(" +-"); ptree_dump(n->r, d+1);
206 #endif /* DUMP_PARSE_TREE */
208 static dict_t *dict_find_root(dict_t *d)
212 for (i=d; i->parent; i=i->parent);
216 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
221 ASSERT(p->type==T_PATHELEM);
222 ASSERT(p->l->type==T_KEY);
223 l=dict_ilookup(context, p->l->data.key);
225 cfgfatal(p->loc,"conffile","can't find key %s\n",
230 if (l->item->type != t_dict) {
231 cfgfatal(p->loc,"conffile","path element \"%s\" "
232 "is not a dictionary\n",p->l->data.key);
234 i=l->item->data.dict; /* First thing in list */
237 l=dict_ilookup_primitive(i, p->l->data.key);
239 cfgfatal(p->loc,"conffile","can't find key %s\n",
246 static item_t *new_item(enum types type, struct cloc loc)
250 i=safe_malloc(sizeof(*i),"new_item");
256 static list_t *process_item(dict_t *context, struct p_node *i)
262 item=new_item(t_string, i->loc);
263 item->data.string=i->data.string; /* XXX maybe strcpy */
266 item=new_item(t_number, i->loc);
267 item->data.number=i->data.number;
270 context=dict_find_root(context);
273 return dict_lookup_path(context, i->r);
274 /* returns immediately */
277 item=new_item(t_dict, i->loc);
278 item->data.dict=dict_new(context);
279 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
280 process_alist(item->data.dict, i->l);
283 return process_invocation(context, i);
284 /* returns immediately */
287 #ifdef DUMP_PARSE_TREE
289 fatal("process_item: invalid node type for a list item (%s)",
292 fatal("process_item: list item has invalid node type %d - recompile "
293 "with DUMP_PARSE_TREE defined in conffile.c for more "
294 "detailed debug output",i->type);
295 #endif /* DUMP_PARSE_TREE */
298 return list_append(NULL,item);
301 static list_t *process_ilist(dict_t *context, struct p_node *l)
306 ASSERT(!l || l->type==T_LISTITEM);
310 for (i=l; i; i=i->r) {
311 r=list_append_list(r,process_item(context,i->l));
316 static list_t *process_invocation(dict_t *context, struct p_node *i)
322 ASSERT(i->type==T_EXEC);
323 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
324 cll=process_item(context,i->l);
326 if (cl->type != t_closure) {
327 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
329 if (!cl->data.closure->apply) {
330 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
332 args=process_ilist(context, i->r);
333 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
336 static void process_alist(dict_t *context, struct p_node *c)
342 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
344 ASSERT(c->type==T_ALIST);
345 if (c->type!=T_ALIST) {
346 fatal("invalid node type in assignment list");
349 for (i=c; i; i=i->r) {
350 ASSERT(i->l && i->l->type==T_ASSIGNMENT);
351 ASSERT(i->l->l->type==T_KEY);
352 ASSERT(i->l->r->type==T_LISTITEM);
354 l=process_ilist(context, i->l->r);
355 dict_iadd(context, k, l);
359 /* Take a list of items; turn any dictionaries in this list into lists */
360 static list_t *makelist(closure_t *self, struct cloc loc,
361 dict_t *context, list_t *args)
366 for (i=args; i; i=i->next) {
367 if (i->item->type==t_dict) {
369 for (e=i->item->data.dict->entries; e; e=e->next) {
370 r=list_append_list(r, e->val);
373 r=list_append_list(r, list_append(NULL,i->item));
379 /* Take a list consisting of a closure and some other things. Apply the
380 closure to the other things, and return the resulting list */
381 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
389 ci=list_elem(args,0);
390 if (ci && ci->type==t_closure) {
393 cfgfatal(loc,"map","closure cannot be applied\n");
395 for (al=args->next; al; al=al->next) {
396 /* Construct a single-element list */
399 /* Invoke the closure, append its result to the output */
400 r=list_append_list(r,cl->apply(cl,loc,context,&se));
403 cfgfatal(loc,"map","you must supply a closure as the "
409 /* Read a file and turn it into a string */
410 static list_t *readfile(closure_t *self, struct cloc loc,
411 dict_t *context, list_t *args)
420 cfgfatal(loc,"readfile","you must supply a filename\n");
422 if (r->type!=t_string) {
423 cfgfatal(loc,"readfile","filename must be a string\n");
425 filename=r->data.string;
426 f=fopen(filename,"rb");
428 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
429 loc.file,loc.line, filename);
431 if (fseek(f, 0, SEEK_END)!=0) {
432 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
436 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
438 if (fseek(f, 0, SEEK_SET)!=0) {
439 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
441 r=new_item(t_string,loc);
442 r->data.string=safe_malloc(length+1,"readfile");
443 if (fread(r->data.string,length,1,f)!=1) {
444 (ferror(f) ? fatal_perror : fatal)
445 ("readfile (%s:%d): fread: could not read all of file",
448 r->data.string[length]=0;
450 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
452 return list_append(NULL,r);
455 static dict_t *process_config(struct p_node *c)
466 /* Predefined keys for boolean values */
467 /* "nowise" and "verily" have the advantage of being the same
468 length, so they line up nicely... thanks VKC and SGT (who also
469 point out that "mayhap" is a good "maybe" value as well) */
470 i=new_item(t_bool,no_loc);
472 false=list_append(NULL,i);
473 i=new_item(t_bool,no_loc);
475 true=list_append(NULL,i);
476 dict_add(root,"false",false);
477 dict_add(root,"False",false);
478 dict_add(root,"FALSE",false);
479 dict_add(root,"no",false);
480 dict_add(root,"No",false);
481 dict_add(root,"NO",false);
482 dict_add(root,"nowise",false);
483 dict_add(root,"Nowise",false);
484 dict_add(root,"NOWISE",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);
491 dict_add(root,"verily",true);
492 dict_add(root,"Verily",true);
493 dict_add(root,"VERILY",true);
495 add_closure(root,"makelist",makelist);
496 add_closure(root,"readfile",readfile);
497 add_closure(root,"map",map);
499 init_builtin_modules(root);
501 process_alist(context, c);
506 /***** Externally accessible functions */
508 atom_t intern(cstring_t s)
512 for (i=atoms; i; i=i->next) {
513 if (strcmp(i->a, s)==0) break;
517 /* Did't find it; create a new one */
518 i=safe_malloc(sizeof(*i),"intern: alloc list entry");
519 i->a=safe_strdup(s,"intern: alloc string");
526 list_t *dict_lookup(dict_t *dict, cstring_t key)
528 return dict_ilookup(dict, intern(key));
531 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
533 return dict_ilookup_primitive(dict, intern(key));
536 void dict_add(dict_t *dict, cstring_t key, list_t *val)
538 dict_iadd(dict,intern(key),val);
541 cstring_t *dict_keys(dict_t *dict)
545 r=safe_malloc(sizeof(*r)*(dict->size+1),"dict_keys");
546 for (i=dict->entries, j=r; i; i=i->next, j++) {
554 /* List-related functions */
556 list_t *list_new(void)
561 uint32_t list_length(list_t *a)
565 for (i=a; i; i=i->next) l++;
569 static list_t *list_copy(list_t *a)
571 list_t *r, *i, *b, *l;
576 for (i=a; i; i=i->next) {
577 b=safe_malloc(sizeof(*b),"list_copy");
578 if (l) l->next=b; else r=b;
586 list_t *list_append_list(list_t *a, list_t *b)
592 for (i=a; i->next; i=i->next);
597 list_t *list_append(list_t *list, item_t *item)
601 l=safe_malloc(sizeof(*l),"list_append");
605 return list_append_list(list,l);
608 item_t *list_elem(list_t *l, uint32_t index)
611 if (index==0) return l->item;
612 return list_elem(l->next, index-1);
615 list_t *new_closure(closure_t *cl)
619 i=new_item(t_closure,no_loc);
621 return list_append(NULL,i);
624 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
627 c=safe_malloc(sizeof(*c),"add_closure");
633 dict_add(dict,name,new_closure(c));
636 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
637 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
643 l=dict_lookup(dict,name);
645 if (!fail_if_invalid) return NULL;
646 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
649 if (i->type!=t_closure) {
650 if (!fail_if_invalid) return NULL;
651 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
654 if (cl->type!=type) {
655 if (!fail_if_invalid) return NULL;
656 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
658 return cl->interface;
661 /* Convenience functions for modules reading configuration dictionaries */
662 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
663 cstring_t desc, struct cloc loc)
668 l=dict_lookup(dict,key);
670 if (!required) return NULL;
671 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
677 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
678 cstring_t desc, struct cloc loc)
683 i=dict_find_item(dict,key,required,desc,loc);
685 if (i->type!=t_string) {
686 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
692 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
693 cstring_t desc, struct cloc loc, uint32_t def)
698 i=dict_find_item(dict,key,required,desc,loc);
700 if (i->type!=t_number) {
701 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
707 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
708 cstring_t desc, struct cloc loc, bool_t def)
713 i=dict_find_item(dict,key,required,desc,loc);
715 if (i->type!=t_bool) {
716 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
722 uint32_t string_to_word(cstring_t s, struct cloc loc,
723 struct flagstr *f, cstring_t desc)
726 for (j=f; j->name; j++)
727 if (strcmp(s,j->name)==0)
729 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
733 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
739 for (i=l; i; i=i->next) {
740 if (i->item->type!=t_string) {
741 cfgfatal(i->item->loc,desc,"all elements of list must be "
744 for (j=f; j->name; j++)
745 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
750 dict_t *read_conffile(const char *name)
753 struct p_node *config;
755 if (strcmp(name,"-")==0) {
758 conffile=fopen(name,"r");
760 fatal_perror("Cannot open configuration file \"%s\"",name);
764 config=parse_conffile(conffile);
767 #ifdef DUMP_PARSE_TREE
768 printf("*** config file parse tree BEFORE MANGLE\n");
769 ptree_dump(config,0);
770 #endif /* DUMP_PARSE_TREE */
771 /* The root of the configuration is a T_ALIST, which needs reversing
772 before we mangle because it isn't the child of a T_DICT. */
773 config=list_reverse(config);
774 ptree_mangle(config);
775 #ifdef DUMP_PARSE_TREE
776 printf("\n\n*** config file parse tree AFTER MANGLE\n");
777 ptree_dump(config,0);
778 #endif /* DUMP_PARSE_TREE */
779 return process_config(config);