1 %{
2 /* cfg_parser.y -- general-purpose configuration file parser
3 Copyright (C) 2007-2021 Free Software Foundation, Inc.
4
5 GNU Mailutils is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 3, or (at
8 your option) any later version.
9
10 GNU Mailutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <netdb.h>
28 #include <mailutils/argcv.h>
29 #include <mailutils/wordsplit.h>
30 #include <mailutils/nls.h>
31 #include <mailutils/cfg.h>
32 #include <mailutils/alloc.h>
33 #include <mailutils/errno.h>
34 #include <mailutils/error.h>
35 #include <mailutils/list.h>
36 #include <mailutils/iterator.h>
37 #include <mailutils/debug.h>
38 #include <mailutils/util.h>
39 #include <mailutils/cctype.h>
40 #include <mailutils/stream.h>
41 #include <mailutils/stdstream.h>
42 #include <mailutils/cidr.h>
43 #include <mailutils/yyloc.h>
44
45 int mu_cfg_parser_verbose;
46 static mu_list_t /* of mu_cfg_node_t */ parse_node_list;
47 size_t mu_cfg_error_count;
48
49 static int _mu_cfg_errcnt;
50
51 int yylex ();
52
53 void _mu_line_begin (void);
54 void _mu_line_add (char *text, size_t len);
55 char *_mu_line_finish (void);
56
57 static int
yyerror(char * s)58 yyerror (char *s)
59 {
60 mu_error ("%s", s);
61 mu_cfg_error_count++;
62 return 0;
63 }
64
65 static mu_config_value_t *
config_value_dup(mu_config_value_t * src)66 config_value_dup (mu_config_value_t *src)
67 {
68 if (!src)
69 return NULL;
70 else
71 {
72 /* FIXME: Use mu_opool_alloc */
73 mu_config_value_t *val = mu_alloc (sizeof (*val));
74 *val = *src;
75 return val;
76 }
77 }
78
79 static int
_node_set_parent(void * item,void * data)80 _node_set_parent (void *item, void *data)
81 {
82 struct mu_cfg_node *node = item;
83 node->parent = data;
84 return 0;
85 }
86
87 static mu_cfg_node_t *
mu_cfg_alloc_node(enum mu_cfg_node_type type,struct mu_locus_range * loc,const char * tag,mu_config_value_t * label,mu_list_t nodelist)88 mu_cfg_alloc_node (enum mu_cfg_node_type type, struct mu_locus_range *loc,
89 const char *tag, mu_config_value_t *label,
90 mu_list_t nodelist)
91 {
92 char *p;
93 mu_cfg_node_t *np;
94 size_t size = sizeof *np + strlen (tag) + 1;
95 np = mu_alloc (size);
96 np->type = type;
97 mu_locus_range_init (&np->locus);
98 mu_locus_range_copy (&np->locus, loc);
99 p = (char*) (np + 1);
100 np->tag = p;
101 strcpy (p, tag);
102 np->label = label;
103 np->nodes = nodelist;
104 np->parent = NULL;
105 return np;
106 }
107
108 void
mu_cfg_free_node(mu_cfg_node_t * node)109 mu_cfg_free_node (mu_cfg_node_t *node)
110 {
111 free (node->label);
112 free (node);
113 }
114
115 #define node_type_str(t) (((t) == mu_cfg_node_statement) ? "stmt" : "param")
116
117 static void
debug_print_node(mu_cfg_node_t * node)118 debug_print_node (mu_cfg_node_t *node)
119 {
120 if (mu_debug_level_p (MU_DEBCAT_CONFIG, MU_DEBUG_TRACE0))
121 {
122 if (node->type == mu_cfg_node_undefined)
123 {
124 /* Stay on the safe side */
125 mu_error (_("unknown statement type!"));
126 mu_cfg_error_count++;
127 }
128 else
129 {
130 /* FIXME: How to print label? */
131 mu_error ("statement: %s, id: %s",
132 node_type_str (node->type),
133 node->tag ? node->tag : "(null)");
134 }
135 }
136 }
137
138 static void
free_node_item(void * item)139 free_node_item (void *item)
140 {
141 mu_cfg_node_t *node = item;
142
143 switch (node->type)
144 {
145 case mu_cfg_node_statement:
146 mu_list_destroy (&node->nodes);
147 break;
148
149 case mu_cfg_node_undefined: /* hmm... */
150 case mu_cfg_node_param:
151 break;
152 }
153 mu_cfg_free_node (node);
154 }
155
156 int
mu_cfg_create_node_list(mu_list_t * plist)157 mu_cfg_create_node_list (mu_list_t *plist)
158 {
159 int rc;
160 mu_list_t list;
161
162 rc = mu_list_create (&list);
163 if (rc)
164 return rc;
165 mu_list_set_destroy_item (list, free_node_item);
166 *plist = list;
167 return 0;
168 }
169
170 %}
171
172 %locations
173 %expect 1
174
175 %union {
176 mu_cfg_node_t node;
177 mu_cfg_node_t *pnode;
178 mu_list_t /* of mu_cfg_node_t */ nodelist;
179 char *string;
180 mu_config_value_t value, *pvalue;
181 mu_list_t list;
182 }
183
184 %token <string> MU_TOK_IDENT MU_TOK_STRING MU_TOK_QSTRING MU_TOK_MSTRING
185 %type <string> string slist
186 %type <list> slist0
187 %type <value> value
188 %type <pvalue> tag vallist
189 %type <list> values list vlist
190 %type <string> ident
191 %type <nodelist> stmtlist
192 %type <pnode> stmt simple block
193
194 %%
195
196 input : stmtlist
197 {
198 parse_node_list = $1;
199 }
200 ;
201
202 stmtlist: stmt
203 {
204 mu_cfg_create_node_list (&$$);
205 mu_list_append ($$, $1);
206 }
207 | stmtlist stmt
208 {
209 mu_list_append ($1, $2);
210 $$ = $1;
211 debug_print_node ($2);
212 }
213 ;
214
215 stmt : simple
216 | block
217 ;
218
219 simple : ident vallist ';'
220 {
221 struct mu_locus_range lr;
222 lr.beg = @1.beg;
223 lr.end = @3.end;
224 $$ = mu_cfg_alloc_node (mu_cfg_node_param, &lr, $1, $2, NULL);
225 }
226 ;
227
228 block : ident tag '{' '}' opt_sc
229 {
230 struct mu_locus_range lr;
231 lr.beg = @1.beg;
232 lr.end = @5.end;
233 $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &lr, $1, $2, NULL);
234 }
235 | ident tag '{' stmtlist '}' opt_sc
236 {
237 struct mu_locus_range lr;
238 lr.beg = @1.beg;
239 lr.end = @6.end;
240 $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &lr, $1, $2, $4);
241 mu_list_foreach ($4, _node_set_parent, $$);
242 }
243 ;
244
245 ident : MU_TOK_IDENT
246 ;
247
248 tag : /* empty */
249 {
250 $$ = NULL;
251 }
252 | vallist
253 ;
254
255 vallist : vlist
256 {
257 size_t n = 0;
258 mu_list_count($1, &n);
259 if (n == 1)
260 {
261 mu_list_get ($1, 0, (void**) &$$);
262 }
263 else
264 {
265 size_t i;
266 mu_config_value_t val;
267
268 val.type = MU_CFG_ARRAY;
269 val.v.arg.c = n;
270 /* FIXME: Use mu_opool_alloc */
271 val.v.arg.v = mu_alloc (n * sizeof (val.v.arg.v[0]));
272 if (!val.v.arg.v)
273 {
274 mu_error (_("not enough memory"));
275 abort();
276 }
277
278 for (i = 0; i < n; i++)
279 {
280 mu_config_value_t *v;
281 mu_list_get ($1, i, (void **) &v);
282 val.v.arg.v[i] = *v;
283 }
284 $$ = config_value_dup (&val);
285 }
286 mu_list_destroy (&$1);
287 }
288 ;
289
290 vlist : value
291 {
292 int rc = mu_list_create (&$$);
293 if (rc)
294 {
295 mu_error (_("cannot create list: %s"), mu_strerror (rc));
296 abort ();
297 }
298 mu_list_append ($$, config_value_dup (&$1)); /* FIXME */
299 }
300 | vlist value
301 {
302 mu_list_append ($1, config_value_dup (&$2));
303 }
304 ;
305
306 value : string
307 {
308 $$.type = MU_CFG_STRING;
309 $$.v.string = $1;
310 }
311 | list
312 {
313 $$.type = MU_CFG_LIST;
314 $$.v.list = $1;
315 }
316 | MU_TOK_MSTRING
317 {
318 $$.type = MU_CFG_STRING;
319 $$.v.string = $1;
320 }
321 ;
322
323 string : MU_TOK_STRING
324 | MU_TOK_IDENT
325 | slist
326 ;
327
328 slist : slist0
329 {
330 mu_iterator_t itr;
331 mu_list_get_iterator ($1, &itr);
332
333 _mu_line_begin ();
334 for (mu_iterator_first (itr);
335 !mu_iterator_is_done (itr); mu_iterator_next (itr))
336 {
337 char *p;
338 mu_iterator_current (itr, (void**)&p);
339 _mu_line_add (p, strlen (p));
340 }
341 $$ = _mu_line_finish ();
342 mu_iterator_destroy (&itr);
343 mu_list_destroy(&$1);
344 }
345 ;
346
347 slist0 : MU_TOK_QSTRING
348 {
349 mu_list_create (&$$);
350 mu_list_append ($$, $1);
351 }
352 | slist0 MU_TOK_QSTRING
353 {
354 mu_list_append ($1, $2);
355 $$ = $1;
356 }
357 ;
358
359 list : '(' values ')'
360 {
361 $$ = $2;
362 }
363 | '(' values ',' ')'
364 {
365 $$ = $2;
366 }
367 ;
368
369 values : value
370 {
371 mu_list_create (&$$);
372 mu_list_append ($$, config_value_dup (&$1));
373 }
374 | values ',' value
375 {
376 mu_list_append ($1, config_value_dup (&$3));
377 $$ = $1;
378 }
379 ;
380
381 opt_sc : /* empty */
382 | ';'
383 ;
384
385
386 %%
387
388 void
389 mu_cfg_set_debug ()
390 {
391 if (mu_debug_level_p (MU_DEBCAT_CONFIG, MU_DEBUG_TRACE7))
392 yydebug = 1;
393 }
394
395 int
mu_cfg_parse(mu_cfg_tree_t ** ptree)396 mu_cfg_parse (mu_cfg_tree_t **ptree)
397 {
398 int rc;
399 mu_cfg_tree_t *tree;
400 mu_opool_t pool;
401 int save_mode = 0, mode;
402 struct mu_locus_range save_locus = MU_LOCUS_RANGE_INITIALIZER;
403
404 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_GET_MODE,
405 &save_mode);
406 mode = save_mode | MU_LOGMODE_LOCUS;
407 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_SET_MODE,
408 &mode);
409 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
410 MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE,
411 &save_locus);
412
413 mu_cfg_set_debug ();
414 _mu_cfg_errcnt = 0;
415
416 rc = yyparse ();
417 pool = mu_cfg_lexer_pool ();
418 if (rc == 0 && _mu_cfg_errcnt)
419 {
420 mu_opool_destroy (&pool);
421 rc = 1;
422 }
423 else
424 {
425 tree = mu_alloc (sizeof (*tree));
426 tree->nodes = parse_node_list;
427 tree->pool = pool;
428 parse_node_list = NULL;
429 *ptree = tree;
430 }
431
432 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM, MU_IOCTL_LOGSTREAM_SET_MODE,
433 &save_mode);
434 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
435 MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE,
436 &save_locus);
437 mu_locus_range_deinit (&save_locus); /* FIXME: refcount? */
438
439 return rc;
440 }
441
442 int
mu_cfg_tree_union(mu_cfg_tree_t ** pa,mu_cfg_tree_t ** pb)443 mu_cfg_tree_union (mu_cfg_tree_t **pa, mu_cfg_tree_t **pb)
444 {
445 mu_cfg_tree_t *a, *b;
446 int rc;
447
448 if (!pb)
449 return EINVAL;
450 if (!*pb)
451 return 0;
452 b = *pb;
453 if (!pa)
454 return EINVAL;
455 if (!*pa)
456 {
457 *pa = b;
458 *pb = NULL;
459 return 0;
460 }
461 else
462 a = *pa;
463
464 /* Merge opools */
465 rc = mu_opool_union (&b->pool, &a->pool);
466 if (rc)
467 return rc;
468
469 /* Link node lists */
470 if (b->nodes)
471 {
472 mu_list_append_list (a->nodes, b->nodes);
473 mu_list_destroy (&b->nodes);
474 }
475
476 free (b);
477 *pb = NULL;
478 return 0;
479 }
480
481 static mu_cfg_tree_t *
do_include(const char * name,struct mu_cfg_parse_hints * hints,struct mu_locus_range const * loc)482 do_include (const char *name, struct mu_cfg_parse_hints *hints,
483 struct mu_locus_range const *loc)
484 {
485 struct stat sb;
486 char *tmpname = NULL;
487 mu_cfg_tree_t *tree = NULL;
488
489 if (name[0] != '/')
490 {
491 name = tmpname = mu_make_file_name (SYSCONFDIR, name);
492 if (!name)
493 {
494 mu_error ("%s", mu_strerror (errno));
495 return NULL;
496 }
497 }
498 if (stat (name, &sb) == 0)
499 {
500 int rc = 0;
501
502 if (S_ISDIR (sb.st_mode))
503 {
504 if (hints->flags & MU_CFHINT_PROGRAM)
505 {
506 char *file = mu_make_file_name (name, hints->program);
507 rc = mu_cfg_parse_file (&tree, file, hints->flags);
508 free (file);
509 }
510 else
511 {
512 mu_diag_at_locus_range (MU_LOG_WARNING, loc,
513 _("ignoring `include': directory argument is allowed only from the top-level configuration file"));
514
515 }
516 }
517 else
518 rc = mu_cfg_parse_file (&tree, name, hints->flags);
519
520 if (rc == 0 && tree)
521 {
522 struct mu_cfg_parse_hints xhints = *hints;
523 xhints.flags &= ~MU_CFHINT_PROGRAM;
524 mu_cfg_tree_postprocess (tree, &xhints);
525 }
526 }
527 else if (errno == ENOENT)
528 {
529 mu_diag_at_locus_range (MU_LOG_ERROR, loc,
530 _("include file or directory does not exist"));
531 mu_cfg_error_count++;
532 }
533 else
534 {
535 mu_diag_at_locus_range (MU_LOG_ERROR, loc,
536 _("cannot stat include file or directory: %s"),
537 mu_strerror (errno));
538 mu_cfg_error_count++;
539 }
540
541 free (tmpname);
542 return tree;
543 }
544
545 int
mu_cfg_tree_postprocess(mu_cfg_tree_t * tree,struct mu_cfg_parse_hints * hints)546 mu_cfg_tree_postprocess (mu_cfg_tree_t *tree, struct mu_cfg_parse_hints *hints)
547 {
548 int rc;
549 mu_iterator_t itr;
550
551 if (!tree->nodes)
552 return 0;
553 rc = mu_list_get_iterator (tree->nodes, &itr);
554 if (rc)
555 return rc;
556 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
557 mu_iterator_next (itr))
558 {
559 mu_cfg_node_t *node;
560
561 mu_iterator_current (itr, (void**) &node);
562
563 if (node->type == mu_cfg_node_statement)
564 {
565 if (strcmp (node->tag, "program") == 0)
566 {
567 if (hints->flags & MU_CFHINT_PROGRAM)
568 {
569 if (node->label->type == MU_CFG_STRING)
570 {
571 if (strcmp (node->label->v.string, hints->program) == 0)
572 {
573 /* Reset the parent node */
574 mu_list_foreach (node->nodes, _node_set_parent,
575 node->parent);
576 /* Move all nodes from this block to the topmost
577 level */
578 mu_iterator_ctl (itr, mu_itrctl_insert_list,
579 node->nodes);
580 mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
581 /*FIXME:mu_cfg_free_node (node);*/
582 }
583 }
584 else
585 {
586 mu_diag_at_locus_range (MU_LOG_ERROR, &node->locus,
587 _("argument to `program' is not a string"));
588 mu_cfg_error_count++;
589 mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
590 }
591 }
592 else
593 {
594 mu_diag_at_locus_range (MU_LOG_WARNING, &node->locus,
595 _("ignoring `program' block: not located in top-level configuration file"));
596 }
597 }
598 }
599 else if (node->type == mu_cfg_node_param &&
600 strcmp (node->tag, "include") == 0)
601 {
602 if (node->label->type == MU_CFG_STRING)
603 {
604 mu_cfg_tree_t *t = do_include (node->label->v.string,
605 hints,
606 &node->locus);
607 if (t)
608 {
609 /* Merge the new tree into the current point and
610 destroy the rest of it */
611 mu_iterator_ctl (itr, mu_itrctl_insert_list, t->nodes);
612 mu_opool_union (&tree->pool, &t->pool);
613 mu_cfg_destroy_tree (&t);
614 }
615 }
616 else
617 {
618 mu_diag_at_locus_range (MU_LOG_ERROR, &node->locus,
619 _("argument to `include' is not a string"));
620 mu_cfg_error_count++;
621 }
622
623 /* Remove node from the list */
624 mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
625 }
626 }
627 mu_iterator_destroy (&itr);
628 return 0;
629 }
630
631 static int
_mu_cfg_preorder_recursive(void * item,void * cbdata)632 _mu_cfg_preorder_recursive (void *item, void *cbdata)
633 {
634 mu_cfg_node_t *node = item;
635 struct mu_cfg_iter_closure *clos = cbdata;
636 int rc;
637
638 switch (node->type)
639 {
640 case mu_cfg_node_undefined:
641 abort ();
642
643 case mu_cfg_node_statement:
644 switch (clos->beg (node, clos->data))
645 {
646 case MU_CFG_ITER_OK:
647 rc = mu_cfg_preorder (node->nodes, clos);
648 if (rc)
649 return rc;
650 if (clos->end && clos->end (node, clos->data) == MU_CFG_ITER_STOP)
651 return MU_ERR_USER0;
652 break;
653
654 case MU_CFG_ITER_SKIP:
655 break;
656
657 case MU_CFG_ITER_STOP:
658 return MU_ERR_USER0;
659 }
660 break;
661
662 case mu_cfg_node_param:
663 if (clos->beg (node, clos->data) == MU_CFG_ITER_STOP)
664 return MU_ERR_USER0;
665 }
666 return 0;
667 }
668
669 int
mu_cfg_preorder(mu_list_t nodelist,struct mu_cfg_iter_closure * clos)670 mu_cfg_preorder (mu_list_t nodelist, struct mu_cfg_iter_closure *clos)
671 {
672 if (!nodelist)
673 return 0;
674 return mu_list_foreach (nodelist, _mu_cfg_preorder_recursive, clos);
675 }
676
677
678
679 void
mu_cfg_destroy_tree(mu_cfg_tree_t ** ptree)680 mu_cfg_destroy_tree (mu_cfg_tree_t **ptree)
681 {
682 if (ptree && *ptree)
683 {
684 mu_cfg_tree_t *tree = *ptree;
685 mu_list_destroy (&tree->nodes);
686 mu_opool_destroy (&tree->pool);
687 free (tree);
688 *ptree = NULL;
689 }
690 }
691
692
693
694 struct mu_cfg_section_list
695 {
696 struct mu_cfg_section_list *next;
697 struct mu_cfg_section *sec;
698 };
699
700 struct scan_tree_data
701 {
702 struct mu_cfg_section_list *list;
703 void *target;
704 void *call_data;
705 mu_cfg_tree_t *tree;
706 int error;
707 };
708
709 static struct mu_cfg_cont *
find_container(mu_list_t list,enum mu_cfg_cont_type type,const char * ident,size_t len)710 find_container (mu_list_t list, enum mu_cfg_cont_type type,
711 const char *ident, size_t len)
712 {
713 mu_iterator_t iter;
714 struct mu_cfg_cont *ret = NULL;
715
716 if (len == 0)
717 len = strlen (ident);
718
719 mu_list_get_iterator (list, &iter);
720 for (mu_iterator_first (iter); !mu_iterator_is_done (iter);
721 mu_iterator_next (iter))
722 {
723 struct mu_cfg_cont *cont;
724 mu_iterator_current (iter, (void**) &cont);
725
726 if (cont->type == type
727 && strlen (cont->v.ident) == len
728 && memcmp (cont->v.ident, ident, len) == 0)
729 {
730 ret = cont;
731 break;
732 }
733 }
734 mu_iterator_destroy (&iter);
735 return ret;
736 }
737
738 static struct mu_cfg_section *
find_subsection(struct mu_cfg_section * sec,const char * ident,size_t len)739 find_subsection (struct mu_cfg_section *sec, const char *ident, size_t len)
740 {
741 if (sec)
742 {
743 if (sec->children)
744 {
745 struct mu_cfg_cont *cont = find_container (sec->children,
746 mu_cfg_cont_section,
747 ident, len);
748 if (cont)
749 return &cont->v.section;
750 }
751 }
752 return NULL;
753 }
754
755 static struct mu_cfg_param *
find_param(struct mu_cfg_section * sec,const char * ident,size_t len)756 find_param (struct mu_cfg_section *sec, const char *ident, size_t len)
757 {
758 if (sec)
759 {
760 if (sec->children)
761 {
762 struct mu_cfg_cont *cont = find_container (sec->children,
763 mu_cfg_cont_param,
764 ident, len);
765 if (cont)
766 return &cont->v.param;
767 }
768 }
769 return NULL;
770 }
771
772 static int
push_section(struct scan_tree_data * dat,struct mu_cfg_section * sec)773 push_section (struct scan_tree_data *dat, struct mu_cfg_section *sec)
774 {
775 struct mu_cfg_section_list *p = mu_alloc (sizeof *p);
776 if (!p)
777 {
778 mu_error (_("not enough memory"));
779 mu_cfg_error_count++;
780 return 1;
781 }
782 p->sec = sec;
783 p->next = dat->list;
784 dat->list = p;
785 return 0;
786 }
787
788 static struct mu_cfg_section *
pop_section(struct scan_tree_data * dat)789 pop_section (struct scan_tree_data *dat)
790 {
791 struct mu_cfg_section_list *p = dat->list;
792 struct mu_cfg_section *sec = p->sec;
793 dat->list = p->next;
794 free (p);
795 return sec;
796 }
797
798 static int
valcvt(const struct mu_locus_range * locus,void * tgt,mu_c_type_t type,mu_config_value_t * val)799 valcvt (const struct mu_locus_range *locus,
800 void *tgt, mu_c_type_t type, mu_config_value_t *val)
801 {
802 int rc;
803 char *errmsg;
804
805 if (val->type != MU_CFG_STRING)
806 {
807 mu_diag_at_locus_range (MU_LOG_ERROR, locus, _("expected string value"));
808 mu_cfg_error_count++;
809 return 1;
810 }
811
812 rc = mu_str_to_c (val->v.string, type, tgt, &errmsg);
813 if (rc)
814 {
815 mu_diag_at_locus_range (MU_LOG_ERROR, locus, "%s",
816 errmsg ? errmsg : mu_strerror (rc));
817 free (errmsg);
818 }
819 return rc;
820 }
821
822 struct set_closure
823 {
824 mu_list_t list;
825 int type;
826 struct scan_tree_data *sdata;
827 const struct mu_locus_range *locus;
828 };
829
830 static size_t config_type_size[] = {
831 [mu_c_string] = sizeof (char*),
832 [mu_c_short] = sizeof (short),
833 [mu_c_ushort] = sizeof (unsigned short),
834 [mu_c_int] = sizeof (int),
835 [mu_c_uint] = sizeof (unsigned),
836 [mu_c_long] = sizeof (long),
837 [mu_c_ulong] = sizeof (unsigned long),
838 [mu_c_size] = sizeof (size_t),
839 [mu_c_time] = sizeof (time_t),
840 [mu_c_bool] = sizeof (int),
841 [mu_c_ipv4] = sizeof (struct in_addr),
842 [mu_c_cidr] = sizeof (struct mu_cidr),
843 [mu_c_host] = sizeof (struct in_addr),
844 };
845
846 static int
_set_fun(void * item,void * data)847 _set_fun (void *item, void *data)
848 {
849 mu_config_value_t *val = item;
850 struct set_closure *clos = data;
851 void *tgt;
852 size_t size;
853
854 if ((size_t) clos->type >= MU_ARRAY_SIZE(config_type_size)
855 || (size = config_type_size[clos->type]) == 0)
856 {
857 mu_diag_at_locus_range (MU_LOG_EMERG, clos->locus,
858 _("INTERNAL ERROR at %s:%d: unhandled data type %d"),
859 __FILE__, __LINE__, clos->type);
860 mu_cfg_error_count++;
861 return 1;
862 }
863
864 tgt = mu_alloc (size);
865 if (!tgt)
866 {
867 mu_diag_at_locus_range (MU_LOG_ERROR, clos->locus,
868 _("not enough memory"));
869 mu_cfg_error_count++;
870 return 1;
871 }
872
873 if (valcvt (clos->locus, &tgt, clos->type, val) == 0)
874 mu_list_append (clos->list, tgt);
875 return 0;
876 }
877
878 static int
parse_param(struct scan_tree_data * sdata,const mu_cfg_node_t * node)879 parse_param (struct scan_tree_data *sdata, const mu_cfg_node_t *node)
880 {
881 void *tgt;
882 struct set_closure clos;
883 struct mu_cfg_param *param = find_param (sdata->list->sec, node->tag,
884 0);
885
886 if (!param)
887 {
888 mu_diag_at_locus_range (MU_LOG_ERROR, &node->locus,
889 _("unknown keyword `%s'"),
890 node->tag);
891 mu_cfg_error_count++;
892 return 1;
893 }
894
895 if (param->data)
896 tgt = param->data;
897 else if (sdata->list->sec->target)
898 tgt = (char*)sdata->list->sec->target + param->offset;
899 else if (sdata->target)
900 tgt = (char*)sdata->target + param->offset;
901 else if (param->type == mu_cfg_callback)
902 tgt = NULL;
903 else
904 {
905 mu_diag_at_locus_range (MU_LOG_EMERG, &node->locus,
906 _("INTERNAL ERROR: cannot determine target offset for "
907 "%s"), param->ident);
908 abort ();
909 }
910
911 memset (&clos, 0, sizeof clos);
912 clos.type = MU_CFG_TYPE (param->type);
913 if (MU_CFG_IS_LIST (param->type))
914 {
915 clos.sdata = sdata;
916 clos.locus = &node->locus;
917 switch (node->label->type)
918 {
919 case MU_CFG_LIST:
920 break;
921
922 case MU_CFG_STRING:
923 {
924 mu_list_t list;
925 mu_list_create (&list);
926 mu_list_append (list, config_value_dup (node->label));
927 node->label->type = MU_CFG_LIST;
928 node->label->v.list = list;
929 }
930 break;
931
932 case MU_CFG_ARRAY:
933 mu_diag_at_locus_range (MU_LOG_ERROR, &node->locus,
934 _("expected list, but found array"));
935 mu_cfg_error_count++;
936 return 1;
937 }
938
939 mu_list_create (&clos.list);
940 mu_list_foreach (node->label->v.list, _set_fun, &clos);
941 *(mu_list_t*)tgt = clos.list;
942 }
943 else if (clos.type == mu_cfg_callback)
944 {
945 if (!param->callback)
946 {
947 mu_diag_at_locus_range (MU_LOG_EMERG, &node->locus,
948 _("INTERNAL ERROR: %s: callback not defined"),
949 node->tag);
950 abort ();
951 }
952 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
953 MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE,
954 (void*) &node->locus);
955 if (param->callback (tgt, node->label))
956 return 1;
957 }
958 else
959 return valcvt (&node->locus, tgt, clos.type, node->label);
960
961 return 0;
962 }
963
964
965 static int
_scan_tree_helper(const mu_cfg_node_t * node,void * data)966 _scan_tree_helper (const mu_cfg_node_t *node, void *data)
967 {
968 struct scan_tree_data *sdata = data;
969 struct mu_cfg_section *sec;
970
971 switch (node->type)
972 {
973 case mu_cfg_node_undefined:
974 abort ();
975
976 case mu_cfg_node_statement:
977 sec = find_subsection (sdata->list->sec, node->tag, 0);
978 if (!sec)
979 {
980 if (mu_cfg_parser_verbose)
981 {
982 mu_diag_at_locus_range (MU_LOG_WARNING, &node->locus,
983 _("unknown section `%s'"),
984 node->tag);
985 }
986 return MU_CFG_ITER_SKIP;
987 }
988 if (!sec->children)
989 return MU_CFG_ITER_SKIP;
990 if (sec->data)
991 sec->target = sec->data;
992 else if (sdata->list->sec->target)
993 sec->target = (char*)sdata->list->sec->target + sec->offset;
994 else if (sdata->target)
995 sec->target = (char*)sdata->target + sec->offset;
996 else
997 sec->target = NULL;
998 if (sec->parser)
999 {
1000 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1001 MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE,
1002 (void*) &node->locus);
1003 if (sec->parser (mu_cfg_section_start, node,
1004 sec->label, &sec->target,
1005 sdata->call_data, sdata->tree))
1006 {
1007 sdata->error++;
1008 return MU_CFG_ITER_SKIP;
1009 }
1010 }
1011 push_section(sdata, sec);
1012 break;
1013
1014 case mu_cfg_node_param:
1015 if (parse_param (sdata, node))
1016 {
1017 sdata->error++;
1018 return MU_CFG_ITER_SKIP;
1019 }
1020 break;
1021 }
1022 return MU_CFG_ITER_OK;
1023 }
1024
1025 static int
_scan_tree_end_helper(const mu_cfg_node_t * node,void * data)1026 _scan_tree_end_helper (const mu_cfg_node_t *node, void *data)
1027 {
1028 struct scan_tree_data *sdata = data;
1029 struct mu_cfg_section *sec;
1030
1031 switch (node->type)
1032 {
1033 default:
1034 abort ();
1035
1036 case mu_cfg_node_statement:
1037 sec = pop_section (sdata);
1038 if (sec && sec->parser)
1039 {
1040 if (sec->parser (mu_cfg_section_end, node,
1041 sec->label, &sec->target,
1042 sdata->call_data, sdata->tree))
1043 {
1044 sdata->error++;
1045 return MU_CFG_ITER_SKIP;
1046 }
1047 }
1048 }
1049 return MU_CFG_ITER_OK;
1050 }
1051
1052 int
mu_cfg_scan_tree(mu_cfg_tree_t * tree,struct mu_cfg_section * sections,void * target,void * data)1053 mu_cfg_scan_tree (mu_cfg_tree_t *tree, struct mu_cfg_section *sections,
1054 void *target, void *data)
1055 {
1056 struct scan_tree_data dat;
1057 struct mu_cfg_iter_closure clos;
1058 int save_mode = 0, mode;
1059 struct mu_locus_range save_locus = MU_LOCUS_RANGE_INITIALIZER;
1060 int rc;
1061
1062 dat.tree = tree;
1063 dat.list = NULL;
1064 dat.error = 0;
1065 dat.call_data = data;
1066 dat.target = target;
1067
1068 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1069 MU_IOCTL_LOGSTREAM_GET_MODE, &save_mode);
1070 mode = save_mode | MU_LOGMODE_LOCUS;
1071 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1072 MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
1073 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1074 MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE, &save_locus);
1075
1076 if (push_section (&dat, sections))
1077 return 1;
1078 clos.beg = _scan_tree_helper;
1079 clos.end = _scan_tree_end_helper;
1080 clos.data = &dat;
1081 rc = mu_cfg_preorder (tree->nodes, &clos);
1082 pop_section (&dat);
1083 if (rc && rc != MU_ERR_USER0)
1084 dat.error++;
1085 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1086 MU_IOCTL_LOGSTREAM_SET_MODE, &save_mode);
1087 mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
1088 MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &save_locus);
1089
1090 return dat.error;
1091 }
1092
1093 int
mu_cfg_find_section(struct mu_cfg_section * root_sec,const char * path,struct mu_cfg_section ** retval)1094 mu_cfg_find_section (struct mu_cfg_section *root_sec,
1095 const char *path, struct mu_cfg_section **retval)
1096 {
1097 while (path[0])
1098 {
1099 struct mu_cfg_section *sec;
1100 size_t len;
1101 const char *p;
1102
1103 while (*path == MU_CFG_PATH_DELIM)
1104 path++;
1105
1106 if (*path == 0)
1107 return MU_ERR_NOENT;
1108
1109 p = strchr (path, MU_CFG_PATH_DELIM);
1110 if (p)
1111 len = p - path;
1112 else
1113 len = strlen (path);
1114
1115 sec = find_subsection (root_sec, path, len);
1116 if (!sec)
1117 return MU_ERR_NOENT;
1118 root_sec = sec;
1119 path += len;
1120 }
1121 if (retval)
1122 *retval = root_sec;
1123 return 0;
1124 }
1125
1126
1127 int
mu_cfg_tree_create(struct mu_cfg_tree ** ptree)1128 mu_cfg_tree_create (struct mu_cfg_tree **ptree)
1129 {
1130 struct mu_cfg_tree *tree = calloc (1, sizeof *tree);
1131 if (!tree)
1132 return errno;
1133 mu_opool_create (&tree->pool, MU_OPOOL_ENOMEMABRT);
1134 *ptree = tree;
1135 return 0;
1136 }
1137
1138 mu_cfg_node_t *
mu_cfg_tree_create_node(struct mu_cfg_tree * tree,enum mu_cfg_node_type type,const struct mu_locus_range * loc,const char * tag,const char * label,mu_list_t nodelist)1139 mu_cfg_tree_create_node (struct mu_cfg_tree *tree,
1140 enum mu_cfg_node_type type,
1141 const struct mu_locus_range *loc,
1142 const char *tag, const char *label,
1143 mu_list_t nodelist)
1144 {
1145 char *p;
1146 mu_cfg_node_t *np;
1147 size_t size = sizeof *np + strlen (tag) + 1;
1148 mu_config_value_t val;
1149
1150 np = mu_alloc (size);
1151 np->type = type;
1152 mu_locus_range_init (&np->locus);
1153 if (loc)
1154 mu_locus_range_copy (&np->locus, loc);
1155 p = (char*) (np + 1);
1156 np->tag = p;
1157 strcpy (p, tag);
1158 p += strlen (p) + 1;
1159 val.type = MU_CFG_STRING;
1160 if (label)
1161 {
1162 mu_opool_clear (tree->pool);
1163 mu_opool_appendz (tree->pool, label);
1164 mu_opool_append_char (tree->pool, 0);
1165 val.v.string = mu_opool_finish (tree->pool, NULL);
1166 np->label = config_value_dup (&val);
1167 }
1168 else
1169 np->label = NULL;
1170 np->nodes = nodelist;
1171 return np;
1172 }
1173
1174 void
mu_cfg_tree_add_node(mu_cfg_tree_t * tree,mu_cfg_node_t * node)1175 mu_cfg_tree_add_node (mu_cfg_tree_t *tree, mu_cfg_node_t *node)
1176 {
1177 if (!node)
1178 return;
1179 if (!tree->nodes)
1180 /* FIXME: return code? */
1181 mu_cfg_create_node_list (&tree->nodes);
1182 mu_list_append (tree->nodes, node);
1183 }
1184
1185 void
mu_cfg_tree_add_nodelist(mu_cfg_tree_t * tree,mu_list_t nodelist)1186 mu_cfg_tree_add_nodelist (mu_cfg_tree_t *tree, mu_list_t nodelist)
1187 {
1188 if (!nodelist)
1189 return;
1190 if (!tree->nodes)
1191 /* FIXME: return code? */
1192 mu_cfg_create_node_list (&tree->nodes);
1193 mu_list_append_list (tree->nodes, nodelist);
1194 }
1195
1196
1197 /* Return 1 if configuration value A equals B */
1198 int
mu_cfg_value_eq(mu_config_value_t * a,mu_config_value_t * b)1199 mu_cfg_value_eq (mu_config_value_t *a, mu_config_value_t *b)
1200 {
1201 if (a->type != b->type)
1202 return 0;
1203 switch (a->type)
1204 {
1205 case MU_CFG_STRING:
1206 if (a->v.string == NULL)
1207 return b->v.string == NULL;
1208 return strcmp (a->v.string, b->v.string) == 0;
1209
1210 case MU_CFG_LIST:
1211 {
1212 int ret = 1;
1213 size_t cnt;
1214 size_t i;
1215 mu_iterator_t aitr, bitr;
1216
1217 mu_list_count (a->v.list, &cnt);
1218 mu_list_count (b->v.list, &i);
1219 if (i != cnt)
1220 return 1;
1221
1222 mu_list_get_iterator (a->v.list, &aitr);
1223 mu_list_get_iterator (b->v.list, &bitr);
1224 for (i = 0,
1225 mu_iterator_first (aitr),
1226 mu_iterator_first (bitr);
1227 !mu_iterator_is_done (aitr) && !mu_iterator_is_done (bitr);
1228 mu_iterator_next (aitr),
1229 mu_iterator_next (bitr),
1230 i++)
1231 {
1232 mu_config_value_t *ap, *bp;
1233 mu_iterator_current (aitr, (void**)&ap);
1234 mu_iterator_current (bitr, (void**)&bp);
1235 ret = mu_cfg_value_eq (ap, bp);
1236 if (!ret)
1237 break;
1238 }
1239 mu_iterator_destroy (&aitr);
1240 mu_iterator_destroy (&bitr);
1241 return ret && i == cnt;
1242 }
1243
1244 case MU_CFG_ARRAY:
1245 if (a->v.arg.c == b->v.arg.c)
1246 {
1247 size_t i;
1248 for (i = 0; i < a->v.arg.c; i++)
1249 if (!mu_cfg_value_eq (&a->v.arg.v[i], &b->v.arg.v[i]))
1250 return 0;
1251 return 1;
1252 }
1253 }
1254 return 0;
1255 }
1256
1257
1258 static int
split_cfg_path(const char * path,int * pargc,char *** pargv)1259 split_cfg_path (const char *path, int *pargc, char ***pargv)
1260 {
1261 int argc;
1262 char **argv;
1263 char *delim = MU_CFG_PATH_DELIM_STR;
1264 char static_delim[2] = { 0, 0 };
1265
1266 if (path[0] == '\\')
1267 {
1268 argv = calloc (2, sizeof (*argv));
1269 if (!argv)
1270 return ENOMEM;
1271 argv[0] = strdup (path + 1);
1272 if (!argv[0])
1273 {
1274 free (argv);
1275 return ENOMEM;
1276 }
1277 argv[1] = NULL;
1278 argc = 1;
1279 }
1280 else
1281 {
1282 struct mu_wordsplit ws;
1283
1284 if (mu_ispunct (path[0]))
1285 {
1286 delim = static_delim;
1287 delim[0] = path[0];
1288 path++;
1289 }
1290 ws.ws_delim = delim;
1291
1292 if (mu_wordsplit (path, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
1293 {
1294 mu_error (_("cannot split line `%s': %s"), path,
1295 mu_wordsplit_strerror (&ws));
1296 mu_wordsplit_free (&ws);
1297 return errno;
1298 }
1299 argc = ws.ws_wordc;
1300 argv = ws.ws_wordv;
1301 ws.ws_wordc = 0;
1302 ws.ws_wordv = NULL;
1303 mu_wordsplit_free (&ws);
1304 }
1305
1306 *pargc = argc;
1307 *pargv = argv;
1308
1309 return 0;
1310 }
1311
1312 struct find_data
1313 {
1314 int argc;
1315 char **argv;
1316 int tag;
1317 mu_config_value_t *label;
1318 const mu_cfg_node_t *node;
1319 };
1320
1321 static void
free_value_mem(mu_config_value_t * p)1322 free_value_mem (mu_config_value_t *p)
1323 {
1324 switch (p->type)
1325 {
1326 case MU_CFG_STRING:
1327 free ((char*)p->v.string);
1328 break;
1329
1330 case MU_CFG_LIST:
1331 /* FIXME */
1332 break;
1333
1334 case MU_CFG_ARRAY:
1335 {
1336 size_t i;
1337 for (i = 0; i < p->v.arg.c; i++)
1338 free_value_mem (&p->v.arg.v[i]);
1339 }
1340 }
1341 }
1342
1343 static void
destroy_value(void * p)1344 destroy_value (void *p)
1345 {
1346 mu_config_value_t *val = p;
1347 if (val)
1348 {
1349 free_value_mem (val);
1350 free (val);
1351 }
1352 }
1353
1354 static mu_config_value_t *
parse_label(const char * str)1355 parse_label (const char *str)
1356 {
1357 mu_config_value_t *val = NULL;
1358 size_t i;
1359 struct mu_wordsplit ws;
1360 size_t len = strlen (str);
1361
1362 if (len > 1 && str[0] == '(' && str[len-1] == ')')
1363 {
1364 mu_list_t lst;
1365
1366 ws.ws_delim = ",";
1367 if (mu_wordsplit_len (str + 1, len - 2, &ws,
1368 MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|MU_WRDSF_WS))
1369 {
1370 mu_error (_("cannot split line `%s': %s"), str,
1371 mu_wordsplit_strerror (&ws));
1372 return NULL;
1373 }
1374
1375 mu_list_create (&lst);
1376 mu_list_set_destroy_item (lst, destroy_value);
1377 for (i = 0; i < ws.ws_wordc; i++)
1378 {
1379 mu_config_value_t *p = mu_alloc (sizeof (*p));
1380 p->type = MU_CFG_STRING;
1381 p->v.string = ws.ws_wordv[i];
1382 mu_list_append (lst, p);
1383 }
1384 val = mu_alloc (sizeof (*val));
1385 val->type = MU_CFG_LIST;
1386 val->v.list = lst;
1387 }
1388 else
1389 {
1390 if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS))
1391 {
1392 mu_error (_("cannot split line `%s': %s"), str,
1393 mu_wordsplit_strerror (&ws));
1394 return NULL;
1395 }
1396 val = mu_alloc (sizeof (*val));
1397 if (ws.ws_wordc == 1)
1398 {
1399 val->type = MU_CFG_STRING;
1400 val->v.string = ws.ws_wordv[0];
1401 }
1402 else
1403 {
1404 val->type = MU_CFG_ARRAY;
1405 val->v.arg.c = ws.ws_wordc;
1406 val->v.arg.v = mu_alloc (ws.ws_wordc * sizeof (val->v.arg.v[0]));
1407 for (i = 0; i < ws.ws_wordc; i++)
1408 {
1409 val->v.arg.v[i].type = MU_CFG_STRING;
1410 val->v.arg.v[i].v.string = ws.ws_wordv[i];
1411 }
1412 }
1413 ws.ws_wordc = 0;
1414 mu_wordsplit_free (&ws);
1415 }
1416 return val;
1417 }
1418
1419 static void
parse_tag(struct find_data * fptr)1420 parse_tag (struct find_data *fptr)
1421 {
1422 char *p = strchr (fptr->argv[fptr->tag], '=');
1423 if (p)
1424 {
1425 *p++ = 0;
1426 fptr->label = parse_label (p);
1427 }
1428 else
1429 fptr->label = NULL;
1430 }
1431
1432 static int
node_finder(const mu_cfg_node_t * node,void * data)1433 node_finder (const mu_cfg_node_t *node, void *data)
1434 {
1435 struct find_data *fdptr = data;
1436 if (strcmp (fdptr->argv[fdptr->tag], node->tag) == 0
1437 && (!fdptr->label || mu_cfg_value_eq (fdptr->label, node->label)))
1438 {
1439 fdptr->tag++;
1440 if (fdptr->tag == fdptr->argc)
1441 {
1442 fdptr->node = node;
1443 return MU_CFG_ITER_STOP;
1444 }
1445 parse_tag (fdptr);
1446 return MU_CFG_ITER_OK;
1447 }
1448
1449 return node->type == mu_cfg_node_statement ?
1450 MU_CFG_ITER_SKIP : MU_CFG_ITER_OK;
1451 }
1452
1453 int
mu_cfg_find_node(mu_cfg_tree_t * tree,const char * path,mu_cfg_node_t ** pval)1454 mu_cfg_find_node (mu_cfg_tree_t *tree, const char *path, mu_cfg_node_t **pval)
1455 {
1456 int rc;
1457 struct find_data data;
1458
1459 rc = split_cfg_path (path, &data.argc, &data.argv);
1460 if (rc)
1461 return rc;
1462 data.tag = 0;
1463 if (data.argc)
1464 {
1465 struct mu_cfg_iter_closure clos;
1466
1467 parse_tag (&data);
1468
1469 clos.beg = node_finder;
1470 clos.end = NULL;
1471 clos.data = &data;
1472 rc = mu_cfg_preorder (tree->nodes, &clos);
1473 destroy_value (data.label);
1474 if (rc == MU_ERR_USER0)
1475 {
1476 *pval = (mu_cfg_node_t *) data.node;
1477 return 0;
1478 }
1479 else if (rc != 0)
1480 mu_diag_funcall (MU_DIAG_ERR, "mu_cfg_preorder", NULL, rc);
1481 }
1482 return MU_ERR_NOENT;
1483 }
1484
1485
1486
1487 int
mu_cfg_create_subtree(const char * path,mu_cfg_node_t ** pnode)1488 mu_cfg_create_subtree (const char *path, mu_cfg_node_t **pnode)
1489 {
1490 int rc;
1491 int argc, i;
1492 char **argv;
1493 enum mu_cfg_node_type type;
1494 mu_cfg_node_t *node = NULL;
1495 struct mu_locus_range locus = MU_LOCUS_RANGE_INITIALIZER;
1496
1497 rc = split_cfg_path (path, &argc, &argv);
1498 if (rc)
1499 return rc;
1500
1501 for (i = argc - 1; i >= 0; i--)
1502 {
1503 mu_list_t nodelist = NULL;
1504 mu_config_value_t *label = NULL;
1505 char *q = argv[i], *p;
1506 mu_cfg_node_t *parent;
1507
1508 type = mu_cfg_node_statement;
1509 do
1510 {
1511 p = strchr (q, '=');
1512 if (p && p > argv[i] && p[-1] != '\\')
1513 {
1514 *p++ = 0;
1515 label = parse_label (p);
1516 if (i == argc - 1)
1517 type = mu_cfg_node_param;
1518 break;
1519 }
1520 else if (p)
1521 q = p + 1;
1522 else
1523 break;
1524 }
1525 while (*q);
1526
1527 if (node)
1528 {
1529 mu_cfg_create_node_list (&nodelist);
1530 mu_list_append (nodelist, node);
1531 }
1532 parent = mu_cfg_alloc_node (type, &locus, argv[i], label, nodelist);
1533 if (node)
1534 node->parent = parent;
1535 node = parent;
1536 }
1537
1538 mu_argcv_free (argc, argv);
1539 *pnode = node;
1540 return 0;
1541 }
1542
1543 int
mu_cfg_parse_config(mu_cfg_tree_t ** ptree,struct mu_cfg_parse_hints * hints)1544 mu_cfg_parse_config (mu_cfg_tree_t **ptree, struct mu_cfg_parse_hints *hints)
1545 {
1546 int rc = 0;
1547 mu_cfg_tree_t *tree = NULL, *tmp;
1548 struct mu_cfg_parse_hints xhints;
1549
1550 if ((hints->flags & MU_CFHINT_SITE_FILE) && hints->site_file)
1551 {
1552 rc = mu_cfg_parse_file (&tmp, hints->site_file, hints->flags);
1553
1554 switch (rc)
1555 {
1556 case 0:
1557 mu_cfg_tree_postprocess (tmp, hints);
1558 mu_cfg_tree_union (&tree, &tmp);
1559
1560 case ENOENT:
1561 rc = 0;
1562 break;
1563
1564 default:
1565 mu_error ("%s", mu_strerror (rc));
1566 return rc;
1567 }
1568 }
1569
1570 xhints = *hints;
1571 xhints.flags &= ~MU_CFHINT_PROGRAM;
1572
1573 if ((hints->flags & MU_CFHINT_PER_USER_FILE)
1574 && (hints->flags & MU_CFHINT_PROGRAM))
1575 {
1576 size_t size = 3 + strlen (hints->program) + 1;
1577 char *file_name = malloc (size);
1578 if (file_name)
1579 {
1580 strcpy (file_name, "~/.");
1581 strcat (file_name, hints->program);
1582
1583 rc = mu_cfg_parse_file (&tmp, file_name, xhints.flags);
1584 switch (rc)
1585 {
1586 case 0:
1587 mu_cfg_tree_postprocess (tmp, &xhints);
1588 mu_cfg_tree_union (&tree, &tmp);
1589 break;
1590
1591 case ENOENT:
1592 rc = 0;
1593 break;
1594
1595 default:
1596 mu_error ("%s", mu_strerror (rc));
1597 mu_cfg_destroy_tree (&tree);
1598 return rc;
1599 }
1600 free (file_name);
1601 }
1602 }
1603
1604 if ((hints->flags & MU_CFHINT_CUSTOM_FILE) && hints->custom_file)
1605 {
1606 rc = mu_cfg_parse_file (&tmp, hints->custom_file, xhints.flags);
1607 if (rc)
1608 {
1609 mu_error (_("errors parsing file %s: %s"), hints->custom_file,
1610 mu_strerror (rc));
1611 mu_cfg_destroy_tree (&tree);
1612 return rc;
1613 }
1614 else
1615 {
1616 mu_cfg_tree_postprocess (tmp, &xhints);
1617 mu_cfg_tree_union (&tree, &tmp);
1618 }
1619 }
1620
1621 *ptree = tree;
1622 return rc;
1623 }
1624
1625
1626
1627
1628
1629