/*
 * 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.
 *
 *	$Id: sm-conf.h,v 1.37 2006/05/14 04:54:05 ca Exp $
 */

#ifndef SM_CONF_H
#define SM_CONF_H 1

#include "sm/generic.h"
#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/error.h"

#include <stdio.h>
#if SM_LIBCONF_ALONE
#include <stddef.h>	/* ptrdiff_t */
#endif

#ifndef SM_LIBCONF_ISSET
#define SM_LIBCONF_ISSET 1
#endif
#ifndef SM_LIBCONF_MAGIC
#define SM_LIBCONF_MAGIC 1
#endif

#ifndef HAVE_SNPRINTF
#define snprintf sm_snprintf
#define vsnprintf sm_vsnprintf
#endif

/* SM-CONF.H -- API declarations. */

/*
**  SM_CONF_NODE_TYPE_E -- node types: value, list, section.
*/

#define SM_CONF_NODE_NONE	0

enum sm_conf_node_type_E
{
	SM_CONF_NODE_VALUE	= 1,
	SM_CONF_NODE_LIST	= 2,
	SM_CONF_NODE_SECTION	= 3
};

typedef void	*sm_conf_iterator_T;

/*
**  SM_CONF_ERROR_E -- errors from tokenizer, parser and file I/O
*/

enum SM_CONF_ERROR_E
{
	 SM_CONF_NO_ERROR		= 0
	,SM_CONF_ERR_NEWLINE_IN_STRING	= SM_CNF_E_NEWLINE_IN_STRING
	,SM_CONF_ERR_EOF_IN_STRING	= SM_CNF_E_EOF_IN_STRING
	,SM_CONF_ERR_BAD_CHAR		= SM_CNF_E_BAD_CHAR
	,SM_CONF_ERR_NO_MEMORY		= SM_CNF_E_NO_MEMORY
	,SM_CONF_ERR_HEX_EXPECTED	= SM_CNF_E_HEX_EXPECTED
	,SM_CONF_ERR_CHAR_OVERFLOW	= SM_CNF_E_CHAR_OVERFLOW
	,SM_CONF_ERR_INVALID		= SM_CNF_E_INVALID
	,SM_CONF_ERR_READ		= SM_CNF_E_READ
	,SM_CONF_ERR_READ_OPEN		= SM_CNF_E_READ_OPEN
	,SM_CONF_ERR_READ_CLOSE		= SM_CNF_E_READ_CLOSE
	,SM_CONF_ERR_EOF		= SM_CNF_E_EOF
	,SM_CONF_ERR_SYNTAX		= SM_CNF_E_SYNTAX
	,SM_CONF_ERR_NOT_FOUND		= SM_CNF_E_NOT_FOUND
	,SM_CONF_ERR_ALREADY		= SM_CNF_E_ALREADY
	,SM_CONF_ERR_TYPE		= SM_CNF_E_TYPE
	,SM_CONF_ERR_NUL_IN_STRING	= SM_CNF_E_NUL_IN_STRING
	,SM_CONF_ERR_TOO_MANY		= SM_CNF_E_TOO_MANY
};

#define SM_CONF_ERROR_BUFFER_SIZE	200

/* complete configuration state; catch-all handle */
typedef struct sm_conf_S	sm_conf_T;

/* a single node in the parsed configuration tree. */
typedef union sm_conf_node_U	sm_conf_node_T;

/* a value type */
typedef struct sm_conf_type_S	sm_conf_type_T;

/* high-level API calls */
sm_conf_T	*sm_conf_new(char const *);

int	sm_conf_read_FILE(sm_conf_T *, char const *, FILE *);
int	sm_conf_read_data(sm_conf_T *, char *, size_t n, bool copy);
void	sm_conf_destroy(sm_conf_T *);
char const	*sm_conf_strerror(int err, char *buf, size_t bufsize);
char const	*sm_conf_syntax_error(sm_conf_T *smc, char const *prev);

/* node access functions */

sm_conf_node_T	*sm_conf_root(sm_conf_T *_smc);

int		sm_conf_node_type(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_node);

char const *	sm_conf_node_type_name(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_node);

sm_conf_node_T *sm_conf_node_next(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_node);

sm_conf_node_T *sm_conf_node_parent(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_node);

int		sm_conf_value(
			sm_conf_T		 *smc,
			sm_conf_node_T const	 *node,
			char const		**text_out,
			size_t			 *text_n_out);

sm_conf_node_T	*sm_conf_list_next(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_list,
			sm_conf_node_T const	*_prev);

size_t		sm_conf_list_n(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_list);

sm_conf_node_T	*sm_conf_section_next(
			sm_conf_T		 *_smc,
			sm_conf_node_T const	 *_section,
			char const		**_name_out,
			size_t			 *_name_n_out,
			sm_conf_node_T const	 *_prev);

sm_conf_node_T	*sm_conf_section_next_subsection(
			sm_conf_T		*smc,
			sm_conf_node_T const	*parent,
			char const		*keyword,
			size_t			 keyword_n,
			char const		*name,
			size_t			 name_n,
			sm_conf_node_T const	*pred);

sm_conf_node_T	*sm_conf_section_next_option(
			sm_conf_T		*smc,
			sm_conf_node_T const	*parent,
			char const		*name,
			size_t			 name_n,
			sm_conf_node_T const	*pred);

int		sm_conf_section_keyword(
			sm_conf_T		 *_smc,
			sm_conf_node_T const	 *_section,
			char const		**_kw_out,
			size_t			 *_kw_n_out);

int		sm_conf_section_name(
			sm_conf_T		 *_smc,
			sm_conf_node_T const	 *_section,
			char const		**_name_out,
			size_t			 *_name_n_out);

size_t		sm_conf_section_n(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_section);

char const	*sm_conf_node_location(
			sm_conf_T		*_smc,
			sm_conf_node_T const	*_node,
			char			*_buf,
			size_t			 _bufsize);

/* types */

typedef struct sm_conf_bytes_S
{
	char const		*scb_data;
	size_t			 scb_size;
} sm_conf_bytes_T;


#define sm_conf_type_bool (&sm_conf_type_bool_data)
#define sm_conf_type_u32	(&sm_conf_type_u32_data)
#define sm_conf_type_u32_suffix	(&sm_conf_type_u32_suffix_data)
#define sm_conf_type_u32_maximum	(&sm_conf_type_u32_maximum_data)
#define sm_conf_type_u32_minimum	(&sm_conf_type_u32_minimum_data)
#define sm_conf_type_ipv4	(&sm_conf_type_ipv4_data)
#define sm_conf_type_char	(&sm_conf_type_char_data)
#define sm_conf_type_string	(&sm_conf_type_string_data)
#if 0
#define sm_conf_type_name	(&sm_conf_type_name_data)
#endif
#define sm_conf_type_argv	(&sm_conf_type_argv_data)
#define sm_conf_type_choice	(&sm_conf_type_choice_data)
#define sm_conf_type_choice_value	(&sm_conf_type_choice_value_data)
#if 0
#define sm_conf_type_flag	(&sm_conf_type_flag_data)
#define sm_conf_type_flag_value	(&sm_conf_type_flag_value_data)
#define sm_conf_type_flags	(&sm_conf_type_flags_data)
#endif /* 0 */
#define sm_conf_type_bytes	(&sm_conf_type_bytes_data)
#define sm_conf_type_section	(&sm_conf_type_section_data)
#define sm_conf_type_section_title	(&sm_conf_type_section_title_data)
#define sm_conf_type_union	(&sm_conf_type_union_data)
#define sm_conf_type_union_choice	(&sm_conf_type_union_choice_data)
#define sm_conf_type_union_type	(&sm_conf_type_union_type_data)
#define sm_conf_type_array	(&sm_conf_type_array_data)
#define sm_conf_type_array_n	(&sm_conf_type_array_n_data)
#define sm_conf_type_array_elem_size	(&sm_conf_type_array_elem_size_data)

extern const sm_conf_type_T

	/*
	**  SM_CONF_TYPE_BOOL -- 1 or 0 in a byte-sized location.
	*/

	sm_conf_type_bool_data,

	/*
	**  SM_CONF_TYPE_U32 -- numbers, stored as unsigned char,
	**	unsigned short, unsigned int, or unsigned long
	**	(sizeof(value) is the size.)
	**
	** Parameter types for use in a sm_conf_type_u32's scd_contents:
	**
	**  SM_CONF_TYPE_U32_SUFFIX -- scd_offset is suffix multiplier
	**  SM_CONF_TYPE_U32_MAXIMUM -- scd_offset is maximum value
	**  SM_CONF_TYPE_U32_MINIMUM -- scd_offset is minimum value
	*/

	sm_conf_type_u32_data,
	sm_conf_type_u32_suffix_data,
	sm_conf_type_u32_maximum_data,
	sm_conf_type_u32_minimum_data,

	/*
	**  SM_CONF_TYPE_IPV4 -- IPv4 addresses
	*/

	sm_conf_type_ipv4_data,

	/*
	**  SM_CONF_TYPE_CHAR -- char
	*/

	sm_conf_type_char_data,

	/*
	**  SM_CONF_TYPE_STRING -- \0-terminated string pointed to by char *
	*/

	sm_conf_type_string_data,

#if 0
	sm_conf_type_name_data,
#endif

	/*
	**  SM_CONF_TYPE_ARGV -- NULL-terminated arary of \0-terminated strings
	*/

	sm_conf_type_argv_data,

	/*
	**  SM_CONF_TYPE_CHOICE -- enumerated values;
	**	contents list values.
	**  SM_CONF_TYPE_CHOICE_VALUE -- one possible value;
	**	scd_offset is the numerical value.
	*/

	sm_conf_type_choice_data,
	sm_conf_type_choice_value_data,

#if 0
	/*
	**  SM_CONF_TYPE_FLAG -- enumerated values;
	**	contents list values.
	**  SM_CONF_TYPE_FLAG_VALUE -- one possible value;
	**	scd_offset is the numerical value.
	*/

	sm_conf_type_flag_data,
	sm_conf_type_flag_value_data,

	sm_conf_type_flags_data,
#endif /* 0 */

	/*
	**  SM_CONF_TYPE_BYTES -- string that can contain \0.
	**	stored in a sm_conf_bytes_T
	*/

	sm_conf_type_bytes_data,

	/*
	**  SM_CONF_TYPE_SECTION --  compound; scd_contents are contents.
	*/

	sm_conf_type_section_data,
	sm_conf_type_section_title_data,

	/*
	**  SM_CONF_TYPE_UNION --  one of multiple types
	*/

	sm_conf_type_union_data,
	sm_conf_type_union_choice_data,
	sm_conf_type_union_type_data,

	/*
	**  SM_CONF_TYPE_ARRAY -- a fixed or dynamic array
	*/

	sm_conf_type_array_data,
	sm_conf_type_array_n_data,
	sm_conf_type_array_elem_size_data;

/*
**  SM_CONF_FLAG_... -- flags for use with sm_conf_scan().
*/

/*
**  SM_CONF_FLAG_ALLOW_ANY_OPTION -- allow options not in the definition
**  SM_CONF_FLAG_ALLOW_ANY_SECTION -- allow section elements
**  SM_CONF_FLAG_ALLOW_ANY --  conjunction of the previous two.
**
**	Scanning with a definition list isn't the only way of getting at
**	values; an application could use the node access interface to
**	read individual values, perhaps under control of user input elsewhere
**	(e.g. a command that causes it to load and execute a new module.)
**
**	The following two flags allow scanned definitions to coexist with
**	values whose meaning isn't yet known to the application.  They can
**	be used as parameters to sm_conf_scan() or with section definitions.
*/

#define SM_CONF_FLAG_ALLOW_ANY_OPTION		0x0001
#define SM_CONF_FLAG_ALLOW_ANY_SECTION		0x0002
#define SM_CONF_FLAG_ALLOW_ANY		( SM_CONF_FLAG_ALLOW_ANY_OPTION	\
					| SM_CONF_FLAG_ALLOW_ANY_SECTION )


/*
**  SM_CONF_FLAG_STRICTLY_REQUIRED -- value must be explicitly provided,
**	can't be defaulted.
**  SM_CONF_FLAG_REQUIRED -- value can be defaulted, but must be there.
*/

#define SM_CONF_FLAG_STRICTLY_REQUIRED		0x0004
#define SM_CONF_FLAG_REQUIRED			0x0008

/*
**  SM_CONF_FLAG_MULTIPLE -- apply definition to multiple occurrences.
**
**	for unnamed entries, the definition applies to this unnamed
**		value and all following.
**	for named entries, multiple mentions of the name are parsed
**		using the same type into the same location (which,
**		hopefully, is some sort of vector that can accomodate
**		multiple values.)
*/

#define SM_CONF_FLAG_MULTIPLE			0x0010

/*
**  SM_CONF_FLAG_SECTION_MUST_BE_NAMED -- section must be named
**  SM_CONF_FLAG_SECTION_MUST_BE_ANONYMOUS -- section must be anonymous
**
**	-- controls whether a section construct can, must, or must not
**	   have a name.
**
**		(A section name is the optional name after the
**		type, but before the opening {.   For example,
**			foo "bar" {
**			}
**		is a foo-section named "bar", and
**
**			foo {
**			}
**		is an anonymous foo-section.)
*/

#define SM_CONF_FLAG_SECTION_MUST_BE_NAMED	0x0020
#define SM_CONF_FLAG_SECTION_MUST_BE_ANONYMOUS	0x0040

/*
**  SM_CONF_FLAG_SECTION_DEFAULT_FROM_ANONYMOUS -- default from anonymous
**
**	-- When reading a named section, if there is an unnamed
**	   section of the same type, default values for not explicitly
**	   listed elements in the named sections from the unnamed section's
**	   elements.
**
**	   For example, in
**
**		interface {
**			port = 25;
**			ip = "0.0.0.0";
**		}
**
**		interface "local" {
**			ip = "127.0.0.1";
**		}
**
**	   the value of interface{local}.port is 25.
*/

#define SM_CONF_FLAG_SECTION_DEFAULT_FROM_ANONYMOUS	0x0080

/*
**  SM_CONF_FLAG_DEFAULT_FROM_ENVIRONMENT -- default a named
**	value from the environment.
**
**	When reading a section, if one of its properties isn't
**	specified in either the section itself or in a defaulting
**	unnamed section (if any), look for the property in one
**	of the containing sections, and default from it.
*/

#define SM_CONF_FLAG_DEFAULT_FROM_ENVIRONMENT	0x0100


/* For arrays, at least one element is required. */
#define SM_CONF_FLAG_AT_LEAST_ONE		0x0004

/* For arrays or choices, choose either none or one. */
#define SM_CONF_FLAG_AT_MOST_ONE		0x0008

/* For arrays or choices, choose exactly one. */
#define SM_CONF_FLAG_ONE			0x000C

/* Don't set this value to zero. */
#define SM_CONF_FLAG_KEEP_DEFAULT		0x0200

/* this section will be scanned explicitly */
/* syntax check only; don't assign a value. */
#define SM_CONF_FLAG_PARSE_ONLY			0x0400

/* Allow numeric specification for a flag or choice */
#define SM_CONF_FLAG_NUMERIC			0x0800

/*
**  SM_CONF_FLAG_FLAT -- For arrays or argv-style arrays:
**	the location is the array itself, not a pointer.
*/

#define SM_CONF_FLAG_FLAT			0x1000

/*
**  SM_CONF_FLAG_OR -- apply a binary OR when setting options
**	as multiple options may point to a single value.
**	This is similar to SM_CONF_FLAG_MULTIPLE in its behavior,
**	but only one value is valid for some (sub)option.
*/

#define SM_CONF_FLAG_OR			0x2000

/* print in hex */
#define SM_CONF_FLAG_HEX		0x4000

/* deprecated option (don't print for now, warn later on) */
#define SM_CONF_FLAG_DPRCD		0x8000

/* "is" vs "are" in printing configuration */
#define SM_CONF_FLAG_PLURAL		0x00010000

/* do not print */
#define SM_CONF_FLAG_DONTPRINT		0x00020000

#if SM_LIBCONF_ISSET
# define SM_LC_SET_DIRECT	1
# define SM_LC_SET_FRM_ENV	2
# define SM_LC_SET_FRM_ANON	3
# define SM_LC_NO_ISSET	, 0
# define SM_LC_ISSET(x)	, x
#else
# define SM_LC_NO_ISSET	
# define SM_LC_ISSET(x)
#endif

typedef struct sm_conf_definition_S sm_conf_definition_T;
typedef int   sm_conf_definition_check_T(
		sm_conf_T			*_handle,
		void				*_callback_data,
		sm_conf_definition_T const	*_definition,
		void const			*_value_data);

#define SM_CONF_DEF_MAGIC SM_MAGIC('C', 'F', 'D', 'E')
#define SM_IS_CONF_DEF(sm_conf_definition)	\
	SM_REQUIRE_ISA((sm_conf_definition), SM_CONF_DEF_MAGIC)

/* definition of a configuration element */
struct sm_conf_definition_S
{
	sm_magic_T			 sm_magic;

	/* name of option */
	char const			*scd_name;

	/* type, see sm_conf_type_* */
	const sm_conf_type_T		*scd_type;

	/* offset of data entry in struct */
	ptrdiff_t			 scd_offset;

	/* size of the data entry */
	size_t				 scd_size;

	/* something to scan as default value */
	char const			*scd_default;

	/* various flags, see SM_CONF_FLAG_* */
	uint32_t			 scd_flags;

	/* configuration description of item (depending on type) */
	sm_conf_definition_T const	*scd_contents;

	/* optional function to check whether entry is valid */
	sm_conf_definition_check_T	*scd_check;

	/* argument for check */
	void				*scd_check_data;

	/* textual description of configuration element */
	char const			*scd_description;
#if SM_LIBCONF_ISSET
	/*
	**  offset of "is this option set" in struct.
	**  0: no field in struct available! that means, that field
	**  MUST NOT be the first element in the struct!
	*/

	ptrdiff_t			 scd_isset_offset;
#endif
#if SM_LIBCONF_MAGIC
	/* check struct for this magic (if != 0) */
	sm_magic_T			 scd_magic;
#endif
};

#if SM_LIBCONF_MAGIC
# define SM_LC_ISA(def, data)					\
	do							\
	{							\
		if ((data) != NULL && (def)->scd_magic != 0)	\
			SM_ASSERT((def)->scd_magic == *(sm_magic_T *)data);	\
	} while (0)
# define SM_LC_SET_MAGIC(magic)	, magic
#else
# define SM_LC_ISA(def, data)	SM_NOOP
# define SM_LC_SET_MAGIC(magic)	
#endif

int sm_conf_scan(
	sm_conf_T			*_handle,
	sm_conf_definition_T const	*_definition,
	unsigned int			 _flags,
	void				*_data);

int sm_conf_get_relative(
	sm_conf_T			*_smc,
	sm_conf_node_T			*_node,
	char const			*_name,
	sm_conf_type_T const		*_type,
	void const			*_type_data,
	unsigned int			 _flags,
	void				*_data,
	size_t				 _size);

int sm_conf_get(
	sm_conf_T			*_smc,
	char const			*_name,
	sm_conf_type_T const		*_type,
	void const			*_type_data,
	unsigned int			 f_lags,
	void				*_data,
	size_t				 _size);

int sm_conf_scan_next_relative(
	sm_conf_T			 *_smc,
	sm_conf_node_T			 *_parent,
	char const			 *_path,
	sm_conf_definition_T const	 *_defs,
	unsigned int			  _flags,
	char const			**_name_out,
	size_t				 *_name_n_out,
	void				 *_data,
	sm_conf_iterator_T		 *_iter);

int sm_conf_scan_next(
	sm_conf_T			 *_smc,
	char const			 *_path,
	sm_conf_definition_T const	 *_defs,
	unsigned int			  _flags,
	char const			**_name_out,
	size_t				 *_name_n_out,
	void				 *_data,
	sm_conf_iterator_T		 *_iter);

int sm_conf_null(
	sm_conf_T			*_handle,
	sm_conf_definition_T const	*_definitions,
	unsigned int			 _flags,
	void				*_data);

#endif /* SM_CONF_H */


syntax highlighted by Code2HTML, v. 0.9.1