3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
27 #include <jmutexautolock.h>
39 VALUETYPE_FLAG // Doesn't take any arguments
44 ValueSpec(ValueType a_type, const char *a_help=NULL)
61 void writeLines(std::ostream &os)
63 JMutexAutoLock lock(m_mutex);
65 for(core::map<std::string, std::string>::Iterator
66 i = m_settings.getIterator();
67 i.atEnd() == false; i++)
69 std::string name = i.getNode()->getKey();
70 std::string value = i.getNode()->getValue();
71 os<<name<<" = "<<value<<"\n";
75 bool parseConfigLine(const std::string &line)
77 JMutexAutoLock lock(m_mutex);
79 std::string trimmedline = trim(line);
81 // Ignore empty lines and comments
82 if(trimmedline.size() == 0 || trimmedline[0] == '#')
85 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
87 Strfnd sf(trim(line));
89 std::string name = sf.next("=");
95 std::string value = sf.next("\n");
98 /*infostream<<"Config name=\""<<name<<"\" value=\""
99 <<value<<"\""<<std::endl;*/
101 m_settings[name] = value;
106 void parseConfigLines(std::istream &is, const std::string &endstring)
112 std::getline(is, line);
113 std::string trimmedline = trim(line);
115 if(trimmedline == endstring)
118 parseConfigLine(line);
122 // Returns false on EOF
123 bool parseConfigObject(std::istream &is)
129 NOTE: This function might be expanded to allow multi-line
133 std::getline(is, line);
134 //infostream<<"got line: \""<<line<<"\""<<std::endl;
136 return parseConfigLine(line);
140 Read configuration file
142 Returns true on success
144 bool readConfigFile(const char *filename)
146 std::ifstream is(filename);
147 if(is.good() == false)
150 infostream<<"Parsing configuration file: \""
151 <<filename<<"\""<<std::endl;
153 while(parseConfigObject(is));
159 Reads a configuration object from stream (usually a single line)
162 Preserves comments and empty lines.
164 Settings that were added to dst are also added to updated.
165 key of updated is setting name, value of updated is dummy.
169 bool getUpdatedConfigObject(std::istream &is,
170 core::list<std::string> &dst,
171 core::map<std::string, bool> &updated,
174 JMutexAutoLock lock(m_mutex);
179 // NOTE: This function will be expanded to allow multi-line settings
181 std::getline(is, line);
183 std::string trimmedline = trim(line);
185 std::string line_end = "";
186 if(is.eof() == false)
189 // Ignore empty lines and comments
190 if(trimmedline.size() == 0 || trimmedline[0] == '#')
192 dst.push_back(line+line_end);
196 Strfnd sf(trim(line));
198 std::string name = sf.next("=");
203 dst.push_back(line+line_end);
207 std::string value = sf.next("\n");
210 if(m_settings.find(name))
212 std::string newvalue = m_settings[name];
214 if(newvalue != value)
216 infostream<<"Changing value of \""<<name<<"\" = \""
217 <<value<<"\" -> \""<<newvalue<<"\""
219 value_changed = true;
222 dst.push_back(name + " = " + newvalue + line_end);
224 updated[name] = true;
231 Updates configuration file
233 Returns true on success
235 bool updateConfigFile(const char *filename)
237 infostream<<"Updating configuration file: \""
238 <<filename<<"\""<<std::endl;
240 core::list<std::string> objects;
241 core::map<std::string, bool> updated;
242 bool something_actually_changed = false;
244 // Read and modify stuff
246 std::ifstream is(filename);
247 if(is.good() == false)
249 infostream<<"updateConfigFile():"
250 " Error opening configuration file"
252 <<filename<<"\""<<std::endl;
256 while(getUpdatedConfigObject(is, objects, updated,
257 something_actually_changed));
261 JMutexAutoLock lock(m_mutex);
263 // If something not yet determined to have been changed, check if
264 // any new stuff was added
265 if(!something_actually_changed){
266 for(core::map<std::string, std::string>::Iterator
267 i = m_settings.getIterator();
268 i.atEnd() == false; i++)
270 if(updated.find(i.getNode()->getKey()))
272 something_actually_changed = true;
277 // If nothing was actually changed, skip writing the file
278 if(!something_actually_changed){
279 infostream<<"Skipping writing of "<<filename
280 <<" because content wouldn't be modified"<<std::endl;
286 std::ofstream os(filename);
287 if(os.good() == false)
289 errorstream<<"Error opening configuration file"
291 <<filename<<"\""<<std::endl;
298 for(core::list<std::string>::Iterator
300 i != objects.end(); i++)
306 Write stuff that was not already in the file
308 for(core::map<std::string, std::string>::Iterator
309 i = m_settings.getIterator();
310 i.atEnd() == false; i++)
312 if(updated.find(i.getNode()->getKey()))
314 std::string name = i.getNode()->getKey();
315 std::string value = i.getNode()->getValue();
316 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
318 os<<name<<" = "<<value<<"\n";
326 NOTE: Types of allowed_options are ignored
328 returns true on success
330 bool parseCommandLine(int argc, char *argv[],
331 core::map<std::string, ValueSpec> &allowed_options)
333 int nonopt_index = 0;
339 std::string argname = argv[i];
340 if(argname.substr(0, 2) != "--")
342 // If option doesn't start with -, read it in as nonoptX
343 if(argname[0] != '-'){
344 std::string name = "nonopt";
345 name += itos(nonopt_index);
351 errorstream<<"Invalid command-line parameter \""
352 <<argname<<"\": --<option> expected."<<std::endl;
357 std::string name = argname.substr(2);
359 core::map<std::string, ValueSpec>::Node *n;
360 n = allowed_options.find(name);
363 errorstream<<"Unknown command-line parameter \""
364 <<argname<<"\""<<std::endl;
368 ValueType type = n->getValue().type;
370 std::string value = "";
372 if(type == VALUETYPE_FLAG)
380 errorstream<<"Invalid command-line parameter \""
381 <<name<<"\": missing value"<<std::endl;
389 infostream<<"Valid command-line parameter: \""
390 <<name<<"\" = \""<<value<<"\""
398 void set(std::string name, std::string value)
400 JMutexAutoLock lock(m_mutex);
402 m_settings[name] = value;
405 void set(std::string name, const char *value)
407 JMutexAutoLock lock(m_mutex);
409 m_settings[name] = value;
413 void setDefault(std::string name, std::string value)
415 JMutexAutoLock lock(m_mutex);
417 m_defaults[name] = value;
420 bool exists(std::string name)
422 JMutexAutoLock lock(m_mutex);
424 return (m_settings.find(name) || m_defaults.find(name));
427 std::string get(std::string name)
429 JMutexAutoLock lock(m_mutex);
431 core::map<std::string, std::string>::Node *n;
432 n = m_settings.find(name);
435 n = m_defaults.find(name);
438 throw SettingNotFoundException("Setting not found");
442 return n->getValue();
445 bool getBool(std::string name)
447 return is_yes(get(name));
450 bool getFlag(std::string name)
454 return getBool(name);
456 catch(SettingNotFoundException &e)
463 bool getBoolAsk(std::string name, std::string question, bool def)
465 // If it is in settings
467 return getBool(name);
471 std::cout<<question<<" [y/N]: ";
472 std::cin.getline(templine, 10);
481 float getFloat(std::string name)
483 return stof(get(name));
486 u16 getU16(std::string name)
488 return stoi(get(name), 0, 65535);
491 u16 getU16Ask(std::string name, std::string question, u16 def)
493 // If it is in settings
499 std::cout<<question<<" ["<<def<<"]: ";
500 std::cin.getline(templine, 10);
506 return stoi(s, 0, 65535);
509 s16 getS16(std::string name)
511 return stoi(get(name), -32768, 32767);
514 s32 getS32(std::string name)
516 return stoi(get(name));
519 v3f getV3F(std::string name)
524 value.X = stof(f.next(","));
525 value.Y = stof(f.next(","));
526 value.Z = stof(f.next(")"));
530 v2f getV2F(std::string name)
535 value.X = stof(f.next(","));
536 value.Y = stof(f.next(")"));
540 u64 getU64(std::string name)
543 std::string s = get(name);
544 std::istringstream ss(s);
549 void setBool(std::string name, bool value)
557 void setS32(std::string name, s32 value)
559 set(name, itos(value));
562 void setFloat(std::string name, float value)
564 set(name, ftos(value));
567 void setV3F(std::string name, v3f value)
569 std::ostringstream os;
570 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
574 void setV2F(std::string name, v2f value)
576 std::ostringstream os;
577 os<<"("<<value.X<<","<<value.Y<<")";
581 void setU64(std::string name, u64 value)
583 std::ostringstream os;
590 JMutexAutoLock lock(m_mutex);
596 void updateValue(Settings &other, const std::string &name)
598 JMutexAutoLock lock(m_mutex);
604 std::string val = other.get(name);
605 m_settings[name] = val;
606 } catch(SettingNotFoundException &e){
612 void update(Settings &other)
614 JMutexAutoLock lock(m_mutex);
615 JMutexAutoLock lock2(other.m_mutex);
620 for(core::map<std::string, std::string>::Iterator
621 i = other.m_settings.getIterator();
622 i.atEnd() == false; i++)
624 m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
627 for(core::map<std::string, std::string>::Iterator
628 i = other.m_defaults.getIterator();
629 i.atEnd() == false; i++)
631 m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
637 Settings & operator+=(Settings &other)
639 JMutexAutoLock lock(m_mutex);
640 JMutexAutoLock lock2(other.m_mutex);
645 for(core::map<std::string, std::string>::Iterator
646 i = other.m_settings.getIterator();
647 i.atEnd() == false; i++)
649 m_settings.insert(i.getNode()->getKey(),
650 i.getNode()->getValue());
653 for(core::map<std::string, std::string>::Iterator
654 i = other.m_defaults.getIterator();
655 i.atEnd() == false; i++)
657 m_defaults.insert(i.getNode()->getKey(),
658 i.getNode()->getValue());
665 Settings & operator=(Settings &other)
667 JMutexAutoLock lock(m_mutex);
668 JMutexAutoLock lock2(other.m_mutex);
680 core::map<std::string, std::string> m_settings;
681 core::map<std::string, std::string> m_defaults;
682 // All methods that access m_settings/m_defaults directly should lock this.