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"
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 // return all keys used
76 std::vector<std::string> getNames(){
77 std::vector<std::string> names;
78 for(core::map<std::string, std::string>::Iterator
79 i = m_settings.getIterator();
80 i.atEnd() == false; i++)
82 std::string name = i.getNode()->getKey();
83 names.push_back(name);
89 bool remove(const std::string& name)
91 return m_settings.remove(name);
95 bool parseConfigLine(const std::string &line)
97 JMutexAutoLock lock(m_mutex);
99 std::string trimmedline = trim(line);
101 // Ignore empty lines and comments
102 if(trimmedline.size() == 0 || trimmedline[0] == '#')
105 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
107 Strfnd sf(trim(line));
109 std::string name = sf.next("=");
115 std::string value = sf.next("\n");
118 /*infostream<<"Config name=\""<<name<<"\" value=\""
119 <<value<<"\""<<std::endl;*/
121 m_settings[name] = value;
126 void parseConfigLines(std::istream &is, const std::string &endstring)
132 std::getline(is, line);
133 std::string trimmedline = trim(line);
135 if(trimmedline == endstring)
138 parseConfigLine(line);
142 // Returns false on EOF
143 bool parseConfigObject(std::istream &is)
149 NOTE: This function might be expanded to allow multi-line
153 std::getline(is, line);
154 //infostream<<"got line: \""<<line<<"\""<<std::endl;
156 return parseConfigLine(line);
160 Read configuration file
162 Returns true on success
164 bool readConfigFile(const char *filename)
166 std::ifstream is(filename);
167 if(is.good() == false)
170 /*infostream<<"Parsing configuration file: \""
171 <<filename<<"\""<<std::endl;*/
173 while(parseConfigObject(is));
179 Reads a configuration object from stream (usually a single line)
182 Preserves comments and empty lines.
184 Settings that were added to dst are also added to updated.
185 key of updated is setting name, value of updated is dummy.
189 bool getUpdatedConfigObject(std::istream &is,
190 core::list<std::string> &dst,
191 core::map<std::string, bool> &updated,
194 JMutexAutoLock lock(m_mutex);
199 // NOTE: This function will be expanded to allow multi-line settings
201 std::getline(is, line);
203 std::string trimmedline = trim(line);
205 std::string line_end = "";
206 if(is.eof() == false)
209 // Ignore empty lines and comments
210 if(trimmedline.size() == 0 || trimmedline[0] == '#')
212 dst.push_back(line+line_end);
216 Strfnd sf(trim(line));
218 std::string name = sf.next("=");
223 dst.push_back(line+line_end);
227 std::string value = sf.next("\n");
230 if(m_settings.find(name))
232 std::string newvalue = m_settings[name];
234 if(newvalue != value)
236 infostream<<"Changing value of \""<<name<<"\" = \""
237 <<value<<"\" -> \""<<newvalue<<"\""
239 value_changed = true;
242 dst.push_back(name + " = " + newvalue + line_end);
244 updated[name] = true;
251 Updates configuration file
253 Returns true on success
255 bool updateConfigFile(const char *filename)
257 infostream<<"Updating configuration file: \""
258 <<filename<<"\""<<std::endl;
260 core::list<std::string> objects;
261 core::map<std::string, bool> updated;
262 bool something_actually_changed = false;
264 // Read and modify stuff
266 std::ifstream is(filename);
267 if(is.good() == false)
269 infostream<<"updateConfigFile():"
270 " Error opening configuration file"
272 <<filename<<"\""<<std::endl;
276 while(getUpdatedConfigObject(is, objects, updated,
277 something_actually_changed));
281 JMutexAutoLock lock(m_mutex);
283 // If something not yet determined to have been changed, check if
284 // any new stuff was added
285 if(!something_actually_changed){
286 for(core::map<std::string, std::string>::Iterator
287 i = m_settings.getIterator();
288 i.atEnd() == false; i++)
290 if(updated.find(i.getNode()->getKey()))
292 something_actually_changed = true;
297 // If nothing was actually changed, skip writing the file
298 if(!something_actually_changed){
299 infostream<<"Skipping writing of "<<filename
300 <<" because content wouldn't be modified"<<std::endl;
306 std::ofstream os(filename);
307 if(os.good() == false)
309 errorstream<<"Error opening configuration file"
311 <<filename<<"\""<<std::endl;
318 for(core::list<std::string>::Iterator
320 i != objects.end(); i++)
326 Write stuff that was not already in the file
328 for(core::map<std::string, std::string>::Iterator
329 i = m_settings.getIterator();
330 i.atEnd() == false; i++)
332 if(updated.find(i.getNode()->getKey()))
334 std::string name = i.getNode()->getKey();
335 std::string value = i.getNode()->getValue();
336 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
338 os<<name<<" = "<<value<<"\n";
346 NOTE: Types of allowed_options are ignored
348 returns true on success
350 bool parseCommandLine(int argc, char *argv[],
351 core::map<std::string, ValueSpec> &allowed_options)
353 int nonopt_index = 0;
359 std::string argname = argv[i];
360 if(argname.substr(0, 2) != "--")
362 // If option doesn't start with -, read it in as nonoptX
363 if(argname[0] != '-'){
364 std::string name = "nonopt";
365 name += itos(nonopt_index);
371 errorstream<<"Invalid command-line parameter \""
372 <<argname<<"\": --<option> expected."<<std::endl;
377 std::string name = argname.substr(2);
379 core::map<std::string, ValueSpec>::Node *n;
380 n = allowed_options.find(name);
383 errorstream<<"Unknown command-line parameter \""
384 <<argname<<"\""<<std::endl;
388 ValueType type = n->getValue().type;
390 std::string value = "";
392 if(type == VALUETYPE_FLAG)
400 errorstream<<"Invalid command-line parameter \""
401 <<name<<"\": missing value"<<std::endl;
409 infostream<<"Valid command-line parameter: \""
410 <<name<<"\" = \""<<value<<"\""
418 void set(std::string name, std::string value)
420 JMutexAutoLock lock(m_mutex);
422 m_settings[name] = value;
425 void set(std::string name, const char *value)
427 JMutexAutoLock lock(m_mutex);
429 m_settings[name] = value;
433 void setDefault(std::string name, std::string value)
435 JMutexAutoLock lock(m_mutex);
437 m_defaults[name] = value;
440 bool exists(std::string name)
442 JMutexAutoLock lock(m_mutex);
444 return (m_settings.find(name) || m_defaults.find(name));
447 std::string get(std::string name)
449 JMutexAutoLock lock(m_mutex);
451 core::map<std::string, std::string>::Node *n;
452 n = m_settings.find(name);
455 n = m_defaults.find(name);
458 throw SettingNotFoundException("Setting not found");
462 return n->getValue();
465 bool getBool(std::string name)
467 return is_yes(get(name));
470 bool getFlag(std::string name)
474 return getBool(name);
476 catch(SettingNotFoundException &e)
483 bool getBoolAsk(std::string name, std::string question, bool def)
485 // If it is in settings
487 return getBool(name);
491 std::cout<<question<<" [y/N]: ";
492 std::cin.getline(templine, 10);
501 float getFloat(std::string name)
503 return stof(get(name));
506 u16 getU16(std::string name)
508 return stoi(get(name), 0, 65535);
511 u16 getU16Ask(std::string name, std::string question, u16 def)
513 // If it is in settings
519 std::cout<<question<<" ["<<def<<"]: ";
520 std::cin.getline(templine, 10);
526 return stoi(s, 0, 65535);
529 s16 getS16(std::string name)
531 return stoi(get(name), -32768, 32767);
534 s32 getS32(std::string name)
536 return stoi(get(name));
539 v3f getV3F(std::string name)
544 value.X = stof(f.next(","));
545 value.Y = stof(f.next(","));
546 value.Z = stof(f.next(")"));
550 v2f getV2F(std::string name)
555 value.X = stof(f.next(","));
556 value.Y = stof(f.next(")"));
560 u64 getU64(std::string name)
563 std::string s = get(name);
564 std::istringstream ss(s);
569 //template<typename T> struct alignment_trick { char c; T member; };
570 //#define ALIGNOF(type) offsetof (alignment_trick<type>, member)
572 #define ALIGNOF(x) __alignof(x)
574 #define ALIGNOF(x) __alignof__(x)
576 #define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1))
578 #define strtok_r(x, y, z) strtok_s(x, y, z)
579 #define strtof(x, y) (float)strtod(x, y)
580 #define strtoll(x, y, z) _strtoi64(x, y, z)
581 #define strtoull(x, y, z) _strtoui64(x, y, z)
584 typedef long long int s64; //to be added to src/irrlichttypes.h later
586 template <class T> T *getStruct(std::string name, std::string format)
588 size_t len = sizeof(T);
589 std::vector<std::string *> strs_alloced;
591 std::string valstr = get(name);
592 char *s = &valstr[0];
594 char *bufpos = (char *)buf;
598 char *fmtpos, *fmt = &format[0];
599 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
602 bool is_unsigned = false;
606 width = (int)strtol(f + 1, &f, 10);
607 if (width && valtype == 's')
616 bufpos += PADDING(bufpos, u16);
617 if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
619 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
621 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
623 bufpos += sizeof(u16);
624 } else if (width == 32) {
625 bufpos += PADDING(bufpos, u32);
626 if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
628 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
630 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
632 bufpos += sizeof(u32);
633 } else if (width == 64) {
634 bufpos += PADDING(bufpos, u64);
635 if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
637 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
639 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
641 bufpos += sizeof(u64);
646 snext = strchr(s, ',');
650 bufpos += PADDING(bufpos, bool);
651 if ((bufpos - (char *)buf) + sizeof(bool) <= len)
652 *(bool *)bufpos = is_yes(std::string(s));
653 bufpos += sizeof(bool);
658 bufpos += PADDING(bufpos, float);
659 if ((bufpos - (char *)buf) + sizeof(float) <= len)
660 *(float *)bufpos = strtof(s, &s);
661 bufpos += sizeof(float);
666 while (*s == ' ' || *s == '\t')
668 if (*s++ != '"') //error, expected string
672 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
676 bufpos += PADDING(bufpos, std::string *);
678 str = new std::string(s);
680 while ((pos = str->find("\\\"", pos)) != std::string::npos)
683 if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
684 *(std::string **)bufpos = str;
685 bufpos += sizeof(std::string *);
686 strs_alloced.push_back(str);
688 s = *snext ? snext + 1 : NULL;
691 while (*s == ' ' || *s == '\t')
693 if (*s++ != '(') //error, expected vector
697 bufpos += PADDING(bufpos, v2f);
699 if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
700 v2f *v = (v2f *)bufpos;
701 v->X = strtof(s, &s);
703 v->Y = strtof(s, &s);
706 bufpos += sizeof(v2f);
707 } else if (width == 3) {
708 bufpos += PADDING(bufpos, v3f);
709 if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
710 v3f *v = (v3f *)bufpos;
711 v->X = strtof(s, &s);
713 v->Y = strtof(s, &s);
715 v->Z = strtof(s, &s);
718 bufpos += sizeof(v3f);
722 default: //error, invalid format specifier
729 if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
733 if (f && *f) { //error, mismatched number of fields and values
735 for (unsigned int i = 0; i != strs_alloced.size(); i++)
736 delete strs_alloced[i];
745 bool setStruct(std::string name, std::string format, void *value)
748 int sbuflen = sizeof(sbuf) - 1;
755 char *bufpos = (char *)value;
756 char *fmtpos, *fmt = &format[0];
757 while ((f = strtok_r(fmt, ",", &fmtpos))) {
759 bool is_unsigned = false;
760 int width = 0, nprinted = 0;
763 width = (int)strtol(f + 1, &f, 10);
764 if (width && valtype == 's')
773 bufpos += PADDING(bufpos, u16);
774 nprinted = snprintf(sbuf + pos, sbuflen,
775 is_unsigned ? "%u, " : "%d, ",
777 bufpos += sizeof(u16);
778 } else if (width == 32) {
779 bufpos += PADDING(bufpos, u32);
780 nprinted = snprintf(sbuf + pos, sbuflen,
781 is_unsigned ? "%u, " : "%d, ",
783 bufpos += sizeof(u32);
784 } else if (width == 64) {
785 bufpos += PADDING(bufpos, u64);
786 nprinted = snprintf(sbuf + pos, sbuflen,
787 is_unsigned ? "%llu, " : "%lli, ",
788 (unsigned long long)*((u64 *)bufpos));
789 bufpos += sizeof(u64);
793 bufpos += PADDING(bufpos, bool);
794 nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
795 *((bool *)bufpos) ? "true" : "false");
796 bufpos += sizeof(bool);
799 bufpos += PADDING(bufpos, float);
800 nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
802 bufpos += sizeof(float);
805 bufpos += PADDING(bufpos, std::string *);
806 str = **((std::string **)bufpos);
809 while ((fpos = str.find('"', fpos)) != std::string::npos) {
810 str.insert(fpos, 1, '\\');
814 nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
815 (*((std::string **)bufpos))->c_str());
816 bufpos += sizeof(std::string *);
820 bufpos += PADDING(bufpos, v2f);
821 v2f *v = (v2f *)bufpos;
822 nprinted = snprintf(sbuf + pos, sbuflen,
823 "(%f, %f), ", v->X, v->Y);
824 bufpos += sizeof(v2f);
826 bufpos += PADDING(bufpos, v3f);
827 v3f *v = (v3f *)bufpos;
828 nprinted = snprintf(sbuf + pos, sbuflen,
829 "(%f, %f, %f), ", v->X, v->Y, v->Z);
830 bufpos += sizeof(v3f);
836 if (nprinted < 0) //error, buffer too small
845 set(name, std::string(sbuf));
849 void setBool(std::string name, bool value)
857 void setFloat(std::string name, float value)
859 set(name, ftos(value));
862 void setV3F(std::string name, v3f value)
864 std::ostringstream os;
865 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
869 void setV2F(std::string name, v2f value)
871 std::ostringstream os;
872 os<<"("<<value.X<<","<<value.Y<<")";
876 void setS16(std::string name, s16 value)
878 set(name, itos(value));
881 void setS32(std::string name, s32 value)
883 set(name, itos(value));
886 void setU64(std::string name, u64 value)
888 std::ostringstream os;
895 JMutexAutoLock lock(m_mutex);
901 void updateValue(Settings &other, const std::string &name)
903 JMutexAutoLock lock(m_mutex);
909 std::string val = other.get(name);
910 m_settings[name] = val;
911 } catch(SettingNotFoundException &e){
917 void update(Settings &other)
919 JMutexAutoLock lock(m_mutex);
920 JMutexAutoLock lock2(other.m_mutex);
925 for(core::map<std::string, std::string>::Iterator
926 i = other.m_settings.getIterator();
927 i.atEnd() == false; i++)
929 m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
932 for(core::map<std::string, std::string>::Iterator
933 i = other.m_defaults.getIterator();
934 i.atEnd() == false; i++)
936 m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
942 Settings & operator+=(Settings &other)
944 JMutexAutoLock lock(m_mutex);
945 JMutexAutoLock lock2(other.m_mutex);
950 for(core::map<std::string, std::string>::Iterator
951 i = other.m_settings.getIterator();
952 i.atEnd() == false; i++)
954 m_settings.insert(i.getNode()->getKey(),
955 i.getNode()->getValue());
958 for(core::map<std::string, std::string>::Iterator
959 i = other.m_defaults.getIterator();
960 i.atEnd() == false; i++)
962 m_defaults.insert(i.getNode()->getKey(),
963 i.getNode()->getValue());
970 Settings & operator=(Settings &other)
972 JMutexAutoLock lock(m_mutex);
973 JMutexAutoLock lock2(other.m_mutex);
985 core::map<std::string, std::string> m_settings;
986 core::map<std::string, std::string> m_defaults;
987 // All methods that access m_settings/m_defaults directly should lock this.