|
|
@@ -0,0 +1,212 @@ |
|
|
|
#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; |
|
|
|
} |
|
|
|
|
|
|
|
#define INI_CONFIG_REGISTER_INT_TYPE(ID, T) \ |
|
|
|
typedef bool INI_ConfigValidate##ID(T); \ |
|
|
|
\ |
|
|
|
typedef T INI_ConfigDeserialize##ID(const char*); \ |
|
|
|
\ |
|
|
|
typedef void INI_ConfigSerialize##ID(T, const char[128]); \ |
|
|
|
\ |
|
|
|
void INI_ConfigEnsureValid##ID(INI_ConfigItem* item, T raw_value, T default_value) { \ |
|
|
|
T* dest = item->dest; \ |
|
|
|
if (item->validator) { \ |
|
|
|
INI_ConfigValidate##ID* validate = item->validator; \ |
|
|
|
if (validate(raw_value)) { \ |
|
|
|
*dest = raw_value; \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
*dest = default_value; \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
*dest = raw_value; \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
void INI_ConfigLoad##ID(INI_ConfigItem* item, const char* config_path) { \ |
|
|
|
static T raw_value; \ |
|
|
|
static T default_value; \ |
|
|
|
default_value = *((T*) item->default_value); \ |
|
|
|
if (item->transformer.deserialize && item->transformer.serialize) { \ |
|
|
|
INI_ConfigDeserialize##ID* deserialize = item->transformer.deserialize; \ |
|
|
|
INI_ConfigSerialize##ID* serialize = item->transformer.serialize; \ |
|
|
|
const char serialized_default_value[128]; \ |
|
|
|
if (default_value) { \ |
|
|
|
serialize(default_value, serialized_default_value); \ |
|
|
|
} \ |
|
|
|
char buffer[128]; \ |
|
|
|
ini_gets(item->section, item->key, serialized_default_value, buffer, 128, config_path); \ |
|
|
|
raw_value = deserialize(buffer); \ |
|
|
|
} else { \ |
|
|
|
raw_value = ini_getl(item->section, item->key, default_value, config_path); \ |
|
|
|
} \ |
|
|
|
INI_ConfigEnsureValid##ID(item, raw_value, default_value); \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
INI_ConfigSaveItemResult INI_ConfigSave##ID(INI_ConfigItem* item, const char* config_path) { \ |
|
|
|
T dest = *((T*) item->dest); \ |
|
|
|
if (item->validator) { \ |
|
|
|
INI_ConfigValidate##ID* validate = item->validator; \ |
|
|
|
if (!validate(dest)) { \ |
|
|
|
dest = *((const T*) item->default_value); \ |
|
|
|
} \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
if (item->transformer.deserialize && item->transformer.serialize) { \ |
|
|
|
INI_ConfigSerialize##ID* serialize = item->transformer.serialize; \ |
|
|
|
const char serialized_value[128]; \ |
|
|
|
serialize(dest, serialized_value); \ |
|
|
|
if (!ini_puts(item->section, item->key, serialized_value, config_path)) { \ |
|
|
|
return -1; \ |
|
|
|
} \ |
|
|
|
return 0; \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
if (!ini_putl(item->section, item->key, dest, config_path)) { \ |
|
|
|
return -1; \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
return 0; \ |
|
|
|
} \ |
|
|
|
\ |
|
|
|
void INI_ConfigOverride##ID(INI_ConfigItem* item, uint8_t argc, const char* argv[]) { \ |
|
|
|
if (!item->cmdline_option) { \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
const char* cmdline_buffer; \ |
|
|
|
char* rest_of_string; \ |
|
|
|
static T dest; \ |
|
|
|
static T config_value; \ |
|
|
|
config_value = *((T*) item->dest); \ |
|
|
|
if ((cmdline_buffer = INI_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) { \ |
|
|
|
dest = strtol(cmdline_buffer, &rest_of_string, 10); \ |
|
|
|
if (strcmp(cmdline_buffer, rest_of_string) != 0) { \ |
|
|
|
INI_ConfigEnsureValid##ID(item, dest, config_value); \ |
|
|
|
return; \ |
|
|
|
} \ |
|
|
|
} \ |
|
|
|
} |
|
|
|
|
|
|
|
INI_CONFIG_REGISTER_INT_TYPE(U8, uint8_t); |
|
|
|
INI_CONFIG_REGISTER_INT_TYPE(U16, uint16_t); |
|
|
|
INI_CONFIG_REGISTER_INT_TYPE(I32, int32_t); |
|
|
|
|
|
|
|
typedef bool INI_ConfigLoadParamsStringValidator(const char*); |
|
|
|
|
|
|
|
void INI_ConfigEnsureValidString(INI_ConfigItem* item, const char* buffer) { |
|
|
|
if (item->validator) { |
|
|
|
INI_ConfigLoadParamsStringValidator* validator = item->validator; |
|
|
|
if (validator(buffer)) { |
|
|
|
memcpy(item->dest, buffer, item->fns.size); |
|
|
|
return; |
|
|
|
} |
|
|
|
memcpy(item->dest, item->default_value, item->fns.size); |
|
|
|
return; |
|
|
|
} |
|
|
|
memcpy(item->dest, buffer, item->fns.size); |
|
|
|
} |
|
|
|
|
|
|
|
void INI_ConfigLoadString(INI_ConfigItem* item, const char* config_path) { |
|
|
|
char buffer[item->fns.size]; |
|
|
|
ini_gets(item->section, item->key, item->default_value, buffer, (int32_t) item->fns.size, config_path); |
|
|
|
INI_ConfigEnsureValidString(item, buffer); |
|
|
|
} |
|
|
|
|
|
|
|
INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem* item, const char* config_path) { |
|
|
|
const char* dest = (const char*) item->dest; |
|
|
|
if (item->validator) { |
|
|
|
INI_ConfigLoadParamsStringValidator* validator = item->validator; |
|
|
|
if (!validator(dest)) { |
|
|
|
dest = (const char*) item->default_value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!ini_puts(item->section, item->key, dest, config_path)) { |
|
|
|
return INI_CONFIG_SAVE_ITEM_ERROR; |
|
|
|
} |
|
|
|
|
|
|
|
return INI_CONFIG_SAVE_ITEM_OK; |
|
|
|
} |
|
|
|
|
|
|
|
void INI_ConfigOverrideString(INI_ConfigItem* item, uint8_t argc, const char* argv[]) { |
|
|
|
if (!item->cmdline_option) { |
|
|
|
return; |
|
|
|
} |
|
|
|
const char* cmdline_buffer; |
|
|
|
if ((cmdline_buffer = INI_ConfigGetCommandlineOption(argc, argv, item->cmdline_option))) { |
|
|
|
INI_ConfigEnsureValidString(item, cmdline_buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void INI_ConfigLoad(INI_ConfigItem item[], const char* config_path) { |
|
|
|
uint8_t i; |
|
|
|
for (i = 0; item[i].fns.size > 0; i += 1) { |
|
|
|
if (!item[i].fns.load) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
item[i].fns.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].fns.size > 0; i += 1) { |
|
|
|
int32_t result = item[i].fns.save ? item[i].fns.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].fns.size > 0; i += 1) { |
|
|
|
if (!item[i].fns.override) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
item[i].fns.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) { |
|
|
|
//INI_LogError("config", "Sync failed! Result: %u", save_result); |
|
|
|
return INI_CONFIG_INITIALIZE_RESULT_ERROR; |
|
|
|
} |
|
|
|
INI_ConfigOverride(item, argc, argv); |
|
|
|
if (save_result > 0) { |
|
|
|
//INI_LogWarn(false, "config", "Sync encountered issues. Result: %u", save_result); |
|
|
|
return INI_CONFIG_INITIALIZE_RESULT_WARNING; |
|
|
|
} |
|
|
|
//INI_LogInfo(INI_LOG_CATEGORY_GLOBAL, "config", "Sync successful."); |
|
|
|
return INI_CONFIG_INITIALIZE_RESULT_OK; |
|
|
|
} |