/*
 * 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-scan.c,v 1.13 2006/04/13 19:00:15 ca Exp $")

#if SM_LIBCONF_ALONE
#include <string.h>

#include "sm-conf.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/string.h"
#include "sm/memops.h"
#include "sm/sm-conf.h"
#endif /* SM_LIBCONF_ALONE */
#include "sm-conf-scan.h"
#include "sm-conf-state.h"
#include "sm-conf-type.h"

/*
**  SM_CONF_SCAN_VALUE_CHECK -- an error with a name, if one is there.
**
**	Parameters:
**		handle -- the overall handle of the configuration file
**		node -- current node
**		name -- name of option
**		name_n -- # of bytes pointed to by <name>
**		errstring -- error string
**
**	Returns:
**		none
*/

static void
named_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];

	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_SCAN_VALUE_CHECK -- check validity of value (via callback)
**
**	Parameters:
**		handle -- the overall handle of the configuration file
**		name -- name of option
**		name_n -- # of bytes pointed to by <name>
**		def -- definition
**		node -- the node we're checking
**		data -- user data (element itself), or NULL
**
**	Returns:
**		0 on success, an error value on error.
*/

int
sm_conf_scan_value_check(
	sm_conf_T			*handle,
	char const			*name,
	size_t				name_n,
	sm_conf_definition_T const	*def,
	sm_conf_node_T const		*node,
	void				*data)
{
	size_t				n_err_before;
	int				err;

	SM_IS_CONF_DEF(def);

	/* Don't check if there's nothing to check. */
	if (data == NULL)
		return 0;

	n_err_before = handle->smc_error_n;

	err = (* def->scd_type->sctp_value_check)(handle, def, data);

	/* there was an error, but it wasn't logged? */
	if (err && n_err_before == handle->smc_error_n)
	{
		char errbuf[SM_CONF_ERROR_BUFFER_SIZE];

		/* log it ourselves. */
		named_error(handle, node, name, name_n,
			sm_conf_strerror(err, errbuf, sizeof errbuf));
	}
	return err;
}

/*
**  SM_CONF_SCAN_NODE_TO_VALUE -- convert content of node into a value
**
**	Parameters:
**		handle -- the overall handle of the configuration file
**		name -- name of option
**		name_n -- # of bytes pointed to by <name>, need
**			not be '\0'-terminated.
**		def -- definition
**		node -- the node we're scanning
**		data -- user data (element itself), or NULL
**
**	Returns:
**		0 on success, an error value on error.
*/

int
sm_conf_scan_node_to_value(
	sm_conf_T			*handle,
	char const			*name,
	size_t				name_n,
	sm_conf_definition_T const	*def,
	sm_conf_node_T			*node,
	void				*data)
{
	size_t				n_err_before;
	int				err;

	SM_IS_CONF_DEF(def);
	n_err_before = handle->smc_error_n;

	err = (* def->scd_type->sctp_node_to_value)(handle, def, node, data);

	/* there was an error, but it wasn't logged? */
	if (err && n_err_before == handle->smc_error_n)
	{
		char errbuf[SM_CONF_ERROR_BUFFER_SIZE];

		/* log it ourselves. */
		named_error(handle, node, name, name_n,
			sm_conf_strerror(err, errbuf, sizeof errbuf));
	}
	return err;
}

/*
**  SM_CONF_SCAN_NODE -- fill out a structure according to definitions
**
**	Use the data and callbacks in <def> to translate
**	the parse tree in <handle> into values assigned to the
**	struct(s) pointed to by <data>.
**
**	Parameters:
**		handle -- the overall handle of the configuration file
**		definitions -- list of names and their meanings.
**		node -- the node we're scanning
**		data -- base pointer of a structure  into which
**			values are copied.
**
**	Returns:
**		0 on success, an error value on error.
*/

int
sm_conf_scan_node(
	sm_conf_T			*handle,
	sm_conf_definition_T const	*def,
	sm_conf_node_T			*node,
	void				*data)
{
	int				err;

	if (handle == NULL || def == NULL)
		return SM_CONF_ERR_INVALID;

	if (def->scd_name == NULL)
	{
		char loc[SM_CONF_ERROR_BUFFER_SIZE];

		sm_conf_error_add(handle, "%s: unexpected value "
			"(NULL definition list)",
			sm_conf_node_location(handle, node, loc, sizeof loc));
		return SM_CONF_ERR_INVALID;
	}
	SM_IS_CONF_DEF(def);

	err = sm_conf_scan_node_to_value(handle,
		def->scd_name, strlen(def->scd_name), def, node, data);
	if (err != 0)
		return err;

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

/*
**  SM_CONF_SCAN -- fill out a structure according to definitions
**
**	Use the data and callbacks in <definitions> to translate
**	the parse tree in <handle> into values assigned to the
**	struct(s) pointed to by <data>.
**
**	Parameters:
**		handle -- the overall handle of the configuration file
**		definitions -- list of names and their meanings.
**			If it starts with a node whose name is "",
**			it's a definition for the whole node;
**			otherwise, the node must be a section, and
**			the definitions are for the section contents.
**		flags -- parameters of the node as a whole
**		data -- base pointer of a structure  into which
**			values are copied.
*/

int
sm_conf_scan(
	sm_conf_T			*handle,
	sm_conf_definition_T const	*definitions,
	unsigned int			flags,
	void				*data)
{
	sm_conf_definition_T		section_def[2];

	if (handle == NULL)
		return SM_CONF_ERR_INVALID;

	if (definitions == NULL || definitions->scd_name == NULL)
		return SM_CONF_ERR_INVALID;

	if (definitions->scd_name[0] != '\0')
	{
		sm_memset(&section_def, 0, sizeof section_def);
		section_def[0].sm_magic = SM_CONF_DEF_MAGIC;
		section_def[0].scd_name = "";
		section_def[0].scd_type = sm_conf_type_section;
		section_def[0].scd_default = NULL;
		section_def[0].scd_flags = flags;
		section_def[0].scd_contents = definitions;
		section_def[1].scd_name = NULL;

		definitions = section_def;
	}

	return sm_conf_scan_node(handle, definitions, handle->smc_root, data);
}


syntax highlighted by Code2HTML, v. 0.9.1