@@ -1,3 +1,2 @@ | |||
.idea/ | |||
cmake-build-*/ | |||
build/ |
@@ -1,18 +1,45 @@ | |||
cmake_minimum_required(VERSION 3.24) | |||
project(ini_config C) | |||
project(ini-config C) | |||
set(CMAKE_C_STANDARD 11) | |||
include(CTest) | |||
include_directories( | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/minIni/dev" | |||
) | |||
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 | |||
mkdir 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 <stdint.h> | |||
#include <stdbool.h> | |||
#include <minIni.h> | |||
#ifdef __cplusplus | |||
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. | |||
*/ | |||
void *serialize; | |||
void* serialize; | |||
/** | |||
* 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? | |||
struct INI_ConfigItem; | |||
@@ -30,7 +29,7 @@ struct INI_ConfigItem; | |||
/** | |||
* 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. | |||
@@ -50,12 +49,12 @@ typedef enum { | |||
/** | |||
* 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. | |||
*/ | |||
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. | |||
@@ -69,17 +68,17 @@ typedef struct { | |||
* Load function. | |||
* @see INI_ConfigTypeLoad | |||
*/ | |||
INI_ConfigTypeLoad *load; | |||
INI_ConfigTypeLoad* load; | |||
/** | |||
* Save function. | |||
* @see INI_ConfigTypeSave | |||
*/ | |||
INI_ConfigTypeSave *save; | |||
INI_ConfigTypeSave* save; | |||
/** | |||
* Override function. | |||
* @see INI_ConfigTypeOverride | |||
*/ | |||
INI_ConfigTypeOverride *override; | |||
INI_ConfigTypeOverride* override; | |||
} INI_ConfigType; | |||
/** | |||
@@ -93,23 +92,23 @@ typedef struct INI_ConfigItem { | |||
/** | |||
* Section where this config item can be found. | |||
*/ | |||
const char *section; | |||
const char* section; | |||
/** | |||
* 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. | |||
*/ | |||
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. | |||
*/ | |||
const void *default_value; | |||
const void* default_value; | |||
/** | |||
* Validator function for the config item's value. | |||
*/ | |||
void *validator; | |||
void* validator; | |||
/** | |||
* Transformer functions. | |||
* @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 | |||
* specified under `type.size`. | |||
*/ | |||
void *dest; | |||
void* dest; | |||
} INI_ConfigItem; | |||
/** | |||
* Retrieves the value from a 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. | |||
@@ -146,11 +145,19 @@ typedef enum { | |||
INI_CONFIG_INITIALIZE_RESULT_WARNING | |||
} 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; | |||
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 | |||
}; | |||
@@ -161,11 +168,17 @@ INI_ConfigTypeLoad INI_ConfigLoad##ID; \ | |||
INI_ConfigTypeSave INI_ConfigSave##ID; \ | |||
INI_ConfigTypeOverride INI_ConfigOverride##ID | |||
/** | |||
* Null transformer. | |||
*/ | |||
#define INI_CONFIG_TRANSFORMER_NONE (INI_ConfigTransformer) { \ | |||
.serialize = NULL, \ | |||
.deserialize = NULL, \ | |||
} | |||
/** | |||
* Null item. Use this to signify the end of the config item list. | |||
*/ | |||
#define INI_CONFIG_ITEM_NULL (INI_ConfigItem) { \ | |||
(INI_ConfigType) { \ | |||
.size = 0, \ |
@@ -1,6 +1,7 @@ | |||
#ifndef INI_CONFIG_TYPES_INT_H | |||
#define INI_CONFIG_TYPES_INT_H | |||
#include <minIni.h> | |||
#include "../ini-config.h" | |||
INI_CONFIG_DECLARE_TYPE(U8); |
@@ -1,6 +1,5 @@ | |||
#include "string.h" | |||
typedef bool INI_ConfigLoadParamsStringValidator(const char*); | |||
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); | |||
} | |||
} | |||
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 | |||
#define INI_CONFIG_TYPES_STRING_H | |||
#include <minIni.h> | |||
#include "../ini-config.h" | |||
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; | |||
} |