/*
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.

This program 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 2
of the License, 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <assert.h>
#include "constants.h"
#include "net.h"
#include "mem.h"
#include "log.h"
#include "debug.h"
#include "conf.h"

// Generate config set-default function definitions, cf_dfl_config_NAME().
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...) \
    int cf_dfl_config_##__name(struct config_##__name *s) { \
      return cf_dfl_config_##__name##_cf_(s); \
    } \
    int cf_dfl_config_##__name##_cf_(struct config_##__name *s) {
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
        s->__element = (__default);
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
        s->__element = (__default);
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
        strncpy(s->__element, (__default), (__size))[(__size)] = '\0';
#define SUB_STRUCT(__name, __element, __flags, __dlflabel...) \
	cf_dfl_config_##__name##_cf_##__dlflabel(&s->__element);
#define NODE_STRUCT(__name, __element, __repr, __flags, __dfllabel...) \
	SUB_STRUCT(__name, __element, __flags, ##__dfllabel)
#define END_STRUCT \
        return CFOK; \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    __attribute__((__ATTRIBUTE_unused)) static void __cf_unused_1_##__substructname(struct config_##__substructname *s) {
#define END_STRUCT_ASSIGN \
    }
#define STRUCT_DEFAULT(__name, __dfllabel) \
    int cf_dfl_config_##__name##_cf_##__dfllabel(struct config_##__name *s) { \
	cf_dfl_config_##__name(s);
#define ATOM_DEFAULT(__element, __default) \
        s->__element = (__default);
#define STRING_DEFAULT(__element, __default) \
        s->__element = (__default);
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...) \
	cf_dfl_config_##__name##_cf_##__dlflabel(&s->__element);
#define END_STRUCT_DEFAULT \
        return CFOK; \
    }
#define ARRAY(__name, __flags, __options...) \
    int cf_dfl_config_##__name(struct config_##__name *s) { \
      return cf_dfl_config_##__name##_cf_(s); \
    } \
    int cf_dfl_config_##__name##_cf_(struct config_##__name *a) { \
        a->ac = 0; \
        return CFOK; \
    }
#define KEY_ATOM(__type, __keyrepr)
#define KEY_STRING(__strsize, __keyrepr)
#define VALUE_ATOM(__type, __eltrepr)
#define VALUE_STRING(__strsize, __eltrepr)
#define VALUE_NODE(__type, __eltrepr)
#define VALUE_SUB_STRUCT(__structname, __dfllabel...)
#define VALUE_NODE_STRUCT(__structname, __eltrepr)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY

// Generate config assign function definitions, cf_cpy_config_NAME().
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...) \
    __attribute__((__ATTRIBUTE_unused)) static void __cf_unused_2_##__name(struct config_##__name *dst, const struct config_##__name *src) {
#define END_STRUCT \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    void cf_cpy_config_##__substructname(struct config_##__substructname *dst, const struct config_##__structname *src) {
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
        dst->__element = src->__element;
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
        dst->__element = src->__element;
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
        strncpy(dst->__element, src->__element, (__size))[(__size)] = '\0';
#define SUB_STRUCT(__name, __element, __flags, __dlflabel...) \
        dst->__element = src->__element;
#define NODE_STRUCT(__name, __element, __repr, __flags, __dfllabel...) \
        dst->__element = src->__element;
#define END_STRUCT_ASSIGN \
    }
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...)
#define KEY_ATOM(__type, __keyrepr)
#define KEY_STRING(__strsize, __keyrepr)
#define VALUE_ATOM(__type, __eltrepr)
#define VALUE_STRING(__strsize, __eltrepr)
#define VALUE_NODE(__type, __eltrepr)
#define VALUE_SUB_STRUCT(__structname, __dfllabel...)
#define VALUE_NODE_STRUCT(__structname, __eltrepr)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY

// Schema item flags.
#define __MANDATORY     (1<<0)
#define __TEXT		(1<<1)
#define __CHILDREN	(1<<2)
#define __NO_DUPLICATES	(1<<3)

// Schema flag symbols, to be used in the '__flags' macro arguments.
#define MANDATORY	|__MANDATORY
#define USES_TEXT	|__TEXT
#define USES_CHILDREN	|__CHILDREN
#define NO_DUPLICATES	|__NO_DUPLICATES

// Generate parsing functions, cf_opt_config_SECTION()
#define VALIDATOR(__validator) \
      validator = (__validator);
#define STRUCT(__name, __options...) \
    int cf_opt_config_##__name(struct config_##__name *strct, const struct cf_om_node *node) { \
      int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = NULL; \
      __options \
      int result = CFEMPTY; \
      char used[node->nodc]; \
      memset(used, 0, node->nodc * sizeof used[0]);
#define __ITEM(__element, __flags, __parseexpr) \
      { \
	int i = cf_om_get_child(node, #__element, NULL); \
	const struct cf_om_node *child = (i != -1) ? node->nodv[i] : NULL; \
	int ret = CFEMPTY; \
	if (child) { \
	  used[i] |= (__flags); \
	  ret = (__parseexpr); \
	  if (ret == CFERROR) \
	    return CFERROR; \
	} \
	result |= ret & CF__SUBFLAGS; \
	ret &= CF__FLAGS; \
	if (!(ret & CFEMPTY)) \
	  result &= ~CFEMPTY; \
	else if ((__flags) & __MANDATORY) { \
	  cf_warn_missing_node(node, #__element); \
	  result |= CFINCOMPLETE; \
	} \
	if (ret & ~CFEMPTY) { \
	  assert(child != NULL); \
	  if (child->text) \
	    cf_warn_node_value(child, ret); \
	  result |= CFSUB(ret); \
	} \
      }
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
        __ITEM(__element, 0 __flags, cf_opt_##__repr(&strct->__element, child))
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
        __ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? cf_opt_##__repr(&strct->__element, child->text) : CFEMPTY)
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
        __ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? cf_opt_##__repr(strct->__element, (__size) + 1, child->text) : CFEMPTY)
#define SUB_STRUCT(__name, __element, __flags, __dfllabel...) \
        __ITEM(__element, (0 __flags)|__CHILDREN, cf_opt_config_##__name(&strct->__element, child))
#define NODE_STRUCT(__name, __element, __repr, __flags, __dfllabel...) \
        __ITEM(__element, (0 __flags)|__TEXT|__CHILDREN, cf_opt_##__repr(&strct->__element, child))
#define END_STRUCT \
      { \
	unsigned i; \
	for (i = 0; i < node->nodc; ++i) { \
	  if (node->nodv[i]->text && !(used[i] & __TEXT)) { \
	    cf_warn_unsupported_node(node->nodv[i]); \
	    result |= CFSUB(CFUNSUPPORTED); \
	  } \
	  if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) { \
	    cf_warn_unsupported_children(node->nodv[i]); \
	    result |= CFSUB(CFUNSUPPORTED); \
	  } \
	} \
      } \
      if (validator) \
	result = (*validator)(node, strct, result); \
      return result; \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    __attribute__((__ATTRIBUTE_unused)) static int __cf_unused_3_##__substructname(struct config_##__substructname *strct, const struct cf_om_node *node) { \
      int result = 0; \
      char used[0];
#define END_STRUCT_ASSIGN \
      return 0; \
    }
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...) \
    int cf_opt_config_##__name(struct config_##__name *array, const struct cf_om_node *node) { \
      int flags = (0 __flags); \
      int (*keycmp)(const void *, const void *) = NULL; \
      int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = NULL; \
      __options \
      int result = CFOK; \
      unsigned i, n; \
      for (n = 0, i = 0; i < node->nodc && n < NELS(array->av); ++i) { \
	const struct cf_om_node *child = node->nodv[i]; \
	int ret = CFEMPTY;
#define __ARRAY_KEY(__parseexpr, __cmpfunc, __cmpfuncargs) \
	keycmp = (int (*)(const void *, const void *)) __cmpfunc; \
	ret = (__parseexpr); \
	if (ret == CFERROR) \
	  return CFERROR; \
	result |= ret & CF__SUBFLAGS; \
	ret &= CF__FLAGS; \
	result |= CFSUB(ret); \
	if (ret == CFOK && (flags & __NO_DUPLICATES)) { \
	  unsigned j; \
	  for (j = 0; j < n; ++j) { \
	    if (__cmpfunc __cmpfuncargs == 0) { \
	      cf_warn_duplicate_node(child, NULL); \
	      ret |= CFDUPLICATE; \
	    } \
	  } \
	} \
	if (ret != CFOK) \
	  cf_warn_array_key(child, ret);
#define __ARRAY_VALUE(__dflexpr, __parseexpr) \
	if (ret == CFOK) { \
	  ret = (__dflexpr); \
	  if (ret == CFOK) \
	    ret = (__parseexpr); \
	  if (ret == CFERROR) \
	    return CFERROR; \
	  result |= ret & CF__SUBFLAGS; \
	  ret &= CF__FLAGS; \
	  result |= CFSUB(ret); \
	  if (ret == CFOK) \
	    ++n; \
	  else \
	    cf_warn_array_value(child, ret); \
	}
#define END_ARRAY(__size) \
      } \
      if (i < node->nodc) { \
	assert(n == NELS(array->av)); \
	result |= CFARRAYOVERFLOW; \
	for (; i < node->nodc; ++i) \
	  cf_warn_list_overflow(node->nodv[i], "list overflow, only %zu elements allowed", NELS(array->av)); \
      } \
      array->ac = n; \
      qsort(array->av, array->ac, sizeof array->av[0], (int (*)(const void *, const void *)) keycmp); \
      if (validator) \
	result = (*validator)(node, array, result); \
      if (result & ~CFEMPTY) { \
	cf_warn_no_array(node, result); \
	array->ac = 0; \
      } \
      if (array->ac == 0) \
	result |= CFEMPTY; \
      return result; \
    }
#define KEY_ATOM(__type, __keyrepr) \
      __ARRAY_KEY(cf_opt_##__keyrepr(&array->av[n].key, child->key), cf_cmp_##__keyrepr, (&array->av[j].key, &array->av[n].key))
#define KEY_STRING(__strsize, __keyrepr) \
      __ARRAY_KEY(cf_opt_##__keyrepr(array->av[n].key, sizeof array->av[n].key, child->key), cf_cmp_##__keyrepr, (&array->av[j].key[0], &array->av[n].key[0]))
#define VALUE_ATOM(__type, __eltrepr) \
      __ARRAY_VALUE(CFOK, child->text ? cf_opt_##__eltrepr(&array->av[n].value, child->text) : CFEMPTY)
#define VALUE_STRING(__strsize, __eltrepr) \
      __ARRAY_VALUE(CFOK, child->text ? cf_opt_##__eltrepr(array->av[n].value, sizeof array->av[n].value, child->text) : CFEMPTY)
#define VALUE_NODE(__type, __eltrepr) \
      __ARRAY_VALUE(CFOK, cf_opt_##__eltrepr(&array->av[n].value, child))
#define VALUE_SUB_STRUCT(__structname, __dfllabel...) \
      __ARRAY_VALUE(cf_dfl_config_##__structname##_cf_##__dfllabel(&array->av[n].value), cf_opt_config_##__structname(&array->av[n].value, child))
#define VALUE_NODE_STRUCT(__structname, __eltrepr) \
      __ARRAY_VALUE(cf_dfl_config_##__structname(&array->av[n].value), cf_opt_##__eltrepr(&array->av[n].value, child))
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef __ARRAY_KEY
#undef __ARRAY_VALUE
#undef END_ARRAY

// Generate config array search-by-key functions.
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...)
#define NODE(__type, __element, __default, __repr, __flags, __comment)
#define ATOM(__type, __element, __default, __repr, __flags, __comment)
#define STRING(__size, __element, __default, __repr, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags, __dfllabel...)
#define NODE_STRUCT(__name, __element, __repr, __flags, __dfllabel...)
#define END_STRUCT
#define STRUCT_ASSIGN(__substructname, __structname)
#define END_STRUCT_ASSIGN
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...) \
    int config_##__name##__get(const struct config_##__name *array,
#define KEY_ATOM(__type, __keyrepr) \
	  const __type *key) { \
      unsigned i; \
      for (i = 0; i < array->ac; ++i) \
	if ((cf_cmp_##__keyrepr(key, &array->av[i].key)) == 0) \
	  return i; \
      return -1; \
    }
#define KEY_STRING(__strsize, __keyrepr) \
	  const char *key) { \
      unsigned i; \
      for (i = 0; i < array->ac; ++i) \
	if ((cf_cmp_##__keyrepr(&key[0], &array->av[i].key[0])) == 0) \
	  return i; \
      return -1; \
    }
#define VALUE_ATOM(__type, __eltrepr)
#define VALUE_STRING(__strsize, __eltrepr)
#define VALUE_NODE(__type, __eltrepr)
#define VALUE_SUB_STRUCT(__structname, __dfllabel...)
#define VALUE_NODE_STRUCT(__structname, __eltrepr)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY

// Generate config schema dump functions, cf_sch_config_NAME().
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...) \
    int cf_sch_config_##__name(struct cf_om_node **rootp) { \
      int i; \
      struct cf_om_node **childp;
#define __ADD_CHILD(nodep, __elementstr) \
	if ((i = cf_om_add_child(nodep, __elementstr)) == -1) \
	  return -1; \
	childp = &(*nodep)->nodv[(unsigned)i];
#define __ATOM(nodep, __text) \
	if (((*nodep)->text = str_edup(__text)) == NULL) \
	  return -1;
#define __STRUCT(nodep, __structname) \
	if (cf_sch_config_##__structname(nodep) == -1) \
	  return -1;
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
	__ADD_CHILD(rootp, #__element) \
	__ATOM(childp, "(" #__repr ")") \
	__ADD_CHILD(childp, "(" #__repr ")") \
	__ATOM(childp, "(" #__repr ")")
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
	__ADD_CHILD(rootp, #__element) \
	__ATOM(childp, "(" #__repr ")")
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
	__ADD_CHILD(rootp, #__element) \
	__ATOM(childp, "(" #__repr ")")
#define SUB_STRUCT(__structname, __element, __flags, __dfllabel...) \
	__ADD_CHILD(rootp, #__element) \
	__STRUCT(childp, __structname)
#define NODE_STRUCT(__structname, __element, __repr, __flags, __dfllabel...) \
	__ADD_CHILD(rootp, #__element) \
	__ATOM(childp, "(" #__repr ")") \
	__STRUCT(childp, __structname)
#define END_STRUCT \
        return 0; \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    __attribute__((__ATTRIBUTE_unused)) static int __cf_unused_4_##__substructname(struct cf_om_node **rootp) { \
      int i; \
      struct cf_om_node **childp;
#define END_STRUCT_ASSIGN \
      return 0; \
    }
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...) \
    int cf_sch_config_##__name(struct cf_om_node **rootp) { \
      int i; \
      struct cf_om_node **childp;
#define KEY_ATOM(__type, __keyrepr) \
	__ADD_CHILD(rootp, "[" #__keyrepr "]")
#define KEY_STRING(__strsize, __keyrepr) \
	__ADD_CHILD(rootp, "[" #__keyrepr "]")
#define VALUE_ATOM(__type, __eltrepr) \
	__ATOM(childp, "(" #__eltrepr ")")
#define VALUE_STRING(__strsize, __eltrepr) \
	__ATOM(childp, "(" #__eltrepr ")")
#define VALUE_NODE(__type, __eltrepr) \
	__ATOM(childp, "(" #__eltrepr ")") \
	__ADD_CHILD(childp, "(" #__eltrepr ")") \
	__ATOM(childp, "(" #__eltrepr ")")
#define VALUE_SUB_STRUCT(__structname, __dfllabel...) \
	__STRUCT(childp, __structname)
#define VALUE_NODE_STRUCT(__structname, __eltrepr) \
	__ATOM(childp, "(" #__eltrepr ")") \
	__STRUCT(childp, __structname)

#define END_ARRAY(__size) \
        return 0; \
    }
#include "conf_schema.h"
#undef __ADD_CHILD
#undef __ATOM
#undef __STRUCT
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY

// Generate formatting functions, cf_fmt_config_SECTION()
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...) \
    int cf_fmt_config_##__name(struct cf_om_node **parentp, const struct config_##__name *strct) { \
      return cf_xfmt_config_##__name(parentp, strct, NULL); \
    } \
    int cf_xfmt_config_##__name(struct cf_om_node **parentp, const struct config_##__name *strct, const struct config_##__name *UNUSED(dflt)) { \
      int result = CFOK; \
      int ret;
#define __FMT_TEXT(__repr, __eltname, __eltexpr, __defaultvar) \
	const char *text = NULL; \
	ret = cf_fmt_##__repr(&text, __eltexpr); \
	if (ret == CFOK) { \
	  int n; \
	  if (text == NULL) { \
	    WHY("cf_fmt_" #__repr "() returned CFOK but text=NULL"); \
	    ret = CFERROR; \
	  } else if ((n = cf_om_add_child(parentp, __eltname)) == -1) { \
	    ret = CFERROR; \
	  } else { \
	    (*parentp)->nodv[n]->text = text; \
	    (*parentp)->nodv[n]->line_number = is_default ? 0 : 1; \
	    text = NULL; \
	  } \
	} else if (ret == CFERROR || !is_default) \
	  WARNF("cf_fmt_" #__repr "() returned %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(300), ret))); \
	if (text) { \
	  free((char *)text); \
	  text = NULL; \
	} \
	if (ret == CFERROR) \
	  return CFERROR; \
	else if (ret != CFOK && !is_default) \
	  result |= (ret & CF__SUBFLAGS) | CFSUB(ret & CF__FLAGS);
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
      { \
	__type dfl = dflt ? dflt->__element : __default; \
	int is_default = cf_cmp_##__repr(&strct->__element, &dfl) == 0; \
	__FMT_TEXT(__repr, #__element, &strct->__element, __default) \
      }
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
      { \
        int is_default = cf_cmp_##__repr(strct->__element, dflt ? dflt->__element : __default) == 0; \
	__FMT_TEXT(__repr, #__element, strct->__element, __default) \
      }
#define __FMT_NODE_START(__element) \
	int n = cf_om_add_child(parentp, #__element); \
	if (n == -1) \
	  ret = CFERROR; \
	else { \
	  const char *funcname = NULL;
#define __FMT_NODE_END \
	  cf_om_remove_null_child(parentp, (unsigned) n); \
	  if (ret != CFOK) \
	    WARNF("%s() returned %s", funcname, strbuf_str(strbuf_cf_flags(strbuf_alloca(300), ret))); \
	  if ((unsigned)n < (*parentp)->nodc && cf_om_remove_empty_child(parentp, (unsigned) n)) { \
	    WHYF("%s() returned empty node at n=%u", funcname, (unsigned) n); \
	    ret = CFERROR; \
	  } \
	} \
	if (ret == CFERROR) \
	  return CFERROR; \
	else if (ret != CFOK) \
	  result |= (ret & CF__SUBFLAGS) | CFSUB(ret & CF__FLAGS);
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
      { \
	__FMT_NODE_START(__element) \
	    ret = cf_fmt_##__repr(&(*parentp)->nodv[(unsigned)n], &strct->__element); \
	    funcname = "cf_fmt_" #__repr; \
	__FMT_NODE_END \
      }
#define SUB_STRUCT(__structname, __element, __flags, __dfllabel...) \
      { \
	__FMT_NODE_START(__element) \
	    if (#__dfllabel[0]) { \
	      struct config_##__structname dfl; \
	      cf_dfl_config_##__structname##_cf_##__dfllabel(&dfl); \
	      ret = cf_xfmt_config_##__structname(&(*parentp)->nodv[(unsigned)n], &strct->__element, &dfl); \
	      funcname = "cf_xfmt_config_" #__structname; \
	    } else { \
	      ret = cf_fmt_config_##__structname(&(*parentp)->nodv[(unsigned)n], &strct->__element); \
	      funcname = "cf_fmt_config_" #__structname; \
	    } \
	__FMT_NODE_END \
      }
#define NODE_STRUCT(__structname, __element, __repr, __flags, __dfllabel...) \
      { \
	__FMT_NODE_START(__element) \
	    ret = cf_fmt_##__repr(&(*parentp)->nodv[(unsigned)n], &strct->__element); \
	    funcname = "cf_fmt_" #__repr; \
	__FMT_NODE_END \
      }
#define END_STRUCT \
      if ((*parentp)->nodc == 0) \
	cf_om_free_node(parentp); \
      return result; \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    __attribute__((__ATTRIBUTE_unused)) static int __cf_unused_5_##__substructname(struct cf_om_node **parentp, const struct config_##__substructname *strct, const struct config_##__substructname *dflt) { \
      int result = 0; \
      int ret;
#define END_STRUCT_ASSIGN \
      return 0; \
    }
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...) \
    int cf_xfmt_config_##__name(struct cf_om_node **parentp, const struct config_##__name *array, const struct config_##__name *UNUSED(dflt)) { \
      return cf_fmt_config_##__name(parentp, array); \
    } \
    int cf_fmt_config_##__name(struct cf_om_node **parentp, const struct config_##__name *array) { \
      int result = CFOK; \
      unsigned i; \
      for (i = 0; i < array->ac; ++i) {
#define __ARRAY_KEY(__keyfunc, __keyexpr) \
	const char *key = NULL; \
	int ret = __keyfunc(&key, __keyexpr); \
	int n = -1; \
	if (ret != CFOK) { \
	  WARNF(#__keyfunc "() returned %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(300), ret))); \
	} else if (key == NULL) { \
	  WHY(#__keyfunc "() returned CFOK but key=NULL"); \
	  ret = CFERROR; \
	} else { \
	  n = cf_om_add_child(parentp, key); \
	  if (n == -1) \
	    ret = CFERROR; \
	} \
	if (key) { \
	  free((char *)key); \
	  key = NULL; \
	} \
	if (ret == CFOK) {
#define __ARRAY_VALUE(__valuefunc) \
	  cf_om_remove_null_child(parentp, (unsigned)n); \
	  if (ret != CFOK) \
	    WARNF(#__valuefunc "() returned %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(300), ret))); \
	  if ((unsigned)n < (*parentp)->nodc && cf_om_remove_empty_child(parentp, (unsigned)n)) { \
	    WHYF(#__valuefunc "() returned empty node at n=%u", (unsigned)n); \
	    ret = CFERROR; \
	  }
#define __ARRAY_TEXT(__valuefunc, __eltexpr) \
	  ret = __valuefunc(&(*parentp)->nodv[(unsigned)n]->text, __eltexpr); \
	  __ARRAY_VALUE(__valuefunc)
#define END_ARRAY(__size) \
	} \
	if (ret == CFERROR) \
	  return CFERROR; \
	else if (ret != CFOK) \
	  result |= (ret & CF__SUBFLAGS) | CFSUB(ret & CF__FLAGS); \
      } \
      if ((*parentp)->nodc == 0) \
	cf_om_free_node(parentp); \
      return result; \
    }
#define KEY_ATOM(__type, __keyrepr) \
	__ARRAY_KEY(cf_fmt_##__keyrepr, &array->av[i].key);
#define KEY_STRING(__strsize, __keyrepr) \
	__ARRAY_KEY(cf_fmt_##__keyrepr, &array->av[i].key[0]);
#define VALUE_ATOM(__type, __eltrepr) \
	__ARRAY_TEXT(cf_fmt_##__eltrepr, &array->av[i].value)
#define VALUE_STRING(__strsize, __eltrepr) \
	__ARRAY_TEXT(cf_fmt_##__eltrepr, &array->av[i].value[0])
#define VALUE_NODE(__type, __eltrepr) \
	ret = cf_fmt_##__eltrepr(&(*parentp)->nodv[(unsigned)n], &array->av[i].value); \
	__ARRAY_VALUE(cf_fmt_##__eltrepr)
#define VALUE_SUB_STRUCT(__structname, __dfllabel...) \
	if (#__dfllabel[0]) { \
	  struct config_##__structname dfl; \
	  cf_dfl_config_##__structname##_cf_##__dfllabel(&dfl); \
	  ret = cf_xfmt_config_##__structname(&(*parentp)->nodv[(unsigned)n], &array->av[i].value, &dfl); \
	} else { \
	  ret = cf_fmt_config_##__structname(&(*parentp)->nodv[(unsigned)n], &array->av[i].value); \
	} \
	__ARRAY_VALUE(cf_fmt_config_##__structname)
#define VALUE_NODE_STRUCT(__structname, __eltrepr) \
	ret = cf_fmt_##__eltrepr(&(*parentp)->nodv[(unsigned)n], &array->av[i].value); \
	__ARRAY_VALUE(cf_fmt_##__eltrepr)
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef __FMT_TEXT
#undef __FMT_NODE_START
#undef __FMT_NODE_END
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef __ARRAY_KEY
#undef __ARRAY_TEXT
#undef __ARRAY_VALUE
#undef END_ARRAY

// Generate comparison functions, cf_cmp_config_SECTION()
#define VALIDATOR(__validator)
#define STRUCT(__name, __options...) \
    int cf_cmp_config_##__name(const struct config_##__name *a, const struct config_##__name *b) { \
      int c;
#define NODE(__type, __element, __default, __repr, __flags, __comment) \
      if ((c = cf_cmp_##__repr(&a->__element, &b->__element))) \
	  return c;
#define ATOM(__type, __element, __default, __repr, __flags, __comment) \
      if ((c = cf_cmp_##__repr(&a->__element, &b->__element))) \
	  return c;
#define STRING(__size, __element, __default, __repr, __flags, __comment) \
      if ((c = cf_cmp_##__repr(&a->__element[0], &b->__element[0]))) \
	  return c;
#define SUB_STRUCT(__structname, __element, __flags, __dfllabel...) \
      if ((c = cf_cmp_config_##__structname(&a->__element, &b->__element))) \
	  return c;
#define NODE_STRUCT(__structname, __element, __repr, __flags, __dfllabel...) \
      if ((c = cf_cmp_##__repr(&a->__element, &b->__element))) \
	  return c;
#define END_STRUCT \
      return 0; \
    }
#define STRUCT_ASSIGN(__substructname, __structname) \
    __attribute__((__ATTRIBUTE_unused)) static int __cf__unused_6_##__substructname(const struct config_##__substructname *a, const struct config_##__substructname *b) { \
      int c;
#define END_STRUCT_ASSIGN \
      return 0; \
    }
#define STRUCT_DEFAULT(__name, __dfllabel)
#define ATOM_DEFAULT(__element, __default)
#define STRING_DEFAULT(__element, __default)
#define SUB_STRUCT_DEFAULT(__name, __element, __dfllabel...)
#define END_STRUCT_DEFAULT
#define ARRAY(__name, __flags, __options...) \
    int cf_cmp_config_##__name(const struct config_##__name *a, const struct config_##__name *b) { \
      int c; \
      unsigned i; \
      for (i = 0; i < a->ac && i < b->ac; ++i) {
#define KEY_ATOM(__type, __keyrepr) \
      if ((c = cf_cmp_##__keyrepr(&a->av[i].key, &b->av[i].key))) \
	  return c;
#define KEY_STRING(__strsize, __keyrepr) \
      if ((c = cf_cmp_##__keyrepr(&a->av[i].key[0], &b->av[i].key[0]))) \
	  return c;
#define VALUE_ATOM(__type, __eltrepr) \
      if ((c = cf_cmp_##__eltrepr(&a->av[i].value, &b->av[i].value))) \
	  return c;
#define VALUE_STRING(__strsize, __eltrepr) \
      if ((c = cf_cmp_##__eltrepr(&a->av[i].value[0], &b->av[i].value[0]))) \
	  return c;
#define VALUE_NODE(__type, __eltrepr) \
      if ((c = cf_cmp_##__eltrepr(&a->av[i].value, &b->av[i].value))) \
	  return c;
#define VALUE_SUB_STRUCT(__structname, __dfllabel...) \
      if ((c = cf_cmp_config_##__structname(&a->av[i].value, &b->av[i].value))) \
	  return c;
#define VALUE_NODE_STRUCT(__structname, __eltrepr) \
      if ((c = cf_cmp_##__eltrepr(&a->av[i].value, &b->av[i].value))) \
	  return c;
#define END_ARRAY(__size) \
      } \
      return (a->ac < b->ac) ? -1 : (a->ac > b->ac) ? 1 : 0; \
    }
#include "conf_schema.h"
#undef VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef STRUCT_ASSIGN
#undef END_STRUCT_ASSIGN
#undef STRUCT_DEFAULT
#undef ATOM_DEFAULT
#undef STRING_DEFAULT
#undef SUB_STRUCT_DEFAULT
#undef END_STRUCT_DEFAULT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY