/*
 * Copyright (c) 2004, 2005 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-u32.c,v 1.19 2005/05/24 17:57:28 ca Exp $")

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

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

/* flag */
sm_conf_type_T const
sm_conf_type_u32_suffix_data,
sm_conf_type_u32_maximum_data,
sm_conf_type_u32_minimum_data;

/* return the first definition of type <type> */
static sm_conf_definition_T const *
first_typed(sm_conf_definition_T const *def, sm_conf_type_T const *type)
{
	if (def != NULL)
	{
		SM_IS_CONF_DEF(def);
		while (def->scd_name != NULL)
		{
			if (def->scd_type == type)
				return def;
			def++;
		}
	}
	return NULL;
}

static int
u32_write(
	sm_conf_T			*smc,
	sm_conf_node_T const		*node,
	sm_conf_definition_T const	*def,
	void				*data,
	unsigned long			val)
{
	char				loc[SM_CONF_ERROR_BUFFER_SIZE];
	unsigned long			limit_min;
	unsigned long			limit_max;
	sm_conf_definition_T const	*limit_def;

	SM_IS_CONF_DEF(def);
	limit_min = 0;
	limit_max = 0;

	if (def->scd_size == 1)
	{
		*(unsigned char *)data = val;
		limit_max = UCHAR_MAX;
	}
	else if (def->scd_size == sizeof(unsigned short))
	{
		*(unsigned short *)data = val;
		limit_max = USHRT_MAX;
	}
	else if (def->scd_size == sizeof(unsigned int))
	{
		*(unsigned int *)data = val;
		limit_max = UINT_MAX;
	}
	else if (def->scd_size == sizeof(unsigned long))
	{
		*(unsigned long *)data = val;
	}
	else
	{
		sm_conf_error_add(smc, "%s: %s%sunexpected size %lu",
			sm_conf_node_location(smc, node, loc, sizeof loc),
			def->scd_name,
			def->scd_name[0] == '\0' ? "" : ": ",
			(unsigned long)def->scd_size);
		return SM_CONF_ERR_INVALID;
	}

	limit_def = first_typed(def->scd_contents, sm_conf_type_u32_maximum);
	if (  limit_def != NULL
	   && (limit_max == 0 || limit_def->scd_offset < limit_max))
		limit_max = limit_def->scd_offset;

	limit_def = first_typed(def->scd_contents, sm_conf_type_u32_minimum);
	if (limit_def != NULL)
		limit_min = limit_def->scd_offset;

	if (limit_max > 0 && val > limit_max)
	{
		sm_conf_error_add(smc, "%s: overflow error in %s '%lu'",
			sm_conf_node_location(smc, node, loc, sizeof loc),
			def->scd_name[0] == '\0' ?  "value" : def->scd_name,
			val);
		return SM_CONF_ERR_TYPE;
	}
	if (val < limit_min)
	{
		sm_conf_error_add(smc, "%s: %s %lu must be at least %lu",
			sm_conf_node_location(smc, node, loc, sizeof loc),
			def->scd_name[0] == '\0' ? "value" : def->scd_name,
			val, limit_min);
		return SM_CONF_ERR_TYPE;
	}
	return 0;
}

static int
sm_conf_type_u32_node_to_value(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	sm_conf_node_T			*node,
	void				*data)
{
	char const			*text;
	size_t				text_n;
	int				err;
	unsigned long			val;

	SM_IS_CONF_DEF(def);
	err = sm_conf_node_to_value(smc, "number", node, &text, &text_n);
	if (err != 0)
		return err;

	err = sm_conf_u32_scan(smc, node, def, text, text_n, &val);
	if (err != 0)
		return err;

	if (data != NULL)
		err = u32_write(smc, node, def, data, val);

	return err;
}

static int
sm_conf_type_u32_value_check(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void const			*data)
{
	SM_IS_CONF_DEF(def);

	/* 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;
}

static int
sm_conf_type_u32_value_null(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void				*data)
{
	unsigned long			val;
	int				err;

	if (data != NULL)
	{
		SM_IS_CONF_DEF(def);
		val = 0;

		/* is there a default? if NULL: don't set value to 0 */
		if (def->scd_default != NULL)
		{
			err = sm_conf_u32_scan(smc, NULL, def,
				def->scd_default,
				strlen(def->scd_default), &val);
			if (err != 0)
				return err;

			err = u32_write(smc, NULL, def, data, val);
			if (err != 0)
				return err;
		}
	}
	return 0;
}

sm_conf_type_T const
sm_conf_type_u32_data =
{
	sm_conf_type_u32_node_to_value,
	sm_conf_type_u32_value_check,
	sm_conf_type_u32_value_null
};


syntax highlighted by Code2HTML, v. 0.9.1