@@ -1,3 +1,2 @@ | |||||
.idea/ | .idea/ | ||||
cmake-build-*/ | |||||
build/ | build/ |
@@ -1,18 +1,45 @@ | |||||
cmake_minimum_required(VERSION 3.24) | cmake_minimum_required(VERSION 3.24) | ||||
project(ini_config C) | |||||
project(ini-config C) | |||||
set(CMAKE_C_STANDARD 11) | set(CMAKE_C_STANDARD 11) | ||||
include(CTest) | |||||
include_directories( | include_directories( | ||||
"${CMAKE_HOME_DIRECTORY}/subprojects/minIni/dev" | "${CMAKE_HOME_DIRECTORY}/subprojects/minIni/dev" | ||||
) | ) | ||||
add_library( | add_library( | ||||
ini_config STATIC | |||||
ini-config.c | |||||
ini-config.h | |||||
types/int.c | |||||
types/int.h | |||||
types/string.c | |||||
types/string.h | |||||
ini-config STATIC | |||||
source/ini-config.c | |||||
source/ini-config.h | |||||
source/types/int.c | |||||
source/types/int.h | |||||
source/types/string.c | |||||
source/types/string.h | |||||
) | |||||
add_executable( | |||||
test-int | |||||
subprojects/minIni/dev/minIni.h | |||||
subprojects/minIni/dev/minIni.c | |||||
tests/test-int.c | |||||
source/ini-config.c | |||||
source/ini-config.h | |||||
source/types/int.c | |||||
source/types/int.h | |||||
) | |||||
set_target_properties (test-int PROPERTIES RUNTIME_OUTPUT_DIRECTORY tests) | |||||
add_test( | |||||
NAME test-int | |||||
COMMAND test-int | |||||
WORKING_DIRECTORY tests | |||||
) | |||||
add_custom_command( | |||||
TARGET test-int POST_BUILD | |||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different | |||||
"${CMAKE_HOME_DIRECTORY}/fixtures/test-int.ini" | |||||
$<TARGET_FILE_DIR:test-int> | |||||
) | ) |
@@ -5,5 +5,14 @@ | |||||
```shell | ```shell | ||||
mkdir build | mkdir build | ||||
cmake "-DCMAKE_MT=%CMAKE_MT%" -G Ninja -S . -B build | cmake "-DCMAKE_MT=%CMAKE_MT%" -G Ninja -S . -B build | ||||
cmake --build build -t ini_config | |||||
cmake --build build -t ini-config | |||||
``` | |||||
## Testing | |||||
```shell | |||||
cmake --build build -t test-int | |||||
cmake --build build -t test-string | |||||
cd build | |||||
ctest | |||||
``` | ``` |
@@ -0,0 +1,29 @@ | |||||
[Int] | |||||
Int8_0=0 | |||||
Int8_1=127 | |||||
Int8_2=-128 | |||||
Int8_3=128 | |||||
Int8_4=-129 | |||||
Int16_0=0 | |||||
Int16_1=32767 | |||||
Int16_2=-32768 | |||||
Int16_3=32768 | |||||
Int16_4=-32769 | |||||
Int32_0=0 | |||||
Int32_1=2147483647 | |||||
Int32_2=-2147483648 | |||||
Int32_3=2147483648 | |||||
Int32_4=-2147483649 | |||||
[Uint] | |||||
Uint8_0=0 | |||||
Uint8_1=255 | |||||
Uint8_2=-1 | |||||
Uint8_3=256 | |||||
Uint16_0=0 | |||||
Uint16_1=65535 | |||||
Uint16_2=-1 | |||||
Uint16_3=65536 |
@@ -1,23 +0,0 @@ | |||||
#include "ini-config.h" | |||||
const char* INI_ConfigGetCommandlineOption(uint8_t argc, const char* argv[], const char* val) { | |||||
size_t n = strlen(val); | |||||
int c = argc; | |||||
while (--c > 0) { | |||||
if (!strncmp(argv[c], val, n)) { | |||||
if (!*(argv[c] + n) && c < argc - 1) { | |||||
/* coverity treats unchecked argv as "tainted" */ | |||||
if (!argv[c + 1] || strlen(argv[c + 1]) > 1024) | |||||
return NULL; | |||||
return argv[c + 1]; | |||||
} | |||||
if (argv[c][n] == '=') | |||||
return &argv[c][n + 1]; | |||||
return argv[c] + n; | |||||
} | |||||
} | |||||
return NULL; | |||||
} |
@@ -0,0 +1,73 @@ | |||||
#include "ini-config.h" | |||||
const char* INI_ConfigGetCommandlineOption(uint8_t argc, const char* argv[], const char* val) { | |||||
size_t n = strlen(val); | |||||
int c = argc; | |||||
while (--c > 0) { | |||||
if (!strncmp(argv[c], val, n)) { | |||||
if (!*(argv[c] + n) && c < argc - 1) { | |||||
/* coverity treats unchecked argv as "tainted" */ | |||||
if (!argv[c + 1] || strlen(argv[c + 1]) > 1024) | |||||
return NULL; | |||||
return argv[c + 1]; | |||||
} | |||||
if (argv[c][n] == '=') | |||||
return &argv[c][n + 1]; | |||||
return argv[c] + n; | |||||
} | |||||
} | |||||
return NULL; | |||||
} | |||||
void INI_ConfigLoad(INI_ConfigItem item[], const char* config_path) { | |||||
uint8_t i; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
if (!item[i].type.load) { | |||||
continue; | |||||
} | |||||
item[i].type.load(&item[i], config_path); | |||||
} | |||||
} | |||||
INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem item[], const char* config_path) { | |||||
uint8_t i; | |||||
int32_t problems = 0; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
int32_t result = item[i].type.save ? item[i].type.save(&item[i], config_path) : 0; | |||||
if (result < 0) { | |||||
problems |= (1 << (int32_t) i); | |||||
} | |||||
} | |||||
return -problems; | |||||
} | |||||
void INI_ConfigOverride(INI_ConfigItem item[], uint8_t argc, const char* argv[]) { | |||||
uint8_t i; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
if (!item[i].type.override) { | |||||
continue; | |||||
} | |||||
item[i].type.override(&item[i], argc, argv); | |||||
// TODO specify command-line arg external from config item? | |||||
} | |||||
} | |||||
INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem item[], const char* config_path, uint8_t argc, const char* argv[]) { | |||||
INI_ConfigLoad(item, config_path); | |||||
INI_ConfigSaveResult save_result = INI_ConfigSave(item, config_path); | |||||
if (save_result < 0) { | |||||
return INI_CONFIG_INITIALIZE_RESULT_ERROR; | |||||
} | |||||
INI_ConfigOverride(item, argc, argv); | |||||
if (save_result > 0) { | |||||
return INI_CONFIG_INITIALIZE_RESULT_WARNING; | |||||
} | |||||
return INI_CONFIG_INITIALIZE_RESULT_OK; | |||||
} |
@@ -5,7 +5,6 @@ | |||||
#include <string.h> | #include <string.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <minIni.h> | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
extern "C" { | extern "C" { | ||||
@@ -18,11 +17,11 @@ typedef struct { | |||||
/** | /** | ||||
* Function that formats the value from memory to a value that is write-friendly to the config file. | * Function that formats the value from memory to a value that is write-friendly to the config file. | ||||
*/ | */ | ||||
void *serialize; | |||||
void* serialize; | |||||
/** | /** | ||||
* Function that formats the value from file to a value that is read-friendly to memory. | * Function that formats the value from file to a value that is read-friendly to memory. | ||||
*/ | */ | ||||
void *deserialize; | |||||
void* deserialize; | |||||
} INI_ConfigTransformer; // TODO: should we unify this with INI_ConfigType? | } INI_ConfigTransformer; // TODO: should we unify this with INI_ConfigType? | ||||
struct INI_ConfigItem; | struct INI_ConfigItem; | ||||
@@ -30,7 +29,7 @@ struct INI_ConfigItem; | |||||
/** | /** | ||||
* Function for loading a config item value from file to memory. | * Function for loading a config item value from file to memory. | ||||
*/ | */ | ||||
typedef void INI_ConfigTypeLoad(struct INI_ConfigItem *, const char *); | |||||
typedef void INI_ConfigTypeLoad(struct INI_ConfigItem*, const char*); | |||||
/** | /** | ||||
* Result enum for saving config items. | * Result enum for saving config items. | ||||
@@ -50,12 +49,12 @@ typedef enum { | |||||
/** | /** | ||||
* Function for saving a config item value from memory to file. | * Function for saving a config item value from memory to file. | ||||
*/ | */ | ||||
typedef INI_ConfigSaveItemResult INI_ConfigTypeSave(struct INI_ConfigItem *, const char *); | |||||
typedef INI_ConfigSaveItemResult INI_ConfigTypeSave(struct INI_ConfigItem*, const char*); | |||||
/** | /** | ||||
* Function for retrieving a config item value from the command-line to memory. | * Function for retrieving a config item value from the command-line to memory. | ||||
*/ | */ | ||||
typedef void INI_ConfigTypeOverride(struct INI_ConfigItem *, uint8_t, const char *[]); | |||||
typedef void INI_ConfigTypeOverride(struct INI_ConfigItem*, uint8_t, const char* []); | |||||
/** | /** | ||||
* Struct for the config item type. | * Struct for the config item type. | ||||
@@ -69,17 +68,17 @@ typedef struct { | |||||
* Load function. | * Load function. | ||||
* @see INI_ConfigTypeLoad | * @see INI_ConfigTypeLoad | ||||
*/ | */ | ||||
INI_ConfigTypeLoad *load; | |||||
INI_ConfigTypeLoad* load; | |||||
/** | /** | ||||
* Save function. | * Save function. | ||||
* @see INI_ConfigTypeSave | * @see INI_ConfigTypeSave | ||||
*/ | */ | ||||
INI_ConfigTypeSave *save; | |||||
INI_ConfigTypeSave* save; | |||||
/** | /** | ||||
* Override function. | * Override function. | ||||
* @see INI_ConfigTypeOverride | * @see INI_ConfigTypeOverride | ||||
*/ | */ | ||||
INI_ConfigTypeOverride *override; | |||||
INI_ConfigTypeOverride* override; | |||||
} INI_ConfigType; | } INI_ConfigType; | ||||
/** | /** | ||||
@@ -93,23 +92,23 @@ typedef struct INI_ConfigItem { | |||||
/** | /** | ||||
* Section where this config item can be found. | * Section where this config item can be found. | ||||
*/ | */ | ||||
const char *section; | |||||
const char* section; | |||||
/** | /** | ||||
* Key where this config item value is serialized and stored. | * Key where this config item value is serialized and stored. | ||||
*/ | */ | ||||
const char *key; | |||||
const char* key; | |||||
/** | /** | ||||
* Command-line option for overriding this config item's value. | * Command-line option for overriding this config item's value. | ||||
*/ | */ | ||||
const char *cmdline_option; // TODO: should we extract commandline parsing logic? | |||||
const char* cmdline_option; // TODO: should we extract commandline parsing logic? | |||||
/** | /** | ||||
* Default value of the config item, when the value could not be read from the config file. | * Default value of the config item, when the value could not be read from the config file. | ||||
*/ | */ | ||||
const void *default_value; | |||||
const void* default_value; | |||||
/** | /** | ||||
* Validator function for the config item's value. | * Validator function for the config item's value. | ||||
*/ | */ | ||||
void *validator; | |||||
void* validator; | |||||
/** | /** | ||||
* Transformer functions. | * Transformer functions. | ||||
* @see INI_ConfigTransformer | * @see INI_ConfigTransformer | ||||
@@ -119,14 +118,14 @@ typedef struct INI_ConfigItem { | |||||
* The memory address where the config item value will reside. This property should allow storing the amount of butes | * The memory address where the config item value will reside. This property should allow storing the amount of butes | ||||
* specified under `type.size`. | * specified under `type.size`. | ||||
*/ | */ | ||||
void *dest; | |||||
void* dest; | |||||
} INI_ConfigItem; | } INI_ConfigItem; | ||||
/** | /** | ||||
* Retrieves the value from a command-line option. | * Retrieves the value from a command-line option. | ||||
* @return The string value from the command-line option. | * @return The string value from the command-line option. | ||||
*/ | */ | ||||
const char *INI_ConfigGetCommandlineOption(uint8_t, const char *[], const char *); | |||||
const char* INI_ConfigGetCommandlineOption(uint8_t, const char*[], const char*); | |||||
/** | /** | ||||
* Result enum for initializing config items. | * Result enum for initializing config items. | ||||
@@ -146,11 +145,19 @@ typedef enum { | |||||
INI_CONFIG_INITIALIZE_RESULT_WARNING | INI_CONFIG_INITIALIZE_RESULT_WARNING | ||||
} INI_ConfigInitializeResult; | } INI_ConfigInitializeResult; | ||||
INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem[], const char *, uint8_t, const char *[]); | |||||
/** | |||||
* Syncs the config file to memory. This includes the loading of the items from the config file. | |||||
* @return Result of the initialization. | |||||
*/ | |||||
INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem[], const char*, uint8_t, const char*[]); | |||||
typedef int32_t INI_ConfigSaveResult; | typedef int32_t INI_ConfigSaveResult; | ||||
INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem[], const char *); | |||||
/** | |||||
* Saves the contents of the memory to the config file. | |||||
* @return | |||||
*/ | |||||
INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem[], const char*); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
}; | }; | ||||
@@ -161,11 +168,17 @@ INI_ConfigTypeLoad INI_ConfigLoad##ID; \ | |||||
INI_ConfigTypeSave INI_ConfigSave##ID; \ | INI_ConfigTypeSave INI_ConfigSave##ID; \ | ||||
INI_ConfigTypeOverride INI_ConfigOverride##ID | INI_ConfigTypeOverride INI_ConfigOverride##ID | ||||
/** | |||||
* Null transformer. | |||||
*/ | |||||
#define INI_CONFIG_TRANSFORMER_NONE (INI_ConfigTransformer) { \ | #define INI_CONFIG_TRANSFORMER_NONE (INI_ConfigTransformer) { \ | ||||
.serialize = NULL, \ | .serialize = NULL, \ | ||||
.deserialize = NULL, \ | .deserialize = NULL, \ | ||||
} | } | ||||
/** | |||||
* Null item. Use this to signify the end of the config item list. | |||||
*/ | |||||
#define INI_CONFIG_ITEM_NULL (INI_ConfigItem) { \ | #define INI_CONFIG_ITEM_NULL (INI_ConfigItem) { \ | ||||
(INI_ConfigType) { \ | (INI_ConfigType) { \ | ||||
.size = 0, \ | .size = 0, \ |
@@ -1,6 +1,7 @@ | |||||
#ifndef INI_CONFIG_TYPES_INT_H | #ifndef INI_CONFIG_TYPES_INT_H | ||||
#define INI_CONFIG_TYPES_INT_H | #define INI_CONFIG_TYPES_INT_H | ||||
#include <minIni.h> | |||||
#include "../ini-config.h" | #include "../ini-config.h" | ||||
INI_CONFIG_DECLARE_TYPE(U8); | INI_CONFIG_DECLARE_TYPE(U8); |
@@ -1,6 +1,5 @@ | |||||
#include "string.h" | #include "string.h" | ||||
typedef bool INI_ConfigLoadParamsStringValidator(const char*); | typedef bool INI_ConfigLoadParamsStringValidator(const char*); | ||||
void INI_ConfigEnsureValidString(INI_ConfigItem* item, const char* buffer) { | void INI_ConfigEnsureValidString(INI_ConfigItem* item, const char* buffer) { | ||||
@@ -47,53 +46,3 @@ void INI_ConfigOverrideString(INI_ConfigItem* item, uint8_t argc, const char* ar | |||||
INI_ConfigEnsureValidString(item, cmdline_buffer); | INI_ConfigEnsureValidString(item, cmdline_buffer); | ||||
} | } | ||||
} | } | ||||
void INI_ConfigLoad(INI_ConfigItem item[], const char* config_path) { | |||||
uint8_t i; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
if (!item[i].type.load) { | |||||
continue; | |||||
} | |||||
item[i].type.load(&item[i], config_path); | |||||
} | |||||
} | |||||
INI_ConfigSaveResult INI_ConfigSave(INI_ConfigItem item[], const char* config_path) { | |||||
uint8_t i; | |||||
int32_t problems = 0; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
int32_t result = item[i].type.save ? item[i].type.save(&item[i], config_path) : 0; | |||||
if (result < 0) { | |||||
problems |= (1 << (int32_t) i); | |||||
} | |||||
} | |||||
return -problems; | |||||
} | |||||
void INI_ConfigOverride(INI_ConfigItem item[], uint8_t argc, const char* argv[]) { | |||||
uint8_t i; | |||||
for (i = 0; item[i].type.size > 0; i += 1) { | |||||
if (!item[i].type.override) { | |||||
continue; | |||||
} | |||||
item[i].type.override(&item[i], argc, argv); | |||||
// TODO specify command-line arg external from config item? | |||||
} | |||||
} | |||||
INI_ConfigInitializeResult INI_ConfigInitialize(INI_ConfigItem item[], const char* config_path, uint8_t argc, const char* argv[]) { | |||||
INI_ConfigLoad(item, config_path); | |||||
INI_ConfigSaveResult save_result = INI_ConfigSave(item, config_path); | |||||
if (save_result < 0) { | |||||
return INI_CONFIG_INITIALIZE_RESULT_ERROR; | |||||
} | |||||
INI_ConfigOverride(item, argc, argv); | |||||
if (save_result > 0) { | |||||
return INI_CONFIG_INITIALIZE_RESULT_WARNING; | |||||
} | |||||
return INI_CONFIG_INITIALIZE_RESULT_OK; | |||||
} |
@@ -1,6 +1,7 @@ | |||||
#ifndef INI_CONFIG_TYPES_STRING_H | #ifndef INI_CONFIG_TYPES_STRING_H | ||||
#define INI_CONFIG_TYPES_STRING_H | #define INI_CONFIG_TYPES_STRING_H | ||||
#include <minIni.h> | |||||
#include "../ini-config.h" | #include "../ini-config.h" | ||||
INI_CONFIG_DECLARE_TYPE(String); | INI_CONFIG_DECLARE_TYPE(String); |
@@ -0,0 +1,77 @@ | |||||
#include <assert.h> | |||||
#include <stdio.h> | |||||
#include <stdint.h> | |||||
#include "../source/ini-config.h" | |||||
#include "../source/types/int.h" | |||||
static int8_t default_int8_value = 0; | |||||
static int8_t read_int8_values[] = { 0, 0, 0, 0, 0 }; | |||||
static int8_t expected_int8_values[] = { 0, 127, -128, -128, 127 }; | |||||
static INI_ConfigItem items[] = { | |||||
{ | |||||
.section = "Int", | |||||
.key = "Int8_0", | |||||
.type = INI_CONFIG_TYPE_I8, | |||||
.default_value = &default_int8_value, | |||||
.transformer = INI_CONFIG_TRANSFORMER_NONE, | |||||
.validator = NULL, | |||||
.cmdline_option = NULL, | |||||
.dest = &read_int8_values[0], | |||||
}, | |||||
{ | |||||
.section = "Int", | |||||
.key = "Int8_1", | |||||
.type = INI_CONFIG_TYPE_I8, | |||||
.default_value = &default_int8_value, | |||||
.transformer = INI_CONFIG_TRANSFORMER_NONE, | |||||
.validator = NULL, | |||||
.cmdline_option = NULL, | |||||
.dest = &read_int8_values[1], | |||||
}, | |||||
{ | |||||
.section = "Int", | |||||
.key = "Int8_2", | |||||
.type = INI_CONFIG_TYPE_I8, | |||||
.default_value = &default_int8_value, | |||||
.transformer = INI_CONFIG_TRANSFORMER_NONE, | |||||
.validator = NULL, | |||||
.cmdline_option = NULL, | |||||
.dest = &read_int8_values[2], | |||||
}, | |||||
{ | |||||
.section = "Int", | |||||
.key = "Int8_3", | |||||
.type = INI_CONFIG_TYPE_I8, | |||||
.default_value = &default_int8_value, | |||||
.transformer = INI_CONFIG_TRANSFORMER_NONE, | |||||
.validator = NULL, | |||||
.cmdline_option = NULL, | |||||
.dest = &read_int8_values[3], | |||||
}, | |||||
{ | |||||
.section = "Int", | |||||
.key = "Int8_4", | |||||
.type = INI_CONFIG_TYPE_I8, | |||||
.default_value = &default_int8_value, | |||||
.transformer = INI_CONFIG_TRANSFORMER_NONE, | |||||
.validator = NULL, | |||||
.cmdline_option = NULL, | |||||
.dest = &read_int8_values[4], | |||||
}, | |||||
INI_CONFIG_ITEM_NULL | |||||
}; | |||||
int main() { | |||||
printf("Fixture file exists? "); | |||||
const char* argv[] = { }; | |||||
int argc = 0; | |||||
assert(INI_ConfigInitialize(items, "test-int.ini", argc, argv) == 0); | |||||
printf("Yes.\n"); | |||||
for (uint8_t i = 0; i < 5; i += 1) { | |||||
printf("read: %d, expected: %d\n", read_int8_values[i], expected_int8_values[i]); | |||||
assert(read_int8_values[i] == expected_int8_values[i]); | |||||
} | |||||
return 0; | |||||
} |