1 /* conffile.c - process the configuration file */
3 /* #define DUMP_PARSE_TREE */
9 #include "conffile_internal.h"
13 static struct cloc no_loc={"none",0};
16 struct atomlist *next;
28 struct searchlist *next;
33 struct searchlist *search;
34 struct entry *entries;
38 static struct atomlist *atoms=NULL;
40 static void process_alist(dict_t *context, struct p_node *c);
41 static list_t *process_invocation(dict_t *context, struct p_node *i);
43 static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
46 for (i=dict->entries; i; i=i->next) {
47 if (key==i->key) return i->val;
52 static list_t *dict_ilookup(dict_t *dict, atom_t key)
57 v=dict_ilookup_primitive(dict, key);
59 /* Check dictionaries in search path */
61 /* Check lexical parents */
62 for (d=dict; d; d=d->parent) {
63 v=dict_ilookup_primitive(d, key);
69 static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
72 if (dict_ilookup_primitive(dict, key)) {
73 fatal("duplicate key \"%s\" in dictionary",key);
75 e=safe_malloc(sizeof(*e),"dict_add");
76 e->next=dict->entries;
83 /***** Functions beyond this point are private to the config system *****/
85 static dict_t *dict_new(dict_t *parent)
89 d=safe_malloc(sizeof(*d),"dict_new");
97 static struct p_node *node_copy(struct p_node *n)
100 r=safe_malloc(sizeof(*r),"node_copy");
105 static struct p_node *list_reverse(struct p_node *list)
107 struct p_node *rl=NULL, *i, *n;
109 for (i=list; i; i=i->r) {
117 /* Since we use left-recursion in the parser for efficiency, sequences
118 end up "backwards" in the parse tree. Rather than have complicated
119 code for, eg. processing assignments in the right order, we reverse
120 these sequences here. */
121 static void ptree_mangle(struct p_node *t)
128 ASSERT(!t->l || t->l->type==T_ALIST);
129 ASSERT(!t->r || t->r->type==T_LISTITEM);
130 t->l=list_reverse(t->l);
131 t->r=list_reverse(t->r);
134 ASSERT(t->l->type==T_KEY);
135 ASSERT(t->r->type==T_LISTITEM);
136 t->r=list_reverse(t->r);
141 ASSERT(t->r->type==T_PATHELEM);
142 t->r=list_reverse(t->r);
146 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
147 t->r=list_reverse(t->r);
152 #ifdef DUMP_PARSE_TREE
153 /* Convert a node type to a string, for parse tree dump */
154 static string_t ntype(uint32_t type)
157 case T_STRING: return "T_STRING";
158 case T_NUMBER: return "T_NUMBER";
159 case T_KEY: return "T_KEY";
160 case T_ASSIGNMENT: return "T_ASSIGNMENT";
161 case T_LISTITEM: return "T_LISTITEM";
162 case T_EXEC: return "T_EXEC";
163 case T_PATHELEM: return "T_PATHELEM";
164 case T_ABSPATH: return "T_ABSPATH";
165 case T_RELPATH: return "T_RELPATH";
166 case T_DICT: return "T_DICT";
167 case T_ALIST: return "T_ALIST";
168 case T_ERROR: return "T_ERROR";
170 return "**unknown**";
173 static void ptree_indent(uint32_t amount)
176 for (i=0; i<amount; i++) printf(" . ");
179 static void ptree_dump(struct p_node *n, uint32_t d)
188 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
189 n->data.string,n->loc.file,n->loc.line); break;
190 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
191 n->data.number, n->loc.file,n->loc.line); break;
192 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
193 n->data.key, n->loc.file,n->loc.line); break;
194 default: printf("**unknown primitive type**\n"); break;
197 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
199 printf(" |-"); ptree_dump(n->l, d+1);
201 printf(" +-"); ptree_dump(n->r, d+1);
205 #endif /* DUMP_PARSE_TREE */
207 static dict_t *dict_find_root(dict_t *d)
211 for (i=d; i->parent; i=i->parent);
215 static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
220 ASSERT(p->type==T_PATHELEM);
221 ASSERT(p->l->type==T_KEY);
222 l=dict_ilookup(context, p->l->data.key);
224 cfgfatal(p->loc,"conffile","can't find key %s\n",
229 if (l->item->type != t_dict) {
230 cfgfatal(p->loc,"conffile","path element \"%s\" "
231 "is not a dictionary\n",p->l->data.key);
233 i=l->item->data.dict; /* First thing in list */
236 l=dict_ilookup_primitive(i, p->l->data.key);
238 cfgfatal(p->loc,"conffile","can't find key %s\n",
245 static item_t *new_item(enum types type, struct cloc loc)
249 i=safe_malloc(sizeof(*i),"new_item");
255 static list_t *process_item(dict_t *context, struct p_node *i)
261 item=new_item(t_string, i->loc);
262 item->data.string=i->data.string; /* XXX maybe strcpy */
265 item=new_item(t_number, i->loc);
266 item->data.number=i->data.number;
269 context=dict_find_root(context);
272 return dict_lookup_path(context, i->r);
273 /* returns immediately */
276 item=new_item(t_dict, i->loc);
277 item->data.dict=dict_new(context);
278 /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
279 process_alist(item->data.dict, i->l);
282 return process_invocation(context, i);
283 /* returns immediately */
286 #ifdef DUMP_PARSE_TREE
288 fatal("process_item: invalid node type for a list item (%s)",
291 fatal("process_item: list item has invalid node type %d - recompile "
292 "with DUMP_PARSE_TREE defined in conffile.c for more "
293 "detailed debug output",i->type);
294 #endif /* DUMP_PARSE_TREE */
297 return list_append(NULL,item);
300 static list_t *process_ilist(dict_t *context, struct p_node *l)
305 ASSERT(!l || l->type==T_LISTITEM);
309 for (i=l; i; i=i->r) {
310 r=list_append_list(r,process_item(context,i->l));
315 static list_t *process_invocation(dict_t *context, struct p_node *i)
321 ASSERT(i->type==T_EXEC);
322 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
323 cll=process_item(context,i->l);
325 if (cl->type != t_closure) {
326 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
328 if (!cl->data.closure->apply) {
329 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
331 args=process_ilist(context, i->r);
332 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
335 static void process_alist(dict_t *context, struct p_node *c)
341 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
343 ASSERT(c->type==T_ALIST);
344 if (c->type!=T_ALIST) {
345 fatal("invalid node type in assignment list");
348 for (i=c; i; i=i->r) {
349 ASSERT(i->l && i->l->type==T_ASSIGNMENT);
350 ASSERT(i->l->l->type==T_KEY);
351 ASSERT(i->l->r->type==T_LISTITEM);
353 l=process_ilist(context, i->l->r);
354 dict_iadd(context, k, l);
358 /* Take a list of items; turn any dictionaries in this list into lists */
359 static list_t *makelist(closure_t *self, struct cloc loc,
360 dict_t *context, list_t *args)
365 for (i=args; i; i=i->next) {
366 if (i->item->type==t_dict) {
368 for (e=i->item->data.dict->entries; e; e=e->next) {
369 r=list_append_list(r, e->val);
372 r=list_append_list(r, list_append(NULL,i->item));
378 /* Take a list consisting of a closure and some other things. Apply the
379 closure to the other things, and return the resulting list */
380 static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
388 ci=list_elem(args,0);
389 if (ci && ci->type==t_closure) {
392 cfgfatal(loc,"map","closure cannot be applied\n");
394 for (al=args->next; al; al=al->next) {
395 /* Construct a single-element list */
398 /* Invoke the closure, append its result to the output */
399 r=list_append_list(r,cl->apply(cl,loc,context,&se));
402 cfgfatal(loc,"map","you must supply a closure as the "
408 /* Read a file and turn it into a string */
409 static list_t *readfile(closure_t *self, struct cloc loc,
410 dict_t *context, list_t *args)
419 cfgfatal(loc,"readfile","you must supply a filename\n");
421 if (r->type!=t_string) {
422 cfgfatal(loc,"readfile","filename must be a string\n");
424 filename=r->data.string;
425 f=fopen(filename,"rb");
427 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
428 loc.file,loc.line, filename);
430 if (fseek(f, 0, SEEK_END)!=0) {
431 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
435 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
437 if (fseek(f, 0, SEEK_SET)!=0) {
438 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
440 r=new_item(t_string,loc);
441 r->data.string=safe_malloc(length+1,"readfile");
442 if (fread(r->data.string,length,1,f)!=1) {
443 (ferror(f) ? fatal_perror : fatal)
444 ("readfile (%s:%d): fread: could not read all of file",
447 r->data.string[length]=0;
449 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
451 return list_append(NULL,r);
454 static dict_t *process_config(struct p_node *c)
465 /* Predefined keys for boolean values */
466 /* "nowise" and "verily" have the advantage of being the same
467 length, so they line up nicely... thanks VKC and SGT (who also
468 point out that "mayhap" is a good "maybe" value as well) */
469 i=new_item(t_bool,no_loc);
471 false=list_append(NULL,i);
472 i=new_item(t_bool,no_loc);
474 true=list_append(NULL,i);
475 dict_add(root,"false",false);
476 dict_add(root,"False",false);
477 dict_add(root,"FALSE",false);
478 dict_add(root,"no",false);
479 dict_add(root,"No",false);
480 dict_add(root,"NO",false);
481 dict_add(root,"nowise",false);
482 dict_add(root,"Nowise",false);
483 dict_add(root,"NOWISE",false);
484 dict_add(root,"true",true);
485 dict_add(root,"True",true);
486 dict_add(root,"TRUE",true);
487 dict_add(root,"yes",true);
488 dict_add(root,"Yes",true);
489 dict_add(root,"YES",true);
490 dict_add(root,"verily",true);
491 dict_add(root,"Verily",true);
492 dict_add(root,"VERILY",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(cstring_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, cstring_t key)
527 return dict_ilookup(dict, intern(key));
530 list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
532 return dict_ilookup_primitive(dict, intern(key));
535 void dict_add(dict_t *dict, cstring_t key, list_t *val)
537 dict_iadd(dict,intern(key),val);
540 cstring_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 static 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, cstring_t name, apply_fn apply)
626 c=safe_malloc(sizeof(*c),"add_closure");
632 dict_add(dict,name,new_closure(c));
635 void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
636 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
642 l=dict_lookup(dict,name);
644 if (!fail_if_invalid) return NULL;
645 cfgfatal(loc,desc,"closure \"%s\" not found\n",name);
648 if (i->type!=t_closure) {
649 if (!fail_if_invalid) return NULL;
650 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
653 if (cl->type!=type) {
654 if (!fail_if_invalid) return NULL;
655 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
657 return cl->interface;
660 /* Convenience functions for modules reading configuration dictionaries */
661 item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
662 cstring_t desc, struct cloc loc)
667 l=dict_lookup(dict,key);
669 if (!required) return NULL;
670 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",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);
691 uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
692 cstring_t desc, struct cloc loc, uint32_t def)
697 i=dict_find_item(dict,key,required,desc,loc);
699 if (i->type!=t_number) {
700 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
706 bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
707 cstring_t desc, struct cloc loc, bool_t def)
712 i=dict_find_item(dict,key,required,desc,loc);
714 if (i->type!=t_bool) {
715 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
721 uint32_t string_to_word(cstring_t s, struct cloc loc,
722 struct flagstr *f, cstring_t desc)
725 for (j=f; j->name; j++)
726 if (strcmp(s,j->name)==0)
728 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
732 uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
738 for (i=l; i; i=i->next) {
739 if (i->item->type!=t_string) {
740 cfgfatal(i->item->loc,desc,"all elements of list must be "
743 for (j=f; j->name; j++)
744 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
749 dict_t *read_conffile(const char *name)
752 struct p_node *config;
754 if (strcmp(name,"-")==0) {
757 conffile=fopen(name,"r");
759 fatal_perror("Cannot open configuration file \"%s\"",name);
763 config=parse_conffile(conffile);
766 #ifdef DUMP_PARSE_TREE
767 printf("*** config file parse tree BEFORE MANGLE\n");
768 ptree_dump(config,0);
769 #endif /* DUMP_PARSE_TREE */
770 /* The root of the configuration is a T_ALIST, which needs reversing
771 before we mangle because it isn't the child of a T_DICT. */
772 config=list_reverse(config);
773 ptree_mangle(config);
774 #ifdef DUMP_PARSE_TREE
775 printf("\n\n*** config file parse tree AFTER MANGLE\n");
776 ptree_dump(config,0);
777 #endif /* DUMP_PARSE_TREE */
778 return process_config(config);