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