#include "config.hh" #include "args.hh" #include "json.hh" namespace nix { void Config::set(const std::string & name, const std::string & value) { auto i = _settings.find(name); if (i == _settings.end()) throw UsageError("unknown setting '%s'", name); i->second.setting->set(value); i->second.setting->overriden = true; } void Config::addSetting(AbstractSetting * setting) { _settings.emplace(setting->name, Config::SettingData(false, setting)); for (auto & alias : setting->aliases) _settings.emplace(alias, Config::SettingData(true, setting)); bool set = false; auto i = initials.find(setting->name); if (i != initials.end()) { setting->set(i->second); setting->overriden = true; initials.erase(i); set = true; } for (auto & alias : setting->aliases) { auto i = initials.find(alias); if (i != initials.end()) { if (set) warn("setting '%s' is set, but it's an alias of '%s' which is also set", alias, setting->name); else { setting->set(i->second); setting->overriden = true; initials.erase(i); set = true; } } } } void Config::warnUnknownSettings() { for (auto & i : initials) warn("unknown setting '%s'", i.first); } StringMap Config::getSettings(bool overridenOnly) { StringMap res; for (auto & opt : _settings) if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden)) res.emplace(opt.first, opt.second.setting->to_string()); return res; } void Config::applyConfigFile(const Path & path, bool fatal) { try { string contents = readFile(path); unsigned int pos = 0; while (pos < contents.size()) { string line; while (pos < contents.size() && contents[pos] != '\n') line += contents[pos++]; pos++; string::size_type hash = line.find('#'); if (hash != string::npos) line = string(line, 0, hash); vector tokens = tokenizeString >(line); if (tokens.empty()) continue; if (tokens.size() < 2 || tokens[1] != "=") throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); string name = tokens[0]; vector::iterator i = tokens.begin(); advance(i, 2); try { set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow } catch (UsageError & e) { if (fatal) throw; warn("in configuration file '%s': %s", path, e.what()); } }; } catch (SysError &) { } } void Config::resetOverriden() { for (auto & s : _settings) s.second.setting->overriden = false; } void Config::toJSON(JSONObject & out) { for (auto & s : _settings) if (!s.second.isAlias) { JSONObject out2(out.object(s.first)); out2.attr("description", s.second.setting->description); JSONPlaceholder out3(out2.placeholder("value")); s.second.setting->toJSON(out3); } } void Config::convertToArgs(Args & args, const std::string & category) { for (auto & s : _settings) if (!s.second.isAlias) s.second.setting->convertToArg(args, category); } AbstractSetting::AbstractSetting( const std::string & name, const std::string & description, const std::set & aliases) : name(name), description(description), aliases(aliases) { } void AbstractSetting::toJSON(JSONPlaceholder & out) { out.write(to_string()); } void AbstractSetting::convertToArg(Args & args, const std::string & category) { } template void BaseSetting::toJSON(JSONPlaceholder & out) { out.write(value); } template void BaseSetting::convertToArg(Args & args, const std::string & category) { args.mkFlag() .longName(name) .description(description) .arity(1) .handler([=](Strings ss) { set(*ss.begin()); }) .category(category); } template<> void BaseSetting::set(const std::string & str) { value = str; } template<> std::string BaseSetting::to_string() { return value; } template void BaseSetting::set(const std::string & str) { static_assert(std::is_integral::value, "Integer required."); if (!string2Int(str, value)) throw UsageError("setting '%s' has invalid value '%s'", name, str); } template std::string BaseSetting::to_string() { static_assert(std::is_integral::value, "Integer required."); return std::to_string(value); } template<> void BaseSetting::set(const std::string & str) { if (str == "true" || str == "yes" || str == "1") value = true; else if (str == "false" || str == "no" || str == "0") value = false; else throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str); } template<> std::string BaseSetting::to_string() { return value ? "true" : "false"; } template<> void BaseSetting::convertToArg(Args & args, const std::string & category) { args.mkFlag() .longName(name) .description(description) .handler([=](Strings ss) { value = true; }) .category(category); args.mkFlag() .longName("no-" + name) .description(description) .handler([=](Strings ss) { value = false; }) .category(category); } template<> void BaseSetting::set(const std::string & str) { value = tokenizeString(str); } template<> std::string BaseSetting::to_string() { return concatStringsSep(" ", value); } template<> void BaseSetting::toJSON(JSONPlaceholder & out) { JSONList list(out.list()); for (auto & s : value) list.elem(s); } template<> void BaseSetting::set(const std::string & str) { value = tokenizeString(str); } template<> std::string BaseSetting::to_string() { return concatStringsSep(" ", value); } template<> void BaseSetting::toJSON(JSONPlaceholder & out) { JSONList list(out.list()); for (auto & s : value) list.elem(s); } template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; template class BaseSetting; void PathSetting::set(const std::string & str) { if (str == "") { if (allowEmpty) value = ""; else throw UsageError("setting '%s' cannot be empty", name); } else value = canonPath(str); } }