forked from KolibriOS/kolibrios
a27452493c
git-svn-id: svn://kolibrios.org@9169 a494cfbc-eb01-0410-851d-a64ba20cac60
597 lines
18 KiB
C
597 lines
18 KiB
C
/*
|
|
* OpenTyrian: A modern cross-platform port of Tyrian
|
|
* Copyright (C) 2015 The OpenTyrian Development Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
/*!
|
|
* \file config_file.h
|
|
* \author Carl Reinke
|
|
* \date 2015
|
|
* \copyright GNU General Public License v2+ or Mozilla Public License 2.0
|
|
*/
|
|
#ifndef CONFIG_FILE_H
|
|
#define CONFIG_FILE_H
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef COMPILE_TIME_ASSERT
|
|
/*!
|
|
* \brief Cause compile error if compile-time computable condition fails.
|
|
*
|
|
* \param[in] name the unique identifier of the assertion
|
|
* \param[in] cond the condition
|
|
*/
|
|
#define COMPILE_TIME_ASSERT(name, cond) typedef int assert_ ## name[(cond) * 2 - 1]
|
|
#endif
|
|
|
|
#ifndef COUNTOF
|
|
/*!
|
|
* \brief Calculate the number of elements in a fixed-length array.
|
|
*
|
|
* \param[in] a the fixed-length array
|
|
* \return the number of elements in the array
|
|
*/
|
|
#define COUNTOF(a) (sizeof(a) / sizeof(*(a)))
|
|
#endif
|
|
|
|
/* string type */
|
|
|
|
/*!
|
|
* \brief A short-string-optimizing string type.
|
|
*
|
|
* This struct allows for storing up to 15 characters (plus a terminating \c '\0') inline. For
|
|
* longer strings memory will be allocated.
|
|
*
|
|
* The tag for this union is the last character of \p short_buf:
|
|
* \li if \c '\0' then \p short_buf is valid,
|
|
* \li otherwise \p long_buf is valid.
|
|
*/
|
|
typedef union
|
|
{
|
|
/*!
|
|
* \brief The inline buffer for short strings.
|
|
*/
|
|
char short_buf[16];
|
|
|
|
/*!
|
|
* \brief The buffer for long strings.
|
|
*
|
|
* May be \c NULL.
|
|
*/
|
|
char *long_buf;
|
|
} ConfigString;
|
|
|
|
/*! \cond suppress_doxygen */
|
|
COMPILE_TIME_ASSERT(string_short_buf_sufficient, sizeof(char *) + 1 <= COUNTOF(((ConfigString *)NULL)->short_buf));
|
|
/*! \endcond */
|
|
|
|
/*! \cond suppress_doxygen */
|
|
#define CONFIG_STRING_LONG_TAG(s) ((s).short_buf[COUNTOF((s).short_buf) - 1])
|
|
/*! \endcond */
|
|
|
|
/*!
|
|
* \brief Return a C-string backed by a string.
|
|
*
|
|
* \param[in] string the string
|
|
* \return the C-string
|
|
*/
|
|
static inline const char *config_string_to_cstr( const ConfigString *string )
|
|
{
|
|
assert(string != NULL);
|
|
char is_long = CONFIG_STRING_LONG_TAG(*string);
|
|
return is_long ?
|
|
string->long_buf :
|
|
string->short_buf;
|
|
}
|
|
|
|
/* config types */
|
|
|
|
/*!
|
|
* \brief An option consisting of one (an item) or many (a list) values.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/*!
|
|
* \brief The key of the option.
|
|
*/
|
|
ConfigString key;
|
|
|
|
/*!
|
|
* \brief The number of values in the option if it is a 'list' option.
|
|
*
|
|
* If \c 0 then the option \e may be an 'item' option.
|
|
*
|
|
* \see ::ConfigOption::value
|
|
*/
|
|
unsigned int values_count;
|
|
|
|
/*!
|
|
* \brief The value or values.
|
|
*
|
|
* The tag for this union is \p value_count:
|
|
* \li if \c 0 then \p value is valid,
|
|
* \li otherwise \p values is valid.
|
|
*/
|
|
union
|
|
{
|
|
/*!
|
|
* \brief The value of an 'item' option or an empty 'list' option.
|
|
*
|
|
* If this field is \c NULL then the option is an empty 'list' option.
|
|
*/
|
|
ConfigString value;
|
|
|
|
/*!
|
|
* \brief The values of a non-empty 'list' option.
|
|
*/
|
|
ConfigString *values;
|
|
} v;
|
|
} ConfigOption;
|
|
|
|
/*!
|
|
* \brief A section consisting of options.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/*!
|
|
* \brief The type of the section.
|
|
*/
|
|
ConfigString type;
|
|
|
|
/*!
|
|
* \brief The optional name of the section.
|
|
*
|
|
* May be \c NULL.
|
|
*/
|
|
ConfigString name;
|
|
|
|
/*!
|
|
* \brief The number of options in the section.
|
|
*/
|
|
unsigned int options_count;
|
|
|
|
/*!
|
|
* \brief The options in the section.
|
|
*
|
|
* \c NULL if \p options_count is \c 0.
|
|
*/
|
|
ConfigOption *options;
|
|
} ConfigSection;
|
|
|
|
/*!
|
|
* \brief A configuration consisting of sections.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/*!
|
|
* \brief The number of sections in the configuration.
|
|
*/
|
|
unsigned int sections_count;
|
|
|
|
/*!
|
|
* \brief The sections in the configuration.
|
|
*
|
|
* \c NULL if \p sections_count is \c 0.
|
|
*/
|
|
ConfigSection *sections;
|
|
} Config;
|
|
|
|
/* config manipulators */
|
|
|
|
/*!
|
|
* \brief Initialize a configuration.
|
|
*
|
|
* \param[in] config the configuration
|
|
* \return void
|
|
*/
|
|
extern void config_init( Config *config );
|
|
|
|
/*!
|
|
* \brief Release any memory allocated inside a configuration.
|
|
*
|
|
* \param[in] config the configuration
|
|
* \return void
|
|
*/
|
|
extern void config_deinit( Config *config );
|
|
|
|
/*!
|
|
* \brief Parse a configuration from a file.
|
|
*
|
|
* \param[in] config the uninitalized configuration
|
|
* \param[in] file the file handle
|
|
* \return whether parsing succeeded
|
|
*/
|
|
extern bool config_parse( Config *config, FILE *file );
|
|
|
|
/*!
|
|
* \brief Write a configuration to a file.
|
|
*
|
|
* \param[in] config the configuration
|
|
* \param[in] file the file handle
|
|
* \return void
|
|
*/
|
|
extern void config_write( const Config *config, FILE *file );
|
|
|
|
/* config section accessors/manipulators -- by type, name */
|
|
|
|
/*! \see ::config_add_section() */
|
|
extern ConfigSection *config_add_section_len( Config *config, const char *type, size_t type_len, const char *name, size_t name_len );
|
|
|
|
/*!
|
|
* \brief Add a section to a configuration.
|
|
*
|
|
* \param[in] config the configuration to contain the section
|
|
* \param[in] type the type of the section
|
|
* \param[in] name the name of the section; may be \c NULL
|
|
* \return the added section; \c NULL if out of memory
|
|
*/
|
|
static inline ConfigSection *config_add_section( Config *config, const char *type, const char *name)
|
|
{
|
|
assert(type != NULL);
|
|
return config_add_section_len(config, type, strlen(type), name, name == NULL ? 0 : strlen(name));
|
|
}
|
|
|
|
// TODO: extern Config *config_remove_section( Config *config, unsigned int i );
|
|
|
|
/*!
|
|
* \brief Iterate sections by type.
|
|
*
|
|
* \param[in] config the configuration containing the sections
|
|
* \param[in] type the type of the section
|
|
* \param[in,out] save the saved state of the iterator; initialize \c *save to \c NULL before
|
|
* iteration
|
|
* \return the section; \c NULL if iteration finished
|
|
*/
|
|
extern ConfigSection *config_find_sections( Config *config, const char *type, ConfigSection **save );
|
|
|
|
/*!
|
|
* \brief Find a section by type and name.
|
|
*
|
|
* \param[in] config the configuration containing the section
|
|
* \param[in] type the type of the section
|
|
* \param[in] name the name of the section
|
|
* \return the section; \c NULL if it does not exist
|
|
*/
|
|
extern ConfigSection *config_find_section( Config *config, const char *type, const char *name );
|
|
|
|
/*!
|
|
* \brief Find a section by type and name, creating the section if it did not exist.
|
|
*
|
|
* \param[in] config the configuration containing the section
|
|
* \param[in] type the type of the section
|
|
* \param[in] name the name of the section; may be \c NULL
|
|
* \return the section; \c NULL if out of memory
|
|
*/
|
|
extern ConfigSection *config_find_or_add_section( Config *config, const char *type, const char *name );
|
|
|
|
/* config option accessors/manipulators -- by key */
|
|
|
|
/*! \see ::config_set_option() */
|
|
extern ConfigOption *config_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len );
|
|
|
|
/*!
|
|
* \brief Set a value of an 'item' option by key, creating the option if necessary.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the item value; \c NULL to set an emtpy 'list' option instead of an 'item'
|
|
* option (can be used to delete an 'item' option)
|
|
* \return the option; \c NULL if out of memory
|
|
*/
|
|
static inline ConfigOption *config_set_option( ConfigSection *section, const char *key, const char *value)
|
|
{
|
|
assert(key != NULL);
|
|
return config_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value));
|
|
}
|
|
|
|
/*!
|
|
* \brief Get an option by key.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \return the option; \c NULL if it does not exist
|
|
*/
|
|
extern ConfigOption *config_get_option( const ConfigSection *section, const char *key );
|
|
|
|
/*! \see ::config_get_or_set_option() */
|
|
extern ConfigOption *config_get_or_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len );
|
|
|
|
/*!
|
|
* \brief Get an option by key, creating an 'item' option if the option did not exist.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the default item value; \c NULL to set an empty 'list' option instead of an
|
|
* 'item' option
|
|
* \return the option; \c NULL if out of memory
|
|
*/
|
|
static inline ConfigOption *config_get_or_set_option( ConfigSection *section, const char *key, const char *value )
|
|
{
|
|
assert(key != NULL);
|
|
return config_get_or_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value));
|
|
}
|
|
|
|
/*! \see ::config_set_string_option() */
|
|
extern void config_set_string_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len );
|
|
|
|
/*!
|
|
* \brief Set a string value of an 'item' option by key, creating the option if necessary.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the item value
|
|
* \return void
|
|
*/
|
|
static inline void config_set_string_option( ConfigSection *section, const char *key, const char *value )
|
|
{
|
|
assert(key != NULL);
|
|
config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value));
|
|
}
|
|
|
|
/*!
|
|
* \brief Get a string value of an 'item' option by key.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[out] out_value the item value if a valid option exists; otherwise unset
|
|
* \return whether \p out_value was set
|
|
*/
|
|
extern bool config_get_string_option( const ConfigSection *section, const char *key, const char **out_value );
|
|
|
|
/*!
|
|
* \brief Get a string value of an 'item' option by key, setting the option if it was invalid or
|
|
* creating the option if it did not exist.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the default item value
|
|
* \return the value
|
|
*/
|
|
extern const char *config_get_or_set_string_option( ConfigSection *section, const char *key, const char *value );
|
|
|
|
/*!
|
|
* \brief The styles of boolean values.
|
|
*/
|
|
typedef enum
|
|
{
|
|
ZERO_ONE = 0,
|
|
NO_YES = 1,
|
|
OFF_ON = 2,
|
|
FALSE_TRUE = 3,
|
|
} ConfigBoolStyle;
|
|
|
|
/*!
|
|
* \brief Set a boolean value of an 'item' option by key, creating the option if necessary.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the item value
|
|
* \param[in] style the style of boolean value
|
|
* \return void
|
|
*/
|
|
extern void config_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style );
|
|
|
|
/*!
|
|
* \brief Get a boolean value of an 'item' option by key.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[out] out_value the item value if a valid option exists; otherwise unset
|
|
* \return whether \p out_value was set
|
|
*/
|
|
extern bool config_get_bool_option( const ConfigSection *section, const char *key, bool *out_value );
|
|
|
|
/*!
|
|
* \brief Get a boolean value of an 'item' option by key, setting the option if it was invalid or
|
|
* creating the option if it did not exist.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the default item value
|
|
* \param[in] style the style of boolean value
|
|
* \return the value
|
|
*/
|
|
extern bool config_get_or_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style );
|
|
|
|
/*!
|
|
* \brief Set an integer value of an 'item' option by key, creating the option if necessary.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the item value
|
|
* \return void
|
|
*/
|
|
extern void config_set_int_option( ConfigSection *section, const char *key, int value );
|
|
|
|
/*!
|
|
* \brief Get an integer value of an 'item' option by key.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[out] out_value the item value if a valid option exists; otherwise unset
|
|
* \return whether \p out_value was set
|
|
*/
|
|
extern bool config_get_int_option( const ConfigSection *section, const char *key, int *out_value );
|
|
|
|
/*!
|
|
* \brief Get an integer value of an 'item' option by key, setting the option if it was invalid or
|
|
* creating the option if it did not exist.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the default item value
|
|
* \return the value
|
|
*/
|
|
extern int config_get_or_set_int_option( ConfigSection *section, const char *key, int value );
|
|
|
|
/*!
|
|
* \brief Set an unsigned integer value of an 'item' option by key, creating the option if
|
|
* necessary.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the item value
|
|
* \return void
|
|
*/
|
|
extern void config_set_uint_option( ConfigSection *section, const char *key, unsigned int value );
|
|
|
|
/*!
|
|
* \brief Get an unsigned integer value of an 'item' option by key.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[out] out_value the item value if a valid option exists; otherwise unset
|
|
* \return whether \p out_value was set
|
|
*/
|
|
extern bool config_get_uint_option( const ConfigSection *section, const char *key, unsigned int *out_value );
|
|
|
|
/*!
|
|
* \brief Get an unsigned integer value of an 'item' option by key, setting the option if it was
|
|
* invalid or creating the option if it did not exist.
|
|
*
|
|
* \param[in] section the section containing the option
|
|
* \param[in] key the option key
|
|
* \param[in] value the default item value
|
|
* \return the value
|
|
*/
|
|
extern unsigned int config_get_or_set_uint_option( ConfigSection *section, const char *key, unsigned int value );
|
|
|
|
/* config option accessors/manipulators -- by reference */
|
|
|
|
/*! \see ::config_set_value() */
|
|
extern ConfigOption *config_set_value_len( ConfigOption *option, const char *value, size_t value_len );
|
|
|
|
/*!
|
|
* \brief Set the value of an 'item' option.
|
|
*
|
|
* \param[in] option the option
|
|
* \param[in] value the value
|
|
* \return the option; \c NULL if out of memory
|
|
*/
|
|
static inline ConfigOption *config_set_value( ConfigOption *option, const char *value )
|
|
{
|
|
return config_set_value_len(option, value, value == NULL ? 0 : strlen(value));
|
|
}
|
|
|
|
/*! \see ::config_add_value() */
|
|
extern ConfigOption *config_add_value_len( ConfigOption *option, const char *value, size_t value_len );
|
|
|
|
/*!
|
|
* \brief Add a value to a 'list' option.
|
|
*
|
|
* \param[in] option the option
|
|
* \param[in] value the value
|
|
* \return the option; \c NULL if out of memory
|
|
*/
|
|
static inline ConfigOption *config_add_value( ConfigOption *option, const char *value )
|
|
{
|
|
assert(value != NULL);
|
|
return config_add_value_len(option, value, strlen(value));
|
|
}
|
|
|
|
/*!
|
|
* \brief Remove a value from a 'list' option.
|
|
*
|
|
* \param[in] option the option
|
|
* \param[in] i the index of the value
|
|
* \return the option; \c NULL if out of memory or invalid \p index
|
|
*/
|
|
extern ConfigOption *config_remove_value( ConfigOption *option, unsigned int i );
|
|
|
|
/*!
|
|
* \brief Get the value of an 'item' option.
|
|
*
|
|
* \param[in] option the option
|
|
* \return the value; \c NULL if \p option was \c NULL or was a 'list' option
|
|
*/
|
|
extern const char *config_get_value( const ConfigOption *option );
|
|
|
|
/*!
|
|
* \brief Get the value that indicates whether the option is a 'list' option.
|
|
*
|
|
* \param[in] option the option
|
|
* \return whether the option is a 'list' option
|
|
*/
|
|
static inline bool config_is_value_list( const ConfigOption *option )
|
|
{
|
|
assert(option != NULL);
|
|
return option->values_count > 0 ||
|
|
config_string_to_cstr(&option->v.value) == NULL;
|
|
}
|
|
|
|
/*!
|
|
* \brief Get the number of values assigned to the option.
|
|
*
|
|
* \param[in] option the option
|
|
* \return \c 1 if the option is an 'item' option; the number of elements if the option is a 'list'
|
|
* option
|
|
*/
|
|
static inline unsigned int config_get_value_count( const ConfigOption *option )
|
|
{
|
|
assert(option != NULL);
|
|
return option->values_count == 0 ?
|
|
(config_string_to_cstr(&option->v.value) == NULL ? 0 : 1) :
|
|
option->values_count;
|
|
}
|
|
|
|
/*!
|
|
* \brief Iterate over the values assigned to the option.
|
|
*
|
|
* \param[out] string_value the value variable to declare
|
|
* \param[in] option the option
|
|
*/
|
|
#define foreach_option_value( string_value, option ) \
|
|
for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \
|
|
for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \
|
|
*_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \
|
|
*_value = _values_begin; _value < _values_end; ++_value) \
|
|
for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL)
|
|
|
|
/*!
|
|
* \brief Iterate over the values assigned to the option.
|
|
*
|
|
* \param[out] i the index variable to declare
|
|
* \param[out] string_value the value variable to declare
|
|
* \param[in] option the option
|
|
*/
|
|
#define foreach_option_i_value( i, string_value, option ) \
|
|
for (unsigned int (i) = 0; (i) == 0; (i) = ~0) \
|
|
for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \
|
|
for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \
|
|
*_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \
|
|
*_value = _values_begin; _value < _values_end; ++_value, (i) = _value - _values_begin) \
|
|
for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL)
|
|
|
|
/*!
|
|
* \brief Remove a value from an option during iteration. Should be followed by \c continue.
|
|
*/
|
|
#define foreach_remove_option_value() \
|
|
{ \
|
|
extern void config_oom( void ); \
|
|
unsigned int _value_i = _value - _values_begin; \
|
|
if (config_remove_value(_option, _value_i) == NULL) \
|
|
config_oom(); \
|
|
_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0]; \
|
|
_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count]; \
|
|
_value = _values_begin + _value_i - 1; \
|
|
}
|
|
|
|
#endif
|