Browse Source

Initial commit

Extract source files from Izanagi.
master
TheoryOfNekomata 1 year ago
commit
6dbbef47a4
8 changed files with 386 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +3
    -0
      .gitmodules
  3. +12
    -0
      CMakeLists.txt
  4. +21
    -0
      LICENSE
  5. +10
    -0
      README.md
  6. +212
    -0
      ini-config.c
  7. +125
    -0
      ini-config.h
  8. +1
    -0
      subprojects/minIni

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
.idea/
cmake-build-*/

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "subprojects/minIni"]
path = subprojects/minIni
url = https://github.com/compuphase/minIni.git

+ 12
- 0
CMakeLists.txt View File

@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.24)
project(ini_config C)

set(CMAKE_C_STANDARD 11)

include_directories(
"${CMAKE_HOME_DIRECTORY}/subprojects/minIni/dev"
)

add_executable(ini_config
ini-config.c
ini-config.h)

+ 21
- 0
LICENSE View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2023 Allan Crisostomo <allan.crisostomo@outlook.com>
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.

+ 10
- 0
README.md View File

@@ -0,0 +1,10 @@
## Procedure

Subsystem calls its initialize method.

1. Subsystem copies hardcoded default values to its in-memory state container.
2. Subsystem defines the available config items it has, and how it is connected to various config sources (i.e. specify its section and key in the config file, as well as supplying command line options connected to this config item).
3. Subsystem binds the config items with their respective sections and keys in the config file (i.e. correctly point the values to the respective internal state container of the app).
4. Subsystem retrieves the config file values as well as command line arguments (higher priority) supplied to the app upon invocation.
5. Subsystem checks if there are some volatile state data to retrieve and syncs it with the state.
6. Subsystem saves the state to the config file.

+ 212
- 0
ini-config.c View File

@@ -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;
}

+ 125
- 0
ini-config.h View File

@@ -0,0 +1,125 @@
#ifndef INI_CONFIG_H
#define INI_CONFIG_H

#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <minIni.h>

typedef struct {
void* serialize;
void* deserialize;
} INI_ConfigSerializerPair;

struct INI_ConfigItem;

typedef void INI_ConfigLoadType(struct INI_ConfigItem*, const char*);

typedef enum {
INI_CONFIG_SAVE_ITEM_ERROR = -1,
INI_CONFIG_SAVE_ITEM_OK,
} INI_ConfigSaveItemResult;

typedef INI_ConfigSaveItemResult INI_ConfigSaveType(struct INI_ConfigItem*, const char*);

typedef void INI_ConfigOverrideType(struct INI_ConfigItem*, uint8_t, const char*[]);
typedef struct {
size_t size;
INI_ConfigLoadType* load;
INI_ConfigSaveType* save;
INI_ConfigOverrideType* override;
} INI_ConfigTypeFns;

typedef struct INI_ConfigItem {
INI_ConfigTypeFns fns;
const char* section;
const char* key;
const char* cmdline_option;
const void* default_value;
void* validator;
INI_ConfigSerializerPair transformer;
void* dest;
} INI_ConfigItem;

void INI_ConfigGetDefaultPath(char*, size_t);

const char* INI_ConfigGetCommandlineOption(uint8_t, const char*[], const char*);

typedef enum {
INI_CONFIG_INITIALIZE_RESULT_ERROR = -1,
INI_CONFIG_INITIALIZE_RESULT_OK,
INI_CONFIG_INITIALIZE_RESULT_WARNING
} INI_ConfigInitializeResult;

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*);

void INI_ConfigLoadU8(INI_ConfigItem*, const char*);
void INI_ConfigLoadU16(INI_ConfigItem*, const char*);
void INI_ConfigLoadI32(INI_ConfigItem*, const char*);
void INI_ConfigLoadString(INI_ConfigItem*, const char*);

INI_ConfigSaveItemResult INI_ConfigSaveU8(INI_ConfigItem*, const char*);
INI_ConfigSaveItemResult INI_ConfigSaveU16(INI_ConfigItem*, const char*);
INI_ConfigSaveItemResult INI_ConfigSaveI32(INI_ConfigItem*, const char*);
INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem*, const char*);

void INI_ConfigOverrideU8(INI_ConfigItem*, uint8_t, const char*[]);
void INI_ConfigOverrideU16(INI_ConfigItem*, uint8_t, const char*[]);
void INI_ConfigOverrideI32(INI_ConfigItem*, uint8_t, const char*[]);
void INI_ConfigOverrideString(INI_ConfigItem*, uint8_t, const char*[]);

#define INI_CONFIG_TYPE_FNS_I32 (INI_ConfigTypeFns) { \
.size = sizeof(int32_t), \
.load = INI_ConfigLoadI32, \
.save = INI_ConfigSaveI32, \
.override = INI_ConfigOverrideI32, \
}

#define INI_CONFIG_TYPE_FNS_U16 (INI_ConfigTypeFns) { \
.size = sizeof(uint16_t), \
.load = INI_ConfigLoadU16, \
.save = INI_ConfigSaveU16, \
.override = INI_ConfigOverrideU16, \
}

#define INI_CONFIG_TYPE_FNS_U8 (INI_ConfigTypeFns) { \
.size = sizeof(uint8_t), \
.load = INI_ConfigLoadU8, \
.save = INI_ConfigSaveU8, \
.override = INI_ConfigOverrideU8, \
}

#define INI_CONFIG_TYPE_FNS_VOID (INI_ConfigTypeFns) { \
.size = 0, \
.load = NULL, \
.save = NULL, \
.override = NULL, \
}

#define INI_CONFIG_TYPE_FNS_STRING(X) (INI_ConfigTypeFns) { \
.size = (X), \
.load = INI_ConfigLoadString, \
.save = INI_ConfigSaveString, \
.override = INI_ConfigOverrideString, \
}

#define INI_CONFIG_ITEM_NULL (INI_ConfigItem) { \
INI_CONFIG_TYPE_FNS_VOID, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
{ \
.serialize = NULL, \
.deserialize = NULL, \
}, \
NULL, \
}

#endif

+ 1
- 0
subprojects/minIni

@@ -0,0 +1 @@
Subproject commit 755f9a8b5c3b8d8ced9bd2d9173dd1d1cac8efc2

Loading…
Cancel
Save