/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: git://neueland.iserlohn-fortress.net/smolbote.git * * SPDX-License-Identifier: GPL-3.0 */ #include "configuration.h" #include #include using namespace libconfig; Configuration::Configuration(const std::string &path, const std::string &home) { m_userCfg = new Config(); // prettier output m_userCfg->setOptions(Config::OptionSemicolonSeparators | Config::OptionOpenBraceOnSeparateLine); m_userCfg->setOption(Config::OptionFsync, true); m_userCfgPath = path; m_homePath = home; m_defaultCfg = new Config(); } Configuration::~Configuration() { delete m_userCfg; delete m_defaultCfg; } bool Configuration::read() { try { m_userCfg->readFile(m_userCfgPath.c_str()); } catch(const FileIOException &e) { return false; } catch(const ParseException &e) { return false; } return true; } bool Configuration::parse(const std::string &contents) { try { m_userCfg->readString(contents); } catch(const ParseException &e) { return false; } return true; } bool Configuration::writeIfNeeded(const std::string &path) { if(!path.empty()) m_userCfgPath = path; if(!changed) return true; try { m_userCfg->writeFile(m_userCfgPath.c_str()); } catch(const FileIOException &e) { return false; } changed = false; return true; } bool Configuration::parseDefaultConfiguration(const std::string &contents) { try { m_defaultCfg->readString(contents); } catch(const ParseException &e) { return false; } return true; } std::vector Configuration::childrenSettings(const char *name) { std::vector groupNames; const Setting &root = m_userCfg->lookup(name); for(const Setting &setting : root) { if(setting.getType() != Setting::TypeGroup) { groupNames.emplace_back(setting.getName()); //groupNames.push_back(setting.getName()); } } return groupNames; } std::vector Configuration::childrenGroups(const char *name) { std::vector groupNames; const Setting &root = m_userCfg->lookup(name); for(const Setting &setting : root) { if(setting.getType() == Setting::TypeGroup) { groupNames.emplace_back(setting.getName()); //groupNames.push_back(setting.getName()); } } return groupNames; } void Configuration::resetValue(const char *path) { if(!m_userCfg->exists(path) && m_defaultCfg->exists(path)) { // entry doesn't exist, create path to it // split path into array of items, sep=. std::string s(path); std::vector pathTokens; std::string::size_type prev_pos = 0, pos = 0; while((pos = s.find('.', pos)) != std::string::npos) { std::string substring(s.substr(prev_pos, pos - prev_pos)); pathTokens.push_back(substring); prev_pos = ++pos; } pathTokens.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word s.clear(); // iterate through items, creating the path along the way for(const std::string &t : pathTokens) { Setting &v = m_userCfg->lookup(s); Setting &d = m_defaultCfg->lookup(s).lookup(t); if(!v.exists(t)) { v.add(t, d.getType()); } if(s.empty()) s.append(t); else s.append('.' + t); } } // if the path doesn't exist in the defaultCfg, this will crash // entry exists, reset it Setting &v = m_userCfg->lookup(path); switch(v.getType()) { case Setting::TypeNone: break; case Setting::TypeInt: v = static_cast(m_defaultCfg->lookup(path)); break; case Setting::TypeInt64: v = static_cast(m_defaultCfg->lookup(path)); break; case Setting::TypeFloat: v = static_cast(m_defaultCfg->lookup(path)); break; case Setting::TypeString: v = static_cast(m_defaultCfg->lookup(path)); break; case Setting::TypeBoolean: v = static_cast(m_defaultCfg->lookup(path)); break; case Setting::TypeGroup: break; case Setting::TypeArray: break; case Setting::TypeList: break; } // if it's a string, it may contain a path if(v.getType() == Setting::TypeString) { std::string val(static_cast(v)); v = patchHome(val, m_homePath).c_str(); } changed = true; } std::string Configuration::castToString(const libconfig::Setting &v) const { // cast depending on type // type checks are done during compile time switch(v.getType()) { case Setting::TypeNone: return std::string(); case Setting::TypeInt: // int, unsigned int, long, unsigned long return std::to_string(static_cast(v)); case Setting::TypeInt64: // int, unsigned int; long long, unsigned long long return std::to_string(static_cast(v)); case Setting::TypeFloat: // float, double return std::to_string(static_cast(v)); case Setting::TypeString: // const char*, std::string return std::string(static_cast(v)); case Setting::TypeBoolean: // bool return std::string(static_cast(v) ? "true" : "false"); case Setting::TypeGroup: case Setting::TypeArray: case Setting::TypeList: return std::string(); } } template bool Configuration::setValue(std::string path, const T &val) { if(m_userCfg->exists(path)) { Setting &setting = m_userCfg->lookup(path); // compiler complained about operator= not taking unsinged ints, longs and long longs if constexpr(std::is_unsigned_v && !std::is_same_v) { setting = static_cast>(val); } else if constexpr(std::is_same_v) { switch(setting.getType()) { case Setting::TypeNone: break; case Setting::TypeInt: case Setting::TypeInt64: setting = std::stoi(static_cast(val)); break; case Setting::TypeFloat: setting = std::stod(static_cast(val)); break; case Setting::TypeString: setting = static_cast(val).c_str(); break; case Setting::TypeBoolean: if(static_cast(val) == "true") { setting = true; } else if(static_cast(val) == "false") { setting = false; } break; case Setting::TypeGroup: break; case Setting::TypeArray: break; case Setting::TypeList: break; } } else { setting = val; } changed = true; return true; } // the entry doesn't exist return false; } template bool Configuration::setValue(std::string path, const int &val); template bool Configuration::setValue(std::string path, const unsigned int &val); template bool Configuration::setValue(std::string path, const long &val); template bool Configuration::setValue(std::string path, const unsigned long &val); template bool Configuration::setValue(std::string path, const long long &val); template bool Configuration::setValue(std::string path, const unsigned long long &val); template bool Configuration::setValue(std::string path, const float &val); template bool Configuration::setValue(std::string path, const double &val); template bool Configuration::setValue(std::string path, const bool &val); template bool Configuration::setValue(std::string path, const std::string &val); std::string patchHome(const std::string &path, const std::string &home) { std::string r = path; const size_t location = path.find('~'); if(location != std::string::npos) { return r.replace(location, 1, home); } return r; }