/* * 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 static Configuration *s_conf = nullptr; 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> l) noexcept #ifndef NO_QT_SPEC : m_homePath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString()) #endif { for(const auto &i : l) { insert_or_assign(i.first, i.second); } } void Configuration::read_file(const std::string &location) { std::fstream fs(location, std::fstream::in); if(fs.is_open()) { read(fs); fs.close(); } } inline auto strip(std::string &value) { value.erase(value.begin(), std::find_if(value.begin(), value.end(), std::bind1st(std::not_equal_to(), ' '))); value.erase(std::find_if(value.rbegin(), value.rend(), std::bind1st(std::not_equal_to(), ' ')).base(), value.end()); return value; } inline auto parse(const std::string &line, std::string §ion) { struct { bool pair = false; std::string key; std::string value; } ret; if(line[0] == '#' || line.length() == 0) { return ret; } if(line.front() == '[' && line.back() == ']') { section = line.substr(1, line.length() - 2) + '/'; return ret; } const auto pos = line.find_first_of('='); if(pos == std::string::npos) { return ret; } ret.key = line.substr(0, pos); strip(ret.key); if(ret.key.empty()) { return ret; } ret.key = section + ret.key; ret.value = line.substr(pos + 1); strip(ret.value); ret.pair = true; return ret; } #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, section; while(std::getline(input, line)) { if(line.rfind("@@") == 0) { if(line.rfind("@@include ") == 0) { read_file(line.substr(10)); } continue; } const auto pair = parse(line, section); if(pair.pair) { if(this->count(pair.key) == 0) { // no type has been specified for this key, assuming std::string insert(std::make_pair(pair.key, pair.value)); continue; } auto v = at(pair.key); if(std::holds_alternative(v)) { at(pair.key) = pair.value; } else if(std::holds_alternative(v)) { at(pair.key) = std::stoi(pair.value); } else if(std::holds_alternative(v)) { at(pair.key) = (pair.value == "true"); } } } } bool Configuration::make_global() { if(use_global || s_conf != nullptr) { return false; } s_conf = this; return true; } Configuration *Configuration::instance() { return s_conf; } 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; for(const auto &pair : obj) { keys.emplace_back(pair.first); } std::sort(keys.begin(), keys.end()); for(const auto &key : keys) { out << key << "=" << obj.value(key.c_str()).value() << "\n"; } return out; }