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