1 /* conffile.c - process the configuration file */
3 /* #define DUMP_PARSE_TREE */
11 #include "conffile_internal.h"
12 #include "conffile.yy.h"
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);
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)
100 static struct p_node *node_copy(struct p_node *n)
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 const char *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(int amount)
179 for (i=0; i<amount; i++) printf(" . ");
182 static void ptree_dump(struct p_node *n, int d)
189 if (T_IS_PRIMITIVE(n->type)) {
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;
201 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
203 printf(" |-"); ptree_dump(n->l, d+1);
205 printf(" +-"); ptree_dump(n->r, d+1);
209 #endif /* DUMP_PARSE_TREE */
211 static dict_t *dict_find_root(dict_t *d)
215 for (i=d; i->parent; i=i->parent);
219 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
224 ASSERT(p->type==T_PATHELEM);
225 ASSERT(p->l->type==T_KEY);
226 l=dict_ilookup(context, p->l->data.key);
228 cfgfatal(p->loc,"conffile","can't find key %s\n",
233 if (l->item->type != t_dict) {
234 cfgfatal(p->loc,"conffile","path element \"%s\" "
235 "is not a dictionary\n",p->l->data.key);
237 i=l->item->data.dict; /* First thing in list */
240 l=dict_ilookup_primitive(i, p->l->data.key);
242 cfgfatal(p->loc,"conffile","can't find key %s\n",
249 static item_t *new_item(enum types type, struct cloc loc)
259 static list_t *process_item(dict_t *context, struct p_node *i)
265 item=new_item(t_string, i->loc);
266 item->data.string=i->data.string; /* XXX maybe strcpy */
269 item=new_item(t_number, i->loc);
270 item->data.number=i->data.number;
273 context=dict_find_root(context);
276 return dict_lookup_path(context, i->r);
277 /* returns immediately */
280 item=new_item(t_dict, i->loc);
281 item->data.dict=dict_new(context);
282 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
283 process_alist(item->data.dict, i->l);
286 return process_invocation(context, i);
287 /* returns immediately */
290 #ifdef DUMP_PARSE_TREE
292 fatal("process_item: invalid node type for a list item (%s)",
295 fatal("process_item: list item has invalid node type %d - recompile "
296 "with DUMP_PARSE_TREE defined in conffile.c for more "
297 "detailed debug output",i->type);
298 #endif /* DUMP_PARSE_TREE */
301 return list_append(NULL,item);
304 static list_t *process_ilist(dict_t *context, struct p_node *l)
309 ASSERT(!l || l->type==T_LISTITEM);
313 for (i=l; i; i=i->r) {
314 r=list_append_list(r,process_item(context,i->l));
319 static list_t *process_invocation(dict_t *context, struct p_node *i)
325 ASSERT(i->type==T_EXEC);
326 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
327 cll=process_item(context,i->l);
329 if (cl->type != t_closure) {
330 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
332 if (!cl->data.closure->apply) {
333 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
335 args=process_ilist(context, i->r);
336 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
339 static void process_alist(dict_t *context, struct p_node *c)
345 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
347 ASSERT(c->type==T_ALIST);
348 if (c->type!=T_ALIST) {
349 fatal("invalid node type in assignment list");
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);
357 l=process_ilist(context, i->l->r);
358 dict_iadd(context, k, l);
362 /* Take a list of items; turn any dictionaries in this list into lists */
363 static list_t *makelist(closure_t *self, struct cloc loc,
364 dict_t *context, list_t *args)
369 for (i=args; i; i=i->next) {
370 if (i->item->type==t_dict) {
372 for (e=i->item->data.dict->entries; e; e=e->next) {
373 r=list_append_list(r, e->val);
376 r=list_append_list(r, list_append(NULL,i->item));
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 */
384 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
392 ci=list_elem(args,0);
393 if (ci && ci->type==t_closure) {
396 cfgfatal(loc,"map","closure cannot be applied\n");
398 for (al=args->next; al; al=al->next) {
399 /* Construct a single-element list */
402 /* Invoke the closure, append its result to the output */
403 r=list_append_list(r,cl->apply(cl,loc,context,&se));
406 cfgfatal(loc,"map","you must supply a closure as the "
412 /* Read a file and turn it into a string */
413 static list_t *readfile(closure_t *self, struct cloc loc,
414 dict_t *context, list_t *args)
423 cfgfatal(loc,"readfile","you must supply a filename\n");
425 if (r->type!=t_string) {
426 cfgfatal(loc,"readfile","filename must be a string\n");
428 filename=r->data.string;
429 f=fopen(filename,"rb");
431 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
432 loc.file,loc.line, filename);
434 if (fseek(f, 0, SEEK_END)!=0) {
435 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
439 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
441 if (fseek(f, 0, SEEK_SET)!=0) {
442 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
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 (ferror(f) ? fatal_perror : fatal)
448 ("readfile (%s:%d): fread: could not read all of file",
451 r->data.string[length]=0;
453 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
455 return list_append(NULL,r);
458 static dict_t *process_config(struct p_node *c)
469 /* Predefined keys for boolean values */
470 /* "nowise" and "verily" have the advantage of being the same
471 length, so they line up nicely... thanks VKC and SGT (who also
472 point out that "mayhap" is a good "maybe" value as well) */
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,"nowise",false);
486 dict_add(root,"Nowise",false);
487 dict_add(root,"NOWISE",false);
488 dict_add(root,"true",true);
489 dict_add(root,"True",true);
490 dict_add(root,"TRUE",true);
491 dict_add(root,"yes",true);
492 dict_add(root,"Yes",true);
493 dict_add(root,"YES",true);
494 dict_add(root,"verily",true);
495 dict_add(root,"Verily",true);
496 dict_add(root,"VERILY",true);
498 add_closure(root,"makelist",makelist);
499 add_closure(root,"readfile",readfile);
500 add_closure(root,"map",map);
502 init_builtin_modules(root);
504 process_alist(context, c);
509 /***** Externally accessible functions */
511 atom_t intern(cstring_t s)
515 for (i=atoms; i; i=i->next) {
516 if (strcmp(i->a, s)==0) break;
520 /* Did't find it; create a new one */
522 i->a=safe_strdup(s,"intern: alloc string");
529 list_t *dict_lookup(dict_t *dict, cstring_t key)
531 return dict_ilookup(dict, intern(key));
534 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
536 return dict_ilookup_primitive(dict, intern(key));
539 void dict_add(dict_t *dict, cstring_t key, list_t *val)
541 dict_iadd(dict,intern(key),val);
544 cstring_t *dict_keys(dict_t *dict)
548 NEW_ARY(r,dict->size+1);
549 for (i=dict->entries, j=r; i; i=i->next, j++) {
557 /* List-related functions */
559 list_t *list_new(void)
564 int32_t list_length(const list_t *a)
568 for (i=a; i; i=i->next) { assert(l < INT_MAX); l++; }
572 static list_t *list_copy(list_t *a)
574 list_t *r, *i, *b, *l;
579 for (i=a; i; i=i->next) {
581 if (l) l->next=b; else r=b;
589 list_t *list_append_list(list_t *a, list_t *b)
595 for (i=a; i->next; i=i->next);
600 list_t *list_append(list_t *list, item_t *item)
608 return list_append_list(list,l);
611 item_t *list_elem(list_t *l, int32_t index)
614 if (index==0) return l->item;
615 return list_elem(l->next, index-1);
618 list_t *new_closure(closure_t *cl)
622 i=new_item(t_closure,no_loc);
624 return list_append(NULL,i);
627 void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
636 dict_add(dict,name,new_closure(c));
639 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
640 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
645 i = dict_find_item(dict,name,fail_if_invalid,desc,loc);
646 if (i->type!=t_closure) {
647 if (!fail_if_invalid) return NULL;
648 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
651 if (cl->type!=type) {
652 if (!fail_if_invalid) return NULL;
653 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
655 return cl->interface;
658 /* Convenience functions for modules reading configuration dictionaries */
659 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
660 cstring_t desc, struct cloc loc)
665 l=dict_lookup(dict,key);
667 if (!required) return NULL;
668 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
670 if(list_length(l) != 1)
671 cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key);
676 string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
677 cstring_t desc, struct cloc loc)
682 i=dict_find_item(dict,key,required,desc,loc);
684 if (i->type!=t_string) {
685 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
687 if (strlen(i->data.string) > INT_MAX/10) {
688 cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key);
694 const char **dict_read_string_array(dict_t *dict, cstring_t key,
695 bool_t required, cstring_t desc,
696 struct cloc loc, const char *const *def)
699 const char **ra, **rap;
701 l=dict_lookup(dict,key);
703 if (!required) return (const char**)def;
704 cfgfatal(loc,desc,"required string list \"%s\" not found\n",key);
707 int32_t ll=list_length(l);
709 for (rap=ra; l; l=l->next,rap++) {
711 if (it->type!=t_string)
712 cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key);
713 *rap=it->data.string;
719 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
720 cstring_t desc, struct cloc loc, uint32_t def)
725 i=dict_find_item(dict,key,required,desc,loc);
727 if (i->type!=t_number) {
728 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
730 if (i->data.number >= 0x80000000) {
731 cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key);
737 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
738 cstring_t desc, struct cloc loc, bool_t def)
743 i=dict_find_item(dict,key,required,desc,loc);
745 if (i->type!=t_bool) {
746 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
752 uint32_t string_to_word(cstring_t s, struct cloc loc,
753 struct flagstr *f, cstring_t desc)
756 for (j=f; j->name; j++)
757 if (strcmp(s,j->name)==0)
759 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
763 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
769 for (i=l; i; i=i->next) {
770 if (i->item->type!=t_string) {
771 cfgfatal(i->item->loc,desc,"all elements of list must be "
774 for (j=f; j->name; j++)
775 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
780 dict_t *read_conffile(const char *name)
783 struct p_node *config;
785 if (strcmp(name,"-")==0) {
788 conffile=fopen(name,"r");
790 fatal_perror("Cannot open configuration file \"%s\"",name);
794 config=parse_conffile(conffile);
797 #ifdef DUMP_PARSE_TREE
798 printf("*** config file parse tree BEFORE MANGLE\n");
799 ptree_dump(config,0);
800 #endif /* DUMP_PARSE_TREE */
801 /* The root of the configuration is a T_ALIST, which needs reversing
802 before we mangle because it isn't the child of a T_DICT. */
803 config=list_reverse(config);
804 ptree_mangle(config);
805 #ifdef DUMP_PARSE_TREE
806 printf("\n\n*** config file parse tree AFTER MANGLE\n");
807 ptree_dump(config,0);
808 #endif /* DUMP_PARSE_TREE */
809 return process_config(config);