3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "irrlichttypes_bloated.h"
22 #include "exceptions.h"
23 #include "jthread/jmutexautolock.h"
30 #include "util/serialize.h"
35 Settings & Settings::operator += (const Settings &other)
43 Settings & Settings::operator = (const Settings &other)
48 JMutexAutoLock lock(m_mutex);
49 JMutexAutoLock lock2(other.m_mutex);
58 bool Settings::parseConfigLines(std::istream &is,
59 const std::string &end)
61 JMutexAutoLock lock(m_mutex);
63 std::string name, value;
64 bool end_found = false;
66 while (is.good() && !end_found) {
67 if (parseConfigObject(is, name, value, end, end_found)) {
68 m_settings[name] = value;
71 if (!end.empty() && !end_found) {
78 bool Settings::readConfigFile(const char *filename)
80 std::ifstream is(filename);
84 JMutexAutoLock lock(m_mutex);
86 std::string name, value;
89 if (parseConfigObject(is, name, value)) {
90 m_settings[name] = value;
98 void Settings::writeLines(std::ostream &os) const
100 JMutexAutoLock lock(m_mutex);
102 for (std::map<std::string, std::string>::const_iterator
103 i = m_settings.begin();
104 i != m_settings.end(); ++i) {
105 os << i->first << " = " << i->second << '\n';
110 bool Settings::updateConfigFile(const char *filename)
112 std::list<std::string> objects;
113 std::set<std::string> updated;
114 bool changed = false;
116 JMutexAutoLock lock(m_mutex);
118 // Read the file and check for differences
120 std::ifstream is(filename);
122 getUpdatedConfigObject(is, objects,
127 // If something not yet determined to have been changed, check if
128 // any new stuff was added
130 for (std::map<std::string, std::string>::const_iterator
131 i = m_settings.begin();
132 i != m_settings.end(); ++i) {
133 if (updated.find(i->first) == updated.end()) {
140 // If nothing was actually changed, skip writing the file
147 std::ostringstream ss(std::ios_base::binary);
149 // Write changes settings
150 for (std::list<std::string>::const_iterator
152 i != objects.end(); ++i) {
156 // Write new settings
157 for (std::map<std::string, std::string>::const_iterator
158 i = m_settings.begin();
159 i != m_settings.end(); ++i) {
160 if (updated.find(i->first) != updated.end())
162 ss << i->first << " = " << i->second << '\n';
165 if (!fs::safeWriteToFile(filename, ss.str())) {
166 errorstream << "Error writing configuration file: \""
167 << filename << "\"" << std::endl;
176 bool Settings::parseCommandLine(int argc, char *argv[],
177 std::map<std::string, ValueSpec> &allowed_options)
179 int nonopt_index = 0;
180 for (int i = 1; i < argc; i++) {
181 std::string arg_name = argv[i];
182 if (arg_name.substr(0, 2) != "--") {
183 // If option doesn't start with -, read it in as nonoptX
184 if (arg_name[0] != '-'){
185 std::string name = "nonopt";
186 name += itos(nonopt_index);
191 errorstream << "Invalid command-line parameter \""
192 << arg_name << "\": --<option> expected." << std::endl;
196 std::string name = arg_name.substr(2);
198 std::map<std::string, ValueSpec>::iterator n;
199 n = allowed_options.find(name);
200 if (n == allowed_options.end()) {
201 errorstream << "Unknown command-line parameter \""
202 << arg_name << "\"" << std::endl;
206 ValueType type = n->second.type;
208 std::string value = "";
210 if (type == VALUETYPE_FLAG) {
213 if ((i + 1) >= argc) {
214 errorstream << "Invalid command-line parameter \""
215 << name << "\": missing value" << std::endl;
234 std::string Settings::get(const std::string &name) const
236 JMutexAutoLock lock(m_mutex);
238 std::map<std::string, std::string>::const_iterator n;
239 if ((n = m_settings.find(name)) == m_settings.end()) {
240 if ((n = m_defaults.find(name)) == m_defaults.end()) {
241 throw SettingNotFoundException("Setting [" + name + "] not found.");
248 bool Settings::getBool(const std::string &name) const
250 return is_yes(get(name));
254 u16 Settings::getU16(const std::string &name) const
256 return stoi(get(name), 0, 65535);
260 s16 Settings::getS16(const std::string &name) const
262 return stoi(get(name), -32768, 32767);
266 s32 Settings::getS32(const std::string &name) const
268 return stoi(get(name));
272 float Settings::getFloat(const std::string &name) const
274 return stof(get(name));
278 u64 Settings::getU64(const std::string &name) const
281 std::string s = get(name);
282 std::istringstream ss(s);
288 v2f Settings::getV2F(const std::string &name) const
293 value.X = stof(f.next(","));
294 value.Y = stof(f.next(")"));
299 v3f Settings::getV3F(const std::string &name) const
304 value.X = stof(f.next(","));
305 value.Y = stof(f.next(","));
306 value.Z = stof(f.next(")"));
311 u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
314 std::string val = get(name);
315 return std::isdigit(val[0])
317 : readFlagString(val, flagdesc, flagmask);
321 // N.B. if getStruct() is used to read a non-POD aggregate type,
322 // the behavior is undefined.
323 bool Settings::getStruct(const std::string &name, const std::string &format,
324 void *out, size_t olen) const
330 } catch (SettingNotFoundException &e) {
334 if (!deSerializeStringToStruct(valstr, format, out, olen))
341 bool Settings::exists(const std::string &name) const
343 JMutexAutoLock lock(m_mutex);
345 return (m_settings.find(name) != m_settings.end() ||
346 m_defaults.find(name) != m_defaults.end());
350 std::vector<std::string> Settings::getNames() const
352 std::vector<std::string> names;
353 for (std::map<std::string, std::string>::const_iterator
354 i = m_settings.begin();
355 i != m_settings.end(); ++i) {
356 names.push_back(i->first);
363 /***************************************
364 * Getters that don't throw exceptions *
365 ***************************************/
368 bool Settings::getNoEx(const std::string &name, std::string &val) const
373 } catch (SettingNotFoundException &e) {
379 bool Settings::getFlag(const std::string &name) const
382 return getBool(name);
383 } catch(SettingNotFoundException &e) {
389 bool Settings::getFloatNoEx(const std::string &name, float &val) const
392 val = getFloat(name);
394 } catch (SettingNotFoundException &e) {
400 bool Settings::getU16NoEx(const std::string &name, u16 &val) const
405 } catch (SettingNotFoundException &e) {
411 bool Settings::getS16NoEx(const std::string &name, s16 &val) const
416 } catch (SettingNotFoundException &e) {
422 bool Settings::getS32NoEx(const std::string &name, s32 &val) const
427 } catch (SettingNotFoundException &e) {
433 bool Settings::getU64NoEx(const std::string &name, u64 &val) const
438 } catch (SettingNotFoundException &e) {
444 bool Settings::getV2FNoEx(const std::string &name, v2f &val) const
449 } catch (SettingNotFoundException &e) {
455 bool Settings::getV3FNoEx(const std::string &name, v3f &val) const
460 } catch (SettingNotFoundException &e) {
466 // N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus,
467 // val must be initialized before using getFlagStrNoEx(). The intention of
468 // this is to simplify modifying a flags field from a default value.
469 bool Settings::getFlagStrNoEx(const std::string &name, u32 &val, FlagDesc *flagdesc) const
474 flags = getFlagStr(name, flagdesc, &flagmask);
480 } catch (SettingNotFoundException &e) {
492 void Settings::set(const std::string &name, std::string value)
494 JMutexAutoLock lock(m_mutex);
496 m_settings[name] = value;
500 void Settings::set(const std::string &name, const char *value)
502 JMutexAutoLock lock(m_mutex);
504 m_settings[name] = value;
508 void Settings::setDefault(const std::string &name, std::string value)
510 JMutexAutoLock lock(m_mutex);
512 m_defaults[name] = value;
516 void Settings::setBool(const std::string &name, bool value)
518 set(name, value ? "true" : "false");
522 void Settings::setS16(const std::string &name, s16 value)
524 set(name, itos(value));
528 void Settings::setS32(const std::string &name, s32 value)
530 set(name, itos(value));
534 void Settings::setU64(const std::string &name, u64 value)
536 std::ostringstream os;
542 void Settings::setFloat(const std::string &name, float value)
544 set(name, ftos(value));
548 void Settings::setV2F(const std::string &name, v2f value)
550 std::ostringstream os;
551 os << "(" << value.X << "," << value.Y << ")";
556 void Settings::setV3F(const std::string &name, v3f value)
558 std::ostringstream os;
559 os << "(" << value.X << "," << value.Y << "," << value.Z << ")";
564 void Settings::setFlagStr(const std::string &name, u32 flags,
565 const FlagDesc *flagdesc, u32 flagmask)
567 set(name, writeFlagString(flags, flagdesc, flagmask));
571 bool Settings::setStruct(const std::string &name, const std::string &format, void *value)
573 std::string structstr;
574 if (!serializeStructToString(&structstr, format, value))
577 set(name, structstr);
582 bool Settings::remove(const std::string &name)
584 JMutexAutoLock lock(m_mutex);
585 return m_settings.erase(name);
589 void Settings::clear()
591 JMutexAutoLock lock(m_mutex);
596 void Settings::updateValue(const Settings &other, const std::string &name)
601 JMutexAutoLock lock(m_mutex);
604 std::string val = other.get(name);
605 m_settings[name] = val;
606 } catch (SettingNotFoundException &e) {
611 void Settings::update(const Settings &other)
616 JMutexAutoLock lock(m_mutex);
617 JMutexAutoLock lock2(other.m_mutex);
623 void Settings::registerChangedCallback(std::string name,
624 setting_changed_callback cbf)
626 m_callbacks[name].push_back(cbf);
630 inline bool Settings::parseConfigObject(std::istream &is,
631 std::string &name, std::string &value)
633 bool end_found = false;
634 return parseConfigObject(is, name, value, "", end_found);
638 // NOTE: This function might be expanded to allow multi-line settings.
639 bool Settings::parseConfigObject(std::istream &is,
640 std::string &name, std::string &value,
641 const std::string &end, bool &end_found)
644 std::getline(is, line);
645 std::string trimmed_line = trim(line);
647 // Ignore empty lines and comments
648 if (trimmed_line.empty() || trimmed_line[0] == '#') {
649 value = trimmed_line;
652 if (trimmed_line == end) {
657 Strfnd sf(trimmed_line);
659 name = trim(sf.next("="));
661 value = trimmed_line;
665 value = trim(sf.next("\n"));
671 void Settings::getUpdatedConfigObject(std::istream &is,
672 std::list<std::string> &dst,
673 std::set<std::string> &updated,
676 std::string name, value;
678 if (!parseConfigObject(is, name, value)) {
679 dst.push_back(value + (is.eof() ? "" : "\n"));
683 if (m_settings.find(name) != m_settings.end()) {
684 std::string new_value = m_settings[name];
686 if (new_value != value) {
690 dst.push_back(name + " = " + new_value + (is.eof() ? "" : "\n"));
691 updated.insert(name);
692 } else { // File contains a setting which is not in m_settings
698 void Settings::updateNoLock(const Settings &other)
700 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
701 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
705 void Settings::clearNoLock()
711 void Settings::doCallbacks(const std::string name)
713 std::vector<setting_changed_callback> tempvector;
715 JMutexAutoLock lock(m_mutex);
716 if (m_callbacks.find(name) != m_callbacks.end())
718 tempvector = m_callbacks[name];
722 for (std::vector<setting_changed_callback>::iterator iter = tempvector.begin();
723 iter != tempvector.end(); iter ++)