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