utility.h: Change Buffer's interface to be more compatible with SharedBuffer's interf...
[oweals/minetest.git] / src / settings.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.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 "utility.h"
34 #include "log.h"
35
36 enum ValueType
37 {
38         VALUETYPE_STRING,
39         VALUETYPE_FLAG // Doesn't take any arguments
40 };
41
42 struct ValueSpec
43 {
44         ValueSpec(ValueType a_type, const char *a_help=NULL)
45         {
46                 type = a_type;
47                 help = a_help;
48         }
49         ValueType type;
50         const char *help;
51 };
52
53 class Settings
54 {
55 public:
56         Settings()
57         {
58                 m_mutex.Init();
59         }
60
61         void writeLines(std::ostream &os)
62         {
63                 JMutexAutoLock lock(m_mutex);
64                 
65                 for(core::map<std::string, std::string>::Iterator
66                                 i = m_settings.getIterator();
67                                 i.atEnd() == false; i++)
68                 {
69                         std::string name = i.getNode()->getKey();
70                         std::string value = i.getNode()->getValue();
71                         os<<name<<" = "<<value<<"\n";
72                 }
73         }
74
75         bool parseConfigLine(const std::string &line)
76         {
77                 JMutexAutoLock lock(m_mutex);
78                 
79                 std::string trimmedline = trim(line);
80                 
81                 // Ignore comments
82                 if(trimmedline[0] == '#')
83                         return true;
84
85                 //infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
86
87                 Strfnd sf(trim(line));
88
89                 std::string name = sf.next("=");
90                 name = trim(name);
91
92                 if(name == "")
93                         return true;
94                 
95                 std::string value = sf.next("\n");
96                 value = trim(value);
97
98                 /*infostream<<"Config name=\""<<name<<"\" value=\""
99                                 <<value<<"\""<<std::endl;*/
100                 
101                 m_settings[name] = value;
102                 
103                 return true;
104         }
105
106         void parseConfigLines(std::istream &is, const std::string &endstring)
107         {
108                 for(;;){
109                         if(is.eof())
110                                 break;
111                         std::string line;
112                         std::getline(is, line);
113                         std::string trimmedline = trim(line);
114                         if(endstring != ""){
115                                 if(trimmedline == endstring)
116                                         break;
117                         }
118                         parseConfigLine(line);
119                 }
120         }
121
122         // Returns false on EOF
123         bool parseConfigObject(std::istream &is)
124         {
125                 if(is.eof())
126                         return false;
127                 
128                 /*
129                         NOTE: This function might be expanded to allow multi-line
130                               settings.
131                 */
132                 std::string line;
133                 std::getline(is, line);
134                 //infostream<<"got line: \""<<line<<"\""<<std::endl;
135
136                 return parseConfigLine(line);
137         }
138
139         /*
140                 Read configuration file
141
142                 Returns true on success
143         */
144         bool readConfigFile(const char *filename)
145         {
146                 std::ifstream is(filename);
147                 if(is.good() == false)
148                 {
149                         errorstream<<"Error opening configuration file \""
150                                         <<filename<<"\""<<std::endl;
151                         return false;
152                 }
153
154                 infostream<<"Parsing configuration file: \""
155                                 <<filename<<"\""<<std::endl;
156                                 
157                 while(parseConfigObject(is));
158                 
159                 return true;
160         }
161
162         /*
163                 Reads a configuration object from stream (usually a single line)
164                 and adds it to dst.
165                 
166                 Preserves comments and empty lines.
167
168                 Settings that were added to dst are also added to updated.
169                 key of updated is setting name, value of updated is dummy.
170
171                 Returns false on EOF
172         */
173         bool getUpdatedConfigObject(std::istream &is,
174                         core::list<std::string> &dst,
175                         core::map<std::string, bool> &updated)
176         {
177                 JMutexAutoLock lock(m_mutex);
178                 
179                 if(is.eof())
180                         return false;
181                 
182                 // NOTE: This function will be expanded to allow multi-line settings
183                 std::string line;
184                 std::getline(is, line);
185
186                 std::string trimmedline = trim(line);
187
188                 std::string line_end = "";
189                 if(is.eof() == false)
190                         line_end = "\n";
191                 
192                 // Ignore comments
193                 if(trimmedline[0] == '#')
194                 {
195                         dst.push_back(line+line_end);
196                         return true;
197                 }
198
199                 Strfnd sf(trim(line));
200
201                 std::string name = sf.next("=");
202                 name = trim(name);
203
204                 if(name == "")
205                 {
206                         dst.push_back(line+line_end);
207                         return true;
208                 }
209                 
210                 std::string value = sf.next("\n");
211                 value = trim(value);
212                 
213                 if(m_settings.find(name))
214                 {
215                         std::string newvalue = m_settings[name];
216                         
217                         if(newvalue != value)
218                         {
219                                 infostream<<"Changing value of \""<<name<<"\" = \""
220                                                 <<value<<"\" -> \""<<newvalue<<"\""
221                                                 <<std::endl;
222                         }
223
224                         dst.push_back(name + " = " + newvalue + line_end);
225
226                         updated[name] = true;
227                 }
228                 
229                 return true;
230         }
231
232         /*
233                 Updates configuration file
234
235                 Returns true on success
236         */
237         bool updateConfigFile(const char *filename)
238         {
239                 infostream<<"Updating configuration file: \""
240                                 <<filename<<"\""<<std::endl;
241                 
242                 core::list<std::string> objects;
243                 core::map<std::string, bool> updated;
244                 
245                 // Read and modify stuff
246                 {
247                         std::ifstream is(filename);
248                         if(is.good() == false)
249                         {
250                                 infostream<<"updateConfigFile():"
251                                                 " Error opening configuration file"
252                                                 " for reading: \""
253                                                 <<filename<<"\""<<std::endl;
254                         }
255                         else
256                         {
257                                 while(getUpdatedConfigObject(is, objects, updated));
258                         }
259                 }
260                 
261                 JMutexAutoLock lock(m_mutex);
262                 
263                 // Write stuff back
264                 {
265                         std::ofstream os(filename);
266                         if(os.good() == false)
267                         {
268                                 errorstream<<"Error opening configuration file"
269                                                 " for writing: \""
270                                                 <<filename<<"\""<<std::endl;
271                                 return false;
272                         }
273                         
274                         /*
275                                 Write updated stuff
276                         */
277                         for(core::list<std::string>::Iterator
278                                         i = objects.begin();
279                                         i != objects.end(); i++)
280                         {
281                                 os<<(*i);
282                         }
283
284                         /*
285                                 Write stuff that was not already in the file
286                         */
287                         for(core::map<std::string, std::string>::Iterator
288                                         i = m_settings.getIterator();
289                                         i.atEnd() == false; i++)
290                         {
291                                 if(updated.find(i.getNode()->getKey()))
292                                         continue;
293                                 std::string name = i.getNode()->getKey();
294                                 std::string value = i.getNode()->getValue();
295                                 infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
296                                                 <<std::endl;
297                                 os<<name<<" = "<<value<<"\n";
298                         }
299                 }
300                 
301                 return true;
302         }
303
304         /*
305                 NOTE: Types of allowed_options are ignored
306
307                 returns true on success
308         */
309         bool parseCommandLine(int argc, char *argv[],
310                         core::map<std::string, ValueSpec> &allowed_options)
311         {
312                 int i=1;
313                 for(;;)
314                 {
315                         if(i >= argc)
316                                 break;
317                         std::string argname = argv[i];
318                         if(argname.substr(0, 2) != "--")
319                         {
320                                 errorstream<<"Invalid command-line parameter \""
321                                                 <<argname<<"\": --<option> expected."<<std::endl;
322                                 return false;
323                         }
324                         i++;
325
326                         std::string name = argname.substr(2);
327
328                         core::map<std::string, ValueSpec>::Node *n;
329                         n = allowed_options.find(name);
330                         if(n == NULL)
331                         {
332                                 errorstream<<"Unknown command-line parameter \""
333                                                 <<argname<<"\""<<std::endl;
334                                 return false;
335                         }
336
337                         ValueType type = n->getValue().type;
338
339                         std::string value = "";
340                         
341                         if(type == VALUETYPE_FLAG)
342                         {
343                                 value = "true";
344                         }
345                         else
346                         {
347                                 if(i >= argc)
348                                 {
349                                         errorstream<<"Invalid command-line parameter \""
350                                                         <<name<<"\": missing value"<<std::endl;
351                                         return false;
352                                 }
353                                 value = argv[i];
354                                 i++;
355                         }
356                         
357
358                         infostream<<"Valid command-line parameter: \""
359                                         <<name<<"\" = \""<<value<<"\""
360                                         <<std::endl;
361                         set(name, value);
362                 }
363
364                 return true;
365         }
366
367         void set(std::string name, std::string value)
368         {
369                 JMutexAutoLock lock(m_mutex);
370                 
371                 m_settings[name] = value;
372         }
373
374         void set(std::string name, const char *value)
375         {
376                 JMutexAutoLock lock(m_mutex);
377
378                 m_settings[name] = value;
379         }
380
381
382         void setDefault(std::string name, std::string value)
383         {
384                 JMutexAutoLock lock(m_mutex);
385                 
386                 m_defaults[name] = value;
387         }
388
389         bool exists(std::string name)
390         {
391                 JMutexAutoLock lock(m_mutex);
392                 
393                 return (m_settings.find(name) || m_defaults.find(name));
394         }
395
396         std::string get(std::string name)
397         {
398                 JMutexAutoLock lock(m_mutex);
399                 
400                 core::map<std::string, std::string>::Node *n;
401                 n = m_settings.find(name);
402                 if(n == NULL)
403                 {
404                         n = m_defaults.find(name);
405                         if(n == NULL)
406                         {
407                                 infostream<<"Settings: Setting not found: \""
408                                                 <<name<<"\""<<std::endl;
409                                 throw SettingNotFoundException("Setting not found");
410                         }
411                 }
412
413                 return n->getValue();
414         }
415
416         bool getBool(std::string name)
417         {
418                 return is_yes(get(name));
419         }
420         
421         bool getFlag(std::string name)
422         {
423                 try
424                 {
425                         return getBool(name);
426                 }
427                 catch(SettingNotFoundException &e)
428                 {
429                         return false;
430                 }
431         }
432
433         // Asks if empty
434         bool getBoolAsk(std::string name, std::string question, bool def)
435         {
436                 // If it is in settings
437                 if(exists(name))
438                         return getBool(name);
439                 
440                 std::string s;
441                 char templine[10];
442                 std::cout<<question<<" [y/N]: ";
443                 std::cin.getline(templine, 10);
444                 s = templine;
445
446                 if(s == "")
447                         return def;
448
449                 return is_yes(s);
450         }
451
452         float getFloat(std::string name)
453         {
454                 return stof(get(name));
455         }
456
457         u16 getU16(std::string name)
458         {
459                 return stoi(get(name), 0, 65535);
460         }
461
462         u16 getU16Ask(std::string name, std::string question, u16 def)
463         {
464                 // If it is in settings
465                 if(exists(name))
466                         return getU16(name);
467                 
468                 std::string s;
469                 char templine[10];
470                 std::cout<<question<<" ["<<def<<"]: ";
471                 std::cin.getline(templine, 10);
472                 s = templine;
473
474                 if(s == "")
475                         return def;
476
477                 return stoi(s, 0, 65535);
478         }
479
480         s16 getS16(std::string name)
481         {
482                 return stoi(get(name), -32768, 32767);
483         }
484
485         s32 getS32(std::string name)
486         {
487                 return stoi(get(name));
488         }
489
490         v3f getV3F(std::string name)
491         {
492                 v3f value;
493                 Strfnd f(get(name));
494                 f.next("(");
495                 value.X = stof(f.next(","));
496                 value.Y = stof(f.next(","));
497                 value.Z = stof(f.next(")"));
498                 return value;
499         }
500
501         v2f getV2F(std::string name)
502         {
503                 v2f value;
504                 Strfnd f(get(name));
505                 f.next("(");
506                 value.X = stof(f.next(","));
507                 value.Y = stof(f.next(")"));
508                 return value;
509         }
510
511         u64 getU64(std::string name)
512         {
513                 u64 value = 0;
514                 std::string s = get(name);
515                 std::istringstream ss(s);
516                 ss>>value;
517                 return value;
518         }
519
520         void setBool(std::string name, bool value)
521         {
522                 if(value)
523                         set(name, "true");
524                 else
525                         set(name, "false");
526         }
527
528         void setS32(std::string name, s32 value)
529         {
530                 set(name, itos(value));
531         }
532
533         void setFloat(std::string name, float value)
534         {
535                 set(name, ftos(value));
536         }
537
538         void setV3F(std::string name, v3f value)
539         {
540                 std::ostringstream os;
541                 os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
542                 set(name, os.str());
543         }
544
545         void setV2F(std::string name, v2f value)
546         {
547                 std::ostringstream os;
548                 os<<"("<<value.X<<","<<value.Y<<")";
549                 set(name, os.str());
550         }
551
552         void setU64(std::string name, u64 value)
553         {
554                 std::ostringstream os;
555                 os<<value;
556                 set(name, os.str());
557         }
558
559         void clear()
560         {
561                 JMutexAutoLock lock(m_mutex);
562                 
563                 m_settings.clear();
564                 m_defaults.clear();
565         }
566
567         void updateValue(Settings &other, const std::string &name)
568         {
569                 JMutexAutoLock lock(m_mutex);
570                 
571                 if(&other == this)
572                         return;
573
574                 try{
575                         std::string val = other.get(name);
576                         m_settings[name] = val;
577                 } catch(SettingNotFoundException &e){
578                 }
579
580                 return;
581         }
582
583         void update(Settings &other)
584         {
585                 JMutexAutoLock lock(m_mutex);
586                 JMutexAutoLock lock2(other.m_mutex);
587                 
588                 if(&other == this)
589                         return;
590
591                 for(core::map<std::string, std::string>::Iterator
592                                 i = other.m_settings.getIterator();
593                                 i.atEnd() == false; i++)
594                 {
595                         m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
596                 }
597                 
598                 for(core::map<std::string, std::string>::Iterator
599                                 i = other.m_defaults.getIterator();
600                                 i.atEnd() == false; i++)
601                 {
602                         m_defaults[i.getNode()->getKey()] = i.getNode()->getValue();
603                 }
604
605                 return;
606         }
607
608         Settings & operator+=(Settings &other)
609         {
610                 JMutexAutoLock lock(m_mutex);
611                 JMutexAutoLock lock2(other.m_mutex);
612                 
613                 if(&other == this)
614                         return *this;
615
616                 for(core::map<std::string, std::string>::Iterator
617                                 i = other.m_settings.getIterator();
618                                 i.atEnd() == false; i++)
619                 {
620                         m_settings.insert(i.getNode()->getKey(),
621                                         i.getNode()->getValue());
622                 }
623                 
624                 for(core::map<std::string, std::string>::Iterator
625                                 i = other.m_defaults.getIterator();
626                                 i.atEnd() == false; i++)
627                 {
628                         m_defaults.insert(i.getNode()->getKey(),
629                                         i.getNode()->getValue());
630                 }
631
632                 return *this;
633
634         }
635
636         Settings & operator=(Settings &other)
637         {
638                 JMutexAutoLock lock(m_mutex);
639                 JMutexAutoLock lock2(other.m_mutex);
640                 
641                 if(&other == this)
642                         return *this;
643
644                 clear();
645                 (*this) += other;
646                 
647                 return *this;
648         }
649
650 private:
651         core::map<std::string, std::string> m_settings;
652         core::map<std::string, std::string> m_defaults;
653         // All methods that access m_settings/m_defaults directly should lock this.
654         JMutex m_mutex;
655 };
656
657 #endif
658