/* * 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: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #include "configuration.h" #include #include #include #include #include #ifndef NO_QT_SPEC #include #endif using namespace std::placeholders; static std::unique_ptr s_conf; Configuration::Configuration() : use_global(true) { if(!s_conf) { throw std::runtime_error("Trying to use default Configuration, but none has been set!"); } } Configuration::Configuration(std::initializer_list> p_list) noexcept #ifndef NO_QT_SPEC : m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()) #endif { for(const auto &item : p_list) { insert_or_assign(item); } } bool Configuration::read_file(const std::string &location) { std::fstream fstream(location, std::fstream::in); bool result = false; if(fstream.is_open()) { read(fstream); fstream.close(); result = true; } return result; } inline auto strip(std::string &value) { // strip whitespace at the begining value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind(std::not_equal_to<>(), ' ', _1))); // strip whitespace from the end value.erase(std::find_if(value.rbegin(), value.rend(), std::bind(std::not_equal_to<>(), ' ', _1)).base(), value.end()); return value; } inline std::optional parse_line(const std::string &line, /* in/out */ std::string §ion) { if(line.front() == '[' && line.back() == ']') { section = line.substr(1, line.length() - 2) + '/'; return std::nullopt; } const auto pos = line.find_first_of('='); if(pos == std::string::npos) { return std::nullopt; } std::string key = line.substr(0, pos); std::string val = line.substr(pos + 1); strip(key); strip(val); if(key.empty()) { return std::nullopt; } key = section + key; return std::make_optional({ key, val }); } #ifdef FUZZER extern "C" int LLVMFuzzerTestOneInput(const char *Data, long long Size) { std::string section; parse(std::string(Data, Size), section); return 0; } #endif void Configuration::read(std::basic_istream &input) { std::string line; std::string section; while(std::getline(input, line)) { // skip empty lines and comments if(line.length() == 0 || line[0] == '#') { continue; } // include file if(line.rfind("@@") == 0) { if(line.rfind("@@include ") == 0) { read_file(line.substr(10)); } continue; } const auto pair = parse_line(line, section); if(pair) { insert_or_assign(*pair); } } } void Configuration::insert_or_assign(const Configuration::kv_pair &pair) { // insert if(count(pair.first) == 0) { insert(pair); } else { // when assigning we have to keep the same type const auto old_value = at(pair.first); if(std::holds_alternative(old_value)) { at(pair.first) = pair.second; } else if(std::holds_alternative(old_value)) { at(pair.first) = std::stoi(std::get(pair.second)); } else if(std::holds_alternative(old_value)) { at(pair.first) = (std::get(pair.second) == "true"); } } } void Configuration::move_global(std::unique_ptr &&conf) { s_conf = std::move(conf); } Configuration *Configuration::instance() { return s_conf.get(); } std::ostream &operator<<(std::ostream &out, const Configuration &obj) { if(obj.use_global) { out << *s_conf; return out; } // unordered_map is, well, unordered, so grab the keys and sort them before printing them std::vector keys; std::transform(obj.cbegin(), obj.cend(), std::inserter(keys, keys.end()), [](const auto &pair) { return pair.first; }); std::sort(keys.begin(), keys.end()); for(const auto &key : keys) { out << key << "=" << obj.value(key.c_str()).value() << "\n"; } return out; }