config: add support for source option for additional config files (#144)
Some checks failed
Build / nix (push) Has been cancelled

* feat: support `source` option for additional config files

* fix: prevent circular dependency
This commit is contained in:
martin 2025-05-02 17:27:53 +02:00 committed by GitHub
parent b18d830276
commit a0037ac40c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 1 deletions

View file

@ -1,6 +1,10 @@
#include "ConfigManager.hpp"
#include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp"
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include <glob.h>
#include <cstring>
static std::string getMainConfigPath() {
static const auto paths = Hyprutils::Path::findConfig("hypridle");
@ -13,6 +17,19 @@ static std::string getMainConfigPath() {
CConfigManager::CConfigManager(std::string configPath) :
m_config(configPath.empty() ? getMainConfigPath().c_str() : configPath.c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = false}) {
;
configCurrentPath = configPath.empty() ? getMainConfigPath() : configPath;
}
static Hyprlang::CParseResult handleSource(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
void CConfigManager::init() {
@ -31,6 +48,12 @@ void CConfigManager::init() {
m_config.addConfigValue("general:ignore_systemd_inhibit", Hyprlang::INT{0});
m_config.addConfigValue("general:inhibit_sleep", Hyprlang::INT{2});
// track the file in the circular dependency chain
const auto mainConfigPath = getMainConfigPath();
alreadyIncludedSourceFiles.insert(std::filesystem::canonical(mainConfigPath));
m_config.registerHandler(&::handleSource, "source", {.allowFlags = false});
m_config.commence();
auto result = m_config.parse();
@ -80,3 +103,56 @@ Hyprlang::CParseResult CConfigManager::postParse() {
std::vector<CConfigManager::STimeoutRule> CConfigManager::getRules() {
return m_vRules;
}
std::optional<std::string> CConfigManager::handleSource(const std::string& command, const std::string& rawpath) {
if (rawpath.length() < 2) {
return "source path " + rawpath + " bogus!";
}
std::unique_ptr<glob_t, void (*)(glob_t*)> glob_buf{new glob_t, [](glob_t* g) { globfree(g); }};
memset(glob_buf.get(), 0, sizeof(glob_t));
const auto CURRENTDIR = std::filesystem::path(configCurrentPath).parent_path().string();
if (auto r = glob(absolutePath(rawpath, CURRENTDIR).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) {
std::string err = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory");
Debug::log(ERR, "{}", err);
return err;
}
for (size_t i = 0; i < glob_buf->gl_pathc; i++) {
const auto PATH = absolutePath(glob_buf->gl_pathv[i], CURRENTDIR);
if (PATH.empty() || PATH == configCurrentPath) {
Debug::log(WARN, "source= skipping invalid path");
continue;
}
if (std::find(alreadyIncludedSourceFiles.begin(), alreadyIncludedSourceFiles.end(), PATH) != alreadyIncludedSourceFiles.end()) {
Debug::log(WARN, "source= skipping already included source file {} to prevent circular dependency", PATH);
continue;
}
if (!std::filesystem::is_regular_file(PATH)) {
if (std::filesystem::exists(PATH)) {
Debug::log(WARN, "source= skipping non-file {}", PATH);
continue;
}
Debug::log(ERR, "source= file doesnt exist");
return "source file " + PATH + " doesn't exist!";
}
// track the file in the circular dependency chain
alreadyIncludedSourceFiles.insert(PATH);
// allow for nested config parsing
auto backupConfigPath = configCurrentPath;
configCurrentPath = PATH;
m_config.parseFile(PATH.c_str());
configCurrentPath = backupConfigPath;
}
return {};
}

View file

@ -4,6 +4,7 @@
#include <hyprlang.hpp>
#include <set>
#include <vector>
#include <memory>
@ -19,6 +20,9 @@ class CConfigManager {
};
std::vector<STimeoutRule> getRules();
std::optional<std::string> handleSource(const std::string&, const std::string&);
std::string configCurrentPath;
std::set<std::string> alreadyIncludedSourceFiles;
template <typename T>
Hyprlang::CSimpleConfigValue<T> getValue(const std::string& name) {

View file

@ -0,0 +1,19 @@
#include <filesystem>
#include "MiscFunctions.hpp"
std::string absolutePath(const std::string& rawpath, const std::string& currentDir) {
std::filesystem::path path(rawpath);
// Handling where rawpath starts with '~'
if (!rawpath.empty() && rawpath[0] == '~') {
static const char* const ENVHOME = getenv("HOME");
path = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
}
// Handling e.g. ./, ../
if (path.is_relative())
return std::filesystem::weakly_canonical(std::filesystem::path(currentDir) / path);
else
return std::filesystem::weakly_canonical(path);
}

View file

@ -0,0 +1,5 @@
#pragma once
#include <string>
std::string absolutePath(const std::string&, const std::string&);