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