/*
 * Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: sm-conf-type-section.c,v 1.23 2006/04/13 16:38:50 ca Exp $")


#if SM_LIBCONF_ALONE
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "sm-conf.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/string.h"
#include "sm/ctype.h"
#include "sm/memops.h"
#include "sm/sm-conf.h"
#endif /* SM_LIBCONF_ALONE */

#include "sm-conf-util.h"
#include "sm-conf-node.h"
#include "sm-conf-scan.h"
#include "sm-conf-state.h"
#include "sm-conf-type.h"


/*
**  DEFINITION_FOR_SECTION -- (Utility) return the best definition
**	for <kw>[ {<name>} ]
**
**	We're looking at a section with a keyword and a title.
**	There may be different definitions for sections
**	with identical keywords but different titles.
**
**	(That is, "server [smtp]" may be a completely different
**	data type from "server [imap]".)
**
**	An exact, more specific match overrides a generic,
**	keyword-only match.
**
**	Parameters:
**		smc -- configuration parser state
**		def -- definition for this section
**		kw -- identifier we're lookign for
**		kw_n -- # of bytes pointed to by <kw>, need
**			not be '\0'-terminated
**		name -- title after the identifier, or NULL
**		name_n -- # of bytes pointed to by <name>, need
**			not be '\0'-terminated.
**
**	Returns:
**		NULL if no matching definition is found,
**		otherwise a pointer to the applicable definition.
*/

static sm_conf_definition_T const *
definition_for_section(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	char const			*kw,
	size_t				kw_n,
	char const			*name,
	size_t				name_n)
{
	char const			*d, *p;
	sm_conf_definition_T const	*generic;
	char const			*kw_e;
	char				cl;
	static char const		obraces[] = "<({[\"'`",
					cbraces[] = ">)}]\"''";

	if (def == NULL)
		return NULL;
	SM_IS_CONF_DEF(def);
	SM_REQUIRE(smc != NULL);

	generic = NULL;
	kw_e = (kw == NULL ? NULL : kw   + kw_n);

	while (def->scd_name != NULL)
	{
		p = kw;
		d = def->scd_name;

		while (isascii(*d) && (isalnum(*d) || *d == '_'))
		{
			if (  p >= kw_e
			   || !isascii(*p)
			   || tolower(*d) != tolower(*p))
				goto next;
			d++;
			p++;
		}
		if (p != kw_e)
			goto next;

		if (name == NULL)
		{
			if (*d != '\0')
				goto next;
			return def;
		}

		if (*d == '\0')
		{
			generic = def;
			goto next;
		}

		cl = 0;
		if ((p = strchr(obraces, *d)) != NULL)
		{
			d++;
			cl = cbraces[p - obraces];
		}
		p = d;
		/* ignore closing bracket */
		d += strlen(d);
		if (d[-1] == cl)
			d--;

		if (sm_conf_token_match(smc, p, d - p, name, name_n))
			return def;

	next:	def++;
	}

	return generic;
}


/*
**  SECTION_ELEMENT_ERROR -- (Utility) print an error
**
**	If name is non-NULL, the error is preceded not just
**	by the ndoe location but also by the involved element name.
**
**	Parameters:
**		handle -- the configuration parser state
**		node -- NULL or node that the error occured with
**		name -- name of the section element, or NULL
**		name_n -- if name is non-NULL, # of bytes pointed to by
**			<name> (doesn't have to be '\0'-terminated)
**		errstring -- error message to log
**
**	Returns:
**		None
**
**	Side effect:
**		Logs an error to the per-configuration-parser error
**		state.
*/

static void
section_element_error(
	sm_conf_T		*handle,
	sm_conf_node_T const	*node,
	char const		*name,
	size_t			name_n,
	char const		*errstring)
{
	char			loc[SM_CONF_ERROR_BUFFER_SIZE];

	SM_REQUIRE(handle != NULL);
	if (name_n > 0 && name != NULL)
		sm_conf_error_add(handle, "%s: %*s: %s",
			sm_conf_node_location(handle, node, loc, sizeof loc),
			(int)name_n, name, errstring);
	else
		sm_conf_error_add(handle, "%s: %s",
			sm_conf_node_location(handle, node, loc, sizeof loc),
			errstring);
}


/*
**  SM_CONF_SECTION_ANONYMOUS_DEFAULT -- (Utility) find a node to
**	default from (1)
**
**	Look in this node's ancestral chain for sections of the
**	specified type (keyword) with no name directly contained in
**      an ancestor.
**
**	Return the lowest section that is <node> or above it.
**
**	Parameters:
**		handle -- the context
**		kw -- type (keyword) we're looking for
**		kw_n -- length kw.
**		node -- where to start looking
**
**	Returns:
**		The lowest section that is <node> or above it.
*/

sm_conf_node_T *
sm_conf_section_anonymous_default(
	sm_conf_T		*handle,
	char const		*kw,
	size_t			kw_n,
	sm_conf_node_T const	*node)
{
	char const		*snm;
	size_t			snm_n;
	sm_conf_node_T		*sib;

	if (node == NULL)
		return NULL;

	while (  (node = sm_conf_node_parent(handle, node)) != NULL
	      && sm_conf_node_type(handle, node) == SM_CONF_NODE_SECTION)
	{
		sib = NULL;
		while ( (sib = sm_conf_section_next_subsection(handle,
			node, kw, kw_n, "", 0, sib)) != NULL)
		{
			if (sm_conf_section_name(handle, sib, &snm, &snm_n) == 0
			  && snm == NULL)
				return sib;
		}
	}
	return NULL;
}

/*
**  SM_CONF_SECTION_ENVIRONMENT_DEFAULT -- (Utility) find a node
**	to default from (2)
**
**	Look in this node's ancestral chain for a containing
**	section that contains the specified property.
**	Return the lowest section that has it.
**
**	Parameters:
**		handle -- the context
**		name -- property we're looking for
**		name_n -- length of <name>.
**		node -- where to start looking
**
**	Returns:
**		Lowest container with a suitable default value,
**		or NULL if none was found.
*/

static sm_conf_node_T const *
sm_conf_section_environment_default(
	sm_conf_T		*handle,
	char const		*name,
	size_t			name_n,
	sm_conf_node_T const	*node)
{
	while (  (node = sm_conf_node_parent(handle, node)) != NULL
	      && sm_conf_node_type(handle, node) == SM_CONF_NODE_SECTION)
	{
		if (sm_conf_section_next_option(handle, node, name,
						name_n,  NULL) != NULL)
			return node;
	}
	return NULL;
}

/*
**  SM_CONF_SCAN_SECTION_DEFAULT_ELEMENT_NODE -- (Utility) scan default values
**
**	We're trying to default one element of a section.
**	(We've got its definition.)  We've found another section
**	that could serve as a template for the one we're parsing
**	right now.
**
**	Scout the template section for values to assing to the
**	empty element.  If you find one, use it.  (If the element
**	is multivalued, keep on scanning after finding one; there
**	may be others.)
**
**	Parameters:
**		handle -- the configuration parser context
**		def -- section definition for the element we're
**			trying to assign.
**		node -- the container section that we're pulling
**			defaults out of
**		data -- user data (begin of struct)
**
**	Returns:
**		If no elements could be defaulted form the surrounding
**		section, the call returns SM_CONF_ERR_NOT_FOUND.
**		Otherwise, it returns 0 on success,
**		potentially other nonzero error codes on error.
*/

static int
sm_conf_scan_section_default_element_node(
	sm_conf_T			*handle,
	sm_conf_definition_T const	*def,
	sm_conf_node_T const		*node,
	void				*data)
{
	size_t				def_name_n;
	int				any = 0;
	int				err = 0;
	sm_conf_node_T			*elem;
	sm_conf_node_T			*pred;

	SM_IS_CONF_DEF(def);
	def_name_n = strlen(def->scd_name);

	SM_LC_ISA(def, data);
	pred = NULL;
	for (;;)
	{
		elem = sm_conf_section_next_option(handle,
			node, def->scd_name, def_name_n, pred);
		if (elem == NULL)
			break;
		pred = elem;

		any = 1;
		err = sm_conf_scan_node_to_value(handle,
			def->scd_name, strlen(def->scd_name),
			def, elem,
			data == NULL ? NULL : (char *)data + def->scd_offset);
		if (err != 0)
			return err;

		if (!(def->scd_flags & SM_CONF_FLAG_MULTIPLE))
			break;
	}
	if (!any)
		return SM_CONF_ERR_NOT_FOUND;

	if (data == NULL)
		return 0;
#if SM_LIBCONF_ISSET
	if (def->scd_isset_offset > 0)
		*((uchar *)data + def->scd_isset_offset) = SM_LC_SET_FRM_ENV;
#endif

	return sm_conf_scan_value_check(handle, def->scd_name,
		strlen(def->scd_name), def, node, data);
}

/*
**  SM_CONF_SCAN_SECTION_DEFAULT_ELEMENT -- (Utility) default an element.
**
**	Find sections we might be defaulting from in the environment
**	and feed them to sm_conf_scan_section_default_element_node()
**	for closer examination, until we find something or run out.
**
**	Parameters:
**		handle -- the configuration parser context
**		def -- section definition for the element we're
**			trying to assign.
**		section -- the section we're defaulting in.
**		default_node -- corresponding anonymous default node
**			in the same parent, if any.
**		data -- user data (begin of struct), or NULL
**
**	Returns:
**		If no elements could be defaulted form the surrounding
**		section, the call returns SM_CONF_ERR_NOT_FOUND.
**		Otherwise, it returns 0 on success,
**		potentially other nonzero error codes on error.
*/

static int
sm_conf_scan_section_default_element(
	sm_conf_T			*handle,
	sm_conf_definition_T const	*def,
	sm_conf_node_T const		*section,
	sm_conf_node_T const		*default_node,
	void				*data)
{
	size_t				def_name_n;
	int				err;

	SM_IS_CONF_DEF(def);
	err = 0;
	def_name_n = strlen(def->scd_name);

	/*
	**  Try defaulting the element using our corresponding
	**  anonymous default_node, if any.
	*/

	if (default_node != NULL)
	{
		err = sm_conf_scan_section_default_element_node(handle, def,
							default_node, data);
		if (err != SM_CONF_ERR_NOT_FOUND)
			return err;
	}

	/*
	**  If that didn't find anything, and we've got a named property,
	**  and this particular property drifts down from the environment,
	**  look for a surrounding section that contains it, and use
	**  those section's settings.
	*/

	if ( !(def->scd_flags & SM_CONF_FLAG_DEFAULT_FROM_ENVIRONMENT)
	   || (default_node = sm_conf_section_environment_default(handle,
					def->scd_name, strlen(def->scd_name),
					section)) == NULL)
		return SM_CONF_ERR_NOT_FOUND;

	return sm_conf_scan_section_default_element_node(handle, def,
							default_node, data);
}

/*
**  SM_CONF_TYPE_SECTION_TITLE_NODE_TO_VALUE -- (Method) store a title
**
**	If specified in a section's definitions, convert the name
**	of a section to a data string and store it.
**
**	This is how you access the "bar" in
**
**		foo "bar"
**		{
**			...
**		}
**
**	Parameters:
**		smc -- the configuration parser context
**		def -- definition for the section title
**		section -- the section whose title is being scanned
**		data -- user data (element itself), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_title_node_to_value(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	sm_conf_node_T			*section,
	void				*data)
{
	int				err;
	char const			*name;
	size_t				name_n;
	char				loc[SM_CONF_ERROR_BUFFER_SIZE];

	SM_IS_CONF_DEF(def);
	err = sm_conf_section_name(smc, section, &name, &name_n);
	if (err != 0)
		return err;

	if (name_n > 0 && memchr(name, '\0', name_n) != NULL)
	{
		sm_conf_error_add(smc, "%s: NUL in section title '%.*s'",
			sm_conf_node_location(smc, section, loc, sizeof loc),
			(int)name_n, name);
		return SM_CONF_ERR_TYPE;
	}

	if (data == NULL)
		return 0;
	SM_LC_ISA(def, data);
	if (def->scd_size == 0)
		*(char const **)data = name;
	else
	{
		if (name_n >= def->scd_size)
		{
			sm_conf_error_add(smc, "%s: overflow error: section "
				"title '%.*s' more than %lu character%s long",
				sm_conf_node_location(smc, section,
					loc, sizeof loc),
				(int)name_n, name,
				(unsigned long)def->scd_size - 1,
				def->scd_size == 2 ? "" : "s");
			return SM_CONF_ERR_TYPE;
		}
		if (name_n != 0)
			memcpy(data, name, name_n);
		((char *)data)[name_n] = '\0';
	}
	return 0;
}


/*
**  SM_CONF_TYPE_SECTION_TITLE_VALUE_CHECK -- (Method) check a value
**
**	(Not yet implemented/No-OP)
**
**	Parameters:
**		smc -- the configuration parser context
**		def -- definition for the section title
**		data -- user data, or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_title_value_check(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void const			*data)
{
	SM_IS_CONF_DEF(def);
	return 0;
}


/*
**  SM_CONF_TYPE_SECTION_TITLE_VALUE_NULL -- (Method) zero out a value
**
**	Parameters:
**		smc -- the configuration parser context
**		def -- definition for the section title
**		data -- user data (element itself), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_title_value_null(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void				*data)
{
	SM_IS_CONF_DEF(def);
	if (def->scd_size > 0)
		sm_memset(data, 0, def->scd_size);
	else
		*(char const **)data = NULL;
	return 0;
}

sm_conf_type_T const
sm_conf_type_section_title_data =
{
	sm_conf_type_section_title_node_to_value,
	sm_conf_type_section_title_value_check,
	sm_conf_type_section_title_value_null
};


/*
**  SM_CONF_TYPE_SECTION_NODE_TO_VALUE_TITLE -- (Utiltiy) convert section title
**
**	This utility not only triggers conversion and storage
**	of the title itself, it also checks for title-related
**	constraint violation (titles of sections that mustn't
**	have one, or the opposite.)
**
**	Parameters:
**		smc -- the configuration parser context
**		defs -- array of definition for the section members
**		flags -- section flags
**		section -- section node
**		data -- user data (begin of struct), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_node_to_value_title(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*defs,
	unsigned int			flags,
	sm_conf_node_T			*section,
	void				*data)
{
	int				err;
	char const			*name;
	size_t				name_n;
	sm_conf_definition_T const	*sub;


	/* Scan the section name, if there is one. */
	err = sm_conf_section_name(smc, section, &name, &name_n);
	if (err != 0)
		return err;

	/* check for constraints on the whole section. */
	if (  (flags & SM_CONF_FLAG_SECTION_MUST_BE_NAMED)
	   && name == NULL)
	{
		if (sm_conf_section_keyword(smc, section, &name, &name_n))
		{
			name = NULL;
			name_n = 0;
		}
		section_element_error(smc, section,
			name, name_n, "section must have a name");
		return SM_CONF_ERR_TYPE;
	}
	else if (  (flags & SM_CONF_FLAG_SECTION_MUST_BE_ANONYMOUS)
		&& name != NULL)
	{
		section_element_error(smc, section, name, name_n,
			"section cannot have a name");
		return SM_CONF_ERR_TYPE;
	}

	sub = sm_conf_subdef(defs, sm_conf_type_section_title, NULL, 0);
	if (sub == NULL)
		return 0;

	return sm_conf_type_section_title_node_to_value(smc, sub, section,
		data == NULL ? NULL : (char *)data + sub->scd_offset);
}


/*
**  SM_CONF_TYPE_SECTION_NODE_TO_VALUE_SUBDEF --
**	(Utiltiy) convert section members
**
**	Parameters:
**		smc -- the configuration parser context
**		defs -- array of definition for the section members
**		flags -- section flags
**		section -- whole section node
**		data -- user data (begin of struct), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

int
sm_conf_type_section_node_to_value_subdef(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*defs,
	unsigned int			flags,
	sm_conf_node_T			*section,
	void				*data)
{
	sm_conf_definition_T const	*def;
	sm_conf_node_T			*default_node;
	sm_conf_node_T			*sub;
	char const			*name;
	size_t				name_n;
	int				err, section_error;
	char				seen_buf[256], *seen_ptr;
	size_t				seen_m;

	if (smc == NULL)
		return SM_CONF_ERR_INVALID;
	err = 0;

	memset(seen_buf, 0, sizeof seen_buf);
	seen_ptr = seen_buf;
	seen_m = sizeof seen_buf;

	if (  (flags & SM_CONF_FLAG_SECTION_MUST_BE_NAMED)
	   && (flags & SM_CONF_FLAG_SECTION_MUST_BE_ANONYMOUS))
	{
		section_element_error(smc, section, NULL, 0, "definition "
			"requires that section be both named and anonymous?");
		return SM_CONF_ERR_INVALID;
	}

	/*
	**  If we default from anonymous sections, and this section
	**  isn't anonymous, find one we can default from.
	*/

	if (  (flags & SM_CONF_FLAG_SECTION_DEFAULT_FROM_ANONYMOUS)
	   && section != NULL
	   && sm_conf_section_name(smc, section, &name, &name_n) == 0
	   && name != NULL
	   && sm_conf_section_keyword(smc, section, &name, &name_n) == 0)
		default_node = sm_conf_section_anonymous_default(smc,
							name, name_n, section);
	else
		default_node = NULL;

	/* Assign NULL values to anything that doesn't keep its default */
	if (defs != NULL && data != NULL)
	{
		for (def = defs; def != NULL && def->scd_name != NULL; def++)
		{
			if (!( (flags | def->scd_flags)
			     & ( SM_CONF_FLAG_KEEP_DEFAULT
			       | SM_CONF_FLAG_PARSE_ONLY)))
			{
				SM_LC_ISA(def, data);
				err = (* def->scd_type->sctp_value_null)(
					smc, def,
					(char *)data + def->scd_offset);
				if (err != 0)
					return err;
			}
		}
	}

	section_error = 0;
	if (section != NULL)
	{
		err = sm_conf_type_section_node_to_value_title(
			smc, defs, flags, section, data);
		if (err != 0)
		{
			section_error = err;
			err = 0;
		}

		/* Scan all section elements. */
		sub = NULL;
		while ((sub = sm_conf_section_next(smc, section,
						&name, &name_n, sub)) != NULL)
		{
			char const	*sub_kw, *sub_name;
			size_t		sub_kw_n, sub_name_n;

			if (name_n == 0)
				name = NULL;

			def = NULL;
			if (name != NULL)
				def = sm_conf_subdef(defs, NULL, name, name_n);
			else
			{
				if (  sm_conf_section_keyword(smc, sub,
						&sub_kw, &sub_kw_n) == 0
				   && sm_conf_section_name(smc, sub,
						&sub_name, &sub_name_n) == 0)
				{
					def = definition_for_section(smc, defs,
						sub_kw, sub_kw_n,
						sub_name, sub_name_n);
				}
			}

			/* def is now NULL or the applicable definition. */
			if (def == NULL)
			{
				/* Undefined name.  Do we allow that? */
				if (!(flags & ( name == NULL
					      ? SM_CONF_FLAG_ALLOW_ANY_SECTION
					      : SM_CONF_FLAG_ALLOW_ANY_OPTION)))
				{
					if (name == NULL)
						section_element_error(smc, sub,
							sub_kw, sub_kw_n,
							"unexpected section");
					else
						section_element_error(smc, sub,
							name, name_n,
							"unexpected option");
					if (section_error == 0)
					   section_error = SM_CONF_ERR_TYPE;
				}

				/*
				**  We have nowhere to store this value now,
				**  but other parts of the using application
				**  may ask for it later by name, e.g.
				**  based on user input.
				*/

				continue;
			}

			if (!(def->scd_flags & SM_CONF_FLAG_MULTIPLE))
			{
				size_t		bit_i;
				unsigned char	bit_mask;

				bit_i = (def - defs) / CHAR_BIT;
				bit_mask = 1 << ((def - defs) % CHAR_BIT);

				if (bit_i >= seen_m)
				{
					sm_conf_definition_T const *hi;

					SM_ASSERT(seen_ptr == seen_buf);

					for (hi = def;
					     hi->scd_name != NULL;
					     hi++)
						;
					seen_m = ((hi - defs) + (CHAR_BIT - 1))
						 / CHAR_BIT;
					seen_ptr = malloc(seen_m);
					if (seen_ptr == NULL)
						return SM_CONF_ERR_NO_MEMORY;
					memset(seen_ptr, 0, seen_m);
					memcpy(seen_ptr, seen_buf,
						sizeof seen_buf);
				}
				if (seen_ptr[bit_i] & bit_mask)
				{
					section_element_error(smc, section,
						def->scd_name,
						strlen(def->scd_name),
						name == NULL
						? "duplicate section"
						: "duplicate option");
					section_error = SM_CONF_ERR_ALREADY;
				}
				seen_ptr[bit_i] |= bit_mask;
			}

			if ((def->scd_flags | flags) & SM_CONF_FLAG_PARSE_ONLY)
			{
				err = sm_conf_scan_node_to_value(smc, name,
					name_n, def, sub, NULL);
			}
			else
			{
				SM_LC_ISA(def, data);

				/*
				**  Inherit the default value for a multiple
				**  choice value now so it can be changed
				**  "locally".
				*/

				if (sm_conf_type_choice == def->scd_type &&
				    data != NULL &&
				    (def->scd_flags & SM_CONF_FLAG_OR) != 0 &&
				    (def->scd_flags &
					SM_CONF_FLAG_STRICTLY_REQUIRED) == 0)
				{
					err = sm_conf_scan_section_default_element(
						smc, def, section, default_node,
						data);
				}

				err = sm_conf_scan_node_to_value(smc, name,
					name_n, def, sub,
					data == NULL ? NULL :
						(char *)data + def->scd_offset);
#if SM_LIBCONF_ISSET
				/*
				**  Use different values depending on how data
				**  got set??
				*/

				if (0 == err && data != NULL &&
				    def->scd_isset_offset > 0)
				{
					*((uchar *)data + def->scd_isset_offset)
						= SM_LC_SET_DIRECT;
				}
#endif
			}
			if (err != 0)
				section_error = err;
			/* If there was an error, find more. */
		}
	}

	if (section_error != 0)
	{
		if (seen_ptr != seen_buf)
			free(seen_ptr);
		return section_error;
	}

	/*
	**  Confirm that all mandatory elements of the definition did occur.
	**  If we can default elements, default those that didn't occur.
	*/

	for (def = defs; def != NULL && def->scd_name != NULL; def++)
	{
		sm_conf_node_T const	*elem;

		elem = NULL;

		if (  section != NULL
		   && def->scd_type == sm_conf_type_section_title)
		{
			if (  (def->scd_flags & ( SM_CONF_FLAG_STRICTLY_REQUIRED
						| SM_CONF_FLAG_REQUIRED))
			   && sm_conf_section_name(smc, section, &name, &name_n)
				== 0
			   && name == NULL)
			{
				if (sm_conf_section_keyword(smc, section,
					&name, &name_n))
				{
					name = NULL;
					name_n = 0;
				}
				section_element_error(smc, section,
					name, name_n,
					"section must have a name");
				return SM_CONF_ERR_TYPE;
			}

			err = sm_conf_scan_value_check(smc,
				"section title", sizeof("section title") - 1,
				def, section,
				data == NULL ? NULL
					: (char *)data + def->scd_offset);
			if (err != 0)
				section_error = err;

			/* The section name is never defaulted. */
			continue;
		}

		if (section != NULL)
			elem = sm_conf_section_next_option(
				smc, section, def->scd_name,
				strlen(def->scd_name), NULL);

		if (elem == NULL)
		{
			/*
			**  The real node didn't match this definition.
			**  See if we default from something.
			*/

			err = SM_CONF_ERR_NOT_FOUND;
			if (!(def->scd_flags & SM_CONF_FLAG_STRICTLY_REQUIRED))
			{
				err = sm_conf_scan_section_default_element(
					smc, def, section, default_node,
					data);
				if (err != 0)
				{
					if (  err == SM_CONF_ERR_NOT_FOUND
					   && !( def->scd_flags
					       & SM_CONF_FLAG_REQUIRED))

						/* it was optional. */
						err = 0;
				}

			}

			if (err != 0)
			{
				section_element_error(smc, section,
					def->scd_name, strlen(def->scd_name),
					def->scd_name[0] == '\0'
					? "missing value"
					: "missing property");
				section_error = err;
			}
		}


		/*
		**  Now that it's been fully converted, check the constraint
		**  for this element.
		*/

		if (section_error == 0 && data != NULL)
		{
			err = sm_conf_scan_value_check(smc,
				def->scd_name, strlen(def->scd_name),
				def, elem,
				(char *)data + def->scd_offset);
			if (err != 0)
				section_error = err;
		}
	}
	if (seen_ptr != seen_buf)
		free(seen_ptr);
	return section_error;
}


/*
**  SM_CONF_TYPE_SECTION_NODE_TO_VALUE -- (Method) convert section
**
**	Parameters:
**		smc -- the configuration parser context
**		section_def -- definition for the section
**		section -- whole section node
**		data -- user data (begin of struct), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_node_to_value(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*section_def,
	sm_conf_node_T			*section,
	void				*data)
{
	return sm_conf_type_section_node_to_value_subdef(
		smc, section_def->scd_contents,
		section_def->scd_flags, section, data);
}


/*
**  SM_CONF_TYPE_SECTION_VALUE_CHECK -- (Method) check value against
**	section constraints
**
**	Parameters:
**		smc -- the configuration parser context
**		def -- definition for the section
**		data -- user data (begin of struct), or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_value_check(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void const			*data)
{
	sm_conf_definition_T const	*sub;
	int				err;

	SM_IS_CONF_DEF(def);

	/* If we have nothing to check, don't do anything. */
	if (data == NULL)
		return 0;

	/* recursively check the section's contents. */
	for (sub = def->scd_contents;
	     sub != NULL && sub->scd_name != NULL;
	     sub++)
	{
		err = (* sub->scd_type->sctp_value_check)(smc, sub,
			(char *)data + sub->scd_offset);
		if (err != 0)
			return err;
	}

	/* if we have a check function, use it. */
	if (def->scd_check != NULL)
		return (* def->scd_check)(smc, def->scd_check_data, def, data);
	return 0;
}


/*
**  SM_CONF_TYPE_SECTION_VALUE_NULL -- (Method) zero out a data buffer
**
**	Parameters:
**		smc -- the configuration parser context
**		def -- definition for the section
**		data -- user data, or NULL
**
**	Returns:
**		0 on success, a nonzero error on error.
*/

static int
sm_conf_type_section_value_null(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void				*data)
{
	sm_conf_definition_T const	*d;
	int				err;

	SM_IS_CONF_DEF(def);
	if (data == NULL)
		return 0;

	/*
	**  This zero's out the user struct; is this really necessary??
	**  It destroy any magic that's in there! It is only prevented if
	**  SM_CONF_FLAG_KEEP_DEFAULT (or SM_CONF_FLAG_PARSE_ONLY) is set!
	*/

	if (def->scd_size > 0)
		sm_memset(data, 0, def->scd_size);

	for (d = def->scd_contents; d != NULL && d->scd_name != NULL; d++)
	{
		SM_LC_ISA(d, data);
		err = (* d->scd_type->sctp_value_null)(
			smc, d, (char *)data + d->scd_offset);
		if (err != 0)
			return err;
	}
	return 0;
}


sm_conf_type_T const
sm_conf_type_section_data =
{
	sm_conf_type_section_node_to_value,
	sm_conf_type_section_value_check,
	sm_conf_type_section_value_null
};


syntax highlighted by Code2HTML, v. 0.9.1