diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2017-02-28 21:50:44 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2017-02-28 21:50:44 +0100 |
| commit | c029d90d1975e124d237605f1edb2be16bd05b5d (patch) | |
| tree | 9df87ffb365354bdb74a969440b32c8304bdbcb7 /src/config.cc | |
Initial commit
Diffstat (limited to 'src/config.cc')
| -rw-r--r-- | src/config.cc | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 0000000..9824044 --- /dev/null +++ b/src/config.cc @@ -0,0 +1,176 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" + +#include "config.hh" +#include "paths.hh" +#include "strings.hh" +#include "xdg.hh" + +#include <cstring> +#include <fstream> +#include <sstream> +#include <unordered_map> + +namespace { + +class ConfigImpl : public Config { +public: + ConfigImpl() + : good_(true) { + } + + bool load_name(std::string const& name) override { + name_ = name; + + auto dirs = XDG::config_dirs(); + bool good = true; + std::string error; + std::unordered_map<std::string,std::string> data; + for (auto& dir : dirs) { + std::string tmp_error; + std::unordered_map<std::string,std::string> tmp_data; + if (load_file(Paths::join(dir, name_), &tmp_data, &tmp_error)) { + data.insert(tmp_data.begin(), tmp_data.end()); + } else { + if (good) { + // We want the most import error + error = tmp_error; + good = false; + } + } + } + return update(good, error, data); + } + + bool load_file(std::string const& filename) override { + std::string error; + std::unordered_map<std::string,std::string> data; + bool good = load_file(filename, &data, &error); + return update(good, error, data); + } + + bool good() const override { + return good_; + } + + std::string const& last_error() const override { + return last_error_; + } + + std::string const& get(std::string const& key, + std::string const& fallback) override { + auto i = override_.find(key); + if (i != override_.end()) return i->second; + i = data_.find(key); + if (i != data_.end()) return i->second; + return fallback; + } + char const* get(std::string const& key, char const* fallback) override { + auto i = override_.find(key); + if (i != override_.end()) return i->second.c_str(); + i = data_.find(key); + if (i != data_.end()) return i->second.c_str(); + return fallback; + } + bool is_set(std::string const& key) override { + return override_.count(key) > 0 || data_.count(key) > 0; + } + + bool get(std::string const& key, bool fallback) override { + auto ret = get(key, nullptr); + if (!ret) return fallback; + return strcmp(ret, "true") == 0; + } + + void set(std::string const& key, std::string const& value) override { + auto ret = override_.insert(std::make_pair(key, value)); + if (!ret.second) { + ret.first->second = value; + } + } + +private: + bool update(bool good, std::string const& last_error, + std::unordered_map<std::string, std::string>& data) { + good_ = good; + if (!good_) { + last_error_ = last_error; + } else { + data_.clear(); + data_.swap(data); + } + return good_; + } + + static bool load_file(std::string const& filename, + std::unordered_map<std::string, std::string>* data, + std::string* error) { + bool good = true; + data->clear(); + error->clear(); + + std::ifstream in(filename); + // Non existent file is not considered an error + if (in) { + std::string line; + uint32_t count = 0; + std::string key, value; + while (std::getline(in, line)) { + count++; + if (line.empty() || line[0] == '#') continue; + auto idx = line.find('='); + if (idx == 0 || idx == std::string::npos) { + std::stringstream ss; + if (idx == 0) { + ss << filename << ':' << count << ": Invalid line, starts with '='"; + } else { + ss << filename << ':' << count << ": Invalid line, no '=' found"; + } + *error = ss.str(); + good = false; + break; + } + size_t start = 0, end = idx; + key.assign(Strings::trim(line, start, end)); + if (data->count(key) > 0) { + std::stringstream ss; + ss << filename << ':' << count << ": '" << key << "' is already set"; + *error = ss.str(); + good = false; + break; + } + start = idx + 1; + end = line.size(); + Strings::trim(line, &start, &end); + if (line[start] == '"' && line[end - 1] == '"') { + value.assign(Strings::unquote(line, start, end)); + } else { + value.assign(line.substr(start, end - start)); + } + (*data)[key] = value; + } + if (good && in.bad()) { + std::stringstream ss; + ss << filename << ": I/O error: " << strerror(errno); + *error = ss.str(); + good = false; + } + if (!good) data->clear(); + } + return good; + } + + bool good_; + std::string name_; + std::string last_error_; + std::unordered_map<std::string, std::string> data_; + std::unordered_map<std::string, std::string> override_; +}; + +} // namespace + +// static +Config* Config::create() { + return new ConfigImpl(); +} |
