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