Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem3/src/core/ConfigFileManager.cc @ 2731

Last change on this file since 2731 was 2702, checked in by rgrieder, 16 years ago

Completed work on installation:

  • The CMake switch INSTALL_COPYABLE tells whether you will be able to move the installed directory or not. If TRUE then all folders, including log and config directory, will be put into the CMAKE_INSTALL_PREFIX. Furthermore, relative paths are used, which get resolved at run time.
  • If INSTALL_COPYABLE is set to FALSE, the standard operating system directories will be used. That also means on Windows files get written to the Application Data/.orxonox folder instead of Program Files/Orxonox
  • Default configuration is INSTALL_COPYABLE=TRUE for Windows and FALSE for Unix
  • Split OrxonoxConfig.h.in in two to avoid complete recompiles when changing only a path or INSTALL_COPYABLE
  • Added a global constant character: CP_SLASH which stands for cross platform slash, meaning '/' on Unix and '
    ' on Windows
  • Core class now has methods getFooPath(), getFooPathString() and getFooPathPOSIXString() where Foo can be Media, Log or Config
  • getFooPathPOSIXString() will always return a directory formatted with slashes, even on Windows
  • getFooPath() returns a reference to the boost::filesystem::path
  • boost/filesystem.hpp does not get included to Core.h because it has a very large rat tail
  • The platform specific directory stuff gets done in Core::postMainInitialisation()
  • Adjusted all classes using the media path
  • Property svn:eol-style set to native
File size: 21.3 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "ConfigFileManager.h"
30
31#include <cassert>
32#include <boost/filesystem.hpp>
33
34#include "util/Convert.h"
35#include "util/String.h"
36#include "ConsoleCommand.h"
37#include "ConfigValueContainer.h"
38#include "Core.h"
39
40namespace orxonox
41{
42    const int CONFIG_FILE_MAX_LINELENGHT  = 1024;
43    const char* const DEFAULT_CONFIG_FILE = "default.ini";
44
45    ConfigFileManager* ConfigFileManager::singletonRef_s = 0;
46
47    SetConsoleCommandShortcutExtern(config).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
48    SetConsoleCommandShortcutExtern(tconfig).argumentCompleter(0, autocompletion::configvalueclasses()).argumentCompleter(1, autocompletion::configvalues()).argumentCompleter(2, autocompletion::configvalue());
49    SetConsoleCommandShortcutExtern(reloadConfig);
50    SetConsoleCommandShortcutExtern(cleanConfig);
51    SetConsoleCommandShortcutExtern(loadSettings).argumentCompleter(0, autocompletion::files());
52
53    bool config(const std::string& classname, const std::string& varname, const std::string& value)
54    {
55        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
56        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
57        {
58            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
59            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
60                return (*variable).second->set(value);
61        }
62        return false;
63    }
64
65    bool tconfig(const std::string& classname, const std::string& varname, const std::string& value)
66    {
67        std::map<std::string, Identifier*>::const_iterator identifier = Identifier::getLowercaseIdentifierMap().find(getLowercase(classname));
68        if (identifier != Identifier::getLowercaseIdentifierMapEnd())
69        {
70            std::map<std::string, ConfigValueContainer*>::const_iterator variable = (*identifier).second->getLowercaseConfigValueMap().find(getLowercase(varname));
71            if (variable != (*identifier).second->getLowercaseConfigValueMapEnd())
72                return (*variable).second->tset(value);
73        }
74        return false;
75    }
76
77    void reloadConfig()
78    {
79        ConfigFileManager::getInstance().load();
80    }
81
82    void cleanConfig()
83    {
84        ConfigFileManager::getInstance().clean(false);
85    }
86
87    void loadSettings(const std::string& filename)
88    {
89        ConfigFileManager::getInstance().setFilename(ConfigFileType::Settings, filename);
90    }
91
92    //////////////////////////
93    // ConfigFileEntryValue //
94    //////////////////////////
95
96    void ConfigFileEntryValue::setValue(const std::string& value)
97    {
98        if (!this->bString_)
99            this->value_ = value;
100        else
101            this->value_ = "\"" + addSlashes(stripEnclosingQuotes(value)) + "\"";
102    }
103
104    std::string ConfigFileEntryValue::getValue() const
105    {
106        if (!this->bString_)
107            return this->value_;
108        else
109            return removeSlashes(stripEnclosingQuotes(this->value_));
110    }
111
112    std::string ConfigFileEntryValue::getFileEntry() const
113    {
114        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
115            return (this->name_ + "=" + this->value_);
116        else
117            return (this->name_ + "=" + this->value_ + " " + this->additionalComment_);
118    }
119
120
121    ////////////////////////////////
122    // ConfigFileEntryVectorValue //
123    ////////////////////////////////
124    std::string ConfigFileEntryVectorValue::getFileEntry() const
125    {
126        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
127            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]" + "=" + this->value_);
128        else
129            return (this->name_ + "[" + getConvertedValue<unsigned int, std::string>(this->index_, "0") + "]=" + this->value_ + " " + this->additionalComment_);
130    }
131
132
133    ///////////////////////
134    // ConfigFileSection //
135    ///////////////////////
136    ConfigFileSection::~ConfigFileSection()
137    {
138        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
139            delete (*(it++));
140    }
141
142    void ConfigFileSection::deleteVectorEntries(const std::string& name, unsigned int startindex)
143    {
144        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); )
145        {
146            if (((*it)->getName() == name) && ((*it)->getIndex() >= startindex))
147            {
148                delete (*it);
149                this->entries_.erase(it++);
150            }
151            else
152            {
153                ++it;
154            }
155        }
156    }
157
158    unsigned int ConfigFileSection::getVectorSize(const std::string& name)
159    {
160        unsigned int size = 0;
161        for (std::list<ConfigFileEntry*>::const_iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
162            if ((*it)->getName() == name)
163                if ((*it)->getIndex() > size)
164                    size = (*it)->getIndex();
165        if (size == 0)
166            return 0;
167        else
168            return (size + 1);
169    }
170
171    std::string ConfigFileSection::getFileEntry() const
172    {
173        if (this->additionalComment_ == "" || this->additionalComment_.size() == 0)
174            return ("[" + this->name_ + "]");
175        else
176            return ("[" + this->name_ + "] " + this->additionalComment_);
177    }
178
179    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, const std::string& fallback, bool bString)
180    {
181        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
182        {
183            if ((*it)->getName() == name)
184            {
185                (*it)->setString(bString);
186                return it;
187            }
188        }
189
190        this->bUpdated_ = true;
191
192        return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryValue(name, fallback, bString)));
193    }
194
195    std::list<ConfigFileEntry*>::iterator ConfigFileSection::getEntryIterator(const std::string& name, unsigned int index, const std::string& fallback, bool bString)
196    {
197        for (std::list<ConfigFileEntry*>::iterator it = this->entries_.begin(); it != this->entries_.end(); ++it)
198        {
199            if (((*it)->getName() == name) && ((*it)->getIndex() == index))
200            {
201                (*it)->setString(bString);
202                return it;
203            }
204        }
205
206        this->bUpdated_ = true;
207
208        if (index == 0)
209            return this->entries_.insert(this->entries_.end(), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
210        else
211            return this->entries_.insert(++this->getEntryIterator(name, index - 1, "", bString), (ConfigFileEntry*)(new ConfigFileEntryVectorValue(name, index, fallback, bString)));
212    }
213
214
215    ////////////////
216    // ConfigFile //
217    ////////////////
218    ConfigFile::~ConfigFile()
219    {
220        this->clear();
221    }
222
223    void ConfigFile::load(bool bCreateIfNotExisting)
224    {
225        // Be sure we start from new in the memory
226        this->clear();
227
228        // Get default file if necessary and available
229        boost::filesystem::path filepath(Core::getConfigPath() / this->filename_);
230        if (!boost::filesystem::exists(filepath))
231        {
232            // Try to get default one from the media folder
233            boost::filesystem::path defaultFilepath(Core::getMediaPath() / "defaultConfig" / this->filename_);
234            if (boost::filesystem::exists(defaultFilepath))
235            {
236                boost::filesystem::copy_file(defaultFilepath, filepath);
237            }
238        }
239
240        // Open the file
241        std::ifstream file;
242        file.open(filepath.file_string().c_str(), std::fstream::in);
243        if (file.is_open())
244        {
245
246            char linearray[CONFIG_FILE_MAX_LINELENGHT];
247
248            ConfigFileSection* newsection = 0;
249
250            while (file.good() && !file.eof())
251            {
252                file.getline(linearray, CONFIG_FILE_MAX_LINELENGHT);
253
254                std::string line = std::string(linearray);
255
256                std::string temp = getStripped(line);
257                if (!isEmpty(temp) && !isComment(temp))
258                {
259                    size_t   pos1 = temp.find('[');
260                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
261                    size_t   pos2 = line.find(']');
262
263                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
264                    {
265                        // New section
266                        std::string comment = line.substr(pos2 + 1);
267                        if (isComment(comment))
268                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
269                        else
270                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
271                        this->sections_.insert(this->sections_.end(), newsection);
272                        continue;
273                    }
274                }
275
276                if (newsection != 0)
277                {
278                    if (isComment(line))
279                    {
280                        // New comment
281                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
282                        continue;
283                    }
284                    else
285                    {
286                        size_t pos1 = line.find('=');
287
288                        if (pos1 != std::string::npos && pos1 > 0)
289                        {
290                            // New entry
291                            size_t pos2 = line.find('[');
292                            size_t pos3 = line.find(']');
293                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
294                            while (isBetweenQuotes(line, commentposition))
295                            {
296                                commentposition = getNextCommentPosition(line, commentposition + 1);
297                            }
298                            std::string value = "", comment = "";
299                            if (commentposition == std::string::npos)
300                            {
301                                value = removeTrailingWhitespaces(line.substr(pos1 + 1));
302                            }
303                            else
304                            {
305                                value = removeTrailingWhitespaces(line.substr(pos1 + 1, commentposition - pos1 - 1));
306                                comment = removeTrailingWhitespaces(line.substr(commentposition));
307                            }
308
309                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
310                            {
311                                // There might be an array index
312                                unsigned int index = 0;
313                                if (ConvertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
314                                {
315                                    // New array
316                                    std::list<ConfigFileEntry*>::iterator it = newsection->getEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
317                                    (*it)->setValue(value);
318                                    (*it)->setComment(comment);
319                                    continue;
320                                }
321                            }
322
323                            // New value
324                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
325                            continue;
326                        }
327                    }
328                }
329            }
330
331            file.close();
332
333            COUT(3) << "Loaded config file \"" << this->filename_ << "\"." << std::endl;
334
335            // Save the file in case something changed (like stripped whitespaces)
336            this->save();
337
338            // Update all ConfigValueContainers
339            this->updateConfigValues();
340        } // end file.is_open()
341    }
342
343    void ConfigFile::save() const
344    {
345        boost::filesystem::path filepath(Core::getConfigPath() / this->filename_);
346
347        std::ofstream file;
348        file.open(filepath.file_string().c_str(), std::fstream::out);
349        file.setf(std::ios::fixed, std::ios::floatfield);
350        file.precision(6);
351
352        if (!file.is_open())
353        {
354            COUT(1) << "An error occurred in ConfigFileManager.cc:" << std::endl;
355            COUT(1) << "Error: Couldn't open config-file \"" << this->filename_ << "\"." << std::endl;
356            return;
357        }
358
359        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
360        {
361            file << (*it)->getFileEntry() << std::endl;
362
363            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
364            {
365                file << (*it_entries)->getFileEntry() << std::endl;
366            }
367
368            file << std::endl;
369        }
370
371        file.close();
372
373        COUT(4) << "Saved config file \"" << this->filename_ << "\"." << std::endl;
374    }
375
376    void ConfigFile::saveAs(const std::string& filename)
377    {
378        std::string temp = this->filename_;
379        this->filename_ = filename;
380        this->save();
381        this->filename_ = temp;
382    }
383
384    void ConfigFile::clean(bool bCleanComments)
385    {
386        for (std::list<ConfigFileSection*>::iterator it1 = this->sections_.begin(); it1 != this->sections_.end(); )
387        {
388            std::map<std::string, Identifier*>::const_iterator it2 = Identifier::getIdentifierMap().find((*it1)->getName());
389            if (it2 != Identifier::getIdentifierMapEnd() && (*it2).second->hasConfigValues())
390            {
391                // The section exists, delete comment
392                if (bCleanComments)
393                    (*it1)->setComment("");
394                for (std::list<ConfigFileEntry*>::iterator it3 = (*it1)->entries_.begin(); it3 != (*it1)->entries_.end(); )
395                {
396                    std::map<std::string, ConfigValueContainer*>::const_iterator it4 = (*it2).second->getConfigValueMap().find((*it3)->getName());
397                    if (it4 != (*it2).second->getConfigValueMapEnd())
398                    {
399                        // The config-value exists, delete comment
400                        if (bCleanComments)
401                            (*it3)->setComment("");
402                        ++it3;
403                    }
404                    else
405                    {
406                        // The config-value doesn't exist
407                        delete (*it3);
408                        (*it1)->entries_.erase(it3++);
409                    }
410                }
411                ++it1;
412            }
413            else
414            {
415                // The section doesn't exist
416                delete (*it1);
417                this->sections_.erase(it1++);
418            }
419        }
420
421        // Save the file
422        this->save();
423    }
424
425    void ConfigFile::clear()
426    {
427        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
428            delete (*(it++));
429        this->sections_.clear();
430    }
431
432    ConfigFileSection* ConfigFile::getSection(const std::string& section)
433    {
434        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
435            if ((*it)->getName() == section)
436                return (*it);
437
438        this->bUpdated_ = true;
439
440        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
441    }
442
443    void ConfigFile::saveIfUpdated()
444    {
445        bool sectionsUpdated = false;
446
447        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
448        {
449            if ((*it)->bUpdated_)
450            {
451                sectionsUpdated = true;
452                (*it)->bUpdated_ = false;
453            }
454        }
455
456        if (this->bUpdated_ || sectionsUpdated)
457        {
458            this->bUpdated_ = false;
459            this->save();
460        }
461    }
462
463    void ConfigFile::updateConfigValues()
464    {
465        if (this->type_ == ConfigFileType::Settings)
466        {
467            for (std::map<std::string, Identifier*>::const_iterator it = Identifier::getIdentifierMapBegin(); it != Identifier::getIdentifierMapEnd(); ++it)
468            {
469                if (it->second->hasConfigValues())
470                {
471                    for (std::map<std::string, ConfigValueContainer*>::const_iterator it2 = (*it).second->getConfigValueMapBegin(); it2 != (*it).second->getConfigValueMapEnd(); ++it2)
472                        it2->second->update();
473
474                    it->second->updateConfigValues();
475                }
476            }
477        }
478    }
479
480
481    ///////////////////////
482    // ConfigFileManager //
483    ///////////////////////
484
485    ConfigFileManager::ConfigFileManager()
486         : mininmalFreeType_(ConfigFileType::numberOfReservedTypes)
487    {
488        assert(singletonRef_s == 0);
489        singletonRef_s = this;
490    }
491
492    ConfigFileManager::~ConfigFileManager()
493    {
494        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); )
495            delete (it++)->second;
496
497        assert(singletonRef_s != 0);
498        singletonRef_s = 0;
499    }
500
501    void ConfigFileManager::setFilename(ConfigFileType type, const std::string& filename)
502    {
503        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
504        if (it != this->configFiles_.end())
505        {
506            assert(it->second);
507            delete it->second;
508        }
509        this->configFiles_[type] = new ConfigFile(filename, type);
510        this->load(type);
511    }
512
513    void ConfigFileManager::load()
514    {
515        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
516            it->second->load();
517    }
518
519    void ConfigFileManager::save()
520    {
521        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
522            it->second->save();
523    }
524
525    void ConfigFileManager::clean(bool bCleanComments)
526    {
527        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
528            this->clean(it->first, bCleanComments);
529    }
530
531    void ConfigFileManager::load(ConfigFileType type)
532    {
533        this->getFile(type)->load();
534    }
535
536    void ConfigFileManager::save(ConfigFileType type)
537    {
538        this->getFile(type)->save();
539    }
540
541    void ConfigFileManager::saveAs(ConfigFileType type, const std::string& saveFilename)
542    {
543        this->getFile(type)->saveAs(saveFilename);
544    }
545
546    void ConfigFileManager::clean(ConfigFileType type, bool bCleanComments)
547    {
548        this->getFile(type)->clean(bCleanComments);
549    }
550
551    void ConfigFileManager::updateConfigValues()
552    {
553        for(std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.begin(); it != this->configFiles_.end(); ++it)
554            it->second->updateConfigValues();
555    }
556
557    void ConfigFileManager::updateConfigValues(ConfigFileType type)
558    {
559        this->getFile(type)->updateConfigValues();
560    }
561
562    const std::string& ConfigFileManager::getFilename(ConfigFileType type)
563    {
564        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
565        if (it != this->configFiles_.end())
566            return it->second->getFilename();
567        else
568            return BLANKSTRING;
569    }
570
571    ConfigFile* ConfigFileManager::getFile(ConfigFileType type)
572    {
573        std::map<ConfigFileType, ConfigFile*>::const_iterator it = this->configFiles_.find(type);
574        if (it != this->configFiles_.end())
575            return it->second;
576        else
577        {
578            COUT(1) << "ConfigFileManager: Can't find a config file for type with ID " << (int)type << std::endl;
579            COUT(1) << "Using " << DEFAULT_CONFIG_FILE << " file." << std::endl;
580            this->setFilename(type, DEFAULT_CONFIG_FILE);
581            return getFile(type);
582        }
583    }
584}
Note: See TracBrowser for help on using the repository browser.