Fix settings to honor numeric conversion errors
[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)
576         {
577                 std::string val = get(name);
578                 return (isdigit(val[0])) ? stoi(val) : readFlagString(val, flagdesc);
579         }
580
581         bool getStruct(std::string name, std::string format, void *out, size_t olen)
582         {
583                 size_t len = olen;
584                 std::vector<std::string *> strs_alloced;
585                 std::string *str, valstr;
586                 char *f, *snext;
587                 size_t pos;
588
589                 try {
590                         valstr = get(name);
591                 } catch (SettingNotFoundException &e) {
592                         return false;
593                 }
594
595                 char *s = &valstr[0];
596                 char *buf = new char[len];
597                 char *bufpos = buf;
598
599                 char *fmtpos, *fmt = &format[0];
600                 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
601                         fmt = NULL;
602
603                         bool is_unsigned = false;
604                         int width = 0;
605                         char valtype = *f;
606
607                         width = (int)strtol(f + 1, &f, 10);
608                         if (width && valtype == 's')
609                                 valtype = 'i';
610
611                         switch (valtype) {
612                                 case 'u':
613                                         is_unsigned = true;
614                                         /* FALLTHROUGH */
615                                 case 'i':
616                                         if (width == 16) {
617                                                 bufpos += PADDING(bufpos, u16);
618                                                 if ((bufpos - buf) + sizeof(u16) <= len) {
619                                                         if (is_unsigned)
620                                                                 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
621                                                         else
622                                                                 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
623                                                 }
624                                                 bufpos += sizeof(u16);
625                                         } else if (width == 32) {
626                                                 bufpos += PADDING(bufpos, u32);
627                                                 if ((bufpos - buf) + sizeof(u32) <= len) {
628                                                         if (is_unsigned)
629                                                                 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
630                                                         else
631                                                                 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
632                                                 }
633                                                 bufpos += sizeof(u32);
634                                         } else if (width == 64) {
635                                                 bufpos += PADDING(bufpos, u64);
636                                                 if ((bufpos - buf) + sizeof(u64) <= len) {
637                                                         if (is_unsigned)
638                                                                 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
639                                                         else
640                                                                 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
641                                                 }
642                                                 bufpos += sizeof(u64);
643                                         }
644                                         s = strchr(s, ',');
645                                         break;
646                                 case 'b':
647                                         snext = strchr(s, ',');
648                                         if (snext)
649                                                 *snext++ = 0;
650
651                                         bufpos += PADDING(bufpos, bool);
652                                         if ((bufpos - buf) + sizeof(bool) <= len)
653                                                 *(bool *)bufpos = is_yes(std::string(s));
654                                         bufpos += sizeof(bool);
655
656                                         s = snext;
657                                         break;
658                                 case 'f':
659                                         bufpos += PADDING(bufpos, float);
660                                         if ((bufpos - buf) + sizeof(float) <= len)
661                                                 *(float *)bufpos = strtof(s, &s);
662                                         bufpos += sizeof(float);
663
664                                         s = strchr(s, ',');
665                                         break;
666                                 case 's':
667                                         while (*s == ' ' || *s == '\t')
668                                                 s++;
669                                         if (*s++ != '"') //error, expected string
670                                                 goto fail;
671                                         snext = s;
672
673                                         while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
674                                                 snext++;
675                                         *snext++ = 0;
676
677                                         bufpos += PADDING(bufpos, std::string *);
678
679                                         str = new std::string(s);
680                                         pos = 0;
681                                         while ((pos = str->find("\\\"", pos)) != std::string::npos)
682                                                 str->erase(pos, 1);
683
684                                         if ((bufpos - buf) + sizeof(std::string *) <= len)
685                                                 *(std::string **)bufpos = str;
686                                         bufpos += sizeof(std::string *);
687                                         strs_alloced.push_back(str);
688
689                                         s = *snext ? snext + 1 : NULL;
690                                         break;
691                                 case 'v':
692                                         while (*s == ' ' || *s == '\t')
693                                                 s++;
694                                         if (*s++ != '(') //error, expected vector
695                                                 goto fail;
696
697                                         if (width == 2) {
698                                                 bufpos += PADDING(bufpos, v2f);
699
700                                                 if ((bufpos - buf) + sizeof(v2f) <= len) {
701                                                 v2f *v = (v2f *)bufpos;
702                                                         v->X = strtof(s, &s);
703                                                         s++;
704                                                         v->Y = strtof(s, &s);
705                                                 }
706
707                                                 bufpos += sizeof(v2f);
708                                         } else if (width == 3) {
709                                                 bufpos += PADDING(bufpos, v3f);
710                                                 if ((bufpos - buf) + sizeof(v3f) <= len) {
711                                                         v3f *v = (v3f *)bufpos;
712                                                         v->X = strtof(s, &s);
713                                                         s++;
714                                                         v->Y = strtof(s, &s);
715                                                         s++;
716                                                         v->Z = strtof(s, &s);
717                                                 }
718
719                                                 bufpos += sizeof(v3f);
720                                         }
721                                         s = strchr(s, ',');
722                                         break;
723                                 default: //error, invalid format specifier
724                                         goto fail;
725                         }
726
727                         if (s && *s == ',')
728                                 s++;
729
730                         if ((size_t)(bufpos - buf) > len) //error, buffer too small
731                                 goto fail;
732                 }
733
734                 if (f && *f) { //error, mismatched number of fields and values
735 fail:
736                         for (size_t i = 0; i != strs_alloced.size(); i++)
737                                 delete strs_alloced[i];
738                         delete[] buf;
739                         return false;
740                 }
741
742                 memcpy(out, buf, olen);
743                 delete[] buf;
744                 return true;
745         }
746
747         //////////// Try to get value, no exception thrown
748         bool getNoEx(std::string name, std::string &val)
749         {
750                 try {
751                         val = get(name);
752                         return true;
753                 } catch (SettingNotFoundException &e) {
754                         return false;
755                 } catch (NumericException &e) {
756                         return false;
757                 }
758         }
759
760         bool getFlagStrNoEx(std::string name, u32 &val, FlagDesc *flagdesc)
761         {
762                 try {
763                         val = getFlagStr(name, flagdesc);
764                         return true;
765                 } catch (SettingNotFoundException &e) {
766                         return false;
767                 }
768         }
769
770         bool getFloatNoEx(std::string name, float &val)
771         {
772                 try {
773                         val = getFloat(name);
774                         return true;
775                 } catch (SettingNotFoundException &e) {
776                         return false;
777                 } catch (NumericException &e) {
778                         return false;
779                 }
780         }
781
782         bool getU16NoEx(std::string name, int &val)
783         {
784                 try {
785                         val = getU16(name);
786                         return true;
787                 } catch (SettingNotFoundException &e) {
788                         return false;
789                 } catch (NumericException &e) {
790                         return false;
791                 }
792         }
793
794         bool getU16NoEx(std::string name, u16 &val)
795         {
796                 try {
797                         val = getU16(name);
798                         return true;
799                 } catch (SettingNotFoundException &e) {
800                         return false;
801                 } catch (NumericException &e) {
802                         return false;
803                 }
804         }
805
806         bool getS16NoEx(std::string name, int &val)
807         {
808                 try {
809                         val = getU16(name);
810                         return true;
811                 } catch (SettingNotFoundException &e) {
812                         return false;
813                 } catch (NumericException &e) {
814                         return false;
815                 }
816         }
817
818         bool getS16NoEx(std::string name, s16 &val)
819         {
820                 try {
821                         val = getS16(name);
822                         return true;
823                 } catch (SettingNotFoundException &e) {
824                         return false;
825                 } catch (NumericException &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                 } catch (NumericException &e) {
838                         return false;
839                 }
840         }
841
842         bool getV3FNoEx(std::string name, v3f &val)
843         {
844                 try {
845                         val = getV3F(name);
846                         return true;
847                 } catch (SettingNotFoundException &e) {
848                         return false;
849                 } catch (NumericException &e) {
850                         return false;
851                 }
852         }
853
854         bool getV2FNoEx(std::string name, v2f &val)
855         {
856                 try {
857                         val = getV2F(name);
858                         return true;
859                 } catch (SettingNotFoundException &e) {
860                         return false;
861                 } catch (NumericException &e) {
862                         return false;
863                 }
864         }
865
866         bool getU64NoEx(std::string name, u64 &val)
867         {
868                 try {
869                         val = getU64(name);
870                         return true;
871                 } catch (SettingNotFoundException &e) {
872                         return false;
873                 } catch (NumericException &e) {
874                         return false;
875                 }
876         }
877
878         //////////// Set setting
879         bool setStruct(std::string name, std::string format, void *value)
880         {
881                 char sbuf[2048];
882                 int sbuflen = sizeof(sbuf) - 1;
883                 sbuf[sbuflen] = 0;
884                 std::string str;
885                 int pos = 0;
886                 size_t fpos;
887                 char *f;
888
889                 char *bufpos = (char *)value;
890                 char *fmtpos, *fmt = &format[0];
891                 while ((f = strtok_r(fmt, ",", &fmtpos))) {
892                         fmt = NULL;
893                         bool is_unsigned = false;
894                         int width = 0, nprinted = 0;
895                         char valtype = *f;
896
897                         width = (int)strtol(f + 1, &f, 10);
898                         if (width && valtype == 's')
899                                 valtype = 'i';
900
901                         switch (valtype) {
902                                 case 'u':
903                                         is_unsigned = true;
904                                         /* FALLTHROUGH */
905                                 case 'i':
906                                         if (width == 16) {
907                                                 bufpos += PADDING(bufpos, u16);
908                                                 nprinted = snprintf(sbuf + pos, sbuflen,
909                                                                         is_unsigned ? "%u, " : "%d, ",
910                                                                         *((u16 *)bufpos));
911                                                 bufpos += sizeof(u16);
912                                         } else if (width == 32) {
913                                                 bufpos += PADDING(bufpos, u32);
914                                                 nprinted = snprintf(sbuf + pos, sbuflen,
915                                                                         is_unsigned ? "%u, " : "%d, ",
916                                                                         *((u32 *)bufpos));
917                                                 bufpos += sizeof(u32);
918                                         } else if (width == 64) {
919                                                 bufpos += PADDING(bufpos, u64);
920                                                 nprinted = snprintf(sbuf + pos, sbuflen,
921                                                                         is_unsigned ? "%llu, " : "%lli, ",
922                                                                         (unsigned long long)*((u64 *)bufpos));
923                                                 bufpos += sizeof(u64);
924                                         }
925                                         break;
926                                 case 'b':
927                                         bufpos += PADDING(bufpos, bool);
928                                         nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
929                                                                                 *((bool *)bufpos) ? "true" : "false");
930                                         bufpos += sizeof(bool);
931                                         break;
932                                 case 'f':
933                                         bufpos += PADDING(bufpos, float);
934                                         nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
935                                                                                 *((float *)bufpos));
936                                         bufpos += sizeof(float);
937                                         break;
938                                 case 's':
939                                         bufpos += PADDING(bufpos, std::string *);
940                                         str = **((std::string **)bufpos);
941
942                                         fpos = 0;
943                                         while ((fpos = str.find('"', fpos)) != std::string::npos) {
944                                                 str.insert(fpos, 1, '\\');
945                                                 fpos += 2;
946                                         }
947
948                                         nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
949                                                                                 (*((std::string **)bufpos))->c_str());
950                                         bufpos += sizeof(std::string *);
951                                         break;
952                                 case 'v':
953                                         if (width == 2) {
954                                                 bufpos += PADDING(bufpos, v2f);
955                                                 v2f *v = (v2f *)bufpos;
956                                                 nprinted = snprintf(sbuf + pos, sbuflen,
957                                                                                         "(%f, %f), ", v->X, v->Y);
958                                                 bufpos += sizeof(v2f);
959                                         } else {
960                                                 bufpos += PADDING(bufpos, v3f);
961                                                 v3f *v = (v3f *)bufpos;
962                                                 nprinted = snprintf(sbuf + pos, sbuflen,
963                                                                                         "(%f, %f, %f), ", v->X, v->Y, v->Z);
964                                                 bufpos += sizeof(v3f);
965                                         }
966                                         break;
967                                 default:
968                                         return false;
969                         }
970                         if (nprinted < 0) //error, buffer too small
971                                 return false;
972                         pos     += nprinted;
973                         sbuflen -= nprinted;
974                 }
975
976                 if (pos >= 2)
977                         sbuf[pos - 2] = 0;
978
979                 set(name, std::string(sbuf));
980                 return true;
981         }
982         
983         void setFlagStr(std::string name, u32 flags, FlagDesc *flagdesc)
984         {
985                 set(name, writeFlagString(flags, flagdesc));
986         }
987
988         void setBool(std::string name, bool value)
989         {
990                 if(value)
991                         set(name, "true");
992                 else
993                         set(name, "false");
994         }
995
996         void setFloat(std::string name, float value)
997         {
998                 set(name, ftos(value));
999         }
1000
1001         void setV3F(std::string name, v3f value)
1002         {
1003                 std::ostringstream os;
1004                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
1005                 set(name, os.str());
1006         }
1007
1008         void setV2F(std::string name, v2f value)
1009         {
1010                 std::ostringstream os;
1011                 os<<"("<<value.X<<","<<value.Y<<")";
1012                 set(name, os.str());
1013         }
1014
1015         void setS16(std::string name, s16 value)
1016         {
1017                 set(name, itos(value));
1018         }
1019
1020         void setS32(std::string name, s32 value)
1021         {
1022                 set(name, itos(value));
1023         }
1024
1025         void setU64(std::string name, u64 value)
1026         {
1027                 std::ostringstream os;
1028                 os<<value;
1029                 set(name, os.str());
1030         }
1031
1032         void clear()
1033         {
1034                 JMutexAutoLock lock(m_mutex);
1035
1036                 m_settings.clear();
1037                 m_defaults.clear();
1038         }
1039
1040         void updateValue(Settings &other, const std::string &name)
1041         {
1042                 JMutexAutoLock lock(m_mutex);
1043
1044                 if(&other == this)
1045                         return;
1046
1047                 try{
1048                         std::string val = other.get(name);
1049                         m_settings[name] = val;
1050                 } catch(SettingNotFoundException &e){
1051                 }
1052
1053                 return;
1054         }
1055
1056         void update(Settings &other)
1057         {
1058                 JMutexAutoLock lock(m_mutex);
1059                 JMutexAutoLock lock2(other.m_mutex);
1060
1061                 if(&other == this)
1062                         return;
1063
1064                 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
1065                 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
1066
1067                 return;
1068         }
1069
1070         Settings & operator+=(Settings &other)
1071         {
1072                 JMutexAutoLock lock(m_mutex);
1073                 JMutexAutoLock lock2(other.m_mutex);
1074
1075                 if(&other == this)
1076                         return *this;
1077
1078                 update(other);
1079
1080                 return *this;
1081
1082         }
1083
1084         Settings & operator=(Settings &other)
1085         {
1086                 JMutexAutoLock lock(m_mutex);
1087                 JMutexAutoLock lock2(other.m_mutex);
1088
1089                 if(&other == this)
1090                         return *this;
1091
1092                 clear();
1093                 (*this) += other;
1094
1095                 return *this;
1096         }
1097
1098 private:
1099         std::map<std::string, std::string> m_settings;
1100         std::map<std::string, std::string> m_defaults;
1101         // All methods that access m_settings/m_defaults directly should lock this.
1102         JMutex m_mutex;
1103 };
1104
1105 #endif
1106