Replace ini with toml
This commit is contained in:
parent
97a19135ce
commit
5796f88e05
11 changed files with 242 additions and 5726 deletions
|
@ -1,16 +0,0 @@
|
|||
[core]
|
||||
server-port = 5000
|
||||
database = emgauwa-core.sqlite
|
||||
content-dir = /usr/share/webapps/emgauwa
|
||||
not-found-file = 404.html
|
||||
not-found-file-mime = text/html
|
||||
not-found-content = 404 - NOT FOUND
|
||||
not-found-content-type = text/plain
|
||||
|
||||
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
||||
discovery-port = 4421
|
||||
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||
mqtt-port = 1885
|
||||
|
||||
log-level = debug
|
||||
log-file = stdout
|
|
@ -4,7 +4,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <mongoose.h>
|
||||
#include <confini.h>
|
||||
#include <toml.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -13,16 +13,25 @@ typedef struct
|
|||
char *database;
|
||||
char *user;
|
||||
char *group;
|
||||
int log_level;
|
||||
FILE *log_file;
|
||||
uint16_t server_port;
|
||||
uint16_t discovery_port;
|
||||
uint16_t mqtt_port;
|
||||
char *content_dir;
|
||||
char *not_found_file;
|
||||
char *not_found_file_type;
|
||||
char *not_found_content;
|
||||
char *not_found_content_type;
|
||||
|
||||
struct
|
||||
{
|
||||
int level;
|
||||
FILE *file;
|
||||
} logging;
|
||||
|
||||
struct
|
||||
{
|
||||
uint16_t server;
|
||||
uint16_t discovery;
|
||||
uint16_t mqtt;
|
||||
} ports;
|
||||
|
||||
struct mg_serve_http_opts http_server_opts;
|
||||
} config_t;
|
||||
|
||||
|
@ -38,6 +47,9 @@ void
|
|||
config_load_string(char **holder, const char *value);
|
||||
|
||||
int
|
||||
config_load(IniDispatch *disp, void *config_void);
|
||||
config_load(config_t *config);
|
||||
|
||||
void
|
||||
config_load_directory(config_t *config, char *directory_name);
|
||||
|
||||
#endif /* CORE_CONFIG_H */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#define PIFACE_GPIO_BASE 200
|
||||
|
||||
#define DEFAULT_CONFIG_PATH "emgauwa-core.ini"
|
||||
#define DEFAULT_CONFIG_PATH "emgauwa-core.toml"
|
||||
#define DEFAULT_DISCOVERY_PORT 4421
|
||||
#define DEFAULT_MQTT_PORT 1885
|
||||
#define DEFAULT_SERVER_PORT 5000
|
||||
|
|
305
src/config.c
305
src/config.c
|
@ -1,5 +1,8 @@
|
|||
#include <bsd/string.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
|
@ -13,72 +16,185 @@ config_t *global_config;
|
|||
ini_string_match_ii(KEY, disp->data, disp->format))
|
||||
|
||||
static int
|
||||
config_load_log_level(IniDispatch *disp, config_t *config)
|
||||
config_load_log_level(config_t *config, char *value)
|
||||
{
|
||||
if(strcmp(disp->value, "debug") == 0)
|
||||
if(strcmp(value, "debug") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||
config->log_level = LOG_DEBUG;
|
||||
config->logging.level = LOG_DEBUG;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "info") == 0)
|
||||
if(strcmp(value, "info") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
config->log_level = LOG_INFO;
|
||||
config->logging.level = LOG_INFO;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "notice") == 0)
|
||||
if(strcmp(value, "notice") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||
config->log_level = LOG_NOTICE;
|
||||
config->logging.level = LOG_NOTICE;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "warning") == 0)
|
||||
if(strcmp(value, "warning") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_WARNING));
|
||||
config->log_level = LOG_WARNING;
|
||||
config->logging.level = LOG_WARNING;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "err") == 0)
|
||||
if(strcmp(value, "err") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_ERR));
|
||||
config->log_level = LOG_ERR;
|
||||
config->logging.level = LOG_ERR;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "crit") == 0)
|
||||
if(strcmp(value, "crit") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_CRIT));
|
||||
config->log_level = LOG_CRIT;
|
||||
config->logging.level = LOG_CRIT;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "emerg") == 0)
|
||||
if(strcmp(value, "emerg") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_EMERG));
|
||||
config->log_level = LOG_EMERG;
|
||||
config->logging.level = LOG_EMERG;
|
||||
return 0;
|
||||
}
|
||||
LOGGER_WARNING("invalid log-level '%s'\n", disp->value);
|
||||
LOGGER_WARNING("invalid log-level '%s'\n", value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
config_load_log_file(IniDispatch *disp, config_t *config)
|
||||
config_load_log_file(config_t *config, char *value)
|
||||
{
|
||||
if(strcmp(disp->value, "stdout") == 0)
|
||||
if(strcmp(value, "stdout") == 0)
|
||||
{
|
||||
config->log_file = stdout;
|
||||
config->logging.file = stdout;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(disp->value, "stderr") == 0)
|
||||
if(strcmp(value, "stderr") == 0)
|
||||
{
|
||||
config->log_file = stderr;
|
||||
config->logging.file = stderr;
|
||||
return 0;
|
||||
}
|
||||
config->log_file = fopen(disp->value, "a+");
|
||||
config->logging.file = fopen(value, "a+");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_core(config_t *config, toml_table_t* core)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_string_in(core, "database");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->database, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "user");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->user, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "group");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->group, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "content-dir");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->content_dir, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "not-found-file");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->not_found_file, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "not-found-file-type");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->not_found_file_type, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "not-found-content");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->not_found_content, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "not-found-content-type");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->not_found_content_type, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(core, "include");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->include, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_logging(config_t *config, toml_table_t* logging)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_string_in(logging, "level");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_log_level(config, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(logging, "file");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_log_file(config, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_ports(config_t *config, toml_table_t* ports)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_int_in(ports, "server");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->ports.server = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(ports, "discovery");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->ports.discovery = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(ports, "mqtt");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->ports.mqtt = config_entry.u.i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
config_init()
|
||||
{
|
||||
|
@ -86,12 +202,12 @@ config_init()
|
|||
|
||||
config_load_string(&global_config->file, DEFAULT_CONFIG_PATH);
|
||||
|
||||
global_config->discovery_port = DEFAULT_DISCOVERY_PORT;
|
||||
global_config->mqtt_port = DEFAULT_MQTT_PORT;
|
||||
global_config->server_port = DEFAULT_SERVER_PORT;
|
||||
global_config->ports.discovery = DEFAULT_DISCOVERY_PORT;
|
||||
global_config->ports.mqtt = DEFAULT_MQTT_PORT;
|
||||
global_config->ports.server = DEFAULT_SERVER_PORT;
|
||||
|
||||
global_config->log_level = LOG_DEBUG;
|
||||
global_config->log_file = stdout;
|
||||
global_config->logging.level = LOG_DEBUG;
|
||||
global_config->logging.file = stdout;
|
||||
|
||||
global_config->user = NULL;
|
||||
global_config->group = NULL;
|
||||
|
@ -136,80 +252,71 @@ config_load_string(char **holder, const char *value)
|
|||
}
|
||||
|
||||
int
|
||||
config_load(IniDispatch *disp, void *config_void)
|
||||
config_load(config_t *config)
|
||||
{
|
||||
config_t *config = (config_t*)config_void;
|
||||
FILE *fp;
|
||||
toml_table_t* config_toml;
|
||||
char errbuf[256];
|
||||
|
||||
if(disp->type == INI_KEY)
|
||||
{
|
||||
if(CONFINI_IS_KEY("core", "include"))
|
||||
{
|
||||
config_load_string(&config->include, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "database"))
|
||||
{
|
||||
config_load_string(&config->database, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "user"))
|
||||
{
|
||||
config_load_string(&config->user, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "group"))
|
||||
{
|
||||
config_load_string(&config->group, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "content-dir"))
|
||||
{
|
||||
config_load_string(&config->content_dir, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "not-found-file"))
|
||||
{
|
||||
config_load_string(&config->not_found_file, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "not-found-file-type"))
|
||||
{
|
||||
config_load_string(&config->not_found_file_type, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "not-found-content"))
|
||||
{
|
||||
config_load_string(&config->not_found_content, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "not-found-content-type"))
|
||||
{
|
||||
config_load_string(&config->not_found_content_type, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "log-level"))
|
||||
{
|
||||
return config_load_log_level(disp, config);
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "log-file"))
|
||||
{
|
||||
return config_load_log_file(disp, config);
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "server-port"))
|
||||
{
|
||||
config->server_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "discovery-port"))
|
||||
{
|
||||
config->discovery_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("core", "mqtt-port"))
|
||||
{
|
||||
config->mqtt_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
/* Open the file and parse content */
|
||||
fp = fopen(global_config->file, "r");
|
||||
if(fp == NULL) {
|
||||
LOGGER_CRIT("unable to open config file '%s'\n", global_config->file);
|
||||
exit(1);
|
||||
}
|
||||
config_toml = toml_parse_file(fp, errbuf, sizeof(errbuf));
|
||||
fclose(fp);
|
||||
if(config_toml == NULL) {
|
||||
LOGGER_CRIT("unable to parse config file '%s': %s\n", global_config->file, errbuf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
toml_table_t* core = toml_table_in(config_toml, "core");
|
||||
if(core)
|
||||
{
|
||||
config_load_section_core(config, core);
|
||||
}
|
||||
|
||||
toml_table_t* logging = toml_table_in(config_toml, "logging");
|
||||
if(logging)
|
||||
{
|
||||
config_load_section_logging(config, logging);
|
||||
}
|
||||
|
||||
toml_table_t* ports = toml_table_in(config_toml, "ports");
|
||||
if(ports)
|
||||
{
|
||||
config_load_section_ports(config, ports);
|
||||
}
|
||||
|
||||
toml_free(config_toml);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
config_load_directory(config_t *config, char *directory_name)
|
||||
{
|
||||
struct dirent *pDirent;
|
||||
DIR *pDir;
|
||||
|
||||
(void)config;
|
||||
|
||||
pDir = opendir (directory_name);
|
||||
if(pDir == NULL)
|
||||
{
|
||||
LOGGER_CRIT("cannot open directory '%s': %s\n", directory_name, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Process each entry.
|
||||
|
||||
while((pDirent = readdir(pDir)) != NULL)
|
||||
{
|
||||
LOGGER_DEBUG("including: %s\n", pDirent->d_name);
|
||||
}
|
||||
|
||||
// Close directory and exit.
|
||||
|
||||
closedir(pDir);
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ api_v1_controllers_discover_PUT(struct mg_connection *nc, struct http_message *h
|
|||
payload[0] = discover_server_port;
|
||||
|
||||
LOGGER_DEBUG("sending udp broadcast\n");
|
||||
if(send_udp_broadcast("255.255.255.255", global_config->discovery_port, payload, sizeof(payload)) < 0)
|
||||
if(send_udp_broadcast("255.255.255.255", global_config->ports.discovery, payload, sizeof(payload)) < 0)
|
||||
{
|
||||
M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to send discovery broadcast");
|
||||
return;
|
||||
|
|
|
@ -17,7 +17,7 @@ const char *COLOR_EMERG = COLOR_MAGENTA;
|
|||
void
|
||||
logger_log(int level, const char *filename, int line, const char *func, const char *msg, ...)
|
||||
{
|
||||
if(global_config->log_level < level || level == LOG_NONE )
|
||||
if(global_config->logging.level < level || level == LOG_NONE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ logger_log(int level, const char *filename, int line, const char *func, const ch
|
|||
|
||||
syslog(level, "%s", log_line + timestamp_len + 1);
|
||||
|
||||
fprintf(global_config->log_file, "%s", log_line);
|
||||
fflush(global_config->log_file);
|
||||
fprintf(global_config->logging.file, "%s", log_line);
|
||||
fflush(global_config->logging.file);
|
||||
|
||||
free(buffer);
|
||||
free(log_line);
|
||||
|
|
28
src/main.c
28
src/main.c
|
@ -3,7 +3,6 @@
|
|||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <confini.h>
|
||||
#include <mongoose.h>
|
||||
|
||||
#include <cache.h>
|
||||
|
@ -62,23 +61,16 @@ main(int argc, const char** argv)
|
|||
|
||||
cli_parse(argc, argv, global_config);
|
||||
|
||||
FILE * const ini_file = fopen(global_config->file, "rb");
|
||||
if(ini_file == NULL)
|
||||
config_load(global_config);
|
||||
|
||||
if(global_config->logging.file == NULL)
|
||||
{
|
||||
LOGGER_CRIT("config file '%s' was not found\n", global_config->file);
|
||||
exit(1);
|
||||
}
|
||||
if(load_ini_file(ini_file, INI_DEFAULT_FORMAT, NULL, config_load, global_config))
|
||||
{
|
||||
LOGGER_CRIT("unable to parse ini file\n");
|
||||
exit(1);
|
||||
global_config->logging.file = stdout;
|
||||
}
|
||||
|
||||
fclose(ini_file);
|
||||
|
||||
if(global_config->log_file == NULL)
|
||||
if(global_config->include)
|
||||
{
|
||||
global_config->log_file = stdout;
|
||||
config_load_directory(global_config, global_config->include);
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("Loaded config from %s\n", global_config->file);
|
||||
|
@ -91,20 +83,20 @@ main(int argc, const char** argv)
|
|||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
char address[100];
|
||||
sprintf(address, "tcp://0.0.0.0:%u", global_config->server_port);
|
||||
sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.server);
|
||||
struct mg_connection *c_http = mg_bind(&mgr, address, handler_http);
|
||||
if(c_http == NULL)
|
||||
{
|
||||
LOGGER_CRIT("failed to bind http server to port %u\n", global_config->server_port);
|
||||
LOGGER_CRIT("failed to bind http server to port %u\n", global_config->ports.server);
|
||||
exit(1);
|
||||
}
|
||||
mg_set_protocol_http_websocket(c_http);
|
||||
|
||||
sprintf(address, "tcp://0.0.0.0:%u", global_config->mqtt_port);
|
||||
sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.mqtt);
|
||||
struct mg_connection *c_mqtt = mg_bind(&mgr, address, handler_mqtt);
|
||||
if(c_mqtt == NULL)
|
||||
{
|
||||
LOGGER_CRIT("failed to bind mqtt server to port %u\n", global_config->mqtt_port);
|
||||
LOGGER_CRIT("failed to bind mqtt server to port %u\n", global_config->ports.mqtt);
|
||||
exit(1);
|
||||
}
|
||||
mg_mqtt_broker_init(&brk, NULL);
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[core]
|
||||
server-port = 5000
|
||||
database = core.sqlite
|
||||
content-dir = .
|
||||
not-found-file = 404.html
|
||||
not-found-file-mime = text/html
|
||||
not-found-content = 404 - NOT FOUND
|
||||
not-found-content-type = text/plain
|
||||
|
||||
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
||||
discovery-port = 4422
|
||||
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||
mqtt-port = 1886
|
||||
|
||||
log-level = debug
|
||||
log-file = stdout
|
|
@ -41,7 +41,7 @@ touch $working_dir/index.html
|
|||
|
||||
cp $1 $working_dir/core
|
||||
|
||||
valgrind_emgauwa $working_dir/core -c $source_dir/emgauwa-core-testing.ini >>$working_dir/core.log 2>&1 &
|
||||
valgrind_emgauwa $working_dir/core -c $source_dir/emgauwa-core-testing.toml >>$working_dir/core.log 2>&1 &
|
||||
core_id=$!
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ kill $core_id
|
|||
kill $controller_id
|
||||
|
||||
timestamp=$(date -Iseconds)
|
||||
for backup_file in core.log controller.log valgrind.log core.sqlite; do
|
||||
for backup_file in core.log controller.log valgrind.log emgauwa-core.sqlite; do
|
||||
mv $backup_file $timestamp.$backup_file
|
||||
ln -sf $timestamp.$backup_file latest.$backup_file
|
||||
done
|
||||
|
|
5016
vendor/confini.c
vendored
5016
vendor/confini.c
vendored
File diff suppressed because it is too large
Load diff
547
vendor/confini.h
vendored
547
vendor/confini.h
vendored
|
@ -1,547 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
|
||||
|
||||
/**
|
||||
|
||||
@file confini.h
|
||||
@brief libconfini header
|
||||
@author Stefano Gioffré
|
||||
@copyright GNU General Public License, version 3 or any later version
|
||||
@version 1.14.0
|
||||
@date 2016-2020
|
||||
@see https://madmurphy.github.io/libconfini
|
||||
|
||||
**/
|
||||
|
||||
|
||||
#ifndef _LIBCONFINI_HEADER_
|
||||
#define _LIBCONFINI_HEADER_
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* PRIVATE (HEADER-SCOPED) MACROS */
|
||||
|
||||
|
||||
#define __INIFORMAT_TABLE_CB_FIELDS__(NAME, OFFSET, SIZE, DEFVAL) \
|
||||
unsigned char NAME:SIZE;
|
||||
#define __INIFORMAT_TABLE_CB_DEFAULT__(NAME, OFFSET, SIZE, DEFVAL) DEFVAL,
|
||||
#define __INIFORMAT_TABLE_CB_ZERO__(NAME, OFFSET, SIZE, DEFVAL) 0,
|
||||
#define _LIBCONFINI_INIFORMAT_TYPE_ \
|
||||
struct IniFormat { INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_FIELDS__) }
|
||||
#define _LIBCONFINI_DEFAULT_FORMAT_ \
|
||||
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_DEFAULT__) }
|
||||
#define _LIBCONFINI_UNIXLIKE_FORMAT_ \
|
||||
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_ZERO__) }
|
||||
|
||||
|
||||
|
||||
/* PUBLIC MACROS */
|
||||
|
||||
|
||||
/**
|
||||
@brief Calls a user-given macro (that accepts four arguments) for each row
|
||||
of the table
|
||||
**/
|
||||
/*
|
||||
NOTE: The following table and the order of its rows **define** (and link
|
||||
together) both the #IniFormat and #IniFormatNum data types declared in this
|
||||
header
|
||||
*/
|
||||
#define INIFORMAT_TABLE_AS(_____) /* IniFormat table *\
|
||||
|
||||
NAME BIT SIZE DEFAULT
|
||||
*/\
|
||||
_____( delimiter_symbol, 0, 7, INI_EQUALS ) \
|
||||
_____( case_sensitive, 7, 1, false )/*
|
||||
*/\
|
||||
_____( semicolon_marker, 8, 2, INI_DISABLED_OR_COMMENT ) \
|
||||
_____( hash_marker, 10, 2, INI_DISABLED_OR_COMMENT ) \
|
||||
_____( section_paths, 12, 2, INI_ABSOLUTE_AND_RELATIVE ) \
|
||||
_____( multiline_nodes, 14, 2, INI_MULTILINE_EVERYWHERE )/*
|
||||
*/\
|
||||
_____( no_single_quotes, 16, 1, false ) \
|
||||
_____( no_double_quotes, 17, 1, false ) \
|
||||
_____( no_spaces_in_names, 18, 1, false ) \
|
||||
_____( implicit_is_not_empty, 19, 1, false ) \
|
||||
_____( do_not_collapse_values, 20, 1, false ) \
|
||||
_____( preserve_empty_quotes, 21, 1, false ) \
|
||||
_____( disabled_after_space, 22, 1, false ) \
|
||||
_____( disabled_can_be_implicit, 23, 1, false )
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@brief Checks whether a format does **not** support escape sequences
|
||||
**/
|
||||
#define INIFORMAT_HAS_NO_ESC(FORMAT) \
|
||||
(FORMAT.multiline_nodes == INI_NO_MULTILINE && \
|
||||
FORMAT.no_double_quotes && FORMAT.no_single_quotes)
|
||||
|
||||
|
||||
|
||||
/* PUBLIC TYPEDEFS */
|
||||
|
||||
|
||||
/**
|
||||
@brief 24-bit bitfield representing the format of an INI file (INI
|
||||
dialect)
|
||||
**/
|
||||
typedef _LIBCONFINI_INIFORMAT_TYPE_ IniFormat;
|
||||
|
||||
|
||||
/**
|
||||
@brief Global statistics about an INI file
|
||||
**/
|
||||
typedef struct IniStatistics {
|
||||
const IniFormat format;
|
||||
const size_t bytes;
|
||||
const size_t members;
|
||||
} IniStatistics;
|
||||
|
||||
|
||||
/**
|
||||
@brief Dispatch of a single INI node
|
||||
**/
|
||||
typedef struct IniDispatch {
|
||||
const IniFormat format;
|
||||
uint8_t type;
|
||||
char * data;
|
||||
char * value;
|
||||
const char * append_to;
|
||||
size_t d_len;
|
||||
size_t v_len;
|
||||
size_t at_len;
|
||||
size_t dispatch_id;
|
||||
} IniDispatch;
|
||||
|
||||
|
||||
/**
|
||||
@brief The unique ID of an INI format (24-bit maximum)
|
||||
**/
|
||||
typedef uint32_t IniFormatNum;
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an #IniStatistics structure
|
||||
**/
|
||||
typedef int (* IniStatsHandler) (
|
||||
IniStatistics * statistics,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an #IniDispatch structure
|
||||
**/
|
||||
typedef int (* IniDispHandler) (
|
||||
IniDispatch * dispatch,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an INI string belonging to a
|
||||
sequence of INI strings
|
||||
**/
|
||||
typedef int (* IniStrHandler) (
|
||||
char * ini_string,
|
||||
size_t string_length,
|
||||
size_t string_num,
|
||||
IniFormat format,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling a selected fragment of an INI string
|
||||
**/
|
||||
typedef int (* IniSubstrHandler) (
|
||||
const char * ini_string,
|
||||
size_t fragm_offset,
|
||||
size_t fragm_length,
|
||||
size_t fragm_num,
|
||||
IniFormat format,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
|
||||
extern int strip_ini_cache (
|
||||
register char * const ini_source,
|
||||
const size_t ini_length,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern int load_ini_file (
|
||||
FILE * const ini_file,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern int load_ini_path (
|
||||
const char * const path,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_ss (
|
||||
const char * const simple_string_a,
|
||||
const char * const simple_string_b,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_si (
|
||||
const char * const simple_string,
|
||||
const char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_ii (
|
||||
const char * const ini_string_a,
|
||||
const char * const ini_string_b,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_array_match (
|
||||
const char * const ini_string_a,
|
||||
const char * const ini_string_b,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_unquote (
|
||||
char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_string_parse (
|
||||
char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_get_length (
|
||||
const char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern int ini_array_foreach (
|
||||
const char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format,
|
||||
const IniSubstrHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_shift (
|
||||
const char ** const ini_strptr,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_collapse (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern char * ini_array_break (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern char * ini_array_release (
|
||||
char ** const ini_strptr,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern int ini_array_split (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format,
|
||||
const IniStrHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern void ini_global_set_lowercase_mode (
|
||||
const bool lowercase
|
||||
);
|
||||
|
||||
|
||||
extern void ini_global_set_implicit_value (
|
||||
char * const implicit_value,
|
||||
const size_t implicit_v_len
|
||||
);
|
||||
|
||||
|
||||
extern IniFormatNum ini_fton (
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern IniFormat ini_ntof (
|
||||
const IniFormatNum format_id
|
||||
);
|
||||
|
||||
|
||||
extern int ini_get_bool (
|
||||
const char * const ini_string,
|
||||
const int when_fail
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* PUBLIC LINKS */
|
||||
|
||||
|
||||
extern int (* const ini_get_int) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern long int (* const ini_get_lint) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern long long int (* const ini_get_llint) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern double (* const ini_get_double) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Legacy support, soon to be replaced with a `float` data type --
|
||||
please **do not use `ini_get_float()`!**
|
||||
**/
|
||||
#define ini_get_float \
|
||||
_Pragma("GCC warning \"function `ini_get_float()` is deprecated for parsing a `double` data type; use `ini_get_double()` instead\"") \
|
||||
ini_get_double
|
||||
|
||||
|
||||
|
||||
/* PUBLIC CONSTANTS AND VARIABLES */
|
||||
|
||||
|
||||
/**
|
||||
@brief Error mask (flags not present in user-generated interruptions)
|
||||
**/
|
||||
#define CONFINI_ERROR 252
|
||||
|
||||
|
||||
/**
|
||||
@brief Error codes
|
||||
**/
|
||||
enum ConfiniInterruptNo {
|
||||
CONFINI_SUCCESS = 0, /**< There have been no interruptions, everything
|
||||
went well [value=0] **/
|
||||
CONFINI_IINTR = 1, /**< Interrupted by the user during `f_init()`
|
||||
[value=1] **/
|
||||
CONFINI_FEINTR = 2, /**< Interrupted by the user during `f_foreach()`
|
||||
[value=2] **/
|
||||
CONFINI_ENOENT = 4, /**< File inaccessible [value=4] **/
|
||||
CONFINI_ENOMEM = 5, /**< Error allocating virtual memory [value=5] **/
|
||||
CONFINI_EIO = 6, /**< Error reading the file [value=6] **/
|
||||
CONFINI_EOOR = 7, /**< Out-of-range error: callbacks are more than
|
||||
expected [value=7] **/
|
||||
CONFINI_EBADF = 8, /**< The stream specified is not a seekable stream
|
||||
[value=8] **/
|
||||
CONFINI_EFBIG = 9, /**< File too large [value=9] **/
|
||||
CONFINI_EROADDR = 10 /**< Address is read-only [value=10] **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief INI node types
|
||||
**/
|
||||
enum IniNodeType {
|
||||
INI_UNKNOWN = 0, /**< This is a node impossible to categorize
|
||||
[value=0] **/
|
||||
INI_VALUE = 1, /**< Not used by **libconfini** (values are
|
||||
dispatched together with keys) -- but
|
||||
available for user's implementations
|
||||
[value=1] **/
|
||||
INI_KEY = 2, /**< This is a key [value=2] **/
|
||||
INI_SECTION = 3, /**< This is a section or a section path
|
||||
[value=3] **/
|
||||
INI_COMMENT = 4, /**< This is a comment [value=4] **/
|
||||
INI_INLINE_COMMENT = 5, /**< This is an inline comment [value=5] **/
|
||||
INI_DISABLED_KEY = 6, /**< This is a disabled key [value=6] **/
|
||||
INI_DISABLED_SECTION = 7 /**< This is a disabled section path
|
||||
[value=7] **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Common array and key-value delimiters (but a delimiter may also be
|
||||
any other ASCII character not present in this list)
|
||||
**/
|
||||
enum IniDelimiters {
|
||||
INI_ANY_SPACE = 0, /**< In multi-line INIs:
|
||||
`/(?:\\(?:\n\r?|\r\n?)|[\t \v\f])+/`, in
|
||||
non-multi-line INIs: `/[\t \v\f])+/` **/
|
||||
INI_EQUALS = '=', /**< Equals character (`=`) **/
|
||||
INI_COLON = ':', /**< Colon character (`:`) **/
|
||||
INI_DOT = '.', /**< Dot character (`.`) **/
|
||||
INI_COMMA = ',' /**< Comma character (`,`) **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::semicolon_marker and
|
||||
#IniFormat::hash_marker (i.e., meaning of `/\s+;/` and `/\s+#/` in
|
||||
respect to a format)
|
||||
**/
|
||||
enum IniCommentMarker {
|
||||
INI_DISABLED_OR_COMMENT = 0, /**< This marker opens a comment or a
|
||||
disabled entry **/
|
||||
INI_ONLY_COMMENT = 1, /**< This marker opens a comment **/
|
||||
INI_IGNORE = 2, /**< This marker opens a comment that has
|
||||
been marked for deletion and must not
|
||||
be dispatched or counted **/
|
||||
INI_IS_NOT_A_MARKER = 3 /**< This is not a marker at all, but a
|
||||
normal character instead **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::section_paths
|
||||
**/
|
||||
enum IniSectionPaths {
|
||||
INI_ABSOLUTE_AND_RELATIVE = 0, /**< Section paths starting with a dot
|
||||
express nesting to the current parent,
|
||||
to root otherwise **/
|
||||
INI_ABSOLUTE_ONLY = 1, /**< Section paths starting with a dot will
|
||||
be cleaned of their leading dot and
|
||||
appended to root **/
|
||||
INI_ONE_LEVEL_ONLY = 2, /**< Format supports sections, but the dot
|
||||
does not express nesting and is not a
|
||||
meta-character **/
|
||||
INI_NO_SECTIONS = 3 /**< Format does *not* support sections --
|
||||
`/\[[^\]]*\]/g`, if any, will be
|
||||
treated as keys! **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::multiline_nodes
|
||||
**/
|
||||
enum IniMultiline {
|
||||
INI_MULTILINE_EVERYWHERE = 0, /**< Comments, section paths and keys
|
||||
-- disabled or not -- are allowed
|
||||
to be multi-line **/
|
||||
INI_BUT_COMMENTS = 1, /**< Only section paths and keys --
|
||||
disabled or not -- are allowed to
|
||||
be multi-line **/
|
||||
INI_BUT_DISABLED_AND_COMMENTS = 2, /**< Only active section paths and
|
||||
active keys are allowed to be
|
||||
multi-line **/
|
||||
INI_NO_MULTILINE = 3 /**< Multi-line escape sequences are
|
||||
disabled **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief A model format for standard INI files
|
||||
**/
|
||||
static const IniFormat INI_DEFAULT_FORMAT = _LIBCONFINI_DEFAULT_FORMAT_;
|
||||
|
||||
|
||||
/**
|
||||
@brief A model format for Unix-like .conf files (where space characters
|
||||
are delimiters between keys and values)
|
||||
**/
|
||||
/* All fields are set to `0` here. */
|
||||
static const IniFormat INI_UNIXLIKE_FORMAT = _LIBCONFINI_UNIXLIKE_FORMAT_;
|
||||
|
||||
|
||||
/**
|
||||
@brief If set to `true`, key and section names in case-insensitive INI
|
||||
formats will be dispatched lowercase, verbatim otherwise (default
|
||||
value: `false`)
|
||||
**/
|
||||
extern bool INI_GLOBAL_LOWERCASE_MODE;
|
||||
|
||||
|
||||
/**
|
||||
@brief Value to be assigned to implicit keys (default value: `NULL`)
|
||||
**/
|
||||
extern char * INI_GLOBAL_IMPLICIT_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
@brief Length of the value assigned to implicit keys (default value: `0`)
|
||||
**/
|
||||
extern size_t INI_GLOBAL_IMPLICIT_V_LEN;
|
||||
|
||||
|
||||
|
||||
/* CLEAN THE PRIVATE ENVIRONMENT */
|
||||
|
||||
|
||||
#undef _LIBCONFINI_UNIXLIKE_FORMAT_
|
||||
#undef _LIBCONFINI_DEFAULT_FORMAT_
|
||||
#undef _LIBCONFINI_INIFORMAT_TYPE_
|
||||
#undef __INIFORMAT_TABLE_CB_ZERO__
|
||||
#undef __INIFORMAT_TABLE_CB_DEFAULT__
|
||||
#undef __INIFORMAT_TABLE_CB_FIELDS__
|
||||
|
||||
|
||||
|
||||
/* END OF `_LIBCONFINI_HEADER_` */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* EOF */
|
||||
|
Loading…
Reference in a new issue