summaryrefslogtreecommitdiff
path: root/src/config.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
committerJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
commitc029d90d1975e124d237605f1edb2be16bd05b5d (patch)
tree9df87ffb365354bdb74a969440b32c8304bdbcb7 /src/config.cc
Initial commit
Diffstat (limited to 'src/config.cc')
-rw-r--r--src/config.cc176
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();
+}