Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenu
[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 <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 #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                 m_mutex.Init();
64         }
65
66         void writeLines(std::ostream &os)
67         {
68                 JMutexAutoLock lock(m_mutex);
69
70                 for(std::map<std::string, std::string>::iterator
71                                 i = m_settings.begin();
72                                 i != m_settings.end(); ++i)
73                 {
74                         std::string name = i->first;
75                         std::string value = i->second;
76                         os<<name<<" = "<<value<<"\n";
77                 }
78         }
79   
80         // return all keys used 
81         std::vector<std::string> getNames(){
82                 std::vector<std::string> names;
83                 for(std::map<std::string, std::string>::iterator
84                                 i = m_settings.begin();
85                                 i != m_settings.end(); ++i)
86                 {
87                         names.push_back(i->first);
88                 }
89                 return names;  
90         }
91
92         // remove a setting
93         bool remove(const std::string& name)
94         {
95                 return m_settings.erase(name);
96         }
97
98
99         bool parseConfigLine(const std::string &line)
100         {
101                 JMutexAutoLock lock(m_mutex);
102
103                 std::string trimmedline = trim(line);
104
105                 // Ignore empty lines and comments
106                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
107                         return true;
108
109                 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
110
111                 Strfnd sf(trim(line));
112
113                 std::string name = sf.next("=");
114                 name = trim(name);
115
116                 if(name == "")
117                         return true;
118
119                 std::string value = sf.next("\n");
120                 value = trim(value);
121
122                 /*infostream<<"Config name=\""<<name<<"\" value=\""
123                                 <<value<<"\""<<std::endl;*/
124
125                 m_settings[name] = value;
126
127                 return true;
128         }
129
130         void parseConfigLines(std::istream &is, const std::string &endstring)
131         {
132                 for(;;){
133                         if(is.eof())
134                                 break;
135                         std::string line;
136                         std::getline(is, line);
137                         std::string trimmedline = trim(line);
138                         if(endstring != ""){
139                                 if(trimmedline == endstring)
140                                         break;
141                         }
142                         parseConfigLine(line);
143                 }
144         }
145
146         // Returns false on EOF
147         bool parseConfigObject(std::istream &is)
148         {
149                 if(is.eof())
150                         return false;
151
152                 /*
153                         NOTE: This function might be expanded to allow multi-line
154                               settings.
155                 */
156                 std::string line;
157                 std::getline(is, line);
158                 //infostream<<"got line: \""<<line<<"\""<<std::endl;
159
160                 return parseConfigLine(line);
161         }
162
163         /*
164                 Read configuration file
165
166                 Returns true on success
167         */
168         bool readConfigFile(const char *filename)
169         {
170                 std::ifstream is(filename);
171                 if(is.good() == false)
172                         return false;
173
174                 /*infostream<<"Parsing configuration file: \""
175                                 <<filename<<"\""<<std::endl;*/
176
177                 while(parseConfigObject(is));
178
179                 return true;
180         }
181
182         /*
183                 Reads a configuration object from stream (usually a single line)
184                 and adds it to dst.
185
186                 Preserves comments and empty lines.
187
188                 Settings that were added to dst are also added to updated.
189                 key of updated is setting name, value of updated is dummy.
190
191                 Returns false on EOF
192         */
193         bool getUpdatedConfigObject(std::istream &is,
194                         std::list<std::string> &dst,
195                         std::set<std::string> &updated,
196                         bool &value_changed)
197         {
198                 JMutexAutoLock lock(m_mutex);
199
200                 if(is.eof())
201                         return false;
202
203                 // NOTE: This function will be expanded to allow multi-line settings
204                 std::string line;
205                 std::getline(is, line);
206
207                 std::string trimmedline = trim(line);
208
209                 std::string line_end = "";
210                 if(is.eof() == false)
211                         line_end = "\n";
212
213                 // Ignore empty lines and comments
214                 if(trimmedline.size() == 0 || trimmedline[0] == '#')
215                 {
216                         dst.push_back(line+line_end);
217                         return true;
218                 }
219
220                 Strfnd sf(trim(line));
221
222                 std::string name = sf.next("=");
223                 name = trim(name);
224
225                 if(name == "")
226                 {
227                         dst.push_back(line+line_end);
228                         return true;
229                 }
230
231                 std::string value = sf.next("\n");
232                 value = trim(value);
233
234                 if(m_settings.find(name) != m_settings.end())
235                 {
236                         std::string newvalue = m_settings[name];
237
238                         if(newvalue != value)
239                         {
240                                 infostream<<"Changing value of \""<<name<<"\" = \""
241                                                 <<value<<"\" -> \""<<newvalue<<"\""
242                                                 <<std::endl;
243                                 value_changed = true;
244                         }
245
246                         dst.push_back(name + " = " + newvalue + line_end);
247
248                         updated.insert(name);
249                 }
250                 else //file contains a setting which is not in m_settings
251                         value_changed=true;
252                         
253                 return true;
254         }
255
256         /*
257                 Updates configuration file
258
259                 Returns true on success
260         */
261         bool updateConfigFile(const char *filename)
262         {
263                 infostream<<"Updating configuration file: \""
264                                 <<filename<<"\""<<std::endl;
265
266                 std::list<std::string> objects;
267                 std::set<std::string> updated;
268                 bool something_actually_changed = false;
269
270                 // Read and modify stuff
271                 {
272                         std::ifstream is(filename);
273                         if(is.good() == false)
274                         {
275                                 infostream<<"updateConfigFile():"
276                                                 " Error opening configuration file"
277                                                 " for reading: \""
278                                                 <<filename<<"\""<<std::endl;
279                         }
280                         else
281                         {
282                                 while(getUpdatedConfigObject(is, objects, updated,
283                                                 something_actually_changed));
284                         }
285                 }
286
287                 JMutexAutoLock lock(m_mutex);
288
289                 // If something not yet determined to have been changed, check if
290                 // any new stuff was added
291                 if(!something_actually_changed){
292                         for(std::map<std::string, std::string>::iterator
293                                         i = m_settings.begin();
294                                         i != m_settings.end(); ++i)
295                         {
296                                 if(updated.find(i->first) != updated.end())
297                                         continue;
298                                 something_actually_changed = true;
299                                 break;
300                         }
301                 }
302
303                 // If nothing was actually changed, skip writing the file
304                 if(!something_actually_changed){
305                         infostream<<"Skipping writing of "<<filename
306                                         <<" because content wouldn't be modified"<<std::endl;
307                         return true;
308                 }
309
310                 // Write stuff back
311                 {
312                         std::ostringstream ss(std::ios_base::binary);
313
314                         /*
315                                 Write updated stuff
316                         */
317                         for(std::list<std::string>::iterator
318                                         i = objects.begin();
319                                         i != objects.end(); ++i)
320                         {
321                                 ss<<(*i);
322                         }
323
324                         /*
325                                 Write stuff that was not already in the file
326                         */
327                         for(std::map<std::string, std::string>::iterator
328                                         i = m_settings.begin();
329                                         i != m_settings.end(); ++i)
330                         {
331                                 if(updated.find(i->first) != updated.end())
332                                         continue;
333                                 std::string name = i->first;
334                                 std::string value = i->second;
335                                 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
336                                                 <<std::endl;
337                                 ss<<name<<" = "<<value<<"\n";
338                         }
339
340                         if(!fs::safeWriteToFile(filename, ss.str()))
341                         {
342                                 errorstream<<"Error writing configuration file: \""
343                                                 <<filename<<"\""<<std::endl;
344                                 return false;
345                         }
346                 }
347
348                 return true;
349         }
350
351         /*
352                 NOTE: Types of allowed_options are ignored
353
354                 returns true on success
355         */
356         bool parseCommandLine(int argc, char *argv[],
357                         std::map<std::string, ValueSpec> &allowed_options)
358         {
359                 int nonopt_index = 0;
360                 int i=1;
361                 for(;;)
362                 {
363                         if(i >= argc)
364                                 break;
365                         std::string argname = argv[i];
366                         if(argname.substr(0, 2) != "--")
367                         {
368                                 // If option doesn't start with -, read it in as nonoptX
369                                 if(argname[0] != '-'){
370                                         std::string name = "nonopt";
371                                         name += itos(nonopt_index);
372                                         set(name, argname);
373                                         nonopt_index++;
374                                         i++;
375                                         continue;
376                                 }
377                                 errorstream<<"Invalid command-line parameter \""
378                                                 <<argname<<"\": --<option> expected."<<std::endl;
379                                 return false;
380                         }
381                         i++;
382
383                         std::string name = argname.substr(2);
384
385                         std::map<std::string, ValueSpec>::iterator n;
386                         n = allowed_options.find(name);
387                         if(n == allowed_options.end())
388                         {
389                                 errorstream<<"Unknown command-line parameter \""
390                                                 <<argname<<"\""<<std::endl;
391                                 return false;
392                         }
393
394                         ValueType type = n->second.type;
395
396                         std::string value = "";
397
398                         if(type == VALUETYPE_FLAG)
399                         {
400                                 value = "true";
401                         }
402                         else
403                         {
404                                 if(i >= argc)
405                                 {
406                                         errorstream<<"Invalid command-line parameter \""
407                                                         <<name<<"\": missing value"<<std::endl;
408                                         return false;
409                                 }
410                                 value = argv[i];
411                                 i++;
412                         }
413
414
415                         infostream<<"Valid command-line parameter: \""
416                                         <<name<<"\" = \""<<value<<"\""
417                                         <<std::endl;
418                         set(name, value);
419                 }
420
421                 return true;
422         }
423
424         void set(std::string name, std::string value)
425         {
426                 JMutexAutoLock lock(m_mutex);
427
428                 m_settings[name] = value;
429         }
430
431         void set(std::string name, const char *value)
432         {
433                 JMutexAutoLock lock(m_mutex);
434
435                 m_settings[name] = value;
436         }
437
438
439         void setDefault(std::string name, std::string value)
440         {
441                 JMutexAutoLock lock(m_mutex);
442
443                 m_defaults[name] = value;
444         }
445
446         bool exists(std::string name)
447         {
448                 JMutexAutoLock lock(m_mutex);
449
450                 return (m_settings.find(name) != m_settings.end() || m_defaults.find(name) != m_defaults.end());
451         }
452
453         std::string get(std::string name)
454         {
455                 JMutexAutoLock lock(m_mutex);
456
457                 std::map<std::string, std::string>::iterator n;
458                 n = m_settings.find(name);
459                 if(n == m_settings.end())
460                 {
461                         n = m_defaults.find(name);
462                         if(n == m_defaults.end())
463                         {
464                                 throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str());
465                         }
466                 }
467
468                 return n->second;
469         }
470
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;
586                 std::string valstr = get(name);
587                 char *s = &valstr[0];
588                 char *buf = new char[len];
589                 char *bufpos = buf;
590                 char *f, *snext;
591                 size_t pos;
592
593                 char *fmtpos, *fmt = &format[0];
594                 while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
595                         fmt = NULL;
596
597                         bool is_unsigned = false;
598                         int width = 0;
599                         char valtype = *f;
600
601                         width = (int)strtol(f + 1, &f, 10);
602                         if (width && valtype == 's')
603                                 valtype = 'i';
604
605                         switch (valtype) {
606                                 case 'u':
607                                         is_unsigned = true;
608                                         /* FALLTHROUGH */
609                                 case 'i':
610                                         if (width == 16) {
611                                                 bufpos += PADDING(bufpos, u16);
612                                                 if ((bufpos - buf) + sizeof(u16) <= len) {
613                                                         if (is_unsigned)
614                                                                 *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
615                                                         else
616                                                                 *(s16 *)bufpos = (s16)strtol(s, &s, 10);
617                                                 }
618                                                 bufpos += sizeof(u16);
619                                         } else if (width == 32) {
620                                                 bufpos += PADDING(bufpos, u32);
621                                                 if ((bufpos - buf) + sizeof(u32) <= len) {
622                                                         if (is_unsigned)
623                                                                 *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
624                                                         else
625                                                                 *(s32 *)bufpos = (s32)strtol(s, &s, 10);
626                                                 }
627                                                 bufpos += sizeof(u32);
628                                         } else if (width == 64) {
629                                                 bufpos += PADDING(bufpos, u64);
630                                                 if ((bufpos - buf) + sizeof(u64) <= len) {
631                                                         if (is_unsigned)
632                                                                 *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
633                                                         else
634                                                                 *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
635                                                 }
636                                                 bufpos += sizeof(u64);
637                                         }
638                                         s = strchr(s, ',');
639                                         break;
640                                 case 'b':
641                                         snext = strchr(s, ',');
642                                         if (snext)
643                                                 *snext++ = 0;
644
645                                         bufpos += PADDING(bufpos, bool);
646                                         if ((bufpos - buf) + sizeof(bool) <= len)
647                                                 *(bool *)bufpos = is_yes(std::string(s));
648                                         bufpos += sizeof(bool);
649
650                                         s = snext;
651                                         break;
652                                 case 'f':
653                                         bufpos += PADDING(bufpos, float);
654                                         if ((bufpos - buf) + sizeof(float) <= len)
655                                                 *(float *)bufpos = strtof(s, &s);
656                                         bufpos += sizeof(float);
657
658                                         s = strchr(s, ',');
659                                         break;
660                                 case 's':
661                                         while (*s == ' ' || *s == '\t')
662                                                 s++;
663                                         if (*s++ != '"') //error, expected string
664                                                 goto fail;
665                                         snext = s;
666
667                                         while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
668                                                 snext++;
669                                         *snext++ = 0;
670
671                                         bufpos += PADDING(bufpos, std::string *);
672
673                                         str = new std::string(s);
674                                         pos = 0;
675                                         while ((pos = str->find("\\\"", pos)) != std::string::npos)
676                                                 str->erase(pos, 1);
677
678                                         if ((bufpos - buf) + sizeof(std::string *) <= len)
679                                                 *(std::string **)bufpos = str;
680                                         bufpos += sizeof(std::string *);
681                                         strs_alloced.push_back(str);
682
683                                         s = *snext ? snext + 1 : NULL;
684                                         break;
685                                 case 'v':
686                                         while (*s == ' ' || *s == '\t')
687                                                 s++;
688                                         if (*s++ != '(') //error, expected vector
689                                                 goto fail;
690
691                                         if (width == 2) {
692                                                 bufpos += PADDING(bufpos, v2f);
693
694                                                 if ((bufpos - buf) + sizeof(v2f) <= len) {
695                                                 v2f *v = (v2f *)bufpos;
696                                                         v->X = strtof(s, &s);
697                                                         s++;
698                                                         v->Y = strtof(s, &s);
699                                                 }
700
701                                                 bufpos += sizeof(v2f);
702                                         } else if (width == 3) {
703                                                 bufpos += PADDING(bufpos, v3f);
704                                                 if ((bufpos - buf) + sizeof(v3f) <= len) {
705                                                         v3f *v = (v3f *)bufpos;
706                                                         v->X = strtof(s, &s);
707                                                         s++;
708                                                         v->Y = strtof(s, &s);
709                                                         s++;
710                                                         v->Z = strtof(s, &s);
711                                                 }
712
713                                                 bufpos += sizeof(v3f);
714                                         }
715                                         s = strchr(s, ',');
716                                         break;
717                                 default: //error, invalid format specifier
718                                         goto fail;
719                         }
720
721                         if (s && *s == ',')
722                                 s++;
723
724                         if ((size_t)(bufpos - buf) > len) //error, buffer too small
725                                 goto fail;
726                 }
727
728                 if (f && *f) { //error, mismatched number of fields and values
729 fail:
730                         for (size_t i = 0; i != strs_alloced.size(); i++)
731                                 delete strs_alloced[i];
732                         delete[] buf;
733                         return false;
734                 }
735
736                 memcpy(out, buf, olen);
737                 delete[] buf;
738                 return true;
739         }
740
741         bool setStruct(std::string name, std::string format, void *value)
742         {
743                 char sbuf[2048];
744                 int sbuflen = sizeof(sbuf) - 1;
745                 sbuf[sbuflen] = 0;
746                 std::string str;
747                 int pos = 0;
748                 size_t fpos;
749                 char *f;
750
751                 char *bufpos = (char *)value;
752                 char *fmtpos, *fmt = &format[0];
753                 while ((f = strtok_r(fmt, ",", &fmtpos))) {
754                         fmt = NULL;
755                         bool is_unsigned = false;
756                         int width = 0, nprinted = 0;
757                         char valtype = *f;
758
759                         width = (int)strtol(f + 1, &f, 10);
760                         if (width && valtype == 's')
761                                 valtype = 'i';
762
763                         switch (valtype) {
764                                 case 'u':
765                                         is_unsigned = true;
766                                         /* FALLTHROUGH */
767                                 case 'i':
768                                         if (width == 16) {
769                                                 bufpos += PADDING(bufpos, u16);
770                                                 nprinted = snprintf(sbuf + pos, sbuflen,
771                                                                         is_unsigned ? "%u, " : "%d, ",
772                                                                         *((u16 *)bufpos));
773                                                 bufpos += sizeof(u16);
774                                         } else if (width == 32) {
775                                                 bufpos += PADDING(bufpos, u32);
776                                                 nprinted = snprintf(sbuf + pos, sbuflen,
777                                                                         is_unsigned ? "%u, " : "%d, ",
778                                                                         *((u32 *)bufpos));
779                                                 bufpos += sizeof(u32);
780                                         } else if (width == 64) {
781                                                 bufpos += PADDING(bufpos, u64);
782                                                 nprinted = snprintf(sbuf + pos, sbuflen,
783                                                                         is_unsigned ? "%llu, " : "%lli, ",
784                                                                         (unsigned long long)*((u64 *)bufpos));
785                                                 bufpos += sizeof(u64);
786                                         }
787                                         break;
788                                 case 'b':
789                                         bufpos += PADDING(bufpos, bool);
790                                         nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
791                                                                                 *((bool *)bufpos) ? "true" : "false");
792                                         bufpos += sizeof(bool);
793                                         break;
794                                 case 'f':
795                                         bufpos += PADDING(bufpos, float);
796                                         nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
797                                                                                 *((float *)bufpos));
798                                         bufpos += sizeof(float);
799                                         break;
800                                 case 's':
801                                         bufpos += PADDING(bufpos, std::string *);
802                                         str = **((std::string **)bufpos);
803
804                                         fpos = 0;
805                                         while ((fpos = str.find('"', fpos)) != std::string::npos) {
806                                                 str.insert(fpos, 1, '\\');
807                                                 fpos += 2;
808                                         }
809
810                                         nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
811                                                                                 (*((std::string **)bufpos))->c_str());
812                                         bufpos += sizeof(std::string *);
813                                         break;
814                                 case 'v':
815                                         if (width == 2) {
816                                                 bufpos += PADDING(bufpos, v2f);
817                                                 v2f *v = (v2f *)bufpos;
818                                                 nprinted = snprintf(sbuf + pos, sbuflen,
819                                                                                         "(%f, %f), ", v->X, v->Y);
820                                                 bufpos += sizeof(v2f);
821                                         } else {
822                                                 bufpos += PADDING(bufpos, v3f);
823                                                 v3f *v = (v3f *)bufpos;
824                                                 nprinted = snprintf(sbuf + pos, sbuflen,
825                                                                                         "(%f, %f, %f), ", v->X, v->Y, v->Z);
826                                                 bufpos += sizeof(v3f);
827                                         }
828                                         break;
829                                 default:
830                                         return false;
831                         }
832                         if (nprinted < 0) //error, buffer too small
833                                 return false;
834                         pos     += nprinted;
835                         sbuflen -= nprinted;
836                 }
837
838                 if (pos >= 2)
839                         sbuf[pos - 2] = 0;
840
841                 set(name, std::string(sbuf));
842                 return true;
843         }
844         
845         void setFlagStr(std::string name, u32 flags, FlagDesc *flagdesc)
846         {
847                 set(name, writeFlagString(flags, flagdesc));
848         }
849
850         void setBool(std::string name, bool value)
851         {
852                 if(value)
853                         set(name, "true");
854                 else
855                         set(name, "false");
856         }
857
858         void setFloat(std::string name, float value)
859         {
860                 set(name, ftos(value));
861         }
862
863         void setV3F(std::string name, v3f value)
864         {
865                 std::ostringstream os;
866                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
867                 set(name, os.str());
868         }
869
870         void setV2F(std::string name, v2f value)
871         {
872                 std::ostringstream os;
873                 os<<"("<<value.X<<","<<value.Y<<")";
874                 set(name, os.str());
875         }
876
877         void setS16(std::string name, s16 value)
878         {
879                 set(name, itos(value));
880         }
881
882         void setS32(std::string name, s32 value)
883         {
884                 set(name, itos(value));
885         }
886
887         void setU64(std::string name, u64 value)
888         {
889                 std::ostringstream os;
890                 os<<value;
891                 set(name, os.str());
892         }
893
894         void clear()
895         {
896                 JMutexAutoLock lock(m_mutex);
897
898                 m_settings.clear();
899                 m_defaults.clear();
900         }
901
902         void updateValue(Settings &other, const std::string &name)
903         {
904                 JMutexAutoLock lock(m_mutex);
905
906                 if(&other == this)
907                         return;
908
909                 try{
910                         std::string val = other.get(name);
911                         m_settings[name] = val;
912                 } catch(SettingNotFoundException &e){
913                 }
914
915                 return;
916         }
917
918         void update(Settings &other)
919         {
920                 JMutexAutoLock lock(m_mutex);
921                 JMutexAutoLock lock2(other.m_mutex);
922
923                 if(&other == this)
924                         return;
925
926                 m_settings.insert(other.m_settings.begin(), other.m_settings.end());
927                 m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
928
929                 return;
930         }
931
932         Settings & operator+=(Settings &other)
933         {
934                 JMutexAutoLock lock(m_mutex);
935                 JMutexAutoLock lock2(other.m_mutex);
936
937                 if(&other == this)
938                         return *this;
939
940                 update(other);
941
942                 return *this;
943
944         }
945
946         Settings & operator=(Settings &other)
947         {
948                 JMutexAutoLock lock(m_mutex);
949                 JMutexAutoLock lock2(other.m_mutex);
950
951                 if(&other == this)
952                         return *this;
953
954                 clear();
955                 (*this) += other;
956
957                 return *this;
958         }
959
960 private:
961         std::map<std::string, std::string> m_settings;
962         std::map<std::string, std::string> m_defaults;
963         // All methods that access m_settings/m_defaults directly should lock this.
964         JMutex m_mutex;
965 };
966
967 #endif
968