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)
448 JMutexAutoLock lock(m_mutex);
450 return (m_settings.find(name) != m_settings.end() || m_defaults.find(name) != m_defaults.end());
453 std::string get(std::string name)
455 JMutexAutoLock lock(m_mutex);
457 std::map<std::string, std::string>::iterator n;
458 n = m_settings.find(name);
459 if(n == m_settings.end())
461 n = m_defaults.find(name);
462 if(n == m_defaults.end())
464 throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
471 //////////// Get setting
472 bool getBool(std::string name)
474 return is_yes(get(name));
477 bool getFlag(std::string name)
481 return getBool(name);
483 catch(SettingNotFoundException &e)
490 bool getBoolAsk(std::string name, std::string question, bool def)
492 // If it is in settings
494 return getBool(name);
498 std::cout<<question<<" [y/N]: ";
499 std::cin.getline(templine, 10);
508 float getFloat(std::string name)
510 return stof(get(name));
513 u16 getU16(std::string name)
515 return stoi(get(name), 0, 65535);
518 u16 getU16Ask(std::string name, std::string question, u16 def)
520 // If it is in settings
526 std::cout<<question<<" ["<<def<<"]: ";
527 std::cin.getline(templine, 10);
533 return stoi(s, 0, 65535);
536 s16 getS16(std::string name)
538 return stoi(get(name), -32768, 32767);
541 s32 getS32(std::string name)
543 return stoi(get(name));
546 v3f getV3F(std::string name)
551 value.X = stof(f.next(","));
552 value.Y = stof(f.next(","));
553 value.Z = stof(f.next(")"));
557 v2f getV2F(std::string name)
562 value.X = stof(f.next(","));
563 value.Y = stof(f.next(")"));
567 u64 getU64(std::string name)
570 std::string s = get(name);
571 std::istringstream ss(s);
576 u32 getFlagStr(std::string name, FlagDesc *flagdesc, u32 *flagmask)
578 std::string val = get(name);
579 return (std::isdigit(val[0])) ? stoi(val) :
580 readFlagString(val, flagdesc, flagmask);
583 // N.B. if getStruct() is used to read a non-POD aggregate type,
584 // the behavior is undefined.
585 bool getStruct(std::string name, std::string format, void *out, size_t olen)
591 } catch (SettingNotFoundException &e) {
595 if (!deSerializeStringToStruct(valstr, format, out, olen))
601 //////////// Try to get value, no exception thrown
602 bool getNoEx(std::string name, std::string &val)
607 } catch (SettingNotFoundException &e) {
612 // N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus,
613 // val must be initialized before using getFlagStrNoEx(). The intention of
614 // this is to simplify modifying a flags field from a default value.
615 bool getFlagStrNoEx(std::string name, u32 &val, FlagDesc *flagdesc)
620 flags = getFlagStr(name, flagdesc, &flagmask);
626 } catch (SettingNotFoundException &e) {
631 bool getFloatNoEx(std::string name, float &val)
634 val = getFloat(name);
636 } catch (SettingNotFoundException &e) {
641 bool getU16NoEx(std::string name, int &val)
646 } catch (SettingNotFoundException &e) {
651 bool getU16NoEx(std::string name, u16 &val)
656 } catch (SettingNotFoundException &e) {
661 bool getS16NoEx(std::string name, int &val)
666 } catch (SettingNotFoundException &e) {
671 bool getS16NoEx(std::string name, s16 &val)
676 } catch (SettingNotFoundException &e) {
681 bool getS32NoEx(std::string name, s32 &val)
686 } catch (SettingNotFoundException &e) {
691 bool getV3FNoEx(std::string name, v3f &val)
696 } catch (SettingNotFoundException &e) {
701 bool getV2FNoEx(std::string name, v2f &val)
706 } catch (SettingNotFoundException &e) {
711 bool getU64NoEx(std::string name, u64 &val)
716 } catch (SettingNotFoundException &e) {
721 //////////// Set setting
723 // N.B. if setStruct() is used to write a non-POD aggregate type,
724 // the behavior is undefined.
725 bool setStruct(std::string name, std::string format, void *value)
727 std::string structstr;
728 if (!serializeStructToString(&structstr, format, value))
731 set(name, structstr);
735 void setFlagStr(std::string name, u32 flags,
736 FlagDesc *flagdesc, u32 flagmask)
738 set(name, writeFlagString(flags, flagdesc, flagmask));
741 void setBool(std::string name, bool value)
749 void setFloat(std::string name, float value)
751 set(name, ftos(value));
754 void setV3F(std::string name, v3f value)
756 std::ostringstream os;
757 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
761 void setV2F(std::string name, v2f value)
763 std::ostringstream os;
764 os<<"("<<value.X<<","<<value.Y<<")";
768 void setS16(std::string name, s16 value)
770 set(name, itos(value));
773 void setS32(std::string name, s32 value)
775 set(name, itos(value));
778 void setU64(std::string name, u64 value)
780 std::ostringstream os;
787 JMutexAutoLock lock(m_mutex);
793 void updateValue(Settings &other, const std::string &name)
795 JMutexAutoLock lock(m_mutex);
801 std::string val = other.get(name);
802 m_settings[name] = val;
803 } catch(SettingNotFoundException &e){
809 void update(Settings &other)
811 JMutexAutoLock lock(m_mutex);
812 JMutexAutoLock lock2(other.m_mutex);
817 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
818 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
823 Settings & operator+=(Settings &other)
825 JMutexAutoLock lock(m_mutex);
826 JMutexAutoLock lock2(other.m_mutex);
837 Settings & operator=(Settings &other)
839 JMutexAutoLock lock(m_mutex);
840 JMutexAutoLock lock2(other.m_mutex);
852 std::map<std::string, std::string> m_settings;
853 std::map<std::string, std::string> m_defaults;
854 // All methods that access m_settings/m_defaults directly should lock this.