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