#include "common.hh" #include "config.hh" #include "io.hh" #include "logger.hh" #include "strutil.hh" #include #include #include #include #include namespace { class ConfigImpl : public Config { public: ConfigImpl() : data_(), root_() {} ConfigImpl(std::unordered_map data, std::filesystem::path root) : data_(std::move(data)), root_(std::move(root)) {} std::string_view get(std::string const& key, std::string_view default_) const override { auto it = data_.find(key); if (it == data_.end()) return default_; return it->second; } char const* get(std::string const& key, char const* default_) const override { auto it = data_.find(key); if (it == data_.end()) return default_; return it->second.c_str(); } std::optional get(std::string const& key, uint64_t default_) const override { auto* data = get(key, nullptr); if (!data) return default_; return str::parse_uint64(data); } std::optional get_size(std::string const& key, uint64_t default_) const override { auto* data = get(key, nullptr); if (!data) return default_; char* end = nullptr; double value = strtod(data, &end); if (end == data) return std::nullopt; std::string_view suffix(end); if (suffix == "t" || suffix == "T" || suffix == "TB" || suffix == "Tb") return value * 1024 * 1024 * 1024 * 1024; if (suffix == "g" || suffix == "G" || suffix == "GB" || suffix == "Gb") return value * 1024 * 1024 * 1024; if (suffix == "m" || suffix == "M" || suffix == "MB" || suffix == "Mb") return value * 1024 * 1024; if (suffix == "k" || suffix == "K" || suffix == "KB" || suffix == "Kb") return value * 1024; if (suffix == "b" || suffix == "B" || suffix == "") return value; return std::nullopt; } std::optional get_duration(std::string const& key, double default_) const override { auto* data = get(key, nullptr); if (!data) return default_; char* end = nullptr; double value = strtod(data, &end); if (end == data) return std::nullopt; std::string_view suffix(end); if (suffix == "h" || suffix == "H") return value * 60.0 * 60.0; if (suffix == "m" || suffix == "M") return value * 60.0; if (suffix == "ms" || suffix == "MS") return value / 1000.0; if (suffix == "ns" || suffix == "NS") return value / 1000000.0; if (suffix == "s" || suffix == "S" || suffix == "") return value; return std::nullopt; } std::filesystem::path get_path(std::string const& key, std::string_view default_) const override { auto it = data_.find(key); if (it == data_.end()) { if (default_.empty()) return std::filesystem::path(); return root_ / default_; } return root_ / it->second; } private: std::unordered_map const data_; std::filesystem::path const root_; }; inline bool is_space(char c) { return c == ' ' || c == '\t'; } void trim(std::string_view str, size_t* start, size_t* end) { while (*start < *end && is_space(str[*start])) ++*start; while (*end > *start && is_space(str[*end - 1])) --*end; } std::unique_ptr load(Logger* logger, std::filesystem::path const& path) { std::ifstream in(path); if (!in.good()) { logger->warn("Unable to open %s for reading: %s", path.c_str(), strerror(errno)); return nullptr; } std::error_code err; auto root = std::filesystem::absolute(path.parent_path(), err); std::unordered_map data; std::string line; unsigned long num = 0; while (std::getline(in, line)) { ++num; if (line.empty() || line.front() == '#') continue; auto eq = line.find('='); if (eq == std::string::npos) { logger->warn("%s:%lu: Invalid line, no equal sign (=).", path.c_str(), num); return nullptr; } size_t key_start = 0; size_t key_end = eq; trim(line, &key_start, &key_end); if (key_start == key_end) { logger->warn("%s:%lu: Invalid line, no key before equal sign (=).", path.c_str(), num); return nullptr; } size_t value_start = eq + 1; size_t value_end = line.size(); trim(line, &value_start, &value_end); auto key = line.substr(key_start, key_end - key_start); data[key] = line.substr(value_start, value_end - value_start); } if (!in.eof()) { logger->warn("Error reading %s: %s", path.c_str(), strerror(errno)); return nullptr; } return Config::create(std::move(data), std::move(root)); } } // namespace std::unique_ptr Config::create(Logger* logger, std::filesystem::path const& filepath) { return load(logger, filepath); } std::unique_ptr Config::create( std::unordered_map data, std::filesystem::path root) { return std::make_unique(std::move(data), std::move(root)); } std::unique_ptr Config::create_empty() { return std::make_unique(); }