diff --git a/include/components/config.hpp b/include/components/config.hpp index 971c2f3e..2ae66769 100644 --- a/include/components/config.hpp +++ b/include/components/config.hpp @@ -48,28 +48,12 @@ class config { /** * Returns true if a given parameter exists */ - bool has(const string& section, const string& key) const { - auto it = m_sections.find(section); - return it != m_sections.end() && it->second.find(key) != it->second.end(); - } + bool has(const string& section, const string& key) const; /** * Set parameter value */ - void set(const string& section, const string& key, string&& value) { - auto it = m_sections.find(section); - if (it == m_sections.end()) { - valuemap_t values; - values[key] = value; - m_sections[section] = move(values); - } - auto it2 = it->second.find(key); - if ((it2 = it->second.find(key)) == it->second.end()) { - it2 = it->second.emplace_hint(it2, key, value); - } else { - it2->second = value; - } - } + void set(const string& section, const string& key, string&& value); /** * Get parameter for the current bar by name @@ -91,7 +75,7 @@ class config { if (it->second.find(key) == it->second.end()) { throw key_error("Missing parameter \"" + section + "." + key + "\""); } - return dereference(section, key, it->second.at(key), convert(string{it->second.at(key)})); + return convert(dereference(section, key, it->second.at(key))); } /** @@ -102,8 +86,7 @@ class config { T get(const string& section, const string& key, const T& default_value) const { try { string string_value{get(section, key)}; - T result{convert(string{string_value})}; - return dereference(move(section), move(key), move(string_value), move(result)); + return convert(dereference(move(section), move(key), move(string_value))); } catch (const key_error& err) { return default_value; } catch (const std::exception& err) { @@ -156,12 +139,11 @@ class config { while (true) { try { string string_value{get(section, key + "-" + to_string(results.size()))}; - T value{convert(string{string_value})}; if (!string_value.empty()) { - results.emplace_back(dereference(section, key, move(string_value), move(value))); + results.emplace_back(convert(dereference(section, key, move(string_value)))); } else { - results.emplace_back(move(value)); + results.emplace_back(convert(move(string_value))); } } catch (const key_error& err) { break; @@ -186,12 +168,11 @@ class config { while (true) { try { string string_value{get(section, key + "-" + to_string(results.size()))}; - T value{convert(string{string_value})}; if (!string_value.empty()) { - results.emplace_back(dereference(section, key, move(string_value), move(value))); + results.emplace_back(convert(dereference(section, key, move(string_value)))); } else { - results.emplace_back(move(value)); + results.emplace_back(convert(move(string_value))); } } catch (const key_error& err) { break; @@ -238,27 +219,7 @@ class config { /** * Dereference value reference */ - template - T dereference(const string& section, const string& key, const string& var, const T& fallback) const { - if (var.substr(0, 2) != "${" || var.substr(var.length() - 1) != "}") { - return fallback; - } - - auto path = var.substr(2, var.length() - 3); - size_t pos; - - if (path.compare(0, 4, "env:") == 0) { - return dereference_env(path.substr(4)); - } else if (path.compare(0, 5, "xrdb:") == 0) { - return dereference_xrdb(path.substr(5)); - } else if (path.compare(0, 5, "file:") == 0) { - return dereference_file(path.substr(5)); - } else if ((pos = path.find(".")) != string::npos) { - return dereference_local(path.substr(0, pos), path.substr(pos + 1), section); - } else { - throw value_error("Invalid reference defined at \"" + section + "." + key + "\""); - } - } + string dereference(const string& section, const string& key, const string& var) const; /** * Dereference local value reference defined using: @@ -269,133 +230,28 @@ class config { * ${section.key} * ${section.key:fallback} */ - template - T dereference_local(string section, const string& key, const string& current_section) const { - if (section == "BAR") { - m_log.warn("${BAR.key} is deprecated. Use ${root.key} instead"); - } - - section = string_util::replace(section, "BAR", this->section(), 0, 3); - section = string_util::replace(section, "root", this->section(), 0, 4); - section = string_util::replace(section, "self", current_section, 0, 4); - - try { - string string_value{get(section, key)}; - T result{convert(string{string_value})}; - return dereference(string(section), move(key), move(string_value), move(result)); - } catch (const key_error& err) { - size_t pos; - if ((pos = key.find(':')) != string::npos) { - string fallback = key.substr(pos + 1); - m_log.info("The reference ${%s.%s} does not exist, using defined fallback value \"%s\"", section, - key.substr(0, pos), fallback); - return convert(move(fallback)); - } - throw value_error("The reference ${" + section + "." + key + "} does not exist (no fallback set)"); - } - } + string dereference_local(string section, const string& key, const string& current_section) const; /** * Dereference environment variable reference defined using: * ${env:key} * ${env:key:fallback value} */ - template - T dereference_env(string var) const { - size_t pos; - string env_default; - /* - * This is needed because with only the string we cannot distinguish - * between an empty string as default and not default - */ - bool has_default = false; - - if ((pos = var.find(':')) != string::npos) { - env_default = var.substr(pos + 1); - has_default = true; - var.erase(pos); - } - - if (env_util::has(var)) { - string env_value{env_util::get(var)}; - m_log.info("Environment var reference ${%s} found (value=%s)", var, env_value); - return convert(move(env_value)); - } else if (has_default) { - m_log.info("Environment var ${%s} is undefined, using defined fallback value \"%s\"", var, env_default); - return convert(move(env_default)); - } else { - throw value_error(sstream() << "Environment var ${" << var << "} does not exist (no fallback set)"); - } - } + string dereference_env(string var) const; /** * Dereference X resource db value defined using: * ${xrdb:key} * ${xrdb:key:fallback value} */ - template - T dereference_xrdb(string var) const { - size_t pos; -#if not WITH_XRM - m_log.warn("No built-in support to dereference ${xrdb:%s} references (requires `xcb-util-xrm`)", var); - if ((pos = var.find(':')) != string::npos) { - return convert(var.substr(pos + 1)); - } - return convert(""); -#else - if (!m_xrm) { - throw application_error("xrm is not initialized"); - } - - string fallback; - bool has_fallback = false; - if ((pos = var.find(':')) != string::npos) { - fallback = var.substr(pos + 1); - has_fallback = true; - var.erase(pos); - } - - try { - auto value = m_xrm->require(var.c_str()); - m_log.info("Found matching X resource \"%s\" (value=%s)", var, value); - return convert(move(value)); - } catch (const xresource_error& err) { - if (has_fallback) { - m_log.info("%s, using defined fallback value \"%s\"", err.what(), fallback); - return convert(move(fallback)); - } - throw value_error(sstream() << err.what() << " (no fallback set)"); - } -#endif - } + string dereference_xrdb(string var) const; /** * Dereference file reference by reading its contents * ${file:/absolute/file/path} * ${file:/absolute/file/path:fallback value} */ - template - T dereference_file(string var) const { - size_t pos; - string fallback; - bool has_fallback = false; - if ((pos = var.find(':')) != string::npos) { - fallback = var.substr(pos + 1); - has_fallback = true; - var.erase(pos); - } - var = file_util::expand(var); - - if (file_util::exists(var)) { - m_log.info("File reference \"%s\" found", var); - return convert(string_util::trim(file_util::contents(var), '\n')); - } else if (has_fallback) { - m_log.info("File reference \"%s\" not found, using defined fallback value \"%s\"", var, fallback); - return convert(move(fallback)); - } else { - throw value_error(sstream() << "The file \"" << var << "\" does not exist (no fallback set)"); - } - } + string dereference_file(string var) const; private: const logger& m_log; diff --git a/src/components/config.cpp b/src/components/config.cpp index be249c4c..9ff3b1e4 100644 --- a/src/components/config.cpp +++ b/src/components/config.cpp @@ -73,6 +73,32 @@ void config::warn_deprecated(const string& section, const string& key, string re } } +/** + * Returns true if a given parameter exists + */ +bool config::has(const string& section, const string& key) const { + auto it = m_sections.find(section); + return it != m_sections.end() && it->second.find(key) != it->second.end(); +} + +/** + * Set parameter value + */ +void config::set(const string& section, const string& key, string&& value) { + auto it = m_sections.find(section); + if (it == m_sections.end()) { + valuemap_t values; + values[key] = value; + m_sections[section] = move(values); + } + auto it2 = it->second.find(key); + if ((it2 = it->second.find(key)) == it->second.end()) { + it2 = it->second.emplace_hint(it2, key, value); + } else { + it2->second = value; + } +} + /** * Look for sections set up to inherit from a base section * and copy the missing parameters @@ -91,7 +117,7 @@ void config::copy_inherited() { string key_name = param.first; if (key_name == "inherit") { auto inherit = param.second; - inherit = dereference(section.first, key_name, inherit, inherit); + inherit = dereference(section.first, key_name, inherit); std::vector sections = string_util::split(std::move(inherit), ' '); @@ -105,7 +131,7 @@ void config::copy_inherited() { section.first, key_name); auto inherit = param.second; - inherit = dereference(section.first, key_name, inherit, inherit); + inherit = dereference(section.first, key_name, inherit); if (inherit.empty() || m_sections.find(inherit) == m_sections.end()) { throw value_error( "Invalid section \"" + inherit + "\" defined for \"" + section.first + "." + key_name + "\""); @@ -134,6 +160,163 @@ void config::copy_inherited() { } } +/** + * Dereference value reference + */ +string config::dereference(const string& section, const string& key, const string& var) const { + if (var.substr(0, 2) != "${" || var.substr(var.length() - 1) != "}") { + return var; + } + + auto path = var.substr(2, var.length() - 3); + size_t pos; + + if (path.compare(0, 4, "env:") == 0) { + return dereference_env(path.substr(4)); + } else if (path.compare(0, 5, "xrdb:") == 0) { + return dereference_xrdb(path.substr(5)); + } else if (path.compare(0, 5, "file:") == 0) { + return dereference_file(path.substr(5)); + } else if ((pos = path.find(".")) != string::npos) { + return dereference_local(path.substr(0, pos), path.substr(pos + 1), section); + } else { + throw value_error("Invalid reference defined at \"" + section + "." + key + "\""); + } +} + +/** + * Dereference local value reference defined using: + * ${root.key} + * ${root.key:fallback} + * ${self.key} + * ${self.key:fallback} + * ${section.key} + * ${section.key:fallback} + */ +string config::dereference_local(string section, const string& key, const string& current_section) const { + if (section == "BAR") { + m_log.warn("${BAR.key} is deprecated. Use ${root.key} instead"); + } + + section = string_util::replace(section, "BAR", this->section(), 0, 3); + section = string_util::replace(section, "root", this->section(), 0, 4); + section = string_util::replace(section, "self", current_section, 0, 4); + + try { + string string_value{get(section, key)}; + return dereference(string(section), move(key), move(string_value)); + } catch (const key_error& err) { + size_t pos; + if ((pos = key.find(':')) != string::npos) { + string fallback = key.substr(pos + 1); + m_log.info("The reference ${%s.%s} does not exist, using defined fallback value \"%s\"", section, + key.substr(0, pos), fallback); + return fallback; + } + throw value_error("The reference ${" + section + "." + key + "} does not exist (no fallback set)"); + } +} + +/** + * Dereference environment variable reference defined using: + * ${env:key} + * ${env:key:fallback value} + */ +string config::dereference_env(string var) const { + size_t pos; + string env_default; + /* + * This is needed because with only the string we cannot distinguish + * between an empty string as default and not default + */ + bool has_default = false; + + if ((pos = var.find(':')) != string::npos) { + env_default = var.substr(pos + 1); + has_default = true; + var.erase(pos); + } + + if (env_util::has(var)) { + string env_value{env_util::get(var)}; + m_log.info("Environment var reference ${%s} found (value=%s)", var, env_value); + return env_value; + } else if (has_default) { + m_log.info("Environment var ${%s} is undefined, using defined fallback value \"%s\"", var, env_default); + return env_default; + } else { + throw value_error(sstream() << "Environment var ${" << var << "} does not exist (no fallback set)"); + } +} + +/** + * Dereference X resource db value defined using: + * ${xrdb:key} + * ${xrdb:key:fallback value} + */ +string config::dereference_xrdb(string var) const { + size_t pos; +#if not WITH_XRM + m_log.warn("No built-in support to dereference ${xrdb:%s} references (requires `xcb-util-xrm`)", var); + if ((pos = var.find(':')) != string::npos) { + return var.substr(pos + 1); + } + return ""; +#else + if (!m_xrm) { + throw application_error("xrm is not initialized"); + } + + string fallback; + bool has_fallback = false; + if ((pos = var.find(':')) != string::npos) { + fallback = var.substr(pos + 1); + has_fallback = true; + var.erase(pos); + } + + try { + auto value = m_xrm->require(var.c_str()); + m_log.info("Found matching X resource \"%s\" (value=%s)", var, value); + return value; + } catch (const xresource_error& err) { + if (has_fallback) { + m_log.info("%s, using defined fallback value \"%s\"", err.what(), fallback); + return fallback; + } + throw value_error(sstream() << err.what() << " (no fallback set)"); + } +#endif +} + +/** + * Dereference file reference by reading its contents + * ${file:/absolute/file/path} + * ${file:/absolute/file/path:fallback value} + */ +string config::dereference_file(string var) const { + size_t pos; + string fallback; + bool has_fallback = false; + if ((pos = var.find(':')) != string::npos) { + fallback = var.substr(pos + 1); + has_fallback = true; + var.erase(pos); + } + var = file_util::expand(var); + + if (file_util::exists(var)) { + m_log.info("File reference \"%s\" found", var); + return string_util::trim(file_util::contents(var), '\n'); + } else if (has_fallback) { + m_log.info("File reference \"%s\" not found, using defined fallback value \"%s\"", var, fallback); + return fallback; + } else { + throw value_error(sstream() << "The file \"" << var << "\" does not exist (no fallback set)"); + } +} + + template <> string config::convert(string&& value) const { return forward(value);