Indent every scope and put opening braces on the signature line
Namespace and extern "C" bodies are indented a level, so nesting is visible from the indentation rather than only from the braces. Opening braces stay on the line that opens the scope, including function definitions, which previously carried theirs on a line of their own.
This commit is contained in:
@@ -33,295 +33,275 @@
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
const char* DEFAULT_CONFIG_DIR = "/etc/dpm/conf.d/";
|
||||
const char* DEFAULT_MODULE_PATH = "/usr/lib/dpm/modules/";
|
||||
const char* DEFAULT_LOG_FILE = "/var/log/dpm/dpm.log";
|
||||
|
||||
const char* DEFAULT_CONFIG_DIR = "/etc/dpm/conf.d/";
|
||||
const char* DEFAULT_MODULE_PATH = "/usr/lib/dpm/modules/";
|
||||
const char* DEFAULT_LOG_FILE = "/var/log/dpm/dpm.log";
|
||||
|
||||
std::string trim(const std::string& s)
|
||||
{
|
||||
const char* ws = " \t\r\n\f\v";
|
||||
size_t start = s.find_first_not_of(ws);
|
||||
if (start == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
size_t end = s.find_last_not_of(ws);
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
std::string with_trailing_slash(std::string s)
|
||||
{
|
||||
if (!s.empty() && s.back() != '/') {
|
||||
s += '/';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool parse_bool(const std::string& v, bool fallback)
|
||||
{
|
||||
std::string lower;
|
||||
for (char c : v) {
|
||||
lower += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
}
|
||||
if (lower == "true" || lower == "yes" || lower == "1" || lower == "on") {
|
||||
return true;
|
||||
}
|
||||
if (lower == "false" || lower == "no" || lower == "0" || lower == "off") {
|
||||
return false;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
int level_from_string(const std::string& v, int fallback)
|
||||
{
|
||||
if (v == "FATAL") { return DPM_LOG_FATAL; }
|
||||
if (v == "ERROR") { return DPM_LOG_ERROR; }
|
||||
if (v == "WARN") { return DPM_LOG_WARN; }
|
||||
if (v == "INFO") { return DPM_LOG_INFO; }
|
||||
if (v == "DEBUG") { return DPM_LOG_DEBUG; }
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const char* level_name(int level)
|
||||
{
|
||||
switch (level) {
|
||||
case DPM_LOG_FATAL: return "FATAL";
|
||||
case DPM_LOG_ERROR: return "ERROR";
|
||||
case DPM_LOG_WARN: return "WARN";
|
||||
case DPM_LOG_INFO: return "INFO";
|
||||
case DPM_LOG_DEBUG: return "DEBUG";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void parse_config_file(dpm_ctx* ctx, const fs::path& file,
|
||||
const std::string& module_name)
|
||||
{
|
||||
std::ifstream in(file);
|
||||
if (!in.is_open()) {
|
||||
return;
|
||||
std::string trim(const std::string& s) {
|
||||
const char* ws = " \t\r\n\f\v";
|
||||
size_t start = s.find_first_not_of(ws);
|
||||
if (start == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
size_t end = s.find_last_not_of(ws);
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::string section = "main";
|
||||
std::string with_trailing_slash(std::string s) {
|
||||
if (!s.empty() && s.back() != '/') {
|
||||
s += '/';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
while (std::getline(in, line)) {
|
||||
line = trim(line);
|
||||
bool parse_bool(const std::string& v, bool fallback) {
|
||||
std::string lower;
|
||||
for (char c : v) {
|
||||
lower += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
}
|
||||
if (lower == "true" || lower == "yes" || lower == "1" || lower == "on") {
|
||||
return true;
|
||||
}
|
||||
if (lower == "false" || lower == "no" || lower == "0" || lower == "off") {
|
||||
return false;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||||
continue;
|
||||
int level_from_string(const std::string& v, int fallback) {
|
||||
if (v == "FATAL") { return DPM_LOG_FATAL; }
|
||||
if (v == "ERROR") { return DPM_LOG_ERROR; }
|
||||
if (v == "WARN") { return DPM_LOG_WARN; }
|
||||
if (v == "INFO") { return DPM_LOG_INFO; }
|
||||
if (v == "DEBUG") { return DPM_LOG_DEBUG; }
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const char* level_name(int level) {
|
||||
switch (level) {
|
||||
case DPM_LOG_FATAL: return "FATAL";
|
||||
case DPM_LOG_ERROR: return "ERROR";
|
||||
case DPM_LOG_WARN: return "WARN";
|
||||
case DPM_LOG_INFO: return "INFO";
|
||||
case DPM_LOG_DEBUG: return "DEBUG";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void parse_config_file(dpm_ctx* ctx, const fs::path& file,
|
||||
const std::string& module_name) {
|
||||
std::ifstream in(file);
|
||||
if (!in.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.front() == '[' && line.back() == ']') {
|
||||
std::string name = trim(line.substr(1, line.length() - 2));
|
||||
section = name.empty() ? "main" : name;
|
||||
continue;
|
||||
}
|
||||
std::string line;
|
||||
std::string section = "main";
|
||||
|
||||
size_t eq = line.find('=');
|
||||
if (eq == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
while (std::getline(in, line)) {
|
||||
line = trim(line);
|
||||
|
||||
std::string key = trim(line.substr(0, eq));
|
||||
std::string value = trim(line.substr(eq + 1));
|
||||
if (key.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx->config[module_name][section][key] = value;
|
||||
if (line.front() == '[' && line.back() == ']') {
|
||||
std::string name = trim(line.substr(1, line.length() - 2));
|
||||
section = name.empty() ? "main" : name;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t eq = line.find('=');
|
||||
if (eq == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key = trim(line.substr(0, eq));
|
||||
std::string value = trim(line.substr(eq + 1));
|
||||
if (key.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx->config[module_name][section][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace dpm_core {
|
||||
|
||||
void set_error(dpm_ctx* ctx, const std::string& msg)
|
||||
{
|
||||
if (ctx) {
|
||||
ctx->last_error = msg;
|
||||
}
|
||||
}
|
||||
|
||||
void load_config_dir(dpm_ctx* ctx)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(ctx->config_dir, ec)) {
|
||||
return;
|
||||
void set_error(dpm_ctx* ctx, const std::string& msg) {
|
||||
if (ctx) {
|
||||
ctx->last_error = msg;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(ctx->config_dir, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
void load_config_dir(dpm_ctx* ctx) {
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(ctx->config_dir, ec)) {
|
||||
return;
|
||||
}
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.path().extension() != ".conf") {
|
||||
continue;
|
||||
}
|
||||
parse_config_file(ctx, entry.path(), entry.path().stem().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(ctx->config_dir, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.path().extension() != ".conf") {
|
||||
continue;
|
||||
}
|
||||
parse_config_file(ctx, entry.path(), entry.path().stem().string());
|
||||
}
|
||||
}
|
||||
} // namespace dpm_core
|
||||
|
||||
extern "C" {
|
||||
|
||||
dpm_ctx* dpm_open(const dpm_open_overrides* overrides)
|
||||
{
|
||||
/* Validate explicit overrides before allocating anything. */
|
||||
if (overrides) {
|
||||
if (overrides->config_dir && *overrides->config_dir) {
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(overrides->config_dir, ec)) {
|
||||
dpm_ctx* dpm_open(const dpm_open_overrides* overrides) {
|
||||
/* Validate explicit overrides before allocating anything. */
|
||||
if (overrides) {
|
||||
if (overrides->config_dir && *overrides->config_dir) {
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(overrides->config_dir, ec)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (overrides->log_level < -1 || overrides->log_level > DPM_LOG_DEBUG) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (overrides->log_level < -1 || overrides->log_level > DPM_LOG_DEBUG) {
|
||||
|
||||
dpm_ctx* ctx = new (std::nothrow) dpm_ctx;
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
dpm_ctx* ctx = new (std::nothrow) dpm_ctx;
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Config directory: override > default. */
|
||||
ctx->config_dir = DEFAULT_CONFIG_DIR;
|
||||
if (overrides && overrides->config_dir && *overrides->config_dir) {
|
||||
ctx->config_dir = with_trailing_slash(overrides->config_dir);
|
||||
}
|
||||
|
||||
dpm_core::load_config_dir(ctx);
|
||||
|
||||
/* Logging: config, then override. */
|
||||
ctx->log_level = DPM_LOG_INFO;
|
||||
ctx->write_to_log = false;
|
||||
ctx->log_file = DEFAULT_LOG_FILE;
|
||||
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "log_level")) {
|
||||
ctx->log_level = level_from_string(v, DPM_LOG_INFO);
|
||||
}
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "write_to_log")) {
|
||||
ctx->write_to_log = parse_bool(v, false);
|
||||
}
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "log_file")) {
|
||||
ctx->log_file = v;
|
||||
}
|
||||
if (overrides && overrides->log_level >= 0) {
|
||||
ctx->log_level = overrides->log_level;
|
||||
}
|
||||
|
||||
/* Module path: override > config > default. */
|
||||
ctx->module_path = DEFAULT_MODULE_PATH;
|
||||
if (const char* v = dpm_config_get(ctx, "core", "modules", "path")) {
|
||||
ctx->module_path = with_trailing_slash(v);
|
||||
}
|
||||
if (overrides && overrides->module_path && *overrides->module_path) {
|
||||
ctx->module_path = with_trailing_slash(overrides->module_path);
|
||||
}
|
||||
|
||||
/* Target root: override > default. */
|
||||
ctx->root = "/";
|
||||
if (overrides && overrides->root && *overrides->root) {
|
||||
ctx->root = overrides->root;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void dpm_close(dpm_ctx* ctx)
|
||||
{
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
/* dpm_module destructors do not dlclose; do it here so the
|
||||
registry teardown order is explicit. */
|
||||
for (auto& [name, mod] : ctx->modules) {
|
||||
if (mod->handle) {
|
||||
dpm_internal_unload(mod->handle);
|
||||
/* Config directory: override > default. */
|
||||
ctx->config_dir = DEFAULT_CONFIG_DIR;
|
||||
if (overrides && overrides->config_dir && *overrides->config_dir) {
|
||||
ctx->config_dir = with_trailing_slash(overrides->config_dir);
|
||||
}
|
||||
}
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
const char* dpm_config_get(dpm_ctx* ctx, const char* module,
|
||||
const char* section, const char* key)
|
||||
{
|
||||
if (!ctx || !module || !section || !key) {
|
||||
return nullptr;
|
||||
dpm_core::load_config_dir(ctx);
|
||||
|
||||
/* Logging: config, then override. */
|
||||
ctx->log_level = DPM_LOG_INFO;
|
||||
ctx->write_to_log = false;
|
||||
ctx->log_file = DEFAULT_LOG_FILE;
|
||||
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "log_level")) {
|
||||
ctx->log_level = level_from_string(v, DPM_LOG_INFO);
|
||||
}
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "write_to_log")) {
|
||||
ctx->write_to_log = parse_bool(v, false);
|
||||
}
|
||||
if (const char* v = dpm_config_get(ctx, "core", "logging", "log_file")) {
|
||||
ctx->log_file = v;
|
||||
}
|
||||
if (overrides && overrides->log_level >= 0) {
|
||||
ctx->log_level = overrides->log_level;
|
||||
}
|
||||
|
||||
/* Module path: override > config > default. */
|
||||
ctx->module_path = DEFAULT_MODULE_PATH;
|
||||
if (const char* v = dpm_config_get(ctx, "core", "modules", "path")) {
|
||||
ctx->module_path = with_trailing_slash(v);
|
||||
}
|
||||
if (overrides && overrides->module_path && *overrides->module_path) {
|
||||
ctx->module_path = with_trailing_slash(overrides->module_path);
|
||||
}
|
||||
|
||||
/* Target root: override > default. */
|
||||
ctx->root = "/";
|
||||
if (overrides && overrides->root && *overrides->root) {
|
||||
ctx->root = overrides->root;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
auto mod_it = ctx->config.find(module);
|
||||
if (mod_it == ctx->config.end()) {
|
||||
return nullptr;
|
||||
void dpm_close(dpm_ctx* ctx) {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
/* dpm_module destructors do not dlclose; do it here so the
|
||||
registry teardown order is explicit. */
|
||||
for (auto& [name, mod] : ctx->modules) {
|
||||
if (mod->handle) {
|
||||
dpm_internal_unload(mod->handle);
|
||||
}
|
||||
}
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
auto sec_it = mod_it->second.find(section);
|
||||
if (sec_it == mod_it->second.end()) {
|
||||
return nullptr;
|
||||
const char* dpm_config_get(dpm_ctx* ctx, const char* module,
|
||||
const char* section, const char* key) {
|
||||
if (!ctx || !module || !section || !key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto mod_it = ctx->config.find(module);
|
||||
if (mod_it == ctx->config.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sec_it = mod_it->second.find(section);
|
||||
if (sec_it == mod_it->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto key_it = sec_it->second.find(key);
|
||||
if (key_it == sec_it->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return key_it->second.c_str();
|
||||
}
|
||||
|
||||
auto key_it = sec_it->second.find(key);
|
||||
if (key_it == sec_it->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
void dpm_log(dpm_ctx* ctx, int level, const char* message) {
|
||||
if (!ctx || !message) {
|
||||
return;
|
||||
}
|
||||
if (level < DPM_LOG_FATAL || level > DPM_LOG_DEBUG) {
|
||||
level = DPM_LOG_INFO;
|
||||
}
|
||||
if (level > ctx->log_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
return key_it->second.c_str();
|
||||
}
|
||||
|
||||
void dpm_log(dpm_ctx* ctx, int level, const char* message)
|
||||
{
|
||||
if (!ctx || !message) {
|
||||
return;
|
||||
}
|
||||
if (level < DPM_LOG_FATAL || level > DPM_LOG_DEBUG) {
|
||||
level = DPM_LOG_INFO;
|
||||
}
|
||||
if (level > ctx->log_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level <= DPM_LOG_WARN) {
|
||||
std::fprintf(stderr, "%s: %s\n", level_name(level), message);
|
||||
} else {
|
||||
std::fprintf(stdout, "%s\n", message);
|
||||
}
|
||||
|
||||
if (ctx->write_to_log) {
|
||||
std::ofstream out(ctx->log_file, std::ios::app);
|
||||
if (out.is_open()) {
|
||||
char stamp[32];
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S",
|
||||
std::localtime(&now));
|
||||
out << stamp << " [" << level_name(level) << "] "
|
||||
<< message << "\n";
|
||||
if (level <= DPM_LOG_WARN) {
|
||||
std::fprintf(stderr, "%s: %s\n", level_name(level), message);
|
||||
} else {
|
||||
/* Log-file trouble never fails the operation; disable and
|
||||
continue on console alone. */
|
||||
ctx->write_to_log = false;
|
||||
std::fprintf(stdout, "%s\n", message);
|
||||
}
|
||||
|
||||
if (ctx->write_to_log) {
|
||||
std::ofstream out(ctx->log_file, std::ios::app);
|
||||
if (out.is_open()) {
|
||||
char stamp[32];
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S",
|
||||
std::localtime(&now));
|
||||
out << stamp << " [" << level_name(level) << "] "
|
||||
<< message << "\n";
|
||||
} else {
|
||||
/* Log-file trouble never fails the operation; disable and
|
||||
continue on console alone. */
|
||||
ctx->write_to_log = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* dpm_module_path(dpm_ctx* ctx)
|
||||
{
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
const char* dpm_module_path(dpm_ctx* ctx) {
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
return ctx->module_path.c_str();
|
||||
}
|
||||
return ctx->module_path.c_str();
|
||||
}
|
||||
|
||||
const char* dpm_last_error(dpm_ctx* ctx)
|
||||
{
|
||||
if (!ctx || ctx->last_error.empty()) {
|
||||
return nullptr;
|
||||
const char* dpm_last_error(dpm_ctx* ctx) {
|
||||
if (!ctx || ctx->last_error.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
Reference in New Issue
Block a user