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:
2026-08-15 23:47:43 -04:00
parent 9fd7d2f433
commit 12a4f68025
13 changed files with 634 additions and 709 deletions

View File

@@ -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" */