diff options
Diffstat (limited to 'libmailutils/cfg_parser.y')
-rw-r--r-- | libmailutils/cfg_parser.y | 1817 |
1 files changed, 1817 insertions, 0 deletions
diff --git a/libmailutils/cfg_parser.y b/libmailutils/cfg_parser.y new file mode 100644 index 000000000..8b13ae10d --- /dev/null +++ b/libmailutils/cfg_parser.y @@ -0,0 +1,1817 @@ +%{ +/* cfg_parser.y -- general-purpose configuration file parser + Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <netdb.h> +#include "intprops.h" +#include <mailutils/argcv.h> +#include <mailutils/nls.h> +#include <mailutils/cfg.h> +#include <mailutils/alloc.h> +#include <mailutils/errno.h> +#include <mailutils/error.h> +#include <mailutils/list.h> +#include <mailutils/iterator.h> +#include <mailutils/debug.h> +#include <mailutils/mutil.h> +#include <mailutils/cctype.h> + +int mu_cfg_parser_verbose; +static mu_list_t /* of mu_cfg_node_t */ parse_node_list; +mu_cfg_locus_t mu_cfg_locus; +size_t mu_cfg_error_count; + +static int _mu_cfg_errcnt; +static mu_debug_t _mu_cfg_debug; + +int yylex (); + +void _mu_line_begin (void); +void _mu_line_add (char *text, size_t len); +char *_mu_line_finish (void); + +static int +yyerror (char *s) +{ + mu_cfg_parse_error ("%s", s); + return 0; +} + +static mu_config_value_t * +config_value_dup (mu_config_value_t *src) +{ + if (!src) + return NULL; + else + { + /* FIXME: Use mu_opool_alloc */ + mu_config_value_t *val = mu_alloc (sizeof (*val)); + *val = *src; + return val; + } +} + +static mu_cfg_node_t * +mu_cfg_alloc_node (enum mu_cfg_node_type type, mu_cfg_locus_t *loc, + const char *tag, mu_config_value_t *label, + mu_list_t nodelist) +{ + char *p; + mu_cfg_node_t *np; + size_t size = sizeof *np + strlen (tag) + 1; + np = mu_alloc (size); + np->type = type; + np->locus = *loc; + p = (char*) (np + 1); + np->tag = p; + strcpy (p, tag); + np->label = label; + np->nodes = nodelist; + return np; +} + +void +mu_cfg_free_node (mu_cfg_node_t *node) +{ + free (node->label); + free (node); +} + +void +mu_cfg_format_error (mu_debug_t debug, size_t level, const char *fmt, ...) +{ + va_list ap; + + if (!debug) + mu_diag_get_debug (&debug); + va_start (ap, fmt); + mu_debug_vprintf (debug, 0, fmt, ap); + mu_debug_printf (debug, 0, "\n"); + va_end (ap); + if (level <= MU_DEBUG_ERROR) + mu_cfg_error_count++; +} + +static void +_mu_cfg_debug_set_locus (mu_debug_t debug, const mu_cfg_locus_t *loc) +{ + mu_debug_set_locus (debug, loc->file ? loc->file : _("unknown file"), + loc->line); +} + +void +mu_cfg_vperror (mu_debug_t debug, const mu_cfg_locus_t *loc, + const char *fmt, va_list ap) +{ + if (!debug) + mu_diag_get_debug (&debug); + _mu_cfg_debug_set_locus (debug, loc); + mu_debug_vprintf (debug, 0, fmt, ap); + mu_debug_printf (debug, 0, "\n"); + mu_debug_set_locus (debug, NULL, 0); + mu_cfg_error_count++; +} + +void +mu_cfg_perror (mu_debug_t debug, const mu_cfg_locus_t *loc, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + mu_cfg_vperror (debug, loc, fmt, ap); + va_end (ap); +} + +void +mu_cfg_parse_error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + mu_cfg_vperror (_mu_cfg_debug, &mu_cfg_locus, fmt, ap); + va_end (ap); +} + +#define node_type_str(t) (((t) == mu_cfg_node_statement) ? "stmt" : "param") + +static void +debug_print_node (mu_cfg_node_t *node) +{ + if (mu_debug_check_level (_mu_cfg_debug, MU_DEBUG_TRACE0)) + { + mu_debug_set_locus (_mu_cfg_debug, + node->locus.file, node->locus.line); + if (node->type == mu_cfg_node_undefined) + /* Stay on the safe side */ + mu_cfg_format_error (_mu_cfg_debug, MU_DEBUG_ERROR, + "unknown statement type!"); + else + /* FIXME: How to print label? */ + mu_cfg_format_error (_mu_cfg_debug, MU_DEBUG_TRACE0, + "statement: %s, id: %s", + node_type_str (node->type), + node->tag ? node->tag : "(null)"); + + mu_debug_set_locus (_mu_cfg_debug, NULL, 0); + } +} + +static void +free_node_item (void *item) +{ + mu_cfg_node_t *node = item; + + switch (node->type) + { + case mu_cfg_node_statement: + mu_list_destroy (&node->nodes); + break; + + case mu_cfg_node_undefined: /* hmm... */ + case mu_cfg_node_param: + break; + } + mu_cfg_free_node (node); +} + +int +mu_cfg_create_node_list (mu_list_t *plist) +{ + int rc; + mu_list_t list; + + rc = mu_list_create (&list); + if (rc) + return rc; + mu_list_set_destroy_item (list, free_node_item); + *plist = list; + return 0; +} + +%} + +%union { + mu_cfg_node_t node; + mu_cfg_node_t *pnode; + mu_list_t /* of mu_cfg_node_t */ nodelist; + char *string; + mu_config_value_t value, *pvalue; + mu_list_t list; + struct { const char *name; mu_cfg_locus_t locus; } ident; +} + +%token <string> MU_TOK_IDENT MU_TOK_STRING MU_TOK_QSTRING MU_TOK_MSTRING +%type <string> string slist +%type <list> slist0 +%type <value> value +%type <pvalue> tag vallist +%type <list> values list vlist +%type <ident> ident +%type <nodelist> stmtlist +%type <pnode> stmt simple block + +%% + +input : stmtlist + { + parse_node_list = $1; + } + ; + +stmtlist: stmt + { + mu_cfg_create_node_list (&$$); + mu_list_append ($$, $1); + } + | stmtlist stmt + { + mu_list_append ($1, $2); + $$ = $1; + debug_print_node ($2); + } + ; + +stmt : simple + | block + ; + +simple : ident vallist ';' + { + $$ = mu_cfg_alloc_node (mu_cfg_node_param, &$1.locus, + $1.name, $2, + NULL); + } + ; + +block : ident tag '{' '}' opt_sc + { + $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &$1.locus, + $1.name, $2, + NULL); + + } + | ident tag '{' stmtlist '}' opt_sc + { + $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &$1.locus, + $1.name, $2, $4); + + } + ; + +ident : MU_TOK_IDENT + { + $$.name = $1; + $$.locus = mu_cfg_locus; + } + ; + +tag : /* empty */ + { + $$ = NULL; + } + | vallist + ; + +vallist : vlist + { + size_t n = 0; + mu_list_count($1, &n); + if (n == 1) + { + mu_list_get ($1, 0, (void**) &$$); + } + else + { + size_t i; + mu_config_value_t val; + + val.type = MU_CFG_ARRAY; + val.v.arg.c = n; + /* FIXME: Use mu_opool_alloc */ + val.v.arg.v = mu_alloc (n * sizeof (val.v.arg.v[0])); + if (!val.v.arg.v) + { + mu_cfg_parse_error (_("not enough memory")); + abort(); + } + + for (i = 0; i < n; i++) + { + mu_config_value_t *v; + mu_list_get ($1, i, (void **) &v); + val.v.arg.v[i] = *v; + } + $$ = config_value_dup (&val); + } + mu_list_destroy (&$1); + } + ; + +vlist : value + { + int rc = mu_list_create (&$$); + if (rc) + { + mu_cfg_parse_error (_("cannot create list: %s"), + mu_strerror (rc)); + abort (); + } + mu_list_append ($$, config_value_dup (&$1)); /* FIXME */ + } + | vlist value + { + mu_list_append ($1, config_value_dup (&$2)); + } + ; + +value : string + { + $$.type = MU_CFG_STRING; + $$.v.string = $1; + } + | list + { + $$.type = MU_CFG_LIST; + $$.v.list = $1; + } + | MU_TOK_MSTRING + { + $$.type = MU_CFG_STRING; + $$.v.string = $1; + } + ; + +string : MU_TOK_STRING + | MU_TOK_IDENT + | slist + ; + +slist : slist0 + { + mu_iterator_t itr; + mu_list_get_iterator ($1, &itr); + + _mu_line_begin (); + for (mu_iterator_first (itr); + !mu_iterator_is_done (itr); mu_iterator_next (itr)) + { + char *p; + mu_iterator_current (itr, (void**)&p); + _mu_line_add (p, strlen (p)); + } + $$ = _mu_line_finish (); + mu_iterator_destroy (&itr); + mu_list_destroy(&$1); + } + ; + +slist0 : MU_TOK_QSTRING + { + mu_list_create (&$$); + mu_list_append ($$, $1); + } + | slist0 MU_TOK_QSTRING + { + mu_list_append ($1, $2); + $$ = $1; + } + ; + +list : '(' values ')' + { + $$ = $2; + } + | '(' values ',' ')' + { + $$ = $2; + } + ; + +values : value + { + mu_list_create (&$$); + mu_list_append ($$, config_value_dup (&$1)); + } + | values ',' value + { + mu_list_append ($1, config_value_dup (&$3)); + $$ = $1; + } + ; + +opt_sc : /* empty */ + | ';' + ; + + +%% + +static int +_cfg_default_printer (void *unused, mu_log_level_t level, const char *str) +{ + fprintf (stderr, "%s", str); + return 0; +} + +mu_debug_t +mu_cfg_get_debug () +{ + if (!_mu_cfg_debug) + { + mu_debug_create (&_mu_cfg_debug, NULL); + mu_debug_set_print (_mu_cfg_debug, _cfg_default_printer, NULL); + mu_debug_set_level (_mu_cfg_debug, mu_global_debug_level ("config")); + } + return _mu_cfg_debug; +} + +void +mu_cfg_set_debug () +{ + if (mu_debug_check_level (mu_cfg_get_debug (), MU_DEBUG_TRACE7)) + yydebug = 1; +} + +int +mu_cfg_parse (mu_cfg_tree_t **ptree) +{ + int rc; + mu_cfg_tree_t *tree; + mu_opool_t pool; + + mu_cfg_set_debug (); + _mu_cfg_errcnt = 0; + + rc = yyparse (); + pool = mu_cfg_lexer_pool (); + if (rc == 0 && _mu_cfg_errcnt) + { + mu_opool_destroy (&pool); + rc = 1; + } + else + { + tree = mu_alloc (sizeof (*tree)); + tree->debug = _mu_cfg_debug; + _mu_cfg_debug = NULL; + tree->nodes = parse_node_list; + tree->pool = pool; + parse_node_list = NULL; + *ptree = tree; + } + return rc; +} + +int +mu_cfg_tree_union (mu_cfg_tree_t **pa, mu_cfg_tree_t **pb) +{ + mu_cfg_tree_t *a, *b; + int rc; + + if (!pb) + return EINVAL; + if (!*pb) + return 0; + b = *pb; + if (!pa) + return EINVAL; + if (!*pa) + { + *pa = b; + *pb = NULL; + return 0; + } + else + a = *pa; + + /* Merge opools */ + rc = mu_opool_union (&b->pool, &a->pool); + if (rc) + return rc; + + /* Link node lists */ + if (b->nodes) + { + mu_list_append_list (a->nodes, b->nodes); + mu_list_destroy (&b->nodes); + } + + mu_debug_destroy (&b->debug, mu_debug_get_owner (b->debug)); + free (b); + *pb = NULL; + return 0; +} + +static mu_cfg_tree_t * +do_include (const char *name, int flags, mu_cfg_locus_t *loc) +{ + struct stat sb; + char *tmpname = NULL; + mu_cfg_tree_t *tree = NULL; + + if (name[0] != '/') + { + name = tmpname = mu_make_file_name (SYSCONFDIR, name); + if (!name) + { + mu_error ("%s", mu_strerror (errno)); + return NULL; + } + } + if (stat (name, &sb) == 0) + { + int rc = 0; + + if (S_ISDIR (sb.st_mode)) + { + if (flags & MU_PARSE_CONFIG_GLOBAL) + { + char *file = mu_make_file_name (name, mu_program_name); + rc = mu_cfg_parse_file (&tree, file, flags); + free (file); + } + } + else + rc = mu_cfg_parse_file (&tree, name, flags); + + if (rc == 0 && tree) + mu_cfg_tree_postprocess (tree, flags & ~MU_PARSE_CONFIG_GLOBAL); + } + else if (errno == ENOENT) + mu_cfg_perror (tree->debug, loc, + _("include file or directory does not exist")); + else + mu_cfg_perror (tree->debug, loc, + _("cannot stat include file or directory: %s"), + mu_strerror (errno)); + free (tmpname); + return tree; +} + +int +mu_cfg_tree_postprocess (mu_cfg_tree_t *tree, int flags) +{ + int rc; + mu_iterator_t itr; + + if (!tree->nodes) + return 0; + rc = mu_list_get_iterator (tree->nodes, &itr); + if (rc) + return rc; + for (mu_iterator_first (itr); !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + mu_cfg_node_t *node; + + mu_iterator_current (itr, (void**) &node); + + if (node->type == mu_cfg_node_statement) + { + if ((flags & MU_PARSE_CONFIG_GLOBAL) && + strcmp (node->tag, "program") == 0) + { + if (node->label->type == MU_CFG_STRING) + { + if (strcmp (node->label->v.string, mu_program_name) == 0) + { + /* Move all nodes from this block to the topmost + level */ + mu_iterator_ctl (itr, mu_itrctl_insert_list, + node->nodes); + mu_iterator_ctl (itr, mu_itrctl_delete, NULL); + /*FIXME:mu_cfg_free_node (node);*/ + } + } + else + { + mu_cfg_perror (tree->debug, &node->locus, + _("argument to `program' is not a string")); + mu_iterator_ctl (itr, mu_itrctl_delete, NULL); + } + } + } + else if (node->type == mu_cfg_node_param && + strcmp (node->tag, "include") == 0) + { + if (node->label->type == MU_CFG_STRING) + { + mu_cfg_tree_t *t = do_include (node->label->v.string, flags, + &node->locus); + if (t) + { + /* Merge the new tree into the current point and + destroy the rest of it */ + mu_iterator_ctl (itr, mu_itrctl_insert_list, t->nodes); + mu_opool_union (&tree->pool, &t->pool); + mu_cfg_destroy_tree (&t); + } + } + else + mu_cfg_perror (tree->debug, &node->locus, + _("argument to `include' is not a string")); + /* Remove node from the list */ + mu_iterator_ctl (itr, mu_itrctl_delete, NULL); + } + } + return 0; +} + +static int +_mu_cfg_preorder_recursive (void *item, void *cbdata) +{ + mu_cfg_node_t *node = item; + struct mu_cfg_iter_closure *clos = cbdata; + + switch (node->type) + { + case mu_cfg_node_undefined: + abort (); + + case mu_cfg_node_statement: + switch (clos->beg (node, clos->data)) + { + case MU_CFG_ITER_OK: + if (mu_cfg_preorder (node->nodes, clos)) + return 1; + if (clos->end && clos->end (node, clos->data) == MU_CFG_ITER_STOP) + return 1; + break; + + case MU_CFG_ITER_SKIP: + break; + + case MU_CFG_ITER_STOP: + return 1; + } + break; + + case mu_cfg_node_param: + return clos->beg (node, clos->data) == MU_CFG_ITER_STOP; + } + return 0; +} + +int +mu_cfg_preorder (mu_list_t nodelist, struct mu_cfg_iter_closure *clos) +{ + return mu_list_do (nodelist, _mu_cfg_preorder_recursive, clos); +} + + + +void +mu_cfg_destroy_tree (mu_cfg_tree_t **ptree) +{ + if (ptree && *ptree) + { + mu_cfg_tree_t *tree = *ptree; + mu_list_destroy (&tree->nodes); + mu_opool_destroy (&tree->pool); + *ptree = NULL; + } +} + + + +struct mu_cfg_section_list +{ + struct mu_cfg_section_list *next; + struct mu_cfg_section *sec; +}; + +struct scan_tree_data +{ + struct mu_cfg_section_list *list; + void *target; + void *call_data; + mu_cfg_tree_t *tree; + int error; +}; + +static struct mu_cfg_cont * +find_container (mu_list_t list, enum mu_cfg_cont_type type, + const char *ident, size_t len) +{ + mu_iterator_t iter; + struct mu_cfg_cont *ret = NULL; + + if (len == 0) + len = strlen (ident); + + mu_list_get_iterator (list, &iter); + for (mu_iterator_first (iter); !mu_iterator_is_done (iter); + mu_iterator_next (iter)) + { + struct mu_cfg_cont *cont; + mu_iterator_current (iter, (void**) &cont); + + if (cont->type == type + && strlen (cont->v.ident) == len + && memcmp (cont->v.ident, ident, len) == 0) + { + ret = cont; + break; + } + } + mu_iterator_destroy (&iter); + return ret; +} + +static struct mu_cfg_section * +find_subsection (struct mu_cfg_section *sec, const char *ident, size_t len) +{ + if (sec) + { + if (sec->children) + { + struct mu_cfg_cont *cont = find_container (sec->children, + mu_cfg_cont_section, + ident, len); + if (cont) + return &cont->v.section; + } + } + return NULL; +} + +static struct mu_cfg_param * +find_param (struct mu_cfg_section *sec, const char *ident, size_t len) +{ + if (sec) + { + if (sec->children) + { + struct mu_cfg_cont *cont = find_container (sec->children, + mu_cfg_cont_param, + ident, len); + if (cont) + return &cont->v.param; + } + } + return NULL; +} + +static int +push_section (struct scan_tree_data *dat, struct mu_cfg_section *sec) +{ + struct mu_cfg_section_list *p = mu_alloc (sizeof *p); + if (!p) + { + mu_cfg_perror (dat->tree->debug, NULL, _("not enough memory")); + return 1; + } + p->sec = sec; + p->next = dat->list; + dat->list = p; + return 0; +} + +static struct mu_cfg_section * +pop_section (struct scan_tree_data *dat) +{ + struct mu_cfg_section_list *p = dat->list; + struct mu_cfg_section *sec = p->sec; + dat->list = p->next; + free (p); + return sec; +} + +#define STRTONUM(s, type, base, res, limit, d, loc) \ + { \ + type sum = 0; \ + \ + for (; *s; s++) \ + { \ + type x; \ + \ + if ('0' <= *s && *s <= '9') \ + x = sum * base + *s - '0'; \ + else if (base == 16 && 'a' <= *s && *s <= 'f') \ + x = sum * base + *s - 'a'; \ + else if (base == 16 && 'A' <= *s && *s <= 'F') \ + x = sum * base + *s - 'A'; \ + else \ + break; \ + if (x <= sum) \ + { \ + mu_cfg_perror (d, loc, _("numeric overflow")); \ + return 1; \ + } \ + else if (limit && x > limit) \ + { \ + mu_cfg_perror (d, loc, \ + _("value out of allowed range")); \ + return 1; \ + } \ + sum = x; \ + } \ + res = sum; \ + } + +#define STRxTONUM(s, type, res, limit, d, loc) \ + { \ + int base; \ + if (*s == '0') \ + { \ + s++; \ + if (*s == 0) \ + base = 10; \ + else if (*s == 'x' || *s == 'X') \ + { \ + s++; \ + base = 16; \ + } \ + else \ + base = 8; \ + } else \ + base = 10; \ + STRTONUM (s, type, base, res, limit, d, loc); \ + } + +#define GETUNUM(str, type, res, d, loc) \ + { \ + type tmpres; \ + const char *s = str; \ + STRxTONUM (s, type, tmpres, 0, d, loc); \ + if (*s) \ + { \ + mu_cfg_perror (d, loc, \ + _("not a number (stopped near `%s')"), \ + s); \ + return 1; \ + } \ + res = tmpres; \ + } + +#define GETSNUM(str, type, res, d, loc) \ + { \ + unsigned type tmpres; \ + const char *s = str; \ + int sign; \ + unsigned type limit; \ + \ + if (*s == '-') \ + { \ + sign++; \ + s++; \ + limit = TYPE_MINIMUM (type); \ + limit = - limit; \ + } \ + else \ + { \ + sign = 0; \ + limit = TYPE_MAXIMUM (type); \ + } \ + \ + STRxTONUM (s, unsigned type, tmpres, limit, d, loc); \ + if (*s) \ + { \ + mu_cfg_perror (d, loc, \ + _("not a number (stopped near `%s')"), \ + s); \ + return 1; \ + } \ + res = sign ? - tmpres : tmpres; \ + } + +static int +parse_ipv4 (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus, + const char *str, struct in_addr *res) +{ + struct in_addr addr; + if (inet_aton (str, &addr) == 0) + { + mu_cfg_perror (sdata->tree->debug, locus, _("not an IPv4")); + return 1; + } + addr.s_addr = ntohl (addr.s_addr); + *res = addr; + return 0; +} + +static int +parse_host (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus, + const char *str, struct in_addr *res) +{ + struct in_addr addr; + struct hostent *hp = gethostbyname (str); + if (hp) + { + addr.s_addr = *(unsigned long *)hp->h_addr; + } + else if (inet_aton (str, &addr) == 0) + { + mu_cfg_perror (sdata->tree->debug, locus, + _("cannot resolve hostname `%s'"), + str); + return 1; + } + addr.s_addr = ntohl (addr.s_addr); + *res = addr; + return 0; +} + +static int +parse_cidr (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus, + const char *str, mu_cfg_cidr_t *res) +{ + struct in_addr addr; + unsigned long mask; + char astr[16]; + const char *p, *s; + + p = strchr (str, '/'); + if (p) + { + int len = p - str; + if (len > sizeof astr - 1) { + mu_cfg_perror (sdata->tree->debug, locus, + _("not a valid IPv4 address in CIDR")); + return 1; + } + memcpy (astr, str, len); + astr[len] = 0; + if (inet_aton (astr, &addr) == 0) + { + mu_cfg_perror (sdata->tree->debug, locus, + _("not a valid IPv4 address in CIDR")); + return 1; + } + addr.s_addr = ntohl (addr.s_addr); + + p++; + s = p; + STRxTONUM (s, unsigned long, mask, 0, sdata->tree->debug, locus); + if (*s == '.') + { + struct in_addr a; + if (inet_aton (p, &a) == 0) + { + mu_cfg_perror (sdata->tree->debug, locus, + _("not a valid network in CIDR")); + return 1; + } + a.s_addr = ntohl (a.s_addr); + for (mask = 0; (a.s_addr & 1) == 0 && mask < 32; ) + { + mask++; + a.s_addr >>= 1; + } + mask = 32 - mask; + } + else if (mask > 32) + { + mu_cfg_perror (sdata->tree->debug, locus, + _("not a valid network mask in CIDR")); + return 1; + } + } + else + { + int i; + unsigned short x; + addr.s_addr = 0; + + p = str; + for (i = 0; i < 3; i++) + { + STRxTONUM (p, unsigned short, x, 255, sdata->tree->debug, locus); + if (*p != '.') + break; + addr.s_addr = (addr.s_addr << 8) + x; + } + + if (*p) + { + mu_cfg_perror (sdata->tree->debug, locus, + _("not a CIDR (stopped near `%s')"), + p); + return 1; + } + + mask = i * 8; + + addr.s_addr <<= (4 - i) * 8; + } + + res->addr = addr; + res->mask = mask; + return 0; +} + +int +mu_cfg_parse_boolean (const char *str, int *res) +{ + if (strcmp (str, "yes") == 0 + || strcmp (str, "on") == 0 + || strcmp (str, "t") == 0 + || strcmp (str, "true") == 0 + || strcmp (str, "1") == 0) + *res = 1; + else if (strcmp (str, "no") == 0 + || strcmp (str, "off") == 0 + || strcmp (str, "nil") == 0 + || strcmp (str, "false") == 0 + || strcmp (str, "0") == 0) + *res = 0; + else + return 1; + return 0; +} + +static int +parse_bool (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus, + const char *str, int *res) +{ + if (mu_cfg_parse_boolean (str, res)) + { + mu_cfg_perror (sdata->tree->debug, locus, _("not a boolean")); + return 1; + } + return 0; +} + +static int +valcvt (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus, + void *tgt, + enum mu_cfg_param_data_type type, mu_config_value_t *val) +{ + if (val->type != MU_CFG_STRING) + { + mu_cfg_perror (sdata->tree->debug, locus, _("expected string value")); + return 1; + } + switch (type) + { + case mu_cfg_string: + { + char *s = mu_strdup (val->v.string); + /* FIXME: free tgt? */ + *(char**)tgt = s; + break; + } + + case mu_cfg_short: + GETSNUM (val->v.string, short, *(short*)tgt, sdata->tree->debug, locus); + break; + + case mu_cfg_ushort: + GETUNUM (val->v.string, unsigned short, *(unsigned short*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_int: + GETSNUM (val->v.string, int, *(int*)tgt, sdata->tree->debug, locus); + break; + + case mu_cfg_uint: + GETUNUM (val->v.string, unsigned int, *(unsigned int*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_long: + GETSNUM (val->v.string, long, *(long*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_ulong: + GETUNUM (val->v.string, unsigned long, *(unsigned long*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_size: + GETUNUM (val->v.string, size_t, *(size_t*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_off: + mu_cfg_perror (sdata->tree->debug, locus, _("not implemented yet")); + /* GETSNUM(node->tag_label, off_t, *(off_t*)tgt); */ + return 1; + + case mu_cfg_time: + GETUNUM (val->v.string, time_t, *(time_t*)tgt, + sdata->tree->debug, locus); + break; + + case mu_cfg_bool: + if (parse_bool (sdata, locus, val->v.string, (int*) tgt)) + return 1; + break; + + case mu_cfg_ipv4: + if (parse_ipv4 (sdata, locus, val->v.string, (struct in_addr *)tgt)) + return 1; + break; + + case mu_cfg_cidr: + if (parse_cidr (sdata, locus, val->v.string, (mu_cfg_cidr_t *)tgt)) + return 1; + break; + + case mu_cfg_host: + if (parse_host (sdata, locus, val->v.string, (struct in_addr *)tgt)) + return 1; + break; + + default: + return 1; + } + return 0; +} + +struct set_closure +{ + mu_list_t list; + enum mu_cfg_param_data_type type; + struct scan_tree_data *sdata; + const mu_cfg_locus_t *locus; +}; + +static size_t config_type_size[] = { + sizeof (char*), /* mu_cfg_string */ + sizeof (short), /* mu_cfg_short */ + sizeof (unsigned short), /* mu_cfg_ushort */ + sizeof (int), /* mu_cfg_int */ + sizeof (unsigned), /* mu_cfg_uint */ + sizeof (long), /* mu_cfg_long */ + sizeof (unsigned long), /* mu_cfg_ulong */ + sizeof (size_t), /* mu_cfg_size */ + sizeof (mu_off_t), /* mu_cfg_off */ + sizeof (time_t), /* mu_cfg_time */ + sizeof (int), /* mu_cfg_bool */ + sizeof (struct in_addr), /* mu_cfg_ipv4 */ + sizeof (mu_cfg_cidr_t), /* mu_cfg_cidr */ + sizeof (struct in_addr), /* mu_cfg_host */ + 0, /* mu_cfg_callback */ + 0, /* mu_cfg_section */ +} ; + +static int +_set_fun (void *item, void *data) +{ + mu_config_value_t *val = item; + struct set_closure *clos = data; + void *tgt; + size_t size; + + if (clos->type >= MU_ARRAY_SIZE(config_type_size) + || (size = config_type_size[clos->type]) == 0) + { + mu_cfg_perror (clos->sdata->tree->debug, clos->locus, + _("INTERNAL ERROR at %s:%d: unhandled data type %d"), + __FILE__, __LINE__, clos->type); + return 1; + } + + tgt = mu_alloc (size); + if (!tgt) + { + mu_cfg_perror (clos->sdata->tree->debug, clos->locus, + _("not enough memory")); + return 1; + } + + if (valcvt (clos->sdata, clos->locus, &tgt, clos->type, val) == 0) + mu_list_append (clos->list, tgt); + return 0; +} + +static int +parse_param (struct scan_tree_data *sdata, const mu_cfg_node_t *node) +{ + void *tgt; + struct set_closure clos; + struct mu_cfg_param *param = find_param (sdata->list->sec, node->tag, + 0); + + if (!param) + { + mu_cfg_perror (sdata->tree->debug, &node->locus, + _("unknown keyword `%s'"), + node->tag); + return 1; + } + + if (param->data) + tgt = param->data; + else if (sdata->list->sec->target) + tgt = (char*)sdata->list->sec->target + param->offset; + else if (sdata->target) + tgt = (char*)sdata->target + param->offset; + else if (param->type == mu_cfg_callback) + tgt = NULL; + else + { + mu_cfg_perror (sdata->tree->debug, &node->locus, + _("INTERNAL ERROR: cannot determine target offset for " + "%s"), param->ident); + abort (); + } + + memset (&clos, 0, sizeof clos); + clos.type = MU_CFG_TYPE (param->type); + if (MU_CFG_IS_LIST (param->type)) + { + clos.sdata = sdata; + clos.locus = &node->locus; + switch (node->label->type) + { + case MU_CFG_LIST: + break; + + case MU_CFG_STRING: + { + mu_list_t list; + mu_list_create (&list); + mu_list_append (list, config_value_dup (node->label)); + node->label->type = MU_CFG_LIST; + node->label->v.list = list; + } + break; + + case MU_CFG_ARRAY: + mu_cfg_perror (sdata->tree->debug, &node->locus, + _("expected list, but found array")); + return 1; + } + + mu_list_create (&clos.list); + mu_list_do (node->label->v.list, _set_fun, &clos); + *(mu_list_t*)tgt = clos.list; + } + else if (clos.type == mu_cfg_callback) + { + mu_debug_set_locus (sdata->tree->debug, node->locus.file, + node->locus.line); + if (!param->callback) + { + mu_cfg_perror (sdata->tree->debug, &node->locus, + _("INTERNAL ERROR: %s: callback not defined"), + node->tag); + abort (); + } + if (param->callback (sdata->tree->debug, tgt, node->label)) + return 1; + + } + else + return valcvt (sdata, &node->locus, tgt, clos.type, node->label); + + return 0; +} + + +static int +_scan_tree_helper (const mu_cfg_node_t *node, void *data) +{ + struct scan_tree_data *sdata = data; + struct mu_cfg_section *sec; + + switch (node->type) + { + case mu_cfg_node_undefined: + abort (); + + case mu_cfg_node_statement: + sec = find_subsection (sdata->list->sec, node->tag, 0); + if (!sec) + { + if (mu_cfg_parser_verbose) + { + _mu_cfg_debug_set_locus (sdata->tree->debug, &node->locus); + mu_cfg_format_error (sdata->tree->debug, MU_DIAG_WARNING, |