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.
20 #ifndef SETTINGS_HEADER
21 #define SETTINGS_HEADER
23 #include "irrlichttypes_bloated.h"
24 #include "exceptions.h"
26 #include "jthread/jmutex.h"
27 #include "jthread/jmutexautolock.h"
34 #include "util/string.h"
35 #include "util/serialize.h"
45 VALUETYPE_FLAG // Doesn't take any arguments
50 ValueSpec(ValueType a_type, const char *a_help=NULL)
66 void writeLines(std::ostream &os) const
68 JMutexAutoLock lock(m_mutex);
70 for(std::map<std::string, std::string>::const_iterator
71 i = m_settings.begin();
72 i != m_settings.end(); ++i)
74 std::string name = i->first;
75 std::string value = i->second;
76 os<<name<<" = "<<value<<"\n";
80 // return all keys used
81 std::vector<std::string> getNames() const
83 std::vector<std::string> names;
84 for(std::map<std::string, std::string>::const_iterator
85 i = m_settings.begin();
86 i != m_settings.end(); ++i)
88 names.push_back(i->first);
94 bool remove(const std::string &name)
96 return m_settings.erase(name);
100 bool parseConfigLine(const std::string &line)
102 JMutexAutoLock lock(m_mutex);
104 std::string trimmedline = trim(line);
106 // Ignore empty lines and comments
107 if(trimmedline.size() == 0 || trimmedline[0] == '#')
110 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
112 Strfnd sf(trim(line));
114 std::string name = sf.next("=");
120 std::string value = sf.next("\n");
123 /*infostream<<"Config name=\""<<name<<"\" value=\""
124 <<value<<"\""<<std::endl;*/
126 m_settings[name] = value;
131 void parseConfigLines(std::istream &is, const std::string &endstring)
137 std::getline(is, line);
138 std::string trimmedline = trim(line);
140 if(trimmedline == endstring)
143 parseConfigLine(line);
147 // Returns false on EOF
148 bool parseConfigObject(std::istream &is)
154 NOTE: This function might be expanded to allow multi-line
158 std::getline(is, line);
159 //infostream<<"got line: \""<<line<<"\""<<std::endl;
161 return parseConfigLine(line);
165 Read configuration file
167 Returns true on success
169 bool readConfigFile(const char *filename)
171 std::ifstream is(filename);
172 if(is.good() == false)
175 /*infostream<<"Parsing configuration file: \""
176 <<filename<<"\""<<std::endl;*/
178 while(parseConfigObject(is));
184 Reads a configuration object from stream (usually a single line)
187 Preserves comments and empty lines.
189 Settings that were added to dst are also added to updated.
190 key of updated is setting name, value of updated is dummy.
194 bool getUpdatedConfigObject(std::istream &is,
195 std::list<std::string> &dst,
196 std::set<std::string> &updated,
199 JMutexAutoLock lock(m_mutex);
204 // NOTE: This function will be expanded to allow multi-line settings
206 std::getline(is, line);
208 std::string trimmedline = trim(line);
210 std::string line_end = "";
211 if(is.eof() == false)
214 // Ignore empty lines and comments
215 if(trimmedline.size() == 0 || trimmedline[0] == '#')
217 dst.push_back(line+line_end);
221 Strfnd sf(trim(line));
223 std::string name = sf.next("=");
228 dst.push_back(line+line_end);
232 std::string value = sf.next("\n");
235 if(m_settings.find(name) != m_settings.end())
237 std::string newvalue = m_settings[name];
239 if(newvalue != value)
241 infostream<<"Changing value of \""<<name<<"\" = \""
242 <<value<<"\" -> \""<<newvalue<<"\""
244 value_changed = true;
247 dst.push_back(name + " = " + newvalue + line_end);
249 updated.insert(name);
251 else //file contains a setting which is not in m_settings
258 Updates configuration file
260 Returns true on success
262 bool updateConfigFile(const char *filename)
264 infostream<<"Updating configuration file: \""
265 <<filename<<"\""<<std::endl;
267 std::list<std::string> objects;
268 std::set<std::string> updated;
269 bool something_actually_changed = false;
271 // Read and modify stuff
273 std::ifstream is(filename);
274 if(is.good() == false)
276 infostream<<"updateConfigFile():"
277 " Error opening configuration file"
279 <<filename<<"\""<<std::endl;
283 while(getUpdatedConfigObject(is, objects, updated,
284 something_actually_changed));
288 JMutexAutoLock lock(m_mutex);
290 // If something not yet determined to have been changed, check if
291 // any new stuff was added
292 if(!something_actually_changed){
293 for(std::map<std::string, std::string>::const_iterator
294 i = m_settings.begin();
295 i != m_settings.end(); ++i)
297 if(updated.find(i->first) != updated.end())
299 something_actually_changed = true;
304 // If nothing was actually changed, skip writing the file
305 if(!something_actually_changed){
306 infostream<<"Skipping writing of "<<filename
307 <<" because content wouldn't be modified"<<std::endl;
313 std::ostringstream ss(std::ios_base::binary);
318 for(std::list<std::string>::const_iterator
320 i != objects.end(); ++i)
326 Write stuff that was not already in the file
328 for(std::map<std::string, std::string>::const_iterator
329 i = m_settings.begin();
330 i != m_settings.end(); ++i)
332 if(updated.find(i->first) != updated.end())
334 std::string name = i->first;
335 std::string value = i->second;
336 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
338 ss<<name<<" = "<<value<<"\n";
341 if(!fs::safeWriteToFile(filename, ss.str()))
343 errorstream<<"Error writing configuration file: \""
344 <<filename<<"\""<<std::endl;
353 NOTE: Types of allowed_options are ignored
355 returns true on success
357 bool parseCommandLine(int argc, char *argv[],
358 std::map<std::string, ValueSpec> &allowed_options)
360 int nonopt_index = 0;
366 std::string argname = argv[i];
367 if(argname.substr(0, 2) != "--")
369 // If option doesn't start with -, read it in as nonoptX
370 if(argname[0] != '-'){
371 std::string name = "nonopt";
372 name += itos(nonopt_index);
378 errorstream<<"Invalid command-line parameter \""
379 <<argname<<"\": --<option> expected."<<std::endl;
384 std::string name = argname.substr(2);
386 std::map<std::string, ValueSpec>::iterator n;
387 n = allowed_options.find(name);
388 if(n == allowed_options.end())
390 errorstream<<"Unknown command-line parameter \""
391 <<argname<<"\""<<std::endl;
395 ValueType type = n->second.type;
397 std::string value = "";
399 if(type == VALUETYPE_FLAG)
407 errorstream<<"Invalid command-line parameter \""
408 <<name<<"\": missing value"<<std::endl;
416 infostream<<"Valid command-line parameter: \""
417 <<name<<"\" = \""<<value<<"\""
425 void set(const std::string &name, std::string value)
427 JMutexAutoLock lock(m_mutex);
429 m_settings[name] = value;
432 void set(const std::string &name, const char *value)
434 JMutexAutoLock lock(m_mutex);
436 m_settings[name] = value;
440 void setDefault(const std::string &name, std::string value)
442 JMutexAutoLock lock(m_mutex);
444 m_defaults[name] = value;
447 bool exists(const std::string &name) const
449 JMutexAutoLock lock(m_mutex);
451 return (m_settings.find(name) != m_settings.end() ||
452 m_defaults.find(name) != m_defaults.end());
455 std::string get(const std::string &name) const
457 JMutexAutoLock lock(m_mutex);
459 std::map<std::string, std::string>::const_iterator n;
460 if ((n = m_settings.find(name)) == m_settings.end())
461 if ((n = m_defaults.find(name)) == m_defaults.end())
462 throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
467 //////////// Get setting
468 bool getBool(const std::string &name) const
470 return is_yes(get(name));
473 bool getFlag(const std::string &name) const
476 return getBool(name);
477 } catch(SettingNotFoundException &e) {
482 float getFloat(const std::string &name) const
484 return stof(get(name));
487 u16 getU16(const std::string &name) const
489 return stoi(get(name), 0, 65535);
492 s16 getS16(const std::string &name) const
494 return stoi(get(name), -32768, 32767);
497 s32 getS32(const std::string &name) const
499 return stoi(get(name));
502 v3f getV3F(const std::string &name) const
507 value.X = stof(f.next(","));
508 value.Y = stof(f.next(","));
509 value.Z = stof(f.next(")"));
513 v2f getV2F(const std::string &name) const
518 value.X = stof(f.next(","));
519 value.Y = stof(f.next(")"));
523 u64 getU64(const std::string &name) const
526 std::string s = get(name);
527 std::istringstream ss(s);
532 u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc,
535 std::string val = get(name);
536 return std::isdigit(val[0])
538 : readFlagString(val, flagdesc, flagmask);
541 // N.B. if getStruct() is used to read a non-POD aggregate type,
542 // the behavior is undefined.
543 bool getStruct(const std::string &name, const std::string &format,
544 void *out, size_t olen) const
550 } catch (SettingNotFoundException &e) {
554 if (!deSerializeStringToStruct(valstr, format, out, olen))
560 //////////// Try to get value, no exception thrown
561 bool getNoEx(const std::string &name, std::string &val) const
566 } catch (SettingNotFoundException &e) {
571 // N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus,
572 // val must be initialized before using getFlagStrNoEx(). The intention of
573 // this is to simplify modifying a flags field from a default value.
574 bool getFlagStrNoEx(const std::string &name, u32 &val, FlagDesc *flagdesc) const
579 flags = getFlagStr(name, flagdesc, &flagmask);
585 } catch (SettingNotFoundException &e) {
590 bool getFloatNoEx(const std::string &name, float &val) const
593 val = getFloat(name);
595 } catch (SettingNotFoundException &e) {
600 bool getU16NoEx(const std::string &name, int &val) const
605 } catch (SettingNotFoundException &e) {
610 bool getU16NoEx(const std::string &name, u16 &val) const
615 } catch (SettingNotFoundException &e) {
620 bool getS16NoEx(const std::string &name, int &val) const
625 } catch (SettingNotFoundException &e) {
630 bool getS16NoEx(const std::string &name, s16 &val) const
635 } catch (SettingNotFoundException &e) {
640 bool getS32NoEx(const std::string &name, s32 &val) const
645 } catch (SettingNotFoundException &e) {
650 bool getV3FNoEx(const std::string &name, v3f &val) const
655 } catch (SettingNotFoundException &e) {
660 bool getV2FNoEx(const std::string &name, v2f &val) const
665 } catch (SettingNotFoundException &e) {
670 bool getU64NoEx(const std::string &name, u64 &val) const
675 } catch (SettingNotFoundException &e) {
680 //////////// Set setting
682 // N.B. if setStruct() is used to write a non-POD aggregate type,
683 // the behavior is undefined.
684 bool setStruct(const std::string &name, const std::string &format, void *value)
686 std::string structstr;
687 if (!serializeStructToString(&structstr, format, value))
690 set(name, structstr);
694 void setFlagStr(const std::string &name, u32 flags,
695 const FlagDesc *flagdesc, u32 flagmask)
697 set(name, writeFlagString(flags, flagdesc, flagmask));
700 void setBool(const std::string &name, bool value)
702 set(name, value ? "true" : "false");
705 void setFloat(const std::string &name, float value)
707 set(name, ftos(value));
710 void setV3F(const std::string &name, v3f value)
712 std::ostringstream os;
713 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
717 void setV2F(const std::string &name, v2f value)
719 std::ostringstream os;
720 os<<"("<<value.X<<","<<value.Y<<")";
724 void setS16(const std::string &name, s16 value)
726 set(name, itos(value));
729 void setS32(const std::string &name, s32 value)
731 set(name, itos(value));
734 void setU64(const std::string &name, u64 value)
736 std::ostringstream os;
743 JMutexAutoLock lock(m_mutex);
747 void updateValue(const Settings &other, const std::string &name)
752 JMutexAutoLock lock(m_mutex);
755 std::string val = other.get(name);
756 m_settings[name] = val;
757 } catch (SettingNotFoundException &e) {
761 void update(const Settings &other)
766 JMutexAutoLock lock(m_mutex);
767 JMutexAutoLock lock2(other.m_mutex);
772 Settings & operator+=(const Settings &other)
779 Settings & operator=(const Settings &other)
784 JMutexAutoLock lock(m_mutex);
785 JMutexAutoLock lock2(other.m_mutex);
795 void updateNoLock(const Settings &other)
797 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
798 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
808 std::map<std::string, std::string> m_settings;
809 std::map<std::string, std::string> m_defaults;
810 // All methods that access m_settings/m_defaults directly should lock this.
811 mutable JMutex m_mutex;