Make flag strings clear specified flag with 'no' prefix
[oweals/minetest.git] / src / settings.h
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #ifndef SETTINGS_HEADER
21 #define SETTINGS_HEADER
22
23 #include "irrlichttypes_bloated.h"
24 #include "exceptions.h"
25 #include <string>
26 #include "jthread/jmutex.h"
27 #include "jthread/jmutexautolock.h"
28 #include "strfnd.h"
29 #include <iostream>
30 #include <fstream>
31 #include <sstream>
32 #include "debug.h"
33 #include "log.h"
34 #include "util/string.h"
35 #include "porting.h"
36 #include <list>
37 #include <map>
38 #include <set>
39 #include "filesys.h"
40
41 enum ValueType
42 {
43         VALUETYPE_STRING,
44         VALUETYPE_FLAG // Doesn't take any arguments
45 };
46
47 struct ValueSpec
48 {
49         ValueSpec(ValueType a_type, const char *a_help=NULL)
50         {
51                 type = a_type;
52                 help = a_help;
53         }
54         ValueType type;
55         const char *help;
56 };
57
58 class Settings
59 {
60 public:
61         Settings()
62         {
63         }
64
65         void writeLines(std::ostream &os)
66         {
67                 JMutexAutoLock lock(m_mutex);
68
69                 for(std::map<std::string, std::string>::iterator
70                                 i = m_settings.begin();
71                                 i != m_settings.end(); ++i)
72                 {
73                         std::string name = i->first;
74                         std::string value = i->second;
75                         os<<name<<" = "<<value<<"\n";
76                 }
77         }
78   
79         // return all keys used
80         std::vector<std::string> getNames(){
81                 std::vector<std::string> names;
82                 for(std::map<std::string, std::string>::iterator
83                                 i = m_settings.begin();
84                                 i != m_settings.end(); ++i)
85                 {
86                         names.push_back(i->first);
87                 }
88                 return names;
89         }
90
91         // remove a setting
92         bool remove(const std::string& name)
93         {
94                 return m_settings.erase(name);
95         }
96
97
98         bool parseConfigLine(const std::string &line)
99         {
100                 JMutexAutoLock lock(m_mutex);
101
102                 std::string trimmedline = trim(line);
103
104                 // Ignore empty lines and comments
105                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
106                         return true;
107
108                 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
109
110                 Strfnd sf(trim(line));
111
112                 std::string name = sf.next("=");
113                 name = trim(name);
114
115                 if(name == "")
116                         return true;
117
118                 std::string value = sf.next("\n");
119                 value = trim(value);
120
121                 /*infostream<<"Config name=\""<<name<<"\" value=\""
122                                 <<value<<"\""<<std::endl;*/
123
124                 m_settings[name] = value;
125
126                 return true;
127         }
128
129         void parseConfigLines(std::istream &is, const std::string &endstring)
130         {
131                 for(;;){
132                         if(is.eof())
133                                 break;
134                         std::string line;
135                         std::getline(is, line);
136                         std::string trimmedline = trim(line);
137                         if(endstring != ""){
138                                 if(trimmedline == endstring)
139                                         break;
140                         }
141                         parseConfigLine(line);
142                 }
143         }
144
145         // Returns false on EOF
146         bool parseConfigObject(std::istream &is)
147         {
148                 if(is.eof())
149                         return false;
150
151                 /*
152                         NOTE: This function might be expanded to allow multi-line
153                               settings.
154                 */
155                 std::string line;
156                 std::getline(is, line);
157                 //infostream<<"got line: \""<<line<<"\""<<std::endl;
158
159                 return parseConfigLine(line);
160         }
161
162         /*
163                 Read configuration file
164
165                 Returns true on success
166         */
167         bool readConfigFile(const char *filename)
168         {
169                 std::ifstream is(filename);
170                 if(is.good() == false)
171                         return false;
172
173                 /*infostream<<"Parsing configuration file: \""
174                                 <<filename<<"\""<<std::endl;*/
175
176                 while(parseConfigObject(is));
177
178                 return true;
179         }
180
181         /*
182                 Reads a configuration object from stream (usually a single line)
183                 and adds it to dst.
184
185                 Preserves comments and empty lines.
186
187                 Settings that were added to dst are also added to updated.
188                 key of updated is setting name, value of updated is dummy.
189
190                 Returns false on EOF
191         */
192         bool getUpdatedConfigObject(std::istream &is,
193                         std::list<std::string> &dst,
194                         std::set<std::string> &updated,
195                         bool &value_changed)
196         {
197                 JMutexAutoLock lock(m_mutex);
198
199                 if(is.eof())
200                         return false;
201
202                 // NOTE: This function will be expanded to allow multi-line settings
203                 std::string line;
204                 std::getline(is, line);
205
206                 std::string trimmedline = trim(line);
207
208                 std::string line_end = "";
209                 if(is.eof() == false)
210                         line_end = "\n";
211
212                 // Ignore empty lines and comments
213                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
214                 {
215                         dst.push_back(line+line_end);
216                         return true;
217                 }
218
219                 Strfnd sf(trim(line));
220
221                 std::string name = sf.next("=");
222                 name = trim(name);
223
224                 if(name == "")
225                 {
226                         dst.push_back(line+line_end);
227                         return true;
228                 }
229
230                 std::string value = sf.next("\n");
231                 value = trim(value);
232
233                 if(m_settings.find(name) != m_settings.end())
234                 {
235                         std::string newvalue = m_settings[name];
236
237                         if(newvalue != value)
238                         {
239                                 infostream<<"Changing value of \""<<name<<"\" = \""
240                                                 <<value<<"\" -> \""<<newvalue<<"\""
241                                                 <<std::endl;
242                                 value_changed = true;
243                         }
244
245                         dst.push_back(name + " = " + newvalue + line_end);
246
247                         updated.insert(name);
248                 }
249                 else //file contains a setting which is not in m_settings
250                         value_changed=true;
251                         
252                 return true;
253         }
254
255         /*
256                 Updates configuration file
257
258                 Returns true on success
259         */
260         bool updateConfigFile(const char *filename)
261         {
262                 infostream<<"Updating configuration file: \""
263                                 <<filename<<"\""<<std::endl;
264
265                 std::list<std::string> objects;
266                 std::set<std::string> updated;
267                 bool something_actually_changed = false;
268
269                 // Read and modify stuff
270                 {
271                         std::ifstream is(filename);
272                         if(is.good() == false)
273                         {
274                                 infostream<<"updateConfigFile():"
275                                                 " Error opening configuration file"
276                                                 " for reading: \""
277                                                 <<filename<<"\""<<std::endl;
278                         }
279                         else
280                         {
281                                 while(getUpdatedConfigObject(is, objects, updated,
282                                                 something_actually_changed));
283                         }
284                 }
285
286                 JMutexAutoLock lock(m_mutex);
287
288                 // If something not yet determined to have been changed, check if
289                 // any new stuff was added
290                 if(!something_actually_changed){
291                         for(std::map<std::string, std::string>::iterator
292                                         i = m_settings.begin();
293                                         i != m_settings.end(); ++i)
294                         {
295                                 if(updated.find(i->first) != updated.end())
296                                         continue;
297                                 something_actually_changed = true;
298                                 break;
299                         }
300                 }
301
302                 // If nothing was actually changed, skip writing the file
303                 if(!something_actually_changed){
304                         infostream<<"Skipping writing of "<<filename
305                                         <<" because content wouldn't be modified"<<std::endl;
306                         return true;
307                 }
308
309                 // Write stuff back
310                 {
311                         std::ostringstream ss(std::ios_base::binary);
312
313                         /*
314                                 Write updated stuff
315                         */
316                         for(std::list<std::string>::iterator
317                                         i = objects.begin();
318                                         i != objects.end(); ++i)
319                         {
320                                 ss<<(*i);
321                         }
322
323                         /*
324                                 Write stuff that was not already in the file
325                         */
326                         for(std::map<std::string, std::string>::iterator
327                                         i = m_settings.begin();
328                                         i != m_settings.end(); ++i)
329                         {
330                                 if(updated.find(i->first) != updated.end())
331                                         continue;
332                                 std::string name = i->first;
333                                 std::string value = i->second;
334                                 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
335                                                 <<std::endl;
336                                 ss<<name<<" = "<<value<<"\n";
337                         }
338
339                         if(!fs::safeWriteToFile(filename, ss.str()))
340                         {
341                                 errorstream<<"Error writing configuration file: \""
342                                                 <<filename<<"\""<<std::endl;
343                                 return false;
344                         }
345                 }
346
347                 return true;
348         }
349
350         /*
351                 NOTE: Types of allowed_options are ignored
352
353                 returns true on success
354         */
355         bool parseCommandLine(int argc, char *argv[],
356                         std::map<std::string, ValueSpec> &allowed_options)
357         {
358                 int nonopt_index = 0;
359                 int i=1;
360                 for(;;)
361                 {
362                         if(i >= argc)
363                                 break;
364                         std::string argname = argv[i];
365                         if(argname.substr(0, 2) != "--")
366                         {
367                                 // If option doesn't start with -, read it in as nonoptX
368                                 if(argname[0] != '-'){
369                                         std::string name = "nonopt";
370                                         name += itos(nonopt_index);
371                                         set(name, argname);
372                                         nonopt_index++;
373                                         i++;
374                                         continue;
375                                 }
376                                 errorstream<<"Invalid command-line parameter \""
377                                                 <<argname<<"\": --<option> expected."<<std::endl;
378                                 return false;
379                         }
380                         i++;
381
382                         std::string name = argname.substr(2);
383
384                         std::map<std::string, ValueSpec>::iterator n;
385                         n = allowed_options.find(name);
386                         if(n == allowed_options.end())
387                         {
388                                 errorstream<<"Unknown command-line parameter \""
389                                                 <<argname<<"\""<<std::endl;
390                                 return false;
391                         }
392
393                         ValueType type = n->second.type;
394
395                         std::string value = "";
396
397                         if(type == VALUETYPE_FLAG)
398                         {
399                                 value = "true";
400                         }
401                         else
402                         {
403                                 if(i >= argc)
404                                 {
405                                         errorstream<<"Invalid command-line parameter \""
406                                                         <<name<<"\": missing value"<<std::endl;
407                                         return false;
408                                 }
409                                 value = argv[i];
410                                 i++;
411                         }
412
413
414                         infostream<<"Valid command-line parameter: \""
415                                         <<name<<"\" = \""<<value<<"\""
416                                         <<std::endl;
417                         set(name, value);
418                 }
419
420                 return true;
421         }
422
423         void set(std::string name, std::string value)
424         {
425                 JMutexAutoLock lock(m_mutex);
426
427                 m_settings[name] = value;
428         }
429
430         void set(std::string name, const char *value)
431         {
432                 JMutexAutoLock lock(m_mutex);
433
434                 m_settings[name] = value;
435         }
436
437
438         void setDefault(std::string name, std::string value)
439         {
440                 JMutexAutoLock lock(m_mutex);
441
442                 m_defaults[name] = value;
443         }
444
445         bool exists(std::string name)
446         {
447                 JMutexAutoLock lock(m_mutex);
448
449                 return (m_settings.find(name) != m_settings.end() || m_defaults.find(name) != m_defaults.end());
450         }
451
452         std::string get(std::string name)
453         {
454                 JMutexAutoLock lock(m_mutex);
455
456                 std::map<std::string, std::string>::iterator n;
457                 n = m_settings.find(name);
458                 if(n == m_settings.end())
459                 {
460                         n = m_defaults.find(name);
461                         if(n == m_defaults.end())
462                         {
463                                 throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
464                         }
465                 }
466
467                 return n->second;
468         }
469
470         //////////// Get setting
471         bool getBool(std::string name)
472         {
473                 return is_yes(get(name));
474         }
475
476         bool getFlag(std::string name)
477         {
478                 try
479                 {
480                         return getBool(name);
481                 }
482                 catch(SettingNotFoundException &e)
483                 {
484                         return false;
485                 }
486         }
487
488         // Asks if empty
489         bool getBoolAsk(std::string name, std::string question, bool def)
490         {
491                 // If it is in settings
492                 if(exists(name))
493                         return getBool(name);
494
495                 std::string s;
496                 char templine[10];
497                 std::cout<<question<<" [y/N]: ";
498                 std::cin.getline(templine, 10);
499                 s = templine;
500
501                 if(s == "")
502                         return def;
503
504                 return is_yes(s);
505         }
506
507         float getFloat(std::string name)
508         {
509                 return stof(get(name));
510         }
511
512         u16 getU16(std::string name)
513         {
514                 return stoi(get(name), 0, 65535);
515         }
516
517         u16 getU16Ask(std::string name, std::string question, u16 def)
518         {
519                 // If it is in settings
520                 if(exists(name))
521                         return getU16(name);
522
523                 std::string s;
524                 char templine[10];
525                 std::cout<<question<<" ["<<def<<"]: ";
526                 std::cin.getline(templine, 10);
527                 s = templine;
528
529                 if(s == "")
530                         return def;
531
532                 return stoi(s, 0, 65535);
533         }
534
535         s16 getS16(std::string name)
536         {
537                 return stoi(get(name), -32768, 32767);
538         }
539
540         s32 getS32(std::string name)
541         {
542                 return stoi(get(name));
543         }
544
545         v3f getV3F(std::string name)
546         {
547                 v3f value;
548                 Strfnd f(get(name));
549                 f.next("(");
550                 value.X = stof(f.next(","));
551                 value.Y = stof(f.next(","));
552                 value.Z = stof(f.next(")"));
553                 return value;
554         }
555
556         v2f getV2F(std::string name)
557         {
558                 v2f value;
559                 Strfnd f(get(name));
560                 f.next("(");
561                 value.X = stof(f.next(","));
562                 value.Y = stof(f.next(")"));
563                 return value;
564         }
565
566         u64 getU64(std::string name)
567         {
568                 u64 value = 0;
569                 std::string s = get(name);
570                 std::istringstream ss(s);
571                 ss>>value;
572                 return value;
573         }
574
575         u32 getFlagStr(std::string name, FlagDesc *flagdesc, u32 *flagmask)
576         {
577                 std::string val = get(name);
578                 return (isdigit(val[0])) ? stoi(val) :
579                         readFlagString(val, flagdesc, flagmask);
580         }
581
582         // N.B. if getStruct() is used to read a non-POD aggregate type,
583         // the behavior is undefined.
584         bool getStruct(std::string name, std::string format, void *out, size_t olen)
585         {
586                 size_t len = olen;
587                 std::vector<std::string *> strs_alloced;
588                 std::string *str, valstr;
589                 char *f, *snext;
590                 size_t pos;
591
592                 try {
593                         valstr = get(name);
594                 } catch (SettingNotFoundException &e) {
595                         return false;
596                 }
597
598                 char *s = &valstr[0];
599                 char *buf = new char[len];
600                 char *bufpos = buf;
601
602                 char *fmtpos, *fmt = &format[0];
603                 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
604                         fmt = NULL;
605
606                         bool is_unsigned = false;
607                         int width = 0;
608                         char valtype = *f;
609
610                         width = (int)strtol(f + 1, &f, 10);
611                         if (width && valtype == 's')
612                                 valtype = 'i';
613
614                         switch (valtype) {
615                                 case 'u':
616                                         is_unsigned = true;
617                                         /* FALLTHROUGH */
618                                 case 'i':
619                                         if (width == 16) {
620                                                 bufpos += PADDING(bufpos, u16);
621                                                 if ((bufpos - buf) + sizeof(u16) <= len) {
622                                                         if (is_unsigned)
623                                                                 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
624                                                         else
625                                                                 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
626                                                 }
627                                                 bufpos += sizeof(u16);
628                                         } else if (width == 32) {
629                                                 bufpos += PADDING(bufpos, u32);
630                                                 if ((bufpos - buf) + sizeof(u32) <= len) {
631                                                         if (is_unsigned)
632                                                                 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
633                                                         else
634                                                                 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
635                                                 }
636                                                 bufpos += sizeof(u32);
637                                         } else if (width == 64) {
638                                                 bufpos += PADDING(bufpos, u64);
639                                                 if ((bufpos - buf) + sizeof(u64) <= len) {
640                                                         if (is_unsigned)
641                                                                 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
642                                                         else
643                                                                 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
644                                                 }
645                                                 bufpos += sizeof(u64);
646                                         }
647                                         s = strchr(s, ',');
648                                         break;
649                                 case 'b':
650                                         snext = strchr(s, ',');
651                                         if (snext)
652                                                 *snext++ = 0;
653
654                                         bufpos += PADDING(bufpos, bool);
655                                         if ((bufpos - buf) + sizeof(bool) <= len)
656                                                 *(bool *)bufpos = is_yes(std::string(s));
657                                         bufpos += sizeof(bool);
658
659                                         s = snext;
660                                         break;
661                                 case 'f':
662                                         bufpos += PADDING(bufpos, float);
663                                         if ((bufpos - buf) + sizeof(float) <= len)
664                                                 *(float *)bufpos = strtof(s, &s);
665                                         bufpos += sizeof(float);
666
667                                         s = strchr(s, ',');
668                                         break;
669                                 case 's':
670                                         while (*s == ' ' || *s == '\t')
671                                                 s++;
672                                         if (*s++ != '"') //error, expected string
673                                                 goto fail;
674                                         snext = s;
675
676                                         while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
677                                                 snext++;
678                                         *snext++ = 0;
679
680                                         bufpos += PADDING(bufpos, std::string *);
681
682                                         str = new std::string(s);
683                                         pos = 0;
684                                         while ((pos = str->find("\\\"", pos)) != std::string::npos)
685                                                 str->erase(pos, 1);
686
687                                         if ((bufpos - buf) + sizeof(std::string *) <= len)
688                                                 *(std::string **)bufpos = str;
689                                         bufpos += sizeof(std::string *);
690                                         strs_alloced.push_back(str);
691
692                                         s = *snext ? snext + 1 : NULL;
693                                         break;
694                                 case 'v':
695                                         while (*s == ' ' || *s == '\t')
696                                                 s++;
697                                         if (*s++ != '(') //error, expected vector
698                                                 goto fail;
699
700                                         if (width == 2) {
701                                                 bufpos += PADDING(bufpos, v2f);
702
703                                                 if ((bufpos - buf) + sizeof(v2f) <= len) {
704                                                 v2f *v = (v2f *)bufpos;
705                                                         v->X = strtof(s, &s);
706                                                         s++;
707                                                         v->Y = strtof(s, &s);
708                                                 }
709
710                                                 bufpos += sizeof(v2f);
711                                         } else if (width == 3) {
712                                                 bufpos += PADDING(bufpos, v3f);
713                                                 if ((bufpos - buf) + sizeof(v3f) <= len) {
714                                                         v3f *v = (v3f *)bufpos;
715                                                         v->X = strtof(s, &s);
716                                                         s++;
717                                                         v->Y = strtof(s, &s);
718                                                         s++;
719                                                         v->Z = strtof(s, &s);
720                                                 }
721
722                                                 bufpos += sizeof(v3f);
723                                         }
724                                         s = strchr(s, ',');
725                                         break;
726                                 default: //error, invalid format specifier
727                                         goto fail;
728                         }
729
730                         if (s && *s == ',')
731                                 s++;
732
733                         if ((size_t)(bufpos - buf) > len) //error, buffer too small
734                                 goto fail;
735                 }
736
737                 if (f && *f) { //error, mismatched number of fields and values
738 fail:
739                         for (size_t i = 0; i != strs_alloced.size(); i++)
740                                 delete strs_alloced[i];
741                         delete[] buf;
742                         return false;
743                 }
744
745                 memcpy(out, buf, olen);
746                 delete[] buf;
747                 return true;
748         }
749
750         //////////// Try to get value, no exception thrown
751         bool getNoEx(std::string name, std::string &val)
752         {
753                 try {
754                         val = get(name);
755                         return true;
756                 } catch (SettingNotFoundException &e) {
757                         return false;
758                 }
759         }
760
761         // N.B. getFlagStrNoEx() does not set val, but merely modifies it.  Thus,
762         // val must be initialized before using getFlagStrNoEx().  The intention of
763         // this is to simplify modifying a flags field from a default value.
764         bool getFlagStrNoEx(std::string name, u32 &val, FlagDesc *flagdesc)
765         {
766                 try {
767                         u32 flags, flagmask;
768
769                         flags = getFlagStr(name, flagdesc, &flagmask);
770
771                         val &= ~flagmask;
772                         val |=  flags;
773
774                         return true;
775                 } catch (SettingNotFoundException &e) {
776                         return false;
777                 }
778         }
779
780         bool getFloatNoEx(std::string name, float &val)
781         {
782                 try {
783                         val = getFloat(name);
784                         return true;
785                 } catch (SettingNotFoundException &e) {
786                         return false;
787                 }
788         }
789
790         bool getU16NoEx(std::string name, int &val)
791         {
792                 try {
793                         val = getU16(name);
794                         return true;
795                 } catch (SettingNotFoundException &e) {
796                         return false;
797                 }
798         }
799
800         bool getU16NoEx(std::string name, u16 &val)
801         {
802                 try {
803                         val = getU16(name);
804                         return true;
805                 } catch (SettingNotFoundException &e) {
806                         return false;
807                 }
808         }
809
810         bool getS16NoEx(std::string name, int &val)
811         {
812                 try {
813                         val = getU16(name);
814                         return true;
815                 } catch (SettingNotFoundException &e) {
816                         return false;
817                 }
818         }
819
820         bool getS16NoEx(std::string name, s16 &val)
821         {
822                 try {
823                         val = getS16(name);
824                         return true;
825                 } catch (SettingNotFoundException &e) {
826                         return false;
827                 }
828         }
829
830         bool getS32NoEx(std::string name, s32 &val)
831         {
832                 try {
833                         val = getS32(name);
834                         return true;
835                 } catch (SettingNotFoundException &e) {
836                         return false;
837                 }
838         }
839
840         bool getV3FNoEx(std::string name, v3f &val)
841         {
842                 try {
843                         val = getV3F(name);
844                         return true;
845                 } catch (SettingNotFoundException &e) {
846                         return false;
847                 }
848         }
849
850         bool getV2FNoEx(std::string name, v2f &val)
851         {
852                 try {
853                         val = getV2F(name);
854                         return true;
855                 } catch (SettingNotFoundException &e) {
856                         return false;
857                 }
858         }
859
860         bool getU64NoEx(std::string name, u64 &val)
861         {
862                 try {
863                         val = getU64(name);
864                         return true;
865                 } catch (SettingNotFoundException &e) {
866                         return false;
867                 }
868         }
869
870         //////////// Set setting
871
872         // N.B. if setStruct() is used to write a non-POD aggregate type,
873         // the behavior is undefined.
874         bool setStruct(std::string name, std::string format, void *value)
875         {
876                 char sbuf[2048];
877                 int sbuflen = sizeof(sbuf) - 1;
878                 sbuf[sbuflen] = 0;
879                 std::string str;
880                 int pos = 0;
881                 size_t fpos;
882                 char *f;
883
884                 char *bufpos = (char *)value;
885                 char *fmtpos, *fmt = &format[0];
886                 while ((f = strtok_r(fmt, ",", &fmtpos))) {
887                         fmt = NULL;
888                         bool is_unsigned = false;
889                         int width = 0, nprinted = 0;
890                         char valtype = *f;
891
892                         width = (int)strtol(f + 1, &f, 10);
893                         if (width && valtype == 's')
894                                 valtype = 'i';
895
896                         switch (valtype) {
897                                 case 'u':
898                                         is_unsigned = true;
899                                         /* FALLTHROUGH */
900                                 case 'i':
901                                         if (width == 16) {
902                                                 bufpos += PADDING(bufpos, u16);
903                                                 nprinted = snprintf(sbuf + pos, sbuflen,
904                                                                         is_unsigned ? "%u, " : "%d, ",
905                                                                         *((u16 *)bufpos));
906                                                 bufpos += sizeof(u16);
907                                         } else if (width == 32) {
908                                                 bufpos += PADDING(bufpos, u32);
909                                                 nprinted = snprintf(sbuf + pos, sbuflen,
910                                                                         is_unsigned ? "%u, " : "%d, ",
911                                                                         *((u32 *)bufpos));
912                                                 bufpos += sizeof(u32);
913                                         } else if (width == 64) {
914                                                 bufpos += PADDING(bufpos, u64);
915                                                 nprinted = snprintf(sbuf + pos, sbuflen,
916                                                                         is_unsigned ? "%llu, " : "%lli, ",
917                                                                         (unsigned long long)*((u64 *)bufpos));
918                                                 bufpos += sizeof(u64);
919                                         }
920                                         break;
921                                 case 'b':
922                                         bufpos += PADDING(bufpos, bool);
923                                         nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
924                                                                                 *((bool *)bufpos) ? "true" : "false");
925                                         bufpos += sizeof(bool);
926                                         break;
927                                 case 'f':
928                                         bufpos += PADDING(bufpos, float);
929                                         nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
930                                                                                 *((float *)bufpos));
931                                         bufpos += sizeof(float);
932                                         break;
933                                 case 's':
934                                         bufpos += PADDING(bufpos, std::string *);
935                                         str = **((std::string **)bufpos);
936
937                                         fpos = 0;
938                                         while ((fpos = str.find('"', fpos)) != std::string::npos) {
939                                                 str.insert(fpos, 1, '\\');
940                                                 fpos += 2;
941                                         }
942
943                                         nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
944                                                                                 (*((std::string **)bufpos))->c_str());
945                                         bufpos += sizeof(std::string *);
946                                         break;
947                                 case 'v':
948                                         if (width == 2) {
949                                                 bufpos += PADDING(bufpos, v2f);
950                                                 v2f *v = (v2f *)bufpos;
951                                                 nprinted = snprintf(sbuf + pos, sbuflen,
952                                                                                         "(%f, %f), ", v->X, v->Y);
953                                                 bufpos += sizeof(v2f);
954                                         } else {
955                                                 bufpos += PADDING(bufpos, v3f);
956                                                 v3f *v = (v3f *)bufpos;
957                                                 nprinted = snprintf(sbuf + pos, sbuflen,
958                                                                                         "(%f, %f, %f), ", v->X, v->Y, v->Z);
959                                                 bufpos += sizeof(v3f);
960                                         }
961                                         break;
962                                 default:
963                                         return false;
964                         }
965                         if (nprinted < 0) //error, buffer too small
966                                 return false;
967                         pos     += nprinted;
968                         sbuflen -= nprinted;
969                 }
970
971                 if (pos >= 2)
972                         sbuf[pos - 2] = 0;
973
974                 set(name, std::string(sbuf));
975                 return true;
976         }
977
978         void setFlagStr(std::string name, u32 flags,
979                 FlagDesc *flagdesc, u32 flagmask)
980         {
981                 set(name, writeFlagString(flags, flagdesc, flagmask));
982         }
983
984         void setBool(std::string name, bool value)
985         {
986                 if(value)
987                         set(name, "true");
988                 else
989                         set(name, "false");
990         }
991
992         void setFloat(std::string name, float value)
993         {
994                 set(name, ftos(value));
995         }
996
997         void setV3F(std::string name, v3f value)
998         {
999                 std::ostringstream os;
1000                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
1001                 set(name, os.str());
1002         }
1003
1004         void setV2F(std::string name, v2f value)
1005         {
1006                 std::ostringstream os;
1007                 os<<"("<<value.X<<","<<value.Y<<")";
1008                 set(name, os.str());
1009         }
1010
1011         void setS16(std::string name, s16 value)
1012         {
1013                 set(name, itos(value));
1014         }
1015
1016         void setS32(std::string name, s32 value)
1017         {
1018                 set(name, itos(value));
1019         }
1020
1021         void setU64(std::string name, u64 value)
1022         {
1023                 std::ostringstream os;
1024                 os<<value;
1025                 set(name, os.str());
1026         }
1027
1028         void clear()
1029         {
1030                 JMutexAutoLock lock(m_mutex);
1031
1032                 m_settings.clear();
1033                 m_defaults.clear();
1034         }
1035
1036         void updateValue(Settings &other, const std::string &name)
1037         {
1038                 JMutexAutoLock lock(m_mutex);
1039
1040                 if(&other == this)
1041                         return;
1042
1043                 try{
1044                         std::string val = other.get(name);
1045                         m_settings[name] = val;
1046                 } catch(SettingNotFoundException &e){
1047                 }
1048
1049                 return;
1050         }
1051
1052         void update(Settings &other)
1053         {
1054                 JMutexAutoLock lock(m_mutex);
1055                 JMutexAutoLock lock2(other.m_mutex);
1056
1057                 if(&other == this)
1058                         return;
1059
1060                 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
1061                 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
1062
1063                 return;
1064         }
1065
1066         Settings & operator+=(Settings &other)
1067         {
1068                 JMutexAutoLock lock(m_mutex);
1069                 JMutexAutoLock lock2(other.m_mutex);
1070
1071                 if(&other == this)
1072                         return *this;
1073
1074                 update(other);
1075
1076                 return *this;
1077
1078         }
1079
1080         Settings & operator=(Settings &other)
1081         {
1082                 JMutexAutoLock lock(m_mutex);
1083                 JMutexAutoLock lock2(other.m_mutex);
1084
1085                 if(&other == this)
1086                         return *this;
1087
1088                 clear();
1089                 (*this) += other;
1090
1091                 return *this;
1092         }
1093
1094 private:
1095         std::map<std::string, std::string> m_settings;
1096         std::map<std::string, std::string> m_defaults;
1097         // All methods that access m_settings/m_defaults directly should lock this.
1098         JMutex m_mutex;
1099 };
1100
1101 #endif
1102