/*
 * Copyright (c) 2004 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-u32.c,v 1.4 2004/09/02 01:14:31 ca Exp $")

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

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

/*
**  SM_MEMTOU32 -- strtoul without the nullbyte and the sign, sort of.
**
**	Convert at most <text_n> bytes pointed to by <text> into
**	an unsigned long no larger than 2^32-1.
**
**	Assign a pointer to the first unconverted byte to
**	*endptr.
**
**	Parameters:
**		text -- the text to be converted
**		text_n -- # of bytes pointed to by text
**		endptr -- if non-NULL, a pointer to the first
**			unconverted byte is stored here.
**		base -- 0 for dynamic, otherwise the base
**			of the number.  For bases above 10,
**			an optional leading 0x is skipped.
**		val_out -- value is stored here.
**
**	Returns:
**		EINVAL for a format error
**		ERANGE for overflow
**		0 for success.
*/

int
sm_memtou32(
	char const		*text,
	size_t			text_n,
	char			**endptr,
	int			base,
	unsigned long		*val_out)
{
	unsigned long		val, n;
	size_t			i;

	if (endptr != NULL)
		*endptr = NULL;
	if (val_out != NULL)
		*val_out = 0;

	i = 0;
	n = 0;

	/* Skip leading spaces. */
	while (i < text_n && ISSPACE(text[i]))
		i++;

	if (text_n > i && text[i] == '0')
	{
		if (  text_n > 1
		   && (base > 10 || base == 0)
		   && (text[i + 1] == 'x' || text[i + 1] == 'X'))
		{
			if (base == 0)
				base = 16;
			i += 2;
		}
		else
		{
			if (base == 0)
				base = 8;
		}
	}
	else
	{
		if (base == 0)
			base = 10;
	}

	if (  i >= text_n
	   || !isascii(text[i])
	   || !(base > 10 ? isalnum(text[i]) : isdigit(text[i])))

		return EINVAL;

	for (; i < text_n; i++)
	{
		if (!isascii(text[i]) || !isalnum(text[i]))
			break;

		if (isdigit(text[i]))
		{
			if ((val = text[i] - '0') >= base)
				return EINVAL;
		}
		else if (base > 10)
		{
			char const *ptr;
			char const *abc = "abcdefghijklmnopqrstuvwxyz";

			ptr = strchr(abc, tolower(text[i]));
			if (ptr == NULL)
				break;
			if (10 + (ptr - abc) >= base)
				break;
			val = 10 + (ptr - abc);
		}
		else
			break;

		if ((UINT32_MAX - val) / base < n)
			return ERANGE;

		n = n * base + val;
	}

	if (endptr != NULL)
		*endptr = (char *)(text + i);
	if (val_out != NULL)
		*val_out = n;

	return 0;
}

/*
**  SM_CONF_U32_SCAN -- scan an integer into an ulong, possibly with suffixes.
**
**	Civilized version of sm_memtou32.  Prints error messages
**	and evaluates suffixes under control of a definition.
**
**	Parameters:
**		smc -- context
**		node -- NULL or node, for error messages
**		def -- definition of the value; def->scd_contents are used
**			for suffix values
**		text -- text to scan
**		text_n -- # of bytes we can access
**		val_out -- result is stored here
**
**	Returns:
**		0 on sucecss, an error code on error.
**
**	Note: should it be optional to have additive suffixes?
*/

int
sm_conf_u32_scan(
	sm_conf_T		*smc,
	sm_conf_node_T const	*node,
	sm_conf_definition_T const *def,
	char const		*text,
	size_t			text_n,
	unsigned long		*val_out)
{
	char			loc[SM_CONF_ERROR_BUFFER_SIZE];
	size_t			i, offset;
	int			err;
	unsigned long		val;
	char			*endptr;

	SM_IS_CONF_DEF(def);
	SM_REQUIRE(val_out != NULL);
	offset = 0;
	val = 0;
	*val_out = 0;

	do
	{
		SM_ASSERT(offset <= text_n);
		err = sm_memtou32(text + offset, text_n - offset, &endptr, 0,
				&val);
		if (err != 0)
		{
			if (err == ERANGE)
				sm_conf_error_add(smc,
					"%s: overflow error in %s '%.*s'",
					sm_conf_node_location(smc, node,
						loc, sizeof loc),
					def->scd_name[0] == '\0' ?  "value"
								: def->scd_name,
					(int)text_n, text);
			else
				sm_conf_error_add(smc,
					"%s: expected number, got '%.*s'",
					sm_conf_node_location(smc, node,
						loc, sizeof loc),
					(int)text_n, text);
			return SM_CONF_ERR_TYPE;
		}

		i = endptr - text;

		/* skip over leading white space */
		while (i < text_n && ISSPACE(text[i]))
			i++;

		/* If there's something left, and we have suffixes, use them. */
		if (i < text_n)
		{
			sm_conf_definition_T const * suffix;
			size_t len;

			suffix = sm_conf_subdef_prefix(
				def->scd_contents, sm_conf_type_u32_suffix,
				text + i, text_n - i, &len);
			SM_IS_CONF_DEF(def);
			if (suffix == NULL || suffix->scd_offset == 0)
			{
				sm_conf_error_add(smc,
					"%s: unexpected trailing characters in"
					" '%.*s'",
					sm_conf_node_location(smc, node,
							loc, sizeof loc),
					(int)text_n, text);
				return SM_CONF_ERR_TYPE;
			}

			if ((UINT32_MAX / suffix->scd_offset) < val)
			{
				sm_conf_error_add(smc,
					"%s: overflow error in %s '%.*s'",
					sm_conf_node_location(smc, node,
						loc, sizeof loc),
					def->scd_name[0] == '\0'
						? "value"
						: def->scd_name,
					(int)text_n, text);
				return SM_CONF_ERR_TYPE;
			}
			val = val * suffix->scd_offset;
			i += len;
		}

		*val_out += val;
		offset = i;
	} while (i < text_n);

	return 0;
}

/*
**  SM_CONF_U32_STORE -- store an unsigned long at a location
**
**	Parameters:
**		data -- the location to store at
**		size -- # of bytes pointed to by <data>
**		val -- the value to store
**
**	Returns:
**		0 if the size is one of the supported integer
**		sizes, otherwise SM_CONF_ERR_INVALID
*/

int
sm_conf_u32_store(
	void				*data,
	size_t				size,
	unsigned long			val)
{
	if (size == 1)
		*(unsigned char *)data = val;
	else if (size == sizeof(unsigned short))
		*(unsigned short *)data = val;
	else if (size == sizeof(unsigned int))
		*(unsigned int *)data = val;
	else if (size == sizeof(unsigned long))
		*(unsigned long *)data = val;
	else
		return SM_CONF_ERR_INVALID;

	return 0;
}

/*
**  SM_CONF_U32_LOAD -- load an unsigned long from a location
**
**	Parameters:
**		data -- the location to load from
**		size -- # of bytes pointed to by <data>
**		val -- the loaded value is assigned to this address
**
**	Returns:
**		0 if the size is one of the supported integer
**		sizes, otherwise SM_CONF_ERR_INVALID
*/

int
sm_conf_u32_load(
	void const			*data,
	size_t				size,
	unsigned long			*val)
{
	if (size == 1)
		*val = *(unsigned char const *)data;
	else if (size == sizeof(unsigned short))
		*val = *(unsigned short const *)data;
	else if (size == sizeof(unsigned int))
		*val = *(unsigned int const *)data;
	else if (size == sizeof(unsigned long))
		*val = *(unsigned long const *)data;
	else
		return SM_CONF_ERR_INVALID;

	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1