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 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"
27 #include <jmutexautolock.h>
34 #include "util/string.h"
40 VALUETYPE_FLAG // Doesn't take any arguments
45 ValueSpec(ValueType a_type, const char *a_help=NULL)
62 void writeLines(std::ostream &os)
64 JMutexAutoLock lock(m_mutex);
66 for(core::map<std::string, std::string>::Iterator
67 i = m_settings.getIterator();
68 i.atEnd() == false; i++)
70 std::string name = i.getNode()->getKey();
71 std::string value = i.getNode()->getValue();
72 os<<name<<" = "<<value<<"\n";
76 // return all keys used
77 std::vector<std::string> getNames(){
78 std::vector<std::string> names;
79 for(core::map<std::string, std::string>::Iterator
80 i = m_settings.getIterator();
81 i.atEnd() == false; i++)
83 std::string name = i.getNode()->getKey();
84 names.push_back(name);
90 bool remove(const std::string& name)
92 return m_settings.remove(name);
96 bool parseConfigLine(const std::string &line)
98 JMutexAutoLock lock(m_mutex);
100 std::string trimmedline = trim(line);
102 // Ignore empty lines and comments
103 if(trimmedline.size() == 0 || trimmedline[0] == '#')
106 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
108 Strfnd sf(trim(line));
110 std::string name = sf.next("=");
116 std::string value = sf.next("\n");
119 /*infostream<<"Config name=\""<<name<<"\" value=\""
120 <<value<<"\""<<std::endl;*/
122 m_settings[name] = value;
127 void parseConfigLines(std::istream &is, const std::string &endstring)
133 std::getline(is, line);
134 std::string trimmedline = trim(line);
136 if(trimmedline == endstring)
139 parseConfigLine(line);
143 // Returns false on EOF
144 bool parseConfigObject(std::istream &is)
150 NOTE: This function might be expanded to allow multi-line
154 std::getline(is, line);
155 //infostream<<"got line: \""<<line<<"\""<<std::endl;
157 return parseConfigLine(line);
161 Read configuration file
163 Returns true on success
165 bool readConfigFile(const char *filename)
167 std::ifstream is(filename);
168 if(is.good() == false)
171 /*infostream<<"Parsing configuration file: \""
172 <<filename<<"\""<<std::endl;*/
174 while(parseConfigObject(is));
180 Reads a configuration object from stream (usually a single line)
183 Preserves comments and empty lines.
185 Settings that were added to dst are also added to updated.
186 key of updated is setting name, value of updated is dummy.
190 bool getUpdatedConfigObject(std::istream &is,
191 core::list<std::string> &dst,
192 core::map<std::string, bool> &updated,
195 JMutexAutoLock lock(m_mutex);
200 // NOTE: This function will be expanded to allow multi-line settings
202 std::getline(is, line);
204 std::string trimmedline = trim(line);
206 std::string line_end = "";
207 if(is.eof() == false)
210 // Ignore empty lines and comments
211 if(trimmedline.size() == 0 || trimmedline[0] == '#')
213 dst.push_back(line+line_end);
217 Strfnd sf(trim(line));
219 std::string name = sf.next("=");
224 dst.push_back(line+line_end);
228 std::string value = sf.next("\n");
231 if(m_settings.find(name))
233 std::string newvalue = m_settings[name];
235 if(newvalue != value)
237 infostream<<"Changing value of \""<<name<<"\" = \""
238 <<value<<"\" -> \""<<newvalue<<"\""
240 value_changed = true;
243 dst.push_back(name + " = " + newvalue + line_end);
245 updated[name] = true;
252 Updates configuration file
254 Returns true on success
256 bool updateConfigFile(const char *filename)
258 infostream<<"Updating configuration file: \""
259 <<filename<<"\""<<std::endl;
261 core::list<std::string> objects;
262 core::map<std::string, bool> updated;
263 bool something_actually_changed = false;
265 // Read and modify stuff
267 std::ifstream is(filename);
268 if(is.good() == false)
270 infostream<<"updateConfigFile():"
271 " Error opening configuration file"
273 <<filename<<"\""<<std::endl;
277 while(getUpdatedConfigObject(is, objects, updated,
278 something_actually_changed));
282 JMutexAutoLock lock(m_mutex);
284 // If something not yet determined to have been changed, check if
285 // any new stuff was added
286 if(!something_actually_changed){
287 for(core::map<std::string, std::string>::Iterator
288 i = m_settings.getIterator();
289 i.atEnd() == false; i++)
291 if(updated.find(i.getNode()->getKey()))
293 something_actually_changed = true;
298 // If nothing was actually changed, skip writing the file
299 if(!something_actually_changed){
300 infostream<<"Skipping writing of "<<filename
301 <<" because content wouldn't be modified"<<std::endl;
307 std::ofstream os(filename);
308 if(os.good() == false)
310 errorstream<<"Error opening configuration file"
312 <<filename<<"\""<<std::endl;
319 for(core::list<std::string>::Iterator
321 i != objects.end(); i++)
327 Write stuff that was not already in the file
329 for(core::map<std::string, std::string>::Iterator
330 i = m_settings.getIterator();
331 i.atEnd() == false; i++)
333 if(updated.find(i.getNode()->getKey()))
335 std::string name = i.getNode()->getKey();
336 std::string value = i.getNode()->getValue();
337 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
339 os<<name<<" = "<<value<<"\n";
347 NOTE: Types of allowed_options are ignored
349 returns true on success
351 bool parseCommandLine(int argc, char *argv[],
352 core::map<std::string, ValueSpec> &allowed_options)
354 int nonopt_index = 0;
360 std::string argname = argv[i];
361 if(argname.substr(0, 2) != "--")
363 // If option doesn't start with -, read it in as nonoptX
364 if(argname[0] != '-'){
365 std::string name = "nonopt";
366 name += itos(nonopt_index);
372 errorstream<<"Invalid command-line parameter \""
373 <<argname<<"\": --<option> expected."<<std::endl;
378 std::string name = argname.substr(2);
380 core::map<std::string, ValueSpec>::Node *n;
381 n = allowed_options.find(name);
384 errorstream<<"Unknown command-line parameter \""
385 <<argname<<"\""<<std::endl;
389 ValueType type = n->getValue().type;
391 std::string value = "";
393 if(type == VALUETYPE_FLAG)
401 errorstream<<"Invalid command-line parameter \""
402 <<name<<"\": missing value"<<std::endl;
410 infostream<<"Valid command-line parameter: \""
411 <<name<<"\" = \""<<value<<"\""
419 void set(std::string name, std::string value)
421 JMutexAutoLock lock(m_mutex);
423 m_settings[name] = value;
426 void set(std::string name, const char *value)
428 JMutexAutoLock lock(m_mutex);
430 m_settings[name] = value;
434 void setDefault(std::string name, std::string value)
436 JMutexAutoLock lock(m_mutex);
438 m_defaults[name] = value;
441 bool exists(std::string name)
443 JMutexAutoLock lock(m_mutex);
445 return (m_settings.find(name) || m_defaults.find(name));
448 std::string get(std::string name)
450 JMutexAutoLock lock(m_mutex);
452 core::map<std::string, std::string>::Node *n;
453 n = m_settings.find(name);
456 n = m_defaults.find(name);
459 throw SettingNotFoundException("Setting not found");
463 return n->getValue();
466 bool getBool(std::string name)
468 return is_yes(get(name));
471 bool getFlag(std::string name)
475 return getBool(name);
477 catch(SettingNotFoundException &e)
484 bool getBoolAsk(std::string name, std::string question, bool def)
486 // If it is in settings
488 return getBool(name);
492 std::cout<<question<<" [y/N]: ";
493 std::cin.getline(templine, 10);
502 float getFloat(std::string name)
504 return stof(get(name));
507 u16 getU16(std::string name)
509 return stoi(get(name), 0, 65535);
512 u16 getU16Ask(std::string name, std::string question, u16 def)
514 // If it is in settings
520 std::cout<<question<<" ["<<def<<"]: ";
521 std::cin.getline(templine, 10);
527 return stoi(s, 0, 65535);
530 s16 getS16(std::string name)
532 return stoi(get(name), -32768, 32767);
535 s32 getS32(std::string name)
537 return stoi(get(name));
540 v3f getV3F(std::string name)
545 value.X = stof(f.next(","));
546 value.Y = stof(f.next(","));
547 value.Z = stof(f.next(")"));
551 v2f getV2F(std::string name)
556 value.X = stof(f.next(","));
557 value.Y = stof(f.next(")"));
561 u64 getU64(std::string name)
564 std::string s = get(name);
565 std::istringstream ss(s);
570 template <class T> T *getStruct(std::string name, std::string format)
572 size_t len = sizeof(T);
573 std::vector<std::string *> strs_alloced;
575 std::string valstr = get(name);
576 char *s = &valstr[0];
578 char *bufpos = (char *)buf;
582 char *fmtpos, *fmt = &format[0];
583 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
586 bool is_unsigned = false;
590 width = (int)strtol(f + 1, &f, 10);
591 if (width && valtype == 's')
600 bufpos += PADDING(bufpos, u16);
601 if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
603 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
605 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
607 bufpos += sizeof(u16);
608 } else if (width == 32) {
609 bufpos += PADDING(bufpos, u32);
610 if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
612 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
614 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
616 bufpos += sizeof(u32);
617 } else if (width == 64) {
618 bufpos += PADDING(bufpos, u64);
619 if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
621 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
623 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
625 bufpos += sizeof(u64);
630 snext = strchr(s, ',');
634 bufpos += PADDING(bufpos, bool);
635 if ((bufpos - (char *)buf) + sizeof(bool) <= len)
636 *(bool *)bufpos = is_yes(std::string(s));
637 bufpos += sizeof(bool);
642 bufpos += PADDING(bufpos, float);
643 if ((bufpos - (char *)buf) + sizeof(float) <= len)
644 *(float *)bufpos = strtof(s, &s);
645 bufpos += sizeof(float);
650 while (*s == ' ' || *s == '\t')
652 if (*s++ != '"') //error, expected string
656 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
660 bufpos += PADDING(bufpos, std::string *);
662 str = new std::string(s);
664 while ((pos = str->find("\\\"", pos)) != std::string::npos)
667 if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
668 *(std::string **)bufpos = str;
669 bufpos += sizeof(std::string *);
670 strs_alloced.push_back(str);
672 s = *snext ? snext + 1 : NULL;
675 while (*s == ' ' || *s == '\t')
677 if (*s++ != '(') //error, expected vector
681 bufpos += PADDING(bufpos, v2f);
683 if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
684 v2f *v = (v2f *)bufpos;
685 v->X = strtof(s, &s);
687 v->Y = strtof(s, &s);
690 bufpos += sizeof(v2f);
691 } else if (width == 3) {
692 bufpos += PADDING(bufpos, v3f);
693 if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
694 v3f *v = (v3f *)bufpos;
695 v->X = strtof(s, &s);
697 v->Y = strtof(s, &s);
699 v->Z = strtof(s, &s);
702 bufpos += sizeof(v3f);
706 default: //error, invalid format specifier
713 if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
717 if (f && *f) { //error, mismatched number of fields and values
719 for (unsigned int i = 0; i != strs_alloced.size(); i++)
720 delete strs_alloced[i];
729 bool setStruct(std::string name, std::string format, void *value)
732 int sbuflen = sizeof(sbuf) - 1;
739 char *bufpos = (char *)value;
740 char *fmtpos, *fmt = &format[0];
741 while ((f = strtok_r(fmt, ",", &fmtpos))) {
743 bool is_unsigned = false;
744 int width = 0, nprinted = 0;
747 width = (int)strtol(f + 1, &f, 10);
748 if (width && valtype == 's')
757 bufpos += PADDING(bufpos, u16);
758 nprinted = snprintf(sbuf + pos, sbuflen,
759 is_unsigned ? "%u, " : "%d, ",
761 bufpos += sizeof(u16);
762 } else if (width == 32) {
763 bufpos += PADDING(bufpos, u32);
764 nprinted = snprintf(sbuf + pos, sbuflen,
765 is_unsigned ? "%u, " : "%d, ",
767 bufpos += sizeof(u32);
768 } else if (width == 64) {
769 bufpos += PADDING(bufpos, u64);
770 nprinted = snprintf(sbuf + pos, sbuflen,
771 is_unsigned ? "%llu, " : "%lli, ",
772 (unsigned long long)*((u64 *)bufpos));
773 bufpos += sizeof(u64);
777 bufpos += PADDING(bufpos, bool);
778 nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
779 *((bool *)bufpos) ? "true" : "false");
780 bufpos += sizeof(bool);
783 bufpos += PADDING(bufpos, float);
784 nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
786 bufpos += sizeof(float);
789 bufpos += PADDING(bufpos, std::string *);
790 str = **((std::string **)bufpos);
793 while ((fpos = str.find('"', fpos)) != std::string::npos) {
794 str.insert(fpos, 1, '\\');
798 nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
799 (*((std::string **)bufpos))->c_str());
800 bufpos += sizeof(std::string *);
804 bufpos += PADDING(bufpos, v2f);
805 v2f *v = (v2f *)bufpos;
806 nprinted = snprintf(sbuf + pos, sbuflen,
807 "(%f, %f), ", v->X, v->Y);
808 bufpos += sizeof(v2f);
810 bufpos += PADDING(bufpos, v3f);
811 v3f *v = (v3f *)bufpos;
812 nprinted = snprintf(sbuf + pos, sbuflen,
813 "(%f, %f, %f), ", v->X, v->Y, v->Z);
814 bufpos += sizeof(v3f);
820 if (nprinted < 0) //error, buffer too small
829 set(name, std::string(sbuf));
833 void setBool(std::string name, bool value)
841 void setFloat(std::string name, float value)
843 set(name, ftos(value));
846 void setV3F(std::string name, v3f value)
848 std::ostringstream os;
849 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
853 void setV2F(std::string name, v2f value)
855 std::ostringstream os;
856 os<<"("<<value.X<<","<<value.Y<<")";
860 void setS16(std::string name, s16 value)
862 set(name, itos(value));
865 void setS32(std::string name, s32 value)
867 set(name, itos(value));
870 void setU64(std::string name, u64 value)
872 std::ostringstream os;
879 JMutexAutoLock lock(m_mutex);
885 void updateValue(Settings &other, const std::string &name)
887 JMutexAutoLock lock(m_mutex);
893 std::string val = other.get(name);
894 m_settings[name] = val;
895 } catch(SettingNotFoundException &e){
901 void update(Settings &other)
903 JMutexAutoLock lock(m_mutex);
904 JMutexAutoLock lock2(other.m_mutex);
909 for(core::map<std::string, std::string>::Iterator
910 i = other.m_settings.getIterator();
911 i.atEnd() == false; i++)
913 m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
916 for(core::map<std::string, std::string>::Iterator
917 i = other.m_defaults.getIterator();
918 i.atEnd() == false; i++)
920 m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
926 Settings & operator+=(Settings &other)
928 JMutexAutoLock lock(m_mutex);
929 JMutexAutoLock lock2(other.m_mutex);
934 for(core::map<std::string, std::string>::Iterator
935 i = other.m_settings.getIterator();
936 i.atEnd() == false; i++)
938 m_settings.insert(i.getNode()->getKey(),
939 i.getNode()->getValue());
942 for(core::map<std::string, std::string>::Iterator
943 i = other.m_defaults.getIterator();
944 i.atEnd() == false; i++)
946 m_defaults.insert(i.getNode()->getKey(),
947 i.getNode()->getValue());
954 Settings & operator=(Settings &other)
956 JMutexAutoLock lock(m_mutex);
957 JMutexAutoLock lock2(other.m_mutex);
969 core::map<std::string, std::string> m_settings;
970 core::map<std::string, std::string> m_defaults;
971 // All methods that access m_settings/m_defaults directly should lock this.