X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fsettings.cpp;h=0e44ee0bcb6c9adbc7915d228843a8c5fd85298d;hb=e16a470d59069692d654f5c1529ab313a01ded67;hp=91ea9c58b64c38bbb30d281eed4f0ed322497b34;hpb=33b874fea3948dfcbcc2d6761c28b325ca4aa367;p=minetest.git diff --git a/src/settings.cpp b/src/settings.cpp index 91ea9c58b..0e44ee0bc 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -33,21 +33,100 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -static Settings main_settings; -Settings *g_settings = &main_settings; +Settings *g_settings = nullptr; +static SettingsHierarchy g_hierarchy; std::string g_settings_path; -Settings::~Settings() +std::unordered_map Settings::s_flags; + +/* Settings hierarchy implementation */ + +SettingsHierarchy::SettingsHierarchy(Settings *fallback) { - clear(); + layers.push_back(fallback); } -Settings & Settings::operator += (const Settings &other) +Settings *SettingsHierarchy::getLayer(int layer) const { - update(other); + if (layer < 0 || layer >= (int)layers.size()) + throw BaseException("Invalid settings layer"); + return layers[layer]; +} - return *this; + +Settings *SettingsHierarchy::getParent(int layer) const +{ + assert(layer >= 0 && layer < (int)layers.size()); + // iterate towards the origin (0) to find the next fallback layer + for (int i = layer - 1; i >= 0; --i) { + if (layers[i]) + return layers[i]; + } + + return nullptr; +} + + +void SettingsHierarchy::onLayerCreated(int layer, Settings *obj) +{ + if (layer < 0) + throw BaseException("Invalid settings layer"); + if ((int)layers.size() < layer + 1) + layers.resize(layer + 1); + + Settings *&pos = layers[layer]; + if (pos) + throw BaseException("Setting layer " + itos(layer) + " already exists"); + + pos = obj; + // This feels bad + if (this == &g_hierarchy && layer == (int)SL_GLOBAL) + g_settings = obj; +} + + +void SettingsHierarchy::onLayerRemoved(int layer) +{ + assert(layer >= 0 && layer < (int)layers.size()); + layers[layer] = nullptr; + if (this == &g_hierarchy && layer == (int)SL_GLOBAL) + g_settings = nullptr; +} + +/* Settings implementation */ + +Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag) +{ + return new Settings(end_tag, &g_hierarchy, (int)sl); +} + + +Settings *Settings::getLayer(SettingsLayer sl) +{ + return g_hierarchy.getLayer(sl); +} + + +Settings::Settings(const std::string &end_tag, SettingsHierarchy *h, + int settings_layer) : + m_end_tag(end_tag), + m_hierarchy(h), + m_settingslayer(settings_layer) +{ + if (m_hierarchy) + m_hierarchy->onLayerCreated(m_settingslayer, this); +} + + +Settings::~Settings() +{ + MutexAutoLock lock(m_mutex); + + if (m_hierarchy) + m_hierarchy->onLayerRemoved(m_settingslayer); + + clearNoLock(); } @@ -56,11 +135,16 @@ Settings & Settings::operator = (const Settings &other) if (&other == this) return *this; + // TODO: Avoid copying Settings objects. Make this private. + FATAL_ERROR_IF(m_hierarchy || other.m_hierarchy, + "Cannot copy or overwrite Settings object that belongs to a hierarchy"); + MutexAutoLock lock(m_mutex); MutexAutoLock lock2(other.m_mutex); clearNoLock(); - updateNoLock(other); + m_settings = other.m_settings; + m_callbacks = other.m_callbacks; return *this; } @@ -69,7 +153,9 @@ Settings & Settings::operator = (const Settings &other) bool Settings::checkNameValid(const std::string &name) { bool valid = name.find_first_of("=\"{}#") == std::string::npos; - if (valid) valid = trim(name) == name; + if (valid) + valid = std::find_if(name.begin(), name.end(), ::isspace) == name.end(); + if (!valid) { errorstream << "Invalid setting name \"" << name << "\"" << std::endl; @@ -90,33 +176,6 @@ bool Settings::checkValueValid(const std::string &value) return true; } - -std::string Settings::sanitizeName(const std::string &name) -{ - std::string n = trim(name); - - for (const char *s = "=\"{}#"; *s; s++) - n.erase(std::remove(n.begin(), n.end(), *s), n.end()); - - return n; -} - - -std::string Settings::sanitizeValue(const std::string &value) -{ - std::string v(value); - size_t p = 0; - - if (v.substr(0, 3) == "\"\"\"") - v.erase(0, 3); - - while ((p = v.find("\n\"\"\"")) != std::string::npos) - v.erase(p, 4); - - return v; -} - - std::string Settings::getMultiline(std::istream &is, size_t *num_lines) { size_t lines = 1; @@ -149,11 +208,11 @@ bool Settings::readConfigFile(const char *filename) if (!is.good()) return false; - return parseConfigLines(is, ""); + return parseConfigLines(is); } -bool Settings::parseConfigLines(std::istream &is, const std::string &end) +bool Settings::parseConfigLines(std::istream &is) { MutexAutoLock lock(m_mutex); @@ -161,7 +220,7 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end) while (is.good()) { std::getline(is, line); - SettingsParseEvent event = parseConfigObject(line, end, name, value); + SettingsParseEvent event = parseConfigObject(line, name, value); switch (event) { case SPE_NONE: @@ -174,8 +233,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end) case SPE_END: return true; case SPE_GROUP: { - Settings *group = new Settings; - if (!group->parseConfigLines(is, "}")) { + Settings *group = new Settings("}"); + if (!group->parseConfigLines(is)) { delete group; return false; } @@ -188,7 +247,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end) } } - return end.empty(); + // false (failure) if end tag not found + return m_end_tag.empty(); } @@ -196,10 +256,16 @@ void Settings::writeLines(std::ostream &os, u32 tab_depth) const { MutexAutoLock lock(m_mutex); - for (std::map::const_iterator - it = m_settings.begin(); - it != m_settings.end(); ++it) - printEntry(os, it->first, it->second, tab_depth); + for (const auto &setting_it : m_settings) + printEntry(os, setting_it.first, setting_it.second, tab_depth); + + // For groups this must be "}" ! + if (!m_end_tag.empty()) { + for (u32 i = 0; i < tab_depth; i++) + os << "\t"; + + os << m_end_tag << "\n"; + } } @@ -214,9 +280,7 @@ void Settings::printEntry(std::ostream &os, const std::string &name, entry.group->writeLines(os, tab_depth + 1); - for (u32 i = 0; i != tab_depth; i++) - os << "\t"; - os << "}\n"; + // Closing bracket handled by writeLines } else { os << name << " = "; @@ -228,10 +292,9 @@ void Settings::printEntry(std::ostream &os, const std::string &name, } -bool Settings::updateConfigObject(std::istream &is, std::ostream &os, - const std::string &end, u32 tab_depth) +bool Settings::updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth) { - std::map::const_iterator it; + SettingEntries::const_iterator it; std::set present_entries; std::string line, name, value; bool was_modified = false; @@ -241,11 +304,11 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os, // in the object if existing while (is.good() && !end_found) { std::getline(is, line); - SettingsParseEvent event = parseConfigObject(line, end, name, value); + SettingsParseEvent event = parseConfigObject(line, name, value); switch (event) { case SPE_END: - os << line << (is.eof() ? "" : "\n"); + // Skip end tag. Append later. end_found = true; break; case SPE_MULTILINE: @@ -254,9 +317,13 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os, case SPE_KVPAIR: it = m_settings.find(name); if (it != m_settings.end() && - (it->second.is_group || it->second.value != value)) { + (it->second.is_group || it->second.value != value)) { printEntry(os, name, it->second, tab_depth); was_modified = true; + } else if (it == m_settings.end()) { + // Remove by skipping + was_modified = true; + break; } else { os << line << "\n"; if (event == SPE_MULTILINE) @@ -269,8 +336,14 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os, if (it != m_settings.end() && it->second.is_group) { os << line << "\n"; sanity_check(it->second.group != NULL); - was_modified |= it->second.group->updateConfigObject(is, os, - "}", tab_depth + 1); + was_modified |= it->second.group->updateConfigObject(is, os, tab_depth + 1); + } else if (it == m_settings.end()) { + // Remove by skipping + was_modified = true; + Settings removed_group("}"); // Move 'is' to group end + std::stringstream ss; + removed_group.updateConfigObject(is, ss, tab_depth + 1); + break; } else { printEntry(os, name, it->second, tab_depth); was_modified = true; @@ -283,6 +356,9 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os, } } + if (!line.empty() && is.eof()) + os << "\n"; + // Add any settings in the object that don't exist in the config file yet for (it = m_settings.begin(); it != m_settings.end(); ++it) { if (present_entries.find(it->first) != present_entries.end()) @@ -292,6 +368,12 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os, was_modified = true; } + // Append ending tag + if (!m_end_tag.empty()) { + os << m_end_tag << "\n"; + was_modified |= !end_found; + } + return was_modified; } @@ -303,7 +385,7 @@ bool Settings::updateConfigFile(const char *filename) std::ifstream is(filename); std::ostringstream os(std::ios_base::binary); - bool was_modified = updateConfigObject(is, os, ""); + bool was_modified = updateConfigObject(is, os); is.close(); if (!was_modified) @@ -351,7 +433,7 @@ bool Settings::parseCommandLine(int argc, char *argv[], ValueType type = n->second.type; - std::string value = ""; + std::string value; if (type == VALUETYPE_FLAG) { value = "true"; @@ -376,17 +458,26 @@ bool Settings::parseCommandLine(int argc, char *argv[], * Getters * ***********/ +Settings *Settings::getParent() const +{ + return m_hierarchy ? m_hierarchy->getParent(m_settingslayer) : nullptr; +} + const SettingsEntry &Settings::getEntry(const std::string &name) const { - MutexAutoLock lock(m_mutex); + { + MutexAutoLock lock(m_mutex); - std::map::const_iterator n; - if ((n = m_settings.find(name)) == m_settings.end()) { - if ((n = m_defaults.find(name)) == m_defaults.end()) - throw SettingNotFoundException("Setting [" + name + "] not found."); + SettingEntries::const_iterator n; + if ((n = m_settings.find(name)) != m_settings.end()) + return n->second; } - return n->second; + + if (auto parent = getParent()) + return parent->getEntry(name); + + throw SettingNotFoundException("Setting [" + name + "] not found."); } @@ -399,7 +490,7 @@ Settings *Settings::getGroup(const std::string &name) const } -std::string Settings::get(const std::string &name) const +const std::string &Settings::get(const std::string &name) const { const SettingsEntry &entry = getEntry(name); if (entry.is_group) @@ -426,6 +517,11 @@ s16 Settings::getS16(const std::string &name) const } +u32 Settings::getU32(const std::string &name) const +{ + return (u32) stoi(get(name)); +} + s32 Settings::getS32(const std::string &name) const { return stoi(get(name)); @@ -440,11 +536,8 @@ float Settings::getFloat(const std::string &name) const u64 Settings::getU64(const std::string &name) const { - u64 value = 0; std::string s = get(name); - std::istringstream ss(s); - ss >> value; - return value; + return from_string(s); } @@ -474,36 +567,39 @@ v3f Settings::getV3F(const std::string &name) const u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc, u32 *flagmask) const { - std::string val = get(name); - return std::isdigit(val[0]) - ? stoi(val) - : readFlagString(val, flagdesc, flagmask); -} - - -// N.B. if getStruct() is used to read a non-POD aggregate type, -// the behavior is undefined. -bool Settings::getStruct(const std::string &name, const std::string &format, - void *out, size_t olen) const -{ - std::string valstr; - - try { - valstr = get(name); - } catch (SettingNotFoundException &e) { - return false; + u32 flags = 0; + + // Read default value (if there is any) + if (auto parent = getParent()) + flags = parent->getFlagStr(name, flagdesc, flagmask); + + // Apply custom flags "on top" + if (m_settings.find(name) != m_settings.end()) { + std::string value = get(name); + u32 flags_user; + u32 mask_user = U32_MAX; + flags_user = std::isdigit(value[0]) + ? stoi(value) // Override default + : readFlagString(value, flagdesc, &mask_user); + + flags &= ~mask_user; + flags |= flags_user; + if (flagmask) + *flagmask |= mask_user; } - if (!deSerializeStringToStruct(valstr, format, out, olen)) - return false; - - return true; + return flags; } bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const { - return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np); + if (getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np)) + return true; + if (auto parent = getParent()) + return parent->getNoiseParams(name, np); + + return false; } @@ -515,6 +611,7 @@ bool Settings::getNoiseParamsFromValue(const std::string &name, if (!getNoEx(name, value)) return false; + // Format: f32,f32,(f32,f32,f32),s32,s32,f32[,f32] Strfnd f(value); np.offset = stof(f.next(",")); @@ -529,7 +626,7 @@ bool Settings::getNoiseParamsFromValue(const std::string &name, np.persist = stof(f.next(",")); std::string optional_params = f.next(""); - if (optional_params != "") + if (!optional_params.empty()) np.lacunarity = stof(optional_params); return true; @@ -561,21 +658,31 @@ bool Settings::getNoiseParamsFromGroup(const std::string &name, bool Settings::exists(const std::string &name) const +{ + if (existsLocal(name)) + return true; + if (auto parent = getParent()) + return parent->exists(name); + return false; +} + + +bool Settings::existsLocal(const std::string &name) const { MutexAutoLock lock(m_mutex); - return (m_settings.find(name) != m_settings.end() || - m_defaults.find(name) != m_defaults.end()); + return m_settings.find(name) != m_settings.end(); } std::vector Settings::getNames() const { + MutexAutoLock lock(m_mutex); + std::vector names; - for (std::map::const_iterator - i = m_settings.begin(); - i != m_settings.end(); ++i) { - names.push_back(i->first); + names.reserve(m_settings.size()); + for (const auto &settings_it : m_settings) { + names.push_back(settings_it.first); } return names; } @@ -586,17 +693,6 @@ std::vector Settings::getNames() const * Getters that don't throw exceptions * ***************************************/ -bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const -{ - try { - val = getEntry(name); - return true; - } catch (SettingNotFoundException &e) { - return false; - } -} - - bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const { try { @@ -661,6 +757,15 @@ bool Settings::getS16NoEx(const std::string &name, s16 &val) const } } +bool Settings::getU32NoEx(const std::string &name, u32 &val) const +{ + try { + val = getU32(name); + return true; + } catch (SettingNotFoundException &e) { + return false; + } +} bool Settings::getS32NoEx(const std::string &name, s32 &val) const { @@ -706,19 +811,16 @@ bool Settings::getV3FNoEx(const std::string &name, v3f &val) const } -// N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus, -// val must be initialized before using getFlagStrNoEx(). The intention of -// this is to simplify modifying a flags field from a default value. bool Settings::getFlagStrNoEx(const std::string &name, u32 &val, - FlagDesc *flagdesc) const + const FlagDesc *flagdesc) const { - try { - u32 flags, flagmask; - - flags = getFlagStr(name, flagdesc, &flagmask); + if (!flagdesc) { + if (!(flagdesc = getFlagDescFallback(name))) + return false; // Not found + } - val &= ~flagmask; - val |= flags; + try { + val = getFlagStr(name, flagdesc, nullptr); return true; } catch (SettingNotFoundException &e) { @@ -732,24 +834,25 @@ bool Settings::getFlagStrNoEx(const std::string &name, u32 &val, ***********/ bool Settings::setEntry(const std::string &name, const void *data, - bool set_group, bool set_default) + bool set_group) { - Settings *old_group = NULL; - if (!checkNameValid(name)) return false; if (!set_group && !checkValueValid(*(const std::string *)data)) return false; + Settings *old_group = NULL; { MutexAutoLock lock(m_mutex); - SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name]; + SettingsEntry &entry = m_settings[name]; old_group = entry.group; entry.value = set_group ? "" : *(const std::string *)data; entry.group = set_group ? *(Settings **)data : NULL; entry.is_group = set_group; + if (set_group) + entry.group->m_end_tag = "}"; } delete old_group; @@ -760,7 +863,7 @@ bool Settings::setEntry(const std::string &name, const void *data, bool Settings::set(const std::string &name, const std::string &value) { - if (!setEntry(name, &value, false, false)) + if (!setEntry(name, &value, false)) return false; doCallbacks(name); @@ -768,21 +871,22 @@ bool Settings::set(const std::string &name, const std::string &value) } +// TODO: Remove this function bool Settings::setDefault(const std::string &name, const std::string &value) { - return setEntry(name, &value, false, true); -} - - -bool Settings::setGroup(const std::string &name, Settings *group) -{ - return setEntry(name, &group, true, false); + FATAL_ERROR_IF(m_hierarchy != &g_hierarchy, "setDefault is only valid on " + "global settings"); + return getLayer(SL_DEFAULTS)->set(name, value); } -bool Settings::setGroupDefault(const std::string &name, Settings *group) +bool Settings::setGroup(const std::string &name, const Settings &group) { - return setEntry(name, &group, true, true); + // Settings must own the group pointer + // avoid double-free by copying the source + Settings *copy = new Settings(); + *copy = group; + return setEntry(name, ©, true); } @@ -843,23 +947,16 @@ bool Settings::setV3F(const std::string &name, v3f value) bool Settings::setFlagStr(const std::string &name, u32 flags, const FlagDesc *flagdesc, u32 flagmask) { - return set(name, writeFlagString(flags, flagdesc, flagmask)); -} - - -bool Settings::setStruct(const std::string &name, const std::string &format, - void *value) -{ - std::string structstr; - if (!serializeStructToString(&structstr, format, value)) - return false; + if (!flagdesc) { + if (!(flagdesc = getFlagDescFallback(name))) + return false; // Not found + } - return set(name, structstr); + return set(name, writeFlagString(flags, flagdesc, flagmask)); } -bool Settings::setNoiseParams(const std::string &name, - const NoiseParams &np, bool set_default) +bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np) { Settings *group = new Settings; @@ -872,67 +969,32 @@ bool Settings::setNoiseParams(const std::string &name, group->setFloat("lacunarity", np.lacunarity); group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags); - return setEntry(name, &group, true, set_default); + return setEntry(name, &group, true); } bool Settings::remove(const std::string &name) { - MutexAutoLock lock(m_mutex); + // Lock as short as possible, unlock before doCallbacks() + m_mutex.lock(); - std::map::iterator it = m_settings.find(name); + SettingEntries::iterator it = m_settings.find(name); if (it != m_settings.end()) { delete it->second.group; m_settings.erase(it); - return true; - } else { - return false; - } -} - - -void Settings::clear() -{ - MutexAutoLock lock(m_mutex); - clearNoLock(); -} + m_mutex.unlock(); -void Settings::clearDefaults() -{ - MutexAutoLock lock(m_mutex); - clearDefaultsNoLock(); -} - -void Settings::updateValue(const Settings &other, const std::string &name) -{ - if (&other == this) - return; - - MutexAutoLock lock(m_mutex); - - try { - std::string val = other.get(name); - - m_settings[name] = val; - } catch (SettingNotFoundException &e) { + doCallbacks(name); + return true; } -} - -void Settings::update(const Settings &other) -{ - if (&other == this) - return; - - MutexAutoLock lock(m_mutex); - MutexAutoLock lock2(other.m_mutex); - - updateNoLock(other); + m_mutex.unlock(); + return false; } SettingsParseEvent Settings::parseConfigObject(const std::string &line, - const std::string &end, std::string &name, std::string &value) + std::string &name, std::string &value) { std::string trimmed_line = trim(line); @@ -940,7 +1002,7 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line, return SPE_NONE; if (trimmed_line[0] == '#') return SPE_COMMENT; - if (trimmed_line == end) + if (trimmed_line == m_end_tag) return SPE_END; size_t pos = trimmed_line.find('='); @@ -959,37 +1021,33 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line, } -void Settings::updateNoLock(const Settings &other) -{ - m_settings.insert(other.m_settings.begin(), other.m_settings.end()); - m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end()); -} - - void Settings::clearNoLock() { - std::map::const_iterator it; - for (it = m_settings.begin(); it != m_settings.end(); ++it) + for (SettingEntries::const_iterator it = m_settings.begin(); + it != m_settings.end(); ++it) delete it->second.group; m_settings.clear(); - - clearDefaultsNoLock(); } -void Settings::clearDefaultsNoLock() + +void Settings::setDefault(const std::string &name, const FlagDesc *flagdesc, + u32 flags) { - std::map::const_iterator it; - for (it = m_defaults.begin(); it != m_defaults.end(); ++it) - delete it->second.group; - m_defaults.clear(); + s_flags[name] = flagdesc; + setDefault(name, writeFlagString(flags, flagdesc, U32_MAX)); } +const FlagDesc *Settings::getFlagDescFallback(const std::string &name) const +{ + auto it = s_flags.find(name); + return it == s_flags.end() ? nullptr : it->second; +} void Settings::registerChangedCallback(const std::string &name, SettingsChangedCallback cbf, void *userdata) { MutexAutoLock lock(m_callback_mutex); - m_callbacks[name].push_back(std::make_pair(cbf, userdata)); + m_callbacks[name].emplace_back(cbf, userdata); } void Settings::deregisterChangedCallback(const std::string &name, @@ -1009,6 +1067,19 @@ void Settings::deregisterChangedCallback(const std::string &name, } } +void Settings::removeSecureSettings() +{ + for (const auto &name : getNames()) { + if (name.compare(0, 7, "secure.") != 0) + continue; + + errorstream << "Secure setting " << name + << " isn't allowed, so was ignored." + << std::endl; + remove(name); + } +} + void Settings::doCallbacks(const std::string &name) const { MutexAutoLock lock(m_callback_mutex);