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)
68 JMutexAutoLock lock(m_mutex);
70 for(std::map<std::string, std::string>::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(){
82 std::vector<std::string> names;
83 for(std::map<std::string, std::string>::iterator
84 i = m_settings.begin();
85 i != m_settings.end(); ++i)
87 names.push_back(i->first);
93 bool remove(const std::string& name)
95 return m_settings.erase(name);
99 bool parseConfigLine(const std::string &line)
101 JMutexAutoLock lock(m_mutex);
103 std::string trimmedline = trim(line);
105 // Ignore empty lines and comments
106 if(trimmedline.size() == 0 || trimmedline[0] == '#')
109 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
111 Strfnd sf(trim(line));
113 std::string name = sf.next("=");
119 std::string value = sf.next("\n");
122 /*infostream<<"Config name=\""<<name<<"\" value=\""
123 <<value<<"\""<<std::endl;*/
125 m_settings[name] = value;
130 void parseConfigLines(std::istream &is, const std::string &endstring)
136 std::getline(is, line);
137 std::string trimmedline = trim(line);
139 if(trimmedline == endstring)
142 parseConfigLine(line);
146 // Returns false on EOF
147 bool parseConfigObject(std::istream &is)
153 NOTE: This function might be expanded to allow multi-line
157 std::getline(is, line);
158 //infostream<<"got line: \""<<line<<"\""<<std::endl;
160 return parseConfigLine(line);
164 Read configuration file
166 Returns true on success
168 bool readConfigFile(const char *filename)
170 std::ifstream is(filename);
171 if(is.good() == false)
174 /*infostream<<"Parsing configuration file: \""
175 <<filename<<"\""<<std::endl;*/
177 while(parseConfigObject(is));
183 Reads a configuration object from stream (usually a single line)
186 Preserves comments and empty lines.
188 Settings that were added to dst are also added to updated.
189 key of updated is setting name, value of updated is dummy.
193 bool getUpdatedConfigObject(std::istream &is,
194 std::list<std::string> &dst,
195 std::set<std::string> &updated,
198 JMutexAutoLock lock(m_mutex);
203 // NOTE: This function will be expanded to allow multi-line settings
205 std::getline(is, line);
207 std::string trimmedline = trim(line);
209 std::string line_end = "";
210 if(is.eof() == false)
213 // Ignore empty lines and comments
214 if(trimmedline.size() == 0 || trimmedline[0] == '#')
216 dst.push_back(line+line_end);
220 Strfnd sf(trim(line));
222 std::string name = sf.next("=");
227 dst.push_back(line+line_end);
231 std::string value = sf.next("\n");
234 if(m_settings.find(name) != m_settings.end())
236 std::string newvalue = m_settings[name];
238 if(newvalue != value)
240 infostream<<"Changing value of \""<<name<<"\" = \""
241 <<value<<"\" -> \""<<newvalue<<"\""
243 value_changed = true;
246 dst.push_back(name + " = " + newvalue + line_end);
248 updated.insert(name);
250 else //file contains a setting which is not in m_settings
257 Updates configuration file
259 Returns true on success
261 bool updateConfigFile(const char *filename)
263 infostream<<"Updating configuration file: \""
264 <<filename<<"\""<<std::endl;
266 std::list<std::string> objects;
267 std::set<std::string> updated;
268 bool something_actually_changed = false;
270 // Read and modify stuff
272 std::ifstream is(filename);
273 if(is.good() == false)
275 infostream<<"updateConfigFile():"
276 " Error opening configuration file"
278 <<filename<<"\""<<std::endl;
282 while(getUpdatedConfigObject(is, objects, updated,
283 something_actually_changed));
287 JMutexAutoLock lock(m_mutex);
289 // If something not yet determined to have been changed, check if
290 // any new stuff was added
291 if(!something_actually_changed){
292 for(std::map<std::string, std::string>::iterator
293 i = m_settings.begin();
294 i != m_settings.end(); ++i)
296 if(updated.find(i->first) != updated.end())
298 something_actually_changed = true;
303 // If nothing was actually changed, skip writing the file
304 if(!something_actually_changed){
305 infostream<<"Skipping writing of "<<filename
306 <<" because content wouldn't be modified"<<std::endl;
312 std::ostringstream ss(std::ios_base::binary);
317 for(std::list<std::string>::iterator
319 i != objects.end(); ++i)
325 Write stuff that was not already in the file
327 for(std::map<std::string, std::string>::iterator
328 i = m_settings.begin();
329 i != m_settings.end(); ++i)
331 if(updated.find(i->first) != updated.end())
333 std::string name = i->first;
334 std::string value = i->second;
335 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
337 ss<<name<<" = "<<value<<"\n";
340 if(!fs::safeWriteToFile(filename, ss.str()))
342 errorstream<<"Error writing configuration file: \""
343 <<filename<<"\""<<std::endl;
352 NOTE: Types of allowed_options are ignored
354 returns true on success
356 bool parseCommandLine(int argc, char *argv[],
357 std::map<std::string, ValueSpec> &allowed_options)
359 int nonopt_index = 0;
365 std::string argname = argv[i];
366 if(argname.substr(0, 2) != "--")
368 // If option doesn't start with -, read it in as nonoptX
369 if(argname[0] != '-'){
370 std::string name = "nonopt";
371 name += itos(nonopt_index);
377 errorstream<<"Invalid command-line parameter \""
378 <<argname<<"\": --<option> expected."<<std::endl;
383 std::string name = argname.substr(2);
385 std::map<std::string, ValueSpec>::iterator n;
386 n = allowed_options.find(name);
387 if(n == allowed_options.end())
389 errorstream<<"Unknown command-line parameter \""
390 <<argname<<"\""<<std::endl;
394 ValueType type = n->second.type;
396 std::string value = "";
398 if(type == VALUETYPE_FLAG)
406 errorstream<<"Invalid command-line parameter \""
407 <<name<<"\": missing value"<<std::endl;
415 infostream<<"Valid command-line parameter: \""
416 <<name<<"\" = \""<<value<<"\""
424 void set(std::string name, std::string value)
426 JMutexAutoLock lock(m_mutex);
428 m_settings[name] = value;
431 void set(std::string name, const char *value)
433 JMutexAutoLock lock(m_mutex);
435 m_settings[name] = value;
439 void setDefault(std::string name, std::string value)
441 JMutexAutoLock lock(m_mutex);
443 m_defaults[name] = value;
446 bool exists(std::string name) const
448 JMutexAutoLock lock(m_mutex);
450 return m_settings.find(name) != m_settings.end()
451 || m_defaults.find(name) != m_defaults.end();
454 std::string get(std::string name) const
456 JMutexAutoLock lock(m_mutex);
458 std::map<std::string, std::string>::const_iterator n;
459 if ((n = m_settings.find(name)) == m_settings.end())
460 if ((n = m_defaults.find(name)) == m_defaults.end())
461 throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
466 //////////// Get setting
467 bool getBool(std::string name) const
469 return is_yes(get(name));
472 bool getFlag(std::string name) const
475 return getBool(name);
476 } catch(SettingNotFoundException &e) {
481 float getFloat(std::string name) const
483 return stof(get(name));
486 u16 getU16(std::string name) const
488 return stoi(get(name), 0, 65535);
491 s16 getS16(std::string name) const
493 return stoi(get(name), -32768, 32767);
496 s32 getS32(std::string name) const
498 return stoi(get(name));
501 v3f getV3F(std::string name) const
506 value.X = stof(f.next(","));
507 value.Y = stof(f.next(","));
508 value.Z = stof(f.next(")"));
512 v2f getV2F(std::string name) const
517 value.X = stof(f.next(","));
518 value.Y = stof(f.next(")"));
522 u64 getU64(std::string name) const
525 std::string s = get(name);
526 std::istringstream ss(s);
531 u32 getFlagStr(std::string name, FlagDesc *flagdesc, u32 *flagmask) const
533 std::string val = get(name);
534 return std::isdigit(val[0])
536 : readFlagString(val, flagdesc, flagmask);
539 // N.B. if getStruct() is used to read a non-POD aggregate type,
540 // the behavior is undefined.
541 bool getStruct(std::string name, std::string format,
542 void *out, size_t olen) const
548 } catch (SettingNotFoundException &e) {
552 if (!deSerializeStringToStruct(valstr, format, out, olen))
558 //////////// Try to get value, no exception thrown
559 bool getNoEx(std::string name, std::string &val) const
564 } catch (SettingNotFoundException &e) {
569 // N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus,
570 // val must be initialized before using getFlagStrNoEx(). The intention of
571 // this is to simplify modifying a flags field from a default value.
572 bool getFlagStrNoEx(std::string name, u32 &val, FlagDesc *flagdesc) const
577 flags = getFlagStr(name, flagdesc, &flagmask);
583 } catch (SettingNotFoundException &e) {
588 bool getFloatNoEx(std::string name, float &val) const
591 val = getFloat(name);
593 } catch (SettingNotFoundException &e) {
598 bool getU16NoEx(std::string name, int &val) const
603 } catch (SettingNotFoundException &e) {
608 bool getU16NoEx(std::string name, u16 &val) const
613 } catch (SettingNotFoundException &e) {
618 bool getS16NoEx(std::string name, int &val) const
623 } catch (SettingNotFoundException &e) {
628 bool getS16NoEx(std::string name, s16 &val) const
633 } catch (SettingNotFoundException &e) {
638 bool getS32NoEx(std::string name, s32 &val) const
643 } catch (SettingNotFoundException &e) {
648 bool getV3FNoEx(std::string name, v3f &val) const
653 } catch (SettingNotFoundException &e) {
658 bool getV2FNoEx(std::string name, v2f &val) const
663 } catch (SettingNotFoundException &e) {
668 bool getU64NoEx(std::string name, u64 &val) const
673 } catch (SettingNotFoundException &e) {
678 //////////// Set setting
680 // N.B. if setStruct() is used to write a non-POD aggregate type,
681 // the behavior is undefined.
682 bool setStruct(std::string name, std::string format, void *value)
684 std::string structstr;
685 if (!serializeStructToString(&structstr, format, value))
688 set(name, structstr);
692 void setFlagStr(std::string name, u32 flags,
693 FlagDesc *flagdesc, u32 flagmask)
695 set(name, writeFlagString(flags, flagdesc, flagmask));
698 void setBool(std::string name, bool value)
706 void setFloat(std::string name, float value)
708 set(name, ftos(value));
711 void setV3F(std::string name, v3f value)
713 std::ostringstream os;
714 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
718 void setV2F(std::string name, v2f value)
720 std::ostringstream os;
721 os<<"("<<value.X<<","<<value.Y<<")";
725 void setS16(std::string name, s16 value)
727 set(name, itos(value));
730 void setS32(std::string name, s32 value)
732 set(name, itos(value));
735 void setU64(std::string name, u64 value)
737 std::ostringstream os;
744 JMutexAutoLock lock(m_mutex);
750 void updateValue(Settings &other, const std::string &name)
752 JMutexAutoLock lock(m_mutex);
758 std::string val = other.get(name);
759 m_settings[name] = val;
760 } catch(SettingNotFoundException &e){
766 void update(Settings &other)
768 JMutexAutoLock lock(m_mutex);
769 JMutexAutoLock lock2(other.m_mutex);
774 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
775 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
780 Settings & operator+=(Settings &other)
782 JMutexAutoLock lock(m_mutex);
783 JMutexAutoLock lock2(other.m_mutex);
794 Settings & operator=(Settings &other)
796 JMutexAutoLock lock(m_mutex);
797 JMutexAutoLock lock2(other.m_mutex);
809 std::map<std::string, std::string> m_settings;
810 std::map<std::string, std::string> m_defaults;
811 // All methods that access m_settings/m_defaults directly should lock this.
812 mutable JMutex m_mutex;