Merge remote-tracking branch 'mdw/mdw/powm-sec'
[secnet] / conffile.c
CommitLineData
2fe58dfd 1/* conffile.c - process the configuration file */
c215a4bc
IJ
2/*
3 * This file is part of secnet.
4 * See README for full list of copyright holders.
5 *
6 * secnet is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version d of the License, or
9 * (at your option) any later version.
10 *
11 * secnet is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 3 along with secnet; if not, see
18 * https://www.gnu.org/licenses/gpl.html.
19 */
2fe58dfd
SE
20
21/* #define DUMP_PARSE_TREE */
22
8689b3a9 23#include "secnet.h"
59230b9b
IJ
24#include <assert.h>
25#include <limits.h>
2fe58dfd 26#include <stdio.h>
2fe58dfd 27#include <string.h>
2fe58dfd
SE
28#include "conffile.h"
29#include "conffile_internal.h"
27f5042b 30#include "conffile.yy.h"
2fe58dfd 31#include "util.h"
7138d0c5
SE
32#include "ipaddr.h"
33
2fe58dfd
SE
34static struct cloc no_loc={"none",0};
35
36struct atomlist {
37 struct atomlist *next;
38 atom_t a;
39};
40
41struct entry {
42 struct entry *next;
43 atom_t key;
44 list_t *val;
45};
46
47struct searchlist {
48 struct dict *d;
49 struct searchlist *next;
50};
51
52struct dict {
53 struct dict *parent;
54 struct searchlist *search;
55 struct entry *entries;
1caa23ff 56 int32_t size;
2fe58dfd
SE
57};
58
59static struct atomlist *atoms=NULL;
60
61static void process_alist(dict_t *context, struct p_node *c);
62static list_t *process_invocation(dict_t *context, struct p_node *i);
63
64static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
65{
66 struct entry *i;
67 for (i=dict->entries; i; i=i->next) {
68 if (key==i->key) return i->val;
69 }
70 return NULL;
71}
72
73static list_t *dict_ilookup(dict_t *dict, atom_t key)
74{
75 dict_t *d;
76 list_t *v;
77
78 v=dict_ilookup_primitive(dict, key);
79 if (v) return v;
80 /* Check dictionaries in search path */
81/* XXX */
82 /* Check lexical parents */
83 for (d=dict; d; d=d->parent) {
84 v=dict_ilookup_primitive(d, key);
85 if (v) return v;
86 }
87 return NULL;
88}
89
90static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
91{
92 struct entry *e;
2fe58dfd 93 if (dict_ilookup_primitive(dict, key)) {
4f5e39ec 94 fatal("duplicate key \"%s\" in dictionary",key);
2fe58dfd 95 }
b7886fd4 96 NEW(e);
2fe58dfd
SE
97 e->next=dict->entries;
98 e->key=key;
99 e->val=val;
100 dict->entries=e;
101 dict->size++;
102}
103
104/***** Functions beyond this point are private to the config system *****/
105
106static dict_t *dict_new(dict_t *parent)
107{
108 dict_t *d;
109
b7886fd4 110 NEW(d);
2fe58dfd
SE
111 d->parent=parent;
112 d->search=NULL;
113 d->entries=NULL;
114 d->size=0;
115 return d;
116}
117
118static struct p_node *node_copy(struct p_node *n)
119{
120 struct p_node *r;
b7886fd4 121 NEW(r);
2fe58dfd
SE
122 *r=*n;
123 return r;
124}
125
126static struct p_node *list_reverse(struct p_node *list)
127{
128 struct p_node *rl=NULL, *i, *n;
129
130 for (i=list; i; i=i->r) {
131 n=node_copy(i);
132 n->r=rl;
133 rl=n;
134 }
135 return rl;
136}
137
138/* Since we use left-recursion in the parser for efficiency, sequences
139 end up "backwards" in the parse tree. Rather than have complicated
140 code for, eg. processing assignments in the right order, we reverse
141 these sequences here. */
142static void ptree_mangle(struct p_node *t)
143{
144 if (!t) return;
145 ptree_mangle(t->l);
146 ptree_mangle(t->r);
147 switch (t->type) {
148 case T_DICT:
4f5e39ec
SE
149 ASSERT(!t->l || t->l->type==T_ALIST);
150 ASSERT(!t->r || t->r->type==T_LISTITEM);
2fe58dfd
SE
151 t->l=list_reverse(t->l);
152 t->r=list_reverse(t->r);
153 break;
154 case T_ASSIGNMENT:
4f5e39ec
SE
155 ASSERT(t->l->type==T_KEY);
156 ASSERT(t->r->type==T_LISTITEM);
2fe58dfd
SE
157 t->r=list_reverse(t->r);
158 break;
159 case T_ABSPATH:
160 case T_RELPATH:
4f5e39ec
SE
161 ASSERT(t->l==NULL);
162 ASSERT(t->r->type==T_PATHELEM);
2fe58dfd
SE
163 t->r=list_reverse(t->r);
164 break;
165 case T_EXEC:
4f5e39ec
SE
166 ASSERT(t->l);
167 ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
2fe58dfd
SE
168 t->r=list_reverse(t->r);
169 break;
170 }
171}
172
173#ifdef DUMP_PARSE_TREE
174/* Convert a node type to a string, for parse tree dump */
3f36eb5f 175static const char *ntype(uint32_t type)
2fe58dfd
SE
176{
177 switch(type) {
178 case T_STRING: return "T_STRING";
179 case T_NUMBER: return "T_NUMBER";
180 case T_KEY: return "T_KEY";
181 case T_ASSIGNMENT: return "T_ASSIGNMENT";
182 case T_LISTITEM: return "T_LISTITEM";
183 case T_EXEC: return "T_EXEC";
184 case T_PATHELEM: return "T_PATHELEM";
185 case T_ABSPATH: return "T_ABSPATH";
186 case T_RELPATH: return "T_RELPATH";
187 case T_DICT: return "T_DICT";
188 case T_ALIST: return "T_ALIST";
189 case T_ERROR: return "T_ERROR";
190 }
191 return "**unknown**";
192}
193
1caa23ff 194static void ptree_indent(int amount)
2fe58dfd 195{
1caa23ff 196 int i;
2fe58dfd
SE
197 for (i=0; i<amount; i++) printf(" . ");
198}
199
3f36eb5f 200static void ptree_dump(struct p_node *n, int d)
2fe58dfd
SE
201{
202 if (!n) {
203 printf("NULL\n");
204 return;
205 }
206
3f36eb5f 207 if (T_IS_PRIMITIVE(n->type)) {
2fe58dfd
SE
208 switch(n->type) {
209 case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
210 n->data.string,n->loc.file,n->loc.line); break;
211 case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
212 n->data.number, n->loc.file,n->loc.line); break;
213 case T_KEY: printf("T_KEY: %s (%s line %d)\n",
214 n->data.key, n->loc.file,n->loc.line); break;
215 default: printf("**unknown primitive type**\n"); break;
216 }
217 } else {
3f36eb5f 218 assert(d<10000);
2fe58dfd
SE
219 printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
220 ptree_indent(d);
221 printf(" |-"); ptree_dump(n->l, d+1);
222 ptree_indent(d);
223 printf(" +-"); ptree_dump(n->r, d+1);
224 }
225}
226
227#endif /* DUMP_PARSE_TREE */
228
229static dict_t *dict_find_root(dict_t *d)
230{
231 dict_t *i;
232
233 for (i=d; i->parent; i=i->parent);
234 return i;
235}
236
237static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
238{
239 dict_t *i;
240 list_t *l;
241
4f5e39ec
SE
242 ASSERT(p->type==T_PATHELEM);
243 ASSERT(p->l->type==T_KEY);
2fe58dfd
SE
244 l=dict_ilookup(context, p->l->data.key);
245 if (!l) {
246 cfgfatal(p->loc,"conffile","can't find key %s\n",
247 p->l->data.key);
248 }
249
250 while (p->r) {
251 if (l->item->type != t_dict) {
252 cfgfatal(p->loc,"conffile","path element \"%s\" "
253 "is not a dictionary\n",p->l->data.key);
254 }
255 i=l->item->data.dict; /* First thing in list */
256
257 p=p->r;
258 l=dict_ilookup_primitive(i, p->l->data.key);
259 if (!l) {
260 cfgfatal(p->loc,"conffile","can't find key %s\n",
261 p->l->data.key);
262 }
263 }
264 return l;
265}
266
267static item_t *new_item(enum types type, struct cloc loc)
268{
269 item_t *i;
270
b7886fd4 271 NEW(i);
2fe58dfd
SE
272 i->type=type;
273 i->loc=loc;
274 return i;
275}
276
277static list_t *process_item(dict_t *context, struct p_node *i)
278{
279 item_t *item=NULL;
280
281 switch (i->type) {
282 case T_STRING:
283 item=new_item(t_string, i->loc);
284 item->data.string=i->data.string; /* XXX maybe strcpy */
285 break;
286 case T_NUMBER:
287 item=new_item(t_number, i->loc);
288 item->data.number=i->data.number;
289 break;
290 case T_ABSPATH:
291 context=dict_find_root(context);
292 /* falls through */
293 case T_RELPATH:
294 return dict_lookup_path(context, i->r);
295 /* returns immediately */
296 break;
297 case T_DICT:
298 item=new_item(t_dict, i->loc);
299 item->data.dict=dict_new(context);
300/* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
301 process_alist(item->data.dict, i->l);
302 break;
303 case T_EXEC:
304 return process_invocation(context, i);
305 /* returns immediately */
306 break;
307 default:
308#ifdef DUMP_PARSE_TREE
309 ptree_dump(i,0);
4f5e39ec 310 fatal("process_item: invalid node type for a list item (%s)",
2fe58dfd
SE
311 ntype(i->type));
312#else
313 fatal("process_item: list item has invalid node type %d - recompile "
314 "with DUMP_PARSE_TREE defined in conffile.c for more "
315 "detailed debug output",i->type);
316#endif /* DUMP_PARSE_TREE */
317 break;
318 }
319 return list_append(NULL,item);
320}
321
322static list_t *process_ilist(dict_t *context, struct p_node *l)
323{
324 struct p_node *i;
325 list_t *r;
326
4f5e39ec 327 ASSERT(!l || l->type==T_LISTITEM);
2fe58dfd
SE
328
329 r=list_new();
330
331 for (i=l; i; i=i->r) {
332 r=list_append_list(r,process_item(context,i->l));
333 }
334 return r;
335}
336
337static list_t *process_invocation(dict_t *context, struct p_node *i)
338{
339 list_t *cll;
340 item_t *cl;
341 list_t *args;
342
4f5e39ec
SE
343 ASSERT(i->type==T_EXEC);
344 ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
2fe58dfd
SE
345 cll=process_item(context,i->l);
346 cl=cll->item;
347 if (cl->type != t_closure) {
348 cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
349 }
469fd1d9
SE
350 if (!cl->data.closure->apply) {
351 cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
352 }
2fe58dfd
SE
353 args=process_ilist(context, i->r);
354 return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
355}
356
357static void process_alist(dict_t *context, struct p_node *c)
358{
359 struct p_node *i;
360 atom_t k;
361 list_t *l;
362
363 if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
364
4f5e39ec 365 ASSERT(c->type==T_ALIST);
2fe58dfd 366 if (c->type!=T_ALIST) {
4f5e39ec 367 fatal("invalid node type in assignment list");
2fe58dfd
SE
368 }
369
370 for (i=c; i; i=i->r) {
4f5e39ec
SE
371 ASSERT(i->l && i->l->type==T_ASSIGNMENT);
372 ASSERT(i->l->l->type==T_KEY);
373 ASSERT(i->l->r->type==T_LISTITEM);
2fe58dfd
SE
374 k=i->l->l->data.key;
375 l=process_ilist(context, i->l->r);
376 dict_iadd(context, k, l);
377 }
378}
379
380/* Take a list of items; turn any dictionaries in this list into lists */
381static list_t *makelist(closure_t *self, struct cloc loc,
382 dict_t *context, list_t *args)
383{
384 list_t *r=NULL, *i;
385 struct entry *e;
386
387 for (i=args; i; i=i->next) {
388 if (i->item->type==t_dict) {
389 /* Convert */
390 for (e=i->item->data.dict->entries; e; e=e->next) {
391 r=list_append_list(r, e->val);
392 }
393 } else {
394 r=list_append_list(r, list_append(NULL,i->item));
395 }
396 }
397 return r;
398}
399
9d3a4132
SE
400/* Take a list consisting of a closure and some other things. Apply the
401 closure to the other things, and return the resulting list */
402static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
403 list_t *args)
404{
405 list_t *r=NULL, *al;
406 item_t *ci;
407 closure_t *cl;
408 list_t se;
409
410 ci=list_elem(args,0);
411 if (ci && ci->type==t_closure) {
412 cl=ci->data.closure;
413 if (!cl->apply) {
414 cfgfatal(loc,"map","closure cannot be applied\n");
415 }
416 for (al=args->next; al; al=al->next) {
417 /* Construct a single-element list */
418 se.next=NULL;
419 se.item=al->item;
420 /* Invoke the closure, append its result to the output */
421 r=list_append_list(r,cl->apply(cl,loc,context,&se));
422 }
423 } else {
424 cfgfatal(loc,"map","you must supply a closure as the "
425 "first argument\n");
426 }
427 return r;
428}
429
2fe58dfd
SE
430/* Read a file and turn it into a string */
431static list_t *readfile(closure_t *self, struct cloc loc,
432 dict_t *context, list_t *args)
433{
434 FILE *f;
435 string_t filename;
436 long length;
437 item_t *r;
438
439 r=list_elem(args,0);
440 if (!r) {
441 cfgfatal(loc,"readfile","you must supply a filename\n");
442 }
443 if (r->type!=t_string) {
444 cfgfatal(loc,"readfile","filename must be a string\n");
445 }
446 filename=r->data.string;
447 f=fopen(filename,"rb");
448 if (!f) {
449 fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
450 loc.file,loc.line, filename);
451 }
452 if (fseek(f, 0, SEEK_END)!=0) {
453 fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
454 }
455 length=ftell(f);
456 if (length<0) {
457 fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
458 }
459 if (fseek(f, 0, SEEK_SET)!=0) {
460 fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
461 }
462 r=new_item(t_string,loc);
463 r->data.string=safe_malloc(length+1,"readfile");
464 if (fread(r->data.string,length,1,f)!=1) {
4f5e39ec
SE
465 (ferror(f) ? fatal_perror : fatal)
466 ("readfile (%s:%d): fread: could not read all of file",
467 loc.file,loc.line);
2fe58dfd
SE
468 }
469 r->data.string[length]=0;
470 if (fclose(f)!=0) {
471 fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
472 }
473 return list_append(NULL,r);
474}
475
476static dict_t *process_config(struct p_node *c)
477{
478 dict_t *root;
479 dict_t *context;
480 item_t *i;
481 list_t *false;
482 list_t *true;
483
484 root=dict_new(NULL);
485 context=root;
486
487 /* Predefined keys for boolean values */
3b83c932
SE
488 /* "nowise" and "verily" have the advantage of being the same
489 length, so they line up nicely... thanks VKC and SGT (who also
490 point out that "mayhap" is a good "maybe" value as well) */
2fe58dfd
SE
491 i=new_item(t_bool,no_loc);
492 i->data.bool=False;
493 false=list_append(NULL,i);
494 i=new_item(t_bool,no_loc);
495 i->data.bool=True;
496 true=list_append(NULL,i);
497 dict_add(root,"false",false);
498 dict_add(root,"False",false);
499 dict_add(root,"FALSE",false);
500 dict_add(root,"no",false);
501 dict_add(root,"No",false);
502 dict_add(root,"NO",false);
3b83c932
SE
503 dict_add(root,"nowise",false);
504 dict_add(root,"Nowise",false);
505 dict_add(root,"NOWISE",false);
2fe58dfd
SE
506 dict_add(root,"true",true);
507 dict_add(root,"True",true);
508 dict_add(root,"TRUE",true);
509 dict_add(root,"yes",true);
510 dict_add(root,"Yes",true);
511 dict_add(root,"YES",true);
3b83c932
SE
512 dict_add(root,"verily",true);
513 dict_add(root,"Verily",true);
514 dict_add(root,"VERILY",true);
2fe58dfd
SE
515
516 add_closure(root,"makelist",makelist);
517 add_closure(root,"readfile",readfile);
9d3a4132 518 add_closure(root,"map",map);
2fe58dfd
SE
519
520 init_builtin_modules(root);
521
522 process_alist(context, c);
523
524 return root;
525}
526
527/***** Externally accessible functions */
528
fe5e9cc4 529atom_t intern(cstring_t s)
2fe58dfd
SE
530{
531 struct atomlist *i;
532
533 for (i=atoms; i; i=i->next) {
534 if (strcmp(i->a, s)==0) break;
535 }
536
537 if (!i) {
538 /* Did't find it; create a new one */
b7886fd4 539 NEW(i);
2fe58dfd
SE
540 i->a=safe_strdup(s,"intern: alloc string");
541 i->next=atoms;
542 atoms=i;
543 }
544 return i->a;
545}
546
fe5e9cc4 547list_t *dict_lookup(dict_t *dict, cstring_t key)
2fe58dfd
SE
548{
549 return dict_ilookup(dict, intern(key));
550}
551
fe5e9cc4 552list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
2fe58dfd
SE
553{
554 return dict_ilookup_primitive(dict, intern(key));
555}
556
fe5e9cc4 557void dict_add(dict_t *dict, cstring_t key, list_t *val)
2fe58dfd
SE
558{
559 dict_iadd(dict,intern(key),val);
560}
561
fe5e9cc4 562cstring_t *dict_keys(dict_t *dict)
2fe58dfd
SE
563{
564 atom_t *r, *j;
565 struct entry *i;
952f601f 566 NEW_ARY(r,dict->size+1);
2fe58dfd
SE
567 for (i=dict->entries, j=r; i; i=i->next, j++) {
568 *j=i->key;
569 }
570 *j=NULL;
571 return r;
572}
573
574
575/* List-related functions */
576
577list_t *list_new(void)
578{
579 return NULL;
580}
581
a094a1ba 582int32_t list_length(const list_t *a)
b2a56f7c 583{
1caa23ff 584 int32_t l=0;
a094a1ba 585 const list_t *i;
59230b9b 586 for (i=a; i; i=i->next) { assert(l < INT_MAX); l++; }
b2a56f7c
SE
587 return l;
588}
589
08ee90a2 590static list_t *list_copy(list_t *a)
3454dce4
SE
591{
592 list_t *r, *i, *b, *l;
593
594 if (!a) return NULL;
595 l=NULL;
596 r=NULL;
597 for (i=a; i; i=i->next) {
b7886fd4 598 NEW(b);
3454dce4
SE
599 if (l) l->next=b; else r=b;
600 l=b;
601 b->item=i->item;
602 b->next=NULL;
603 }
604 return r;
605}
606
2fe58dfd
SE
607list_t *list_append_list(list_t *a, list_t *b)
608{
609 list_t *i;
610
3454dce4 611 b=list_copy(b);
2fe58dfd
SE
612 if (!a) return b;
613 for (i=a; i->next; i=i->next);
614 i->next=b;
615 return a;
616}
617
618list_t *list_append(list_t *list, item_t *item)
619{
620 list_t *l;
621
b7886fd4 622 NEW(l);
2fe58dfd
SE
623 l->item=item;
624 l->next=NULL;
625
626 return list_append_list(list,l);
627}
628
1caa23ff 629item_t *list_elem(list_t *l, int32_t index)
2fe58dfd
SE
630{
631 if (!l) return NULL;
632 if (index==0) return l->item;
633 return list_elem(l->next, index-1);
634}
635
636list_t *new_closure(closure_t *cl)
637{
638 item_t *i;
639
640 i=new_item(t_closure,no_loc);
641 i->data.closure=cl;
642 return list_append(NULL,i);
643}
644
fe5e9cc4 645void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
2fe58dfd
SE
646{
647 closure_t *c;
b7886fd4 648 NEW(c);
2fe58dfd 649 c->description=name;
3b83c932 650 c->type=CL_PURE;
2fe58dfd
SE
651 c->apply=apply;
652 c->interface=NULL;
653
654 dict_add(dict,name,new_closure(c));
655}
656
fe5e9cc4
SE
657void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
658 bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
2fe58dfd 659{
2fe58dfd
SE
660 item_t *i;
661 closure_t *cl;
662
6416c7a1 663 i = dict_find_item(dict,name,fail_if_invalid,desc,loc);
2fe58dfd
SE
664 if (i->type!=t_closure) {
665 if (!fail_if_invalid) return NULL;
666 cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
667 }
668 cl=i->data.closure;
669 if (cl->type!=type) {
670 if (!fail_if_invalid) return NULL;
671 cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
672 }
673 return cl->interface;
674}
675
676/* Convenience functions for modules reading configuration dictionaries */
fe5e9cc4
SE
677item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
678 cstring_t desc, struct cloc loc)
2fe58dfd
SE
679{
680 list_t *l;
681 item_t *i;
682
683 l=dict_lookup(dict,key);
684 if (!l) {
685 if (!required) return NULL;
686 cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
687 }
6416c7a1
RK
688 if(list_length(l) != 1)
689 cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key);
2fe58dfd
SE
690 i=list_elem(l,0);
691 return i;
692}
693
fe5e9cc4
SE
694string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
695 cstring_t desc, struct cloc loc)
2fe58dfd
SE
696{
697 item_t *i;
698 string_t r;
699
700 i=dict_find_item(dict,key,required,desc,loc);
701 if (!i) return NULL;
702 if (i->type!=t_string) {
703 cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
704 }
59230b9b
IJ
705 if (strlen(i->data.string) > INT_MAX/10) {
706 cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key);
707 }
2fe58dfd
SE
708 r=i->data.string;
709 return r;
710}
711
55e96f16
IJ
712const char **dict_read_string_array(dict_t *dict, cstring_t key,
713 bool_t required, cstring_t desc,
714 struct cloc loc, const char *const *def)
715{
716 list_t *l;
717 const char **ra, **rap;
718
719 l=dict_lookup(dict,key);
720 if (!l) {
721 if (!required) return (const char**)def;
722 cfgfatal(loc,desc,"required string list \"%s\" not found\n",key);
723 }
724
725 int32_t ll=list_length(l);
b4ececfc 726 NEW_ARY(ra, ll+1);
55e96f16
IJ
727 for (rap=ra; l; l=l->next,rap++) {
728 item_t *it=l->item;
729 if (it->type!=t_string)
730 cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key);
731 *rap=it->data.string;
732 }
733 *rap=0;
734 return ra;
735}
736
fe5e9cc4
SE
737uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
738 cstring_t desc, struct cloc loc, uint32_t def)
2fe58dfd
SE
739{
740 item_t *i;
741 uint32_t r;
742
743 i=dict_find_item(dict,key,required,desc,loc);
744 if (!i) return def;
745 if (i->type!=t_number) {
746 cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
747 }
59230b9b
IJ
748 if (i->data.number >= 0x80000000) {
749 cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key);
750 }
2fe58dfd
SE
751 r=i->data.number;
752 return r;
753}
754
fe5e9cc4
SE
755bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
756 cstring_t desc, struct cloc loc, bool_t def)
2fe58dfd
SE
757{
758 item_t *i;
759 bool_t r;
760
761 i=dict_find_item(dict,key,required,desc,loc);
762 if (!i) return def;
763 if (i->type!=t_bool) {
764 cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
765 }
766 r=i->data.bool;
767 return r;
768}
769
cb6dee01
IJ
770dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
771 cstring_t desc, struct cloc loc)
772{
773 item_t *i;
774 dict_t *r;
775
776 i=dict_find_item(dict,key,required,desc,loc);
777 if (!i) return NULL;
778 if (i->type!=t_dict) {
779 cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key);
780 }
781 r=i->data.dict;
782 return r;
783}
784
fe5e9cc4
SE
785uint32_t string_to_word(cstring_t s, struct cloc loc,
786 struct flagstr *f, cstring_t desc)
b2a56f7c
SE
787{
788 struct flagstr *j;
789 for (j=f; j->name; j++)
790 if (strcmp(s,j->name)==0)
791 return j->value;
792 cfgfatal(loc,desc,"option \"%s\" not known\n",s);
793 return 0;
794}
795
fe5e9cc4 796uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
9d3a4132
SE
797{
798 list_t *i;
799 uint32_t r=0;
800 struct flagstr *j;
801
802 for (i=l; i; i=i->next) {
803 if (i->item->type!=t_string) {
804 cfgfatal(i->item->loc,desc,"all elements of list must be "
805 "strings\n");
806 }
807 for (j=f; j->name; j++)
b2a56f7c 808 r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
9d3a4132
SE
809 }
810 return r;
811}
812
fe5e9cc4 813dict_t *read_conffile(const char *name)
2fe58dfd
SE
814{
815 FILE *conffile;
816 struct p_node *config;
817
818 if (strcmp(name,"-")==0) {
819 conffile=stdin;
820 } else {
821 conffile=fopen(name,"r");
822 if (!conffile)
823 fatal_perror("Cannot open configuration file \"%s\"",name);
824 }
825 config_lineno=1;
826 config_file=name;
827 config=parse_conffile(conffile);
828 fclose(conffile);
829
830#ifdef DUMP_PARSE_TREE
831 printf("*** config file parse tree BEFORE MANGLE\n");
832 ptree_dump(config,0);
833#endif /* DUMP_PARSE_TREE */
834 /* The root of the configuration is a T_ALIST, which needs reversing
835 before we mangle because it isn't the child of a T_DICT. */
836 config=list_reverse(config);
837 ptree_mangle(config);
838#ifdef DUMP_PARSE_TREE
839 printf("\n\n*** config file parse tree AFTER MANGLE\n");
840 ptree_dump(config,0);
841#endif /* DUMP_PARSE_TREE */
842 return process_config(config);
843}