/* * Copyright © 2026 Kana Steimle * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * This library uses my own dialect of the INI file format; it won't be able to work with all INI files without some modification. * The aim is a configuration file format that is easily readable and writable both directly and through programs * * Currently strings, booleans, integers, and floats are supported. * - Strings may be wrapped in single-quotes (') or double-quotes ("). No quotes is not supported. * - Booleans may be in the form of 'true'/'false', 'on'/'off', or 'yes'/'no'. * - Integers may be decimal numbers, hexadecimal numbers, and colors without or with the alpha channel. * - Any precision of float may be read (though it's up to strtod to include that precision), but a specific presision may be set to be saved * * The dialect allows for both '#' and ';' to indicate comments. Each have different meanings. * - Comments starting with '#' are considered 'user comments'. They are to be left by users who directly edit the file themselves, for personal use. * They are preserved at their precise location in the file. * - Comments starting with ';' are considered 'system comments'. They should only be generated by programs using the config file, and are not preserved. * They are used to represent the variables that can be set and their default values, and apply descriptions to them. * The main reason I don't preserve them is so that they can easily be changed by the program. * Empty lines are also preserved, as well as the order of uncommented variables. The order of commented variables (Specifically commented with ';') are not preserved. * * A program that uses this library should define default values and preferred formats for all the variables it uses in config files; though it doesn't necessarily have to. * Currently iterating over variable and section names is not implemented, but if I find myself needing those features I might implement them. */ #ifndef _CONFIG_H #define _CONFIG_H #include #include #include // Config files are based on the INI file format. // Sections can be specified as [sectionname]. By default sections are not required for finding a variable, but if a variable is searched for in a particular section, it won't be found if it isn't in a section. typedef struct config config_t; // Creates an empty configuration. Returns NULL on alloc error. config_t *config_create (char const *filename); // Loads a configuration file. Returns NULL if the file couldn't be read or on an alloc error. config_t *config_load (char const *filename); // Saves configuration. The filename written to will be the same filename it was created with or loaded from. int config_save (config_t *config); // Frees the data associated with a configuration void config_delete (config_t *config); enum configtype { CT_UNDEFINED, CT_STRING, CT_INT, CT_BOOL, CT_FLOAT }; enum strformat { SF_SINGLEQUOTES, SF_DOUBLEQUOTES, SF_DEFAULT = SF_SINGLEQUOTES }; enum boolformat { BF_TRUEFALSE, BF_ONOFF, BF_YESNO, BF_DEFAULT = BF_TRUEFALSE }; enum intformat { IF_DECIMAL, IF_HEX, // Treated as hexadecimal number. Prefixed with 0x, and output in base 16. IF_COLOR, // In hex, but uses '#' and always has 6 digits. IF_COLORALPHA, // Like IF_COLOR, but with 8 digits. IF_DEFAULT = IF_DECIMAL }; // Configurations accept both names and sections. The section can be NULL, in which case it will refer to the top of the file where there is no section. // Though I'm calling them ints and floats in the code, use longs and doubles for higher precision. I could change this though if I want to use slightly less memory. typedef long configint_t; typedef double configfloat_t; #define CONFIGINT_MAX LONG_MAX #define CONFIGINT_MIN LONG_MIN #define CONFIGFLT_MAX DBL_MAX #define CONFIGFLT_MIN DBL_MIN // I don't want to have to change every one of these when I make a change #define CONFIG_VARIABLE_ARGS config_t *config, char const *name, char const *section #define CONFIG_VARIABLE_ARGS_BOOL config_t *config, char const *name, char const *section, bool value #define CONFIG_VARIABLE_ARGS_STRING config_t *config, char const *name, char const *section, char const *value #define CONFIG_VARIABLE_ARGS_INT config_t *config, char const *name, char const *section, configint_t value #define CONFIG_VARIABLE_ARGS_FLOAT config_t *config, char const *name, char const *section, configfloat_t value // Gets the type of the variable. enum configtype config_get_type (CONFIG_VARIABLE_ARGS); // config_get_*: Get the value of a configuration variable. If not set or of a different type, a default value will be returned. char const *config_get_string (CONFIG_VARIABLE_ARGS); bool config_get_bool (CONFIG_VARIABLE_ARGS); configint_t config_get_int (CONFIG_VARIABLE_ARGS); configfloat_t config_get_float (CONFIG_VARIABLE_ARGS); // The rest of these return 0 on success, 1 on failure. // Sets the default values of configuration variables. If still in their default state, they will be written as comments to the configuration file. // If the default type differs from the type specified in the configuration file, the value in the configuration file will be ignored, and reset to default upon writing. // If no default is specified for a variable, it will be assumed that the variable in the configuration file is of the correct type. int config_default_string (CONFIG_VARIABLE_ARGS_STRING); int config_default_bool (CONFIG_VARIABLE_ARGS_BOOL); int config_default_int (CONFIG_VARIABLE_ARGS_INT); int config_default_float (CONFIG_VARIABLE_ARGS_FLOAT); // Sets the value of the configuration variable. If a default value for the variable has been set and the type doesn't match the expected type, the set will fail. int config_set_string (CONFIG_VARIABLE_ARGS_STRING); int config_set_bool (CONFIG_VARIABLE_ARGS_BOOL); int config_set_int (CONFIG_VARIABLE_ARGS_INT); int config_set_float (CONFIG_VARIABLE_ARGS_FLOAT); // Resets the configuration value to its default value. int config_reset_variable (CONFIG_VARIABLE_ARGS); // Sets the description of a variable. This will be written as a comment prefixed with semicolons right before the variable. int config_describe_variable (CONFIG_VARIABLE_ARGS, char const *description); // Sets the description of a section. This will be written as a comment prefixed with semicolons right before the section. int config_describe_section (config_t *config, char const *name, char const *description); // Set the format that is used to print the variables to the config file upon saving. Doesn't affect what values are accepted at load time. int config_format_string (CONFIG_VARIABLE_ARGS, enum strformat format); int config_format_bool (CONFIG_VARIABLE_ARGS, enum boolformat format); int config_format_int (CONFIG_VARIABLE_ARGS, enum intformat format); // For floating point numbers, the format is an integer specifying the maximum number of digits to save after the decimal point. // At least one will always be used, though, to ensure it is read as a float. The default number of digits preserved is 6. int config_format_float (CONFIG_VARIABLE_ARGS, unsigned digits); // Set minimum and maximum values for integer and float variables. If set to below the minimum or above the maximum, it will be set to the minimum or maximum, respectively. int config_set_min_int (CONFIG_VARIABLE_ARGS_INT); int config_set_max_int (CONFIG_VARIABLE_ARGS_INT); int config_set_min_float (CONFIG_VARIABLE_ARGS_FLOAT); int config_set_max_float (CONFIG_VARIABLE_ARGS_FLOAT); // Get minimum and maximum values for integer and float variables. // If the variable is unset or not an int / float, 0 is returned. configint_t config_get_min_int (CONFIG_VARIABLE_ARGS); configint_t config_get_max_int (CONFIG_VARIABLE_ARGS); configfloat_t config_get_min_float (CONFIG_VARIABLE_ARGS); configfloat_t config_get_max_float (CONFIG_VARIABLE_ARGS); #endif