Merge remote branch 'origin/master'
[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 <string>
25 #include <jthread.h>
26 #include <jmutex.h>
27 #include <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
40 enum ValueType
41 {
42         VALUETYPE_STRING,
43         VALUETYPE_FLAG // Doesn't take any arguments
44 };
45
46 struct ValueSpec
47 {
48         ValueSpec(ValueType a_type, const char *a_help=NULL)
49         {
50                 type = a_type;
51                 help = a_help;
52         }
53         ValueType type;
54         const char *help;
55 };
56
57 class Settings
58 {
59 public:
60         Settings()
61         {
62                 m_mutex.Init();
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::ofstream os(filename);
312                         if(os.good() == false)
313                         {
314                                 errorstream<<"Error opening configuration file"
315                                                 " for writing: \""
316                                                 <<filename<<"\""<<std::endl;
317                                 return false;
318                         }
319
320                         /*
321                                 Write updated stuff
322                         */
323                         for(std::list<std::string>::iterator
324                                         i = objects.begin();
325                                         i != objects.end(); ++i)
326                         {
327                                 os<<(*i);
328                         }
329
330                         /*
331                                 Write stuff that was not already in the file
332                         */
333                         for(std::map<std::string, std::string>::iterator
334                                         i = m_settings.begin();
335                                         i != m_settings.end(); ++i)
336                         {
337                                 if(updated.find(i->first) != updated.end())
338                                         continue;
339                                 std::string name = i->first;
340                                 std::string value = i->second;
341                                 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
342                                                 <<std::endl;
343                                 os<<name<<" = "<<value<<"\n";
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         bool getBool(std::string name)
471         {
472                 return is_yes(get(name));
473         }
474
475         bool getFlag(std::string name)
476         {
477                 try
478                 {
479                         return getBool(name);
480                 }
481                 catch(SettingNotFoundException &e)
482                 {
483                         return false;
484                 }
485         }
486
487         // Asks if empty
488         bool getBoolAsk(std::string name, std::string question, bool def)
489         {
490                 // If it is in settings
491                 if(exists(name))
492                         return getBool(name);
493
494                 std::string s;
495                 char templine[10];
496                 std::cout<<question<<" [y/N]: ";
497                 std::cin.getline(templine, 10);
498                 s = templine;
499
500                 if(s == "")
501                         return def;
502
503                 return is_yes(s);
504         }
505
506         float getFloat(std::string name)
507         {
508                 return stof(get(name));
509         }
510
511         u16 getU16(std::string name)
512         {
513                 return stoi(get(name), 0, 65535);
514         }
515
516         u16 getU16Ask(std::string name, std::string question, u16 def)
517         {
518                 // If it is in settings
519                 if(exists(name))
520                         return getU16(name);
521
522                 std::string s;
523                 char templine[10];
524                 std::cout<<question<<" ["<<def<<"]: ";
525                 std::cin.getline(templine, 10);
526                 s = templine;
527
528                 if(s == "")
529                         return def;
530
531                 return stoi(s, 0, 65535);
532         }
533
534         s16 getS16(std::string name)
535         {
536                 return stoi(get(name), -32768, 32767);
537         }
538
539         s32 getS32(std::string name)
540         {
541                 return stoi(get(name));
542         }
543
544         v3f getV3F(std::string name)
545         {
546                 v3f value;
547                 Strfnd f(get(name));
548                 f.next("(");
549                 value.X = stof(f.next(","));
550                 value.Y = stof(f.next(","));
551                 value.Z = stof(f.next(")"));
552                 return value;
553         }
554
555         v2f getV2F(std::string name)
556         {
557                 v2f value;
558                 Strfnd f(get(name));
559                 f.next("(");
560                 value.X = stof(f.next(","));
561                 value.Y = stof(f.next(")"));
562                 return value;
563         }
564
565         u64 getU64(std::string name)
566         {
567                 u64 value = 0;
568                 std::string s = get(name);
569                 std::istringstream ss(s);
570                 ss>>value;
571                 return value;
572         }
573
574         u32 getFlagStr(std::string name, FlagDesc *flagdesc)
575         {
576                 std::string val = get(name);
577                 return (isdigit(val[0])) ? stoi(val) : readFlagString(val, flagdesc);
578         }
579
580         template <class T> T *getStruct(std::string name, std::string format)
581         {
582                 size_t len = sizeof(T);
583                 std::vector<std::string *> strs_alloced;
584                 std::string *str;
585                 std::string valstr = get(name);
586                 char *s = &valstr[0];
587                 T *buf = new T;
588                 char *bufpos = (char *)buf;
589                 char *f, *snext;
590                 size_t pos;
591
592                 char *fmtpos, *fmt = &format[0];
593                 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
594                         fmt = NULL;
595
596                         bool is_unsigned = false;
597                         int width = 0;
598                         char valtype = *f;
599
600                         width = (int)strtol(f + 1, &f, 10);
601                         if (width && valtype == 's')
602                                 valtype = 'i';
603
604                         switch (valtype) {
605                                 case 'u':
606                                         is_unsigned = true;
607                                         /* FALLTHROUGH */
608                                 case 'i':
609                                         if (width == 16) {
610                                                 bufpos += PADDING(bufpos, u16);
611                                                 if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
612                                                         if (is_unsigned)
613                                                                 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
614                                                         else
615                                                                 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
616                                                 }
617                                                 bufpos += sizeof(u16);
618                                         } else if (width == 32) {
619                                                 bufpos += PADDING(bufpos, u32);
620                                                 if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
621                                                         if (is_unsigned)
622                                                                 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
623                                                         else
624                                                                 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
625                                                 }
626                                                 bufpos += sizeof(u32);
627                                         } else if (width == 64) {
628                                                 bufpos += PADDING(bufpos, u64);
629                                                 if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
630                                                         if (is_unsigned)
631                                                                 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
632                                                         else
633                                                                 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
634                                                 }
635                                                 bufpos += sizeof(u64);
636                                         }
637                                         s = strchr(s, ',');
638                                         break;
639                                 case 'b':
640                                         snext = strchr(s, ',');
641                                         if (snext)
642                                                 *snext++ = 0;
643
644                                         bufpos += PADDING(bufpos, bool);
645                                         if ((bufpos - (char *)buf) + sizeof(bool) <= len)
646                                                 *(bool *)bufpos = is_yes(std::string(s));
647                                         bufpos += sizeof(bool);
648
649                                         s = snext;
650                                         break;
651                                 case 'f':
652                                         bufpos += PADDING(bufpos, float);
653                                         if ((bufpos - (char *)buf) + sizeof(float) <= len)
654                                                 *(float *)bufpos = strtof(s, &s);
655                                         bufpos += sizeof(float);
656
657                                         s = strchr(s, ',');
658                                         break;
659                                 case 's':
660                                         while (*s == ' ' || *s == '\t')
661                                                 s++;
662                                         if (*s++ != '"') //error, expected string
663                                                 goto fail;
664                                         snext = s;
665
666                                         while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
667                                                 snext++;
668                                         *snext++ = 0;
669
670                                         bufpos += PADDING(bufpos, std::string *);
671
672                                         str = new std::string(s);
673                                         pos = 0;
674                                         while ((pos = str->find("\\\"", pos)) != std::string::npos)
675                                                 str->erase(pos, 1);
676
677                                         if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
678                                                 *(std::string **)bufpos = str;
679                                         bufpos += sizeof(std::string *);
680                                         strs_alloced.push_back(str);
681
682                                         s = *snext ? snext + 1 : NULL;
683                                         break;
684                                 case 'v':
685                                         while (*s == ' ' || *s == '\t')
686                                                 s++;
687                                         if (*s++ != '(') //error, expected vector
688                                                 goto fail;
689
690                                         if (width == 2) {
691                                                 bufpos += PADDING(bufpos, v2f);
692
693                                                 if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
694                                                 v2f *v = (v2f *)bufpos;
695                                                         v->X = strtof(s, &s);
696                                                         s++;
697                                                         v->Y = strtof(s, &s);
698                                                 }
699
700                                                 bufpos += sizeof(v2f);
701                                         } else if (width == 3) {
702                                                 bufpos += PADDING(bufpos, v3f);
703                                                 if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
704                                                         v3f *v = (v3f *)bufpos;
705                                                         v->X = strtof(s, &s);
706                                                         s++;
707                                                         v->Y = strtof(s, &s);
708                                                         s++;
709                                                         v->Z = strtof(s, &s);
710                                                 }
711
712                                                 bufpos += sizeof(v3f);
713                                         }
714                                         s = strchr(s, ',');
715                                         break;
716                                 default: //error, invalid format specifier
717                                         goto fail;
718                         }
719
720                         if (s && *s == ',')
721                                 s++;
722
723                         if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
724                                 goto fail;
725                 }
726
727                 if (f && *f) { //error, mismatched number of fields and values
728 fail:
729                         for (unsigned int i = 0; i != strs_alloced.size(); i++)
730                                 delete strs_alloced[i];
731                         delete buf;
732                         //delete[] buf;
733                         buf = NULL;
734                 }
735
736                 return buf;
737         }
738
739         bool setStruct(std::string name, std::string format, void *value)
740         {
741                 char sbuf[2048];
742                 int sbuflen = sizeof(sbuf) - 1;
743                 sbuf[sbuflen] = 0;
744                 std::string str;
745                 int pos = 0;
746                 size_t fpos;
747                 char *f;
748
749                 char *bufpos = (char *)value;
750                 char *fmtpos, *fmt = &format[0];
751                 while ((f = strtok_r(fmt, ",", &fmtpos))) {
752                         fmt = NULL;
753                         bool is_unsigned = false;
754                         int width = 0, nprinted = 0;
755                         char valtype = *f;
756
757                         width = (int)strtol(f + 1, &f, 10);
758                         if (width && valtype == 's')
759                                 valtype = 'i';
760
761                         switch (valtype) {
762                                 case 'u':
763                                         is_unsigned = true;
764                                         /* FALLTHROUGH */
765                                 case 'i':
766                                         if (width == 16) {
767                                                 bufpos += PADDING(bufpos, u16);
768                                                 nprinted = snprintf(sbuf + pos, sbuflen,
769                                                                         is_unsigned ? "%u, " : "%d, ",
770                                                                         *((u16 *)bufpos));
771                                                 bufpos += sizeof(u16);
772                                         } else if (width == 32) {
773                                                 bufpos += PADDING(bufpos, u32);
774                                                 nprinted = snprintf(sbuf + pos, sbuflen,
775                                                                         is_unsigned ? "%u, " : "%d, ",
776                                                                         *((u32 *)bufpos));
777                                                 bufpos += sizeof(u32);
778                                         } else if (width == 64) {
779                                                 bufpos += PADDING(bufpos, u64);
780                                                 nprinted = snprintf(sbuf + pos, sbuflen,
781                                                                         is_unsigned ? "%llu, " : "%lli, ",
782                                                                         (unsigned long long)*((u64 *)bufpos));
783                                                 bufpos += sizeof(u64);
784                                         }
785                                         break;
786                                 case 'b':
787                                         bufpos += PADDING(bufpos, bool);
788                                         nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
789                                                                                 *((bool *)bufpos) ? "true" : "false");
790                                         bufpos += sizeof(bool);
791                                         break;
792                                 case 'f':
793                                         bufpos += PADDING(bufpos, float);
794                                         nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
795                                                                                 *((float *)bufpos));
796                                         bufpos += sizeof(float);
797                                         break;
798                                 case 's':
799                                         bufpos += PADDING(bufpos, std::string *);
800                                         str = **((std::string **)bufpos);
801
802                                         fpos = 0;
803                                         while ((fpos = str.find('"', fpos)) != std::string::npos) {
804                                                 str.insert(fpos, 1, '\\');
805                                                 fpos += 2;
806                                         }
807
808                                         nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
809                                                                                 (*((std::string **)bufpos))->c_str());
810                                         bufpos += sizeof(std::string *);
811                                         break;
812                                 case 'v':
813                                         if (width == 2) {
814                                                 bufpos += PADDING(bufpos, v2f);
815                                                 v2f *v = (v2f *)bufpos;
816                                                 nprinted = snprintf(sbuf + pos, sbuflen,
817                                                                                         "(%f, %f), ", v->X, v->Y);
818                                                 bufpos += sizeof(v2f);
819                                         } else {
820                                                 bufpos += PADDING(bufpos, v3f);
821                                                 v3f *v = (v3f *)bufpos;
822                                                 nprinted = snprintf(sbuf + pos, sbuflen,
823                                                                                         "(%f, %f, %f), ", v->X, v->Y, v->Z);
824                                                 bufpos += sizeof(v3f);
825                                         }
826                                         break;
827                                 default:
828                                         return false;
829                         }
830                         if (nprinted < 0) //error, buffer too small
831                                 return false;
832                         pos     += nprinted;
833                         sbuflen -= nprinted;
834                 }
835
836                 if (pos >= 2)
837                         sbuf[pos - 2] = 0;
838
839                 set(name, std::string(sbuf));
840                 return true;
841         }
842         
843         void setFlagStr(std::string name, u32 flags, FlagDesc *flagdesc)
844         {
845                 set(name, writeFlagString(flags, flagdesc));
846         }
847
848         void setBool(std::string name, bool value)
849         {
850                 if(value)
851                         set(name, "true");
852                 else
853                         set(name, "false");
854         }
855
856         void setFloat(std::string name, float value)
857         {
858                 set(name, ftos(value));
859         }
860
861         void setV3F(std::string name, v3f value)
862         {
863                 std::ostringstream os;
864                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
865                 set(name, os.str());
866         }
867
868         void setV2F(std::string name, v2f value)
869         {
870                 std::ostringstream os;
871                 os<<"("<<value.X<<","<<value.Y<<")";
872                 set(name, os.str());
873         }
874
875         void setS16(std::string name, s16 value)
876         {
877                 set(name, itos(value));
878         }
879
880         void setS32(std::string name, s32 value)
881         {
882                 set(name, itos(value));
883         }
884
885         void setU64(std::string name, u64 value)
886         {
887                 std::ostringstream os;
888                 os<<value;
889                 set(name, os.str());
890         }
891
892         void clear()
893         {
894                 JMutexAutoLock lock(m_mutex);
895
896                 m_settings.clear();
897                 m_defaults.clear();
898         }
899
900         void updateValue(Settings &other, const std::string &name)
901         {
902                 JMutexAutoLock lock(m_mutex);
903
904                 if(&other == this)
905                         return;
906
907                 try{
908                         std::string val = other.get(name);
909                         m_settings[name] = val;
910                 } catch(SettingNotFoundException &e){
911                 }
912
913                 return;
914         }
915
916         void update(Settings &other)
917         {
918                 JMutexAutoLock lock(m_mutex);
919                 JMutexAutoLock lock2(other.m_mutex);
920
921                 if(&other == this)
922                         return;
923
924                 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
925                 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
926
927                 return;
928         }
929
930         Settings & operator+=(Settings &other)
931         {
932                 JMutexAutoLock lock(m_mutex);
933                 JMutexAutoLock lock2(other.m_mutex);
934
935                 if(&other == this)
936                         return *this;
937
938                 update(other);
939
940                 return *this;
941
942         }
943
944         Settings & operator=(Settings &other)
945         {
946                 JMutexAutoLock lock(m_mutex);
947                 JMutexAutoLock lock2(other.m_mutex);
948
949                 if(&other == this)
950                         return *this;
951
952                 clear();
953                 (*this) += other;
954
955                 return *this;
956         }
957
958 private:
959         std::map<std::string, std::string> m_settings;
960         std::map<std::string, std::string> m_defaults;
961         // All methods that access m_settings/m_defaults directly should lock this.
962         JMutex m_mutex;
963 };
964
965 #endif
966