]> git.lizzy.rs Git - dragonfireclient.git/blob - src/settings.cpp
Schematics: Add indent-with-space option for schematic Lua table serialization
[dragonfireclient.git] / src / settings.cpp
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 #include "settings.h"
21 #include "irrlichttypes_bloated.h"
22 #include "exceptions.h"
23 #include "jthread/jmutexautolock.h"
24 #include "strfnd.h"
25 #include <iostream>
26 #include <fstream>
27 #include <sstream>
28 #include "debug.h"
29 #include "log.h"
30 #include "util/serialize.h"
31 #include "filesys.h"
32 #include "noise.h"
33 #include <cctype>
34 #include <algorithm>
35
36 static Settings main_settings;
37 Settings *g_settings = &main_settings;
38 std::string g_settings_path;
39
40 Settings::~Settings()
41 {
42         clear();
43 }
44
45
46 Settings & Settings::operator += (const Settings &other)
47 {
48         update(other);
49
50         return *this;
51 }
52
53
54 Settings & Settings::operator = (const Settings &other)
55 {
56         if (&other == this)
57                 return *this;
58
59         JMutexAutoLock lock(m_mutex);
60         JMutexAutoLock lock2(other.m_mutex);
61
62         clearNoLock();
63         updateNoLock(other);
64
65         return *this;
66 }
67
68
69 bool Settings::checkNameValid(const std::string &name)
70 {
71         size_t pos = name.find_first_of("\t\n\v\f\r\b =\"{}#");
72         if (pos != std::string::npos) {
73                 errorstream << "Invalid character '" << name[pos]
74                         << "' found in setting name" << std::endl;
75                 return false;
76         }
77         return true;
78 }
79
80
81 bool Settings::checkValueValid(const std::string &value)
82 {
83         if (value.substr(0, 3) == "\"\"\"" ||
84                 value.find("\n\"\"\"") != std::string::npos) {
85                 errorstream << "Invalid character sequence '\"\"\"' found in"
86                         " setting value" << std::endl;
87                 return false;
88         }
89         return true;
90 }
91
92
93 std::string Settings::sanitizeName(const std::string &name)
94 {
95         std::string n(name);
96
97         for (const char *s = "\t\n\v\f\r\b =\"{}#"; *s; s++)
98                 n.erase(std::remove(n.begin(), n.end(), *s), n.end());
99
100         return n;
101 }
102
103
104 std::string Settings::sanitizeValue(const std::string &value)
105 {
106         std::string v(value);
107         size_t p = 0;
108
109         if (v.substr(0, 3) == "\"\"\"")
110                 v.erase(0, 3);
111
112         while ((p = v.find("\n\"\"\"")) != std::string::npos)
113                 v.erase(p, 4);
114
115         return v;
116 }
117
118
119 std::string Settings::getMultiline(std::istream &is, size_t *num_lines)
120 {
121         size_t lines = 1;
122         std::string value;
123         std::string line;
124
125         while (is.good()) {
126                 lines++;
127                 std::getline(is, line);
128                 if (line == "\"\"\"")
129                         break;
130                 value += line;
131                 value.push_back('\n');
132         }
133
134         size_t len = value.size();
135         if (len)
136                 value.erase(len - 1);
137
138         if (num_lines)
139                 *num_lines = lines;
140
141         return value;
142 }
143
144
145 bool Settings::readConfigFile(const char *filename)
146 {
147         std::ifstream is(filename);
148         if (!is.good())
149                 return false;
150
151         return parseConfigLines(is, "");
152 }
153
154
155 bool Settings::parseConfigLines(std::istream &is, const std::string &end)
156 {
157         JMutexAutoLock lock(m_mutex);
158
159         std::string line, name, value;
160
161         while (is.good()) {
162                 std::getline(is, line);
163                 SettingsParseEvent event = parseConfigObject(line, end, name, value);
164
165                 switch (event) {
166                 case SPE_NONE:
167                 case SPE_INVALID:
168                 case SPE_COMMENT:
169                         break;
170                 case SPE_KVPAIR:
171                         m_settings[name] = SettingsEntry(value);
172                         break;
173                 case SPE_END:
174                         return true;
175                 case SPE_GROUP: {
176                         Settings *group = new Settings;
177                         if (!group->parseConfigLines(is, "}")) {
178                                 delete group;
179                                 return false;
180                         }
181                         m_settings[name] = SettingsEntry(group);
182                         break;
183                 }
184                 case SPE_MULTILINE:
185                         m_settings[name] = SettingsEntry(getMultiline(is));
186                         break;
187                 }
188         }
189
190         return end.empty();
191 }
192
193
194 void Settings::writeLines(std::ostream &os, u32 tab_depth) const
195 {
196         JMutexAutoLock lock(m_mutex);
197
198         for (std::map<std::string, SettingsEntry>::const_iterator
199                         it = m_settings.begin();
200                         it != m_settings.end(); ++it)
201                 printEntry(os, it->first, it->second, tab_depth);
202 }
203
204
205 void Settings::printEntry(std::ostream &os, const std::string &name,
206         const SettingsEntry &entry, u32 tab_depth)
207 {
208         for (u32 i = 0; i != tab_depth; i++)
209                 os << "\t";
210
211         if (entry.is_group) {
212                 os << name << " = {\n";
213
214                 entry.group->writeLines(os, tab_depth + 1);
215
216                 for (u32 i = 0; i != tab_depth; i++)
217                         os << "\t";
218                 os << "}\n";
219         } else {
220                 os << name << " = ";
221
222                 if (entry.value.find('\n') != std::string::npos)
223                         os << "\"\"\"\n" << entry.value << "\n\"\"\"\n";
224                 else
225                         os << entry.value << "\n";
226         }
227 }
228
229
230 bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
231         const std::string &end, u32 tab_depth)
232 {
233         std::map<std::string, SettingsEntry>::const_iterator it;
234         std::set<std::string> present_entries;
235         std::string line, name, value;
236         bool was_modified = false;
237         bool end_found = false;
238
239         // Add any settings that exist in the config file with the current value
240         // in the object if existing
241         while (is.good() && !end_found) {
242                 std::getline(is, line);
243                 SettingsParseEvent event = parseConfigObject(line, end, name, value);
244
245                 switch (event) {
246                 case SPE_END:
247                         os << line << (is.eof() ? "" : "\n");
248                         end_found = true;
249                         break;
250                 case SPE_MULTILINE:
251                         value = getMultiline(is);
252                         /* FALLTHROUGH */
253                 case SPE_KVPAIR:
254                         it = m_settings.find(name);
255                         if (it != m_settings.end() &&
256                                 (it->second.is_group || it->second.value != value)) {
257                                 printEntry(os, name, it->second, tab_depth);
258                                 was_modified = true;
259                         } else {
260                                 os << line << "\n";
261                                 if (event == SPE_MULTILINE)
262                                         os << value << "\n\"\"\"\n";
263                         }
264                         present_entries.insert(name);
265                         break;
266                 case SPE_GROUP:
267                         it = m_settings.find(name);
268                         if (it != m_settings.end() && it->second.is_group) {
269                                 os << line << "\n";
270                                 sanity_check(it->second.group != NULL);
271                                 was_modified |= it->second.group->updateConfigObject(is, os,
272                                         "}", tab_depth + 1);
273                         } else {
274                                 printEntry(os, name, it->second, tab_depth);
275                                 was_modified = true;
276                         }
277                         present_entries.insert(name);
278                         break;
279                 default:
280                         os << line << (is.eof() ? "" : "\n");
281                         break;
282                 }
283         }
284
285         // Add any settings in the object that don't exist in the config file yet
286         for (it = m_settings.begin(); it != m_settings.end(); ++it) {
287                 if (present_entries.find(it->first) != present_entries.end())
288                         continue;
289
290                 printEntry(os, it->first, it->second, tab_depth);
291                 was_modified = true;
292         }
293
294         return was_modified;
295 }
296
297
298 bool Settings::updateConfigFile(const char *filename)
299 {
300         JMutexAutoLock lock(m_mutex);
301
302         std::ifstream is(filename);
303         std::ostringstream os(std::ios_base::binary);
304
305         bool was_modified = updateConfigObject(is, os, "");
306         is.close();
307
308         if (!was_modified)
309                 return true;
310
311         if (!fs::safeWriteToFile(filename, os.str())) {
312                 errorstream << "Error writing configuration file: \""
313                         << filename << "\"" << std::endl;
314                 return false;
315         }
316
317         return true;
318 }
319
320
321 bool Settings::parseCommandLine(int argc, char *argv[],
322                 std::map<std::string, ValueSpec> &allowed_options)
323 {
324         int nonopt_index = 0;
325         for (int i = 1; i < argc; i++) {
326                 std::string arg_name = argv[i];
327                 if (arg_name.substr(0, 2) != "--") {
328                         // If option doesn't start with -, read it in as nonoptX
329                         if (arg_name[0] != '-'){
330                                 std::string name = "nonopt";
331                                 name += itos(nonopt_index);
332                                 set(name, arg_name);
333                                 nonopt_index++;
334                                 continue;
335                         }
336                         errorstream << "Invalid command-line parameter \""
337                                         << arg_name << "\": --<option> expected." << std::endl;
338                         return false;
339                 }
340
341                 std::string name = arg_name.substr(2);
342
343                 std::map<std::string, ValueSpec>::iterator n;
344                 n = allowed_options.find(name);
345                 if (n == allowed_options.end()) {
346                         errorstream << "Unknown command-line parameter \""
347                                         << arg_name << "\"" << std::endl;
348                         return false;
349                 }
350
351                 ValueType type = n->second.type;
352
353                 std::string value = "";
354
355                 if (type == VALUETYPE_FLAG) {
356                         value = "true";
357                 } else {
358                         if ((i + 1) >= argc) {
359                                 errorstream << "Invalid command-line parameter \""
360                                                 << name << "\": missing value" << std::endl;
361                                 return false;
362                         }
363                         value = argv[++i];
364                 }
365
366                 set(name, value);
367         }
368
369         return true;
370 }
371
372
373
374 /***********
375  * Getters *
376  ***********/
377
378
379 const SettingsEntry &Settings::getEntry(const std::string &name) const
380 {
381         JMutexAutoLock lock(m_mutex);
382
383         std::map<std::string, SettingsEntry>::const_iterator n;
384         if ((n = m_settings.find(name)) == m_settings.end()) {
385                 if ((n = m_defaults.find(name)) == m_defaults.end())
386                         throw SettingNotFoundException("Setting [" + name + "] not found.");
387         }
388         return n->second;
389 }
390
391
392 Settings *Settings::getGroup(const std::string &name) const
393 {
394         const SettingsEntry &entry = getEntry(name);
395         if (!entry.is_group)
396                 throw SettingNotFoundException("Setting [" + name + "] is not a group.");
397         return entry.group;
398 }
399
400
401 std::string Settings::get(const std::string &name) const
402 {
403         const SettingsEntry &entry = getEntry(name);
404         if (entry.is_group)
405                 throw SettingNotFoundException("Setting [" + name + "] is a group.");
406         return entry.value;
407 }
408
409
410 bool Settings::getBool(const std::string &name) const
411 {
412         return is_yes(get(name));
413 }
414
415
416 u16 Settings::getU16(const std::string &name) const
417 {
418         return stoi(get(name), 0, 65535);
419 }
420
421
422 s16 Settings::getS16(const std::string &name) const
423 {
424         return stoi(get(name), -32768, 32767);
425 }
426
427
428 s32 Settings::getS32(const std::string &name) const
429 {
430         return stoi(get(name));
431 }
432
433
434 float Settings::getFloat(const std::string &name) const
435 {
436         return stof(get(name));
437 }
438
439
440 u64 Settings::getU64(const std::string &name) const
441 {
442         u64 value = 0;
443         std::string s = get(name);
444         std::istringstream ss(s);
445         ss >> value;
446         return value;
447 }
448
449
450 v2f Settings::getV2F(const std::string &name) const
451 {
452         v2f value;
453         Strfnd f(get(name));
454         f.next("(");
455         value.X = stof(f.next(","));
456         value.Y = stof(f.next(")"));
457         return value;
458 }
459
460
461 v3f Settings::getV3F(const std::string &name) const
462 {
463         v3f value;
464         Strfnd f(get(name));
465         f.next("(");
466         value.X = stof(f.next(","));
467         value.Y = stof(f.next(","));
468         value.Z = stof(f.next(")"));
469         return value;
470 }
471
472
473 u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
474         u32 *flagmask) const
475 {
476         std::string val = get(name);
477         return std::isdigit(val[0])
478                 ? stoi(val)
479                 : readFlagString(val, flagdesc, flagmask);
480 }
481
482
483 // N.B. if getStruct() is used to read a non-POD aggregate type,
484 // the behavior is undefined.
485 bool Settings::getStruct(const std::string &name, const std::string &format,
486         void *out, size_t olen) const
487 {
488         std::string valstr;
489
490         try {
491                 valstr = get(name);
492         } catch (SettingNotFoundException &e) {
493                 return false;
494         }
495
496         if (!deSerializeStringToStruct(valstr, format, out, olen))
497                 return false;
498
499         return true;
500 }
501
502
503 bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
504 {
505         return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np);
506 }
507
508
509 bool Settings::getNoiseParamsFromValue(const std::string &name,
510         NoiseParams &np) const
511 {
512         std::string value;
513
514         if (!getNoEx(name, value))
515                 return false;
516
517         Strfnd f(value);
518
519         np.offset   = stof(f.next(","));
520         np.scale    = stof(f.next(","));
521         f.next("(");
522         np.spread.X = stof(f.next(","));
523         np.spread.Y = stof(f.next(","));
524         np.spread.Z = stof(f.next(")"));
525         f.next(",");
526         np.seed     = stoi(f.next(","));
527         np.octaves  = stoi(f.next(","));
528         np.persist  = stof(f.next(","));
529
530         std::string optional_params = f.next("");
531         if (optional_params != "")
532                 np.lacunarity = stof(optional_params);
533
534         return true;
535 }
536
537
538 bool Settings::getNoiseParamsFromGroup(const std::string &name,
539         NoiseParams &np) const
540 {
541         Settings *group = NULL;
542
543         if (!getGroupNoEx(name, group))
544                 return false;
545
546         group->getFloatNoEx("offset",      np.offset);
547         group->getFloatNoEx("scale",       np.scale);
548         group->getV3FNoEx("spread",        np.spread);
549         group->getS32NoEx("seed",          np.seed);
550         group->getU16NoEx("octaves",       np.octaves);
551         group->getFloatNoEx("persistence", np.persist);
552         group->getFloatNoEx("lacunarity",  np.lacunarity);
553
554         np.flags = 0;
555         if (!group->getFlagStrNoEx("flags", np.flags, flagdesc_noiseparams))
556                 np.flags = NOISE_FLAG_DEFAULTS;
557
558         return true;
559 }
560
561
562 bool Settings::exists(const std::string &name) const
563 {
564         JMutexAutoLock lock(m_mutex);
565
566         return (m_settings.find(name) != m_settings.end() ||
567                 m_defaults.find(name) != m_defaults.end());
568 }
569
570
571 std::vector<std::string> Settings::getNames() const
572 {
573         std::vector<std::string> names;
574         for (std::map<std::string, SettingsEntry>::const_iterator
575                         i = m_settings.begin();
576                         i != m_settings.end(); ++i) {
577                 names.push_back(i->first);
578         }
579         return names;
580 }
581
582
583
584 /***************************************
585  * Getters that don't throw exceptions *
586  ***************************************/
587
588 bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const
589 {
590         try {
591                 val = getEntry(name);
592                 return true;
593         } catch (SettingNotFoundException &e) {
594                 return false;
595         }
596 }
597
598
599 bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const
600 {
601         try {
602                 val = getGroup(name);
603                 return true;
604         } catch (SettingNotFoundException &e) {
605                 return false;
606         }
607 }
608
609
610 bool Settings::getNoEx(const std::string &name, std::string &val) const
611 {
612         try {
613                 val = get(name);
614                 return true;
615         } catch (SettingNotFoundException &e) {
616                 return false;
617         }
618 }
619
620
621 bool Settings::getFlag(const std::string &name) const
622 {
623         try {
624                 return getBool(name);
625         } catch(SettingNotFoundException &e) {
626                 return false;
627         }
628 }
629
630
631 bool Settings::getFloatNoEx(const std::string &name, float &val) const
632 {
633         try {
634                 val = getFloat(name);
635                 return true;
636         } catch (SettingNotFoundException &e) {
637                 return false;
638         }
639 }
640
641
642 bool Settings::getU16NoEx(const std::string &name, u16 &val) const
643 {
644         try {
645                 val = getU16(name);
646                 return true;
647         } catch (SettingNotFoundException &e) {
648                 return false;
649         }
650 }
651
652
653 bool Settings::getS16NoEx(const std::string &name, s16 &val) const
654 {
655         try {
656                 val = getS16(name);
657                 return true;
658         } catch (SettingNotFoundException &e) {
659                 return false;
660         }
661 }
662
663
664 bool Settings::getS32NoEx(const std::string &name, s32 &val) const
665 {
666         try {
667                 val = getS32(name);
668                 return true;
669         } catch (SettingNotFoundException &e) {
670                 return false;
671         }
672 }
673
674
675 bool Settings::getU64NoEx(const std::string &name, u64 &val) const
676 {
677         try {
678                 val = getU64(name);
679                 return true;
680         } catch (SettingNotFoundException &e) {
681                 return false;
682         }
683 }
684
685
686 bool Settings::getV2FNoEx(const std::string &name, v2f &val) const
687 {
688         try {
689                 val = getV2F(name);
690                 return true;
691         } catch (SettingNotFoundException &e) {
692                 return false;
693         }
694 }
695
696
697 bool Settings::getV3FNoEx(const std::string &name, v3f &val) const
698 {
699         try {
700                 val = getV3F(name);
701                 return true;
702         } catch (SettingNotFoundException &e) {
703                 return false;
704         }
705 }
706
707
708 // N.B. getFlagStrNoEx() does not set val, but merely modifies it.  Thus,
709 // val must be initialized before using getFlagStrNoEx().  The intention of
710 // this is to simplify modifying a flags field from a default value.
711 bool Settings::getFlagStrNoEx(const std::string &name, u32 &val,
712         FlagDesc *flagdesc) const
713 {
714         try {
715                 u32 flags, flagmask;
716
717                 flags = getFlagStr(name, flagdesc, &flagmask);
718
719                 val &= ~flagmask;
720                 val |=  flags;
721
722                 return true;
723         } catch (SettingNotFoundException &e) {
724                 return false;
725         }
726 }
727
728
729 /***********
730  * Setters *
731  ***********/
732
733 bool Settings::setEntry(const std::string &name, const void *data,
734         bool set_group, bool set_default)
735 {
736         Settings *old_group = NULL;
737
738         if (!checkNameValid(name))
739                 return false;
740         if (!set_group && !checkValueValid(*(const std::string *)data))
741                 return false;
742
743         {
744                 JMutexAutoLock lock(m_mutex);
745
746                 SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name];
747                 old_group = entry.group;
748
749                 entry.value    = set_group ? "" : *(const std::string *)data;
750                 entry.group    = set_group ? *(Settings **)data : NULL;
751                 entry.is_group = set_group;
752         }
753
754         delete old_group;
755
756         return true;
757 }
758
759
760 bool Settings::set(const std::string &name, const std::string &value)
761 {
762         if (!setEntry(name, &value, false, false))
763                 return false;
764
765         doCallbacks(name);
766         return true;
767 }
768
769
770 bool Settings::setDefault(const std::string &name, const std::string &value)
771 {
772         return setEntry(name, &value, false, true);
773 }
774
775
776 bool Settings::setGroup(const std::string &name, Settings *group)
777 {
778         return setEntry(name, &group, true, false);
779 }
780
781
782 bool Settings::setGroupDefault(const std::string &name, Settings *group)
783 {
784         return setEntry(name, &group, true, true);
785 }
786
787
788 bool Settings::setBool(const std::string &name, bool value)
789 {
790         return set(name, value ? "true" : "false");
791 }
792
793
794 bool Settings::setS16(const std::string &name, s16 value)
795 {
796         return set(name, itos(value));
797 }
798
799
800 bool Settings::setU16(const std::string &name, u16 value)
801 {
802         return set(name, itos(value));
803 }
804
805
806 bool Settings::setS32(const std::string &name, s32 value)
807 {
808         return set(name, itos(value));
809 }
810
811
812 bool Settings::setU64(const std::string &name, u64 value)
813 {
814         std::ostringstream os;
815         os << value;
816         return set(name, os.str());
817 }
818
819
820 bool Settings::setFloat(const std::string &name, float value)
821 {
822         return set(name, ftos(value));
823 }
824
825
826 bool Settings::setV2F(const std::string &name, v2f value)
827 {
828         std::ostringstream os;
829         os << "(" << value.X << "," << value.Y << ")";
830         return set(name, os.str());
831 }
832
833
834 bool Settings::setV3F(const std::string &name, v3f value)
835 {
836         std::ostringstream os;
837         os << "(" << value.X << "," << value.Y << "," << value.Z << ")";
838         return set(name, os.str());
839 }
840
841
842 bool Settings::setFlagStr(const std::string &name, u32 flags,
843         const FlagDesc *flagdesc, u32 flagmask)
844 {
845         return set(name, writeFlagString(flags, flagdesc, flagmask));
846 }
847
848
849 bool Settings::setStruct(const std::string &name, const std::string &format,
850         void *value)
851 {
852         std::string structstr;
853         if (!serializeStructToString(&structstr, format, value))
854                 return false;
855
856         return set(name, structstr);
857 }
858
859
860 bool Settings::setNoiseParams(const std::string &name,
861         const NoiseParams &np, bool set_default)
862 {
863         Settings *group = new Settings;
864
865         group->setFloat("offset",      np.offset);
866         group->setFloat("scale",       np.scale);
867         group->setV3F("spread",        np.spread);
868         group->setS32("seed",          np.seed);
869         group->setU16("octaves",       np.octaves);
870         group->setFloat("persistence", np.persist);
871         group->setFloat("lacunarity",  np.lacunarity);
872         group->setFlagStr("flags",     np.flags, flagdesc_noiseparams, np.flags);
873
874         return setEntry(name, &group, true, set_default);
875 }
876
877
878 bool Settings::remove(const std::string &name)
879 {
880         JMutexAutoLock lock(m_mutex);
881
882         delete m_settings[name].group;
883         return m_settings.erase(name);
884 }
885
886
887 void Settings::clear()
888 {
889         JMutexAutoLock lock(m_mutex);
890         clearNoLock();
891 }
892
893 void Settings::clearDefaults()
894 {
895         JMutexAutoLock lock(m_mutex);
896         clearDefaultsNoLock();
897 }
898
899 void Settings::updateValue(const Settings &other, const std::string &name)
900 {
901         if (&other == this)
902                 return;
903
904         JMutexAutoLock lock(m_mutex);
905
906         try {
907                 std::string val = other.get(name);
908
909                 m_settings[name] = val;
910         } catch (SettingNotFoundException &e) {
911         }
912 }
913
914
915 void Settings::update(const Settings &other)
916 {
917         if (&other == this)
918                 return;
919
920         JMutexAutoLock lock(m_mutex);
921         JMutexAutoLock lock2(other.m_mutex);
922
923         updateNoLock(other);
924 }
925
926
927 SettingsParseEvent Settings::parseConfigObject(const std::string &line,
928         const std::string &end, std::string &name, std::string &value)
929 {
930         std::string trimmed_line = trim(line);
931
932         if (trimmed_line.empty())
933                 return SPE_NONE;
934         if (trimmed_line[0] == '#')
935                 return SPE_COMMENT;
936         if (trimmed_line == end)
937                 return SPE_END;
938
939         size_t pos = trimmed_line.find('=');
940         if (pos == std::string::npos)
941                 return SPE_INVALID;
942
943         name  = trim(trimmed_line.substr(0, pos));
944         value = trim(trimmed_line.substr(pos + 1));
945
946         if (value == "{")
947                 return SPE_GROUP;
948         if (value == "\"\"\"")
949                 return SPE_MULTILINE;
950
951         return SPE_KVPAIR;
952 }
953
954
955 void Settings::updateNoLock(const Settings &other)
956 {
957         m_settings.insert(other.m_settings.begin(), other.m_settings.end());
958         m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
959 }
960
961
962 void Settings::clearNoLock()
963 {
964         std::map<std::string, SettingsEntry>::const_iterator it;
965         for (it = m_settings.begin(); it != m_settings.end(); ++it)
966                 delete it->second.group;
967         m_settings.clear();
968
969         clearDefaultsNoLock();
970 }
971
972 void Settings::clearDefaultsNoLock()
973 {
974         std::map<std::string, SettingsEntry>::const_iterator it;
975         for (it = m_defaults.begin(); it != m_defaults.end(); ++it)
976                 delete it->second.group;
977         m_defaults.clear();
978 }
979
980
981 void Settings::registerChangedCallback(std::string name,
982         setting_changed_callback cbf, void *userdata)
983 {
984         JMutexAutoLock lock(m_callbackMutex);
985         m_callbacks[name].push_back(std::make_pair(cbf, userdata));
986 }
987
988 void Settings::deregisterChangedCallback(std::string name, setting_changed_callback cbf, void *userdata)
989 {
990         JMutexAutoLock lock(m_callbackMutex);
991         std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
992         if (iterToVector != m_callbacks.end())
993         {
994                 std::vector<std::pair<setting_changed_callback, void*> > &vector = iterToVector->second;
995
996                 std::vector<std::pair<setting_changed_callback, void*> >::iterator position =
997                         std::find(vector.begin(), vector.end(), std::make_pair(cbf, userdata));
998
999                 if (position != vector.end())
1000                         vector.erase(position);
1001         }
1002 }
1003
1004 void Settings::doCallbacks(const std::string name)
1005 {
1006         JMutexAutoLock lock(m_callbackMutex);
1007         std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
1008         if (iterToVector != m_callbacks.end())
1009         {
1010                 std::vector<std::pair<setting_changed_callback, void*> >::iterator iter;
1011                 for (iter = iterToVector->second.begin(); iter != iterToVector->second.end(); iter++)
1012                 {
1013                         (iter->first)(name, iter->second);
1014                 }
1015         }
1016 }