Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/config/ConfigFile.cc @ 10951

Last change on this file since 10951 was 10624, checked in by landauf, 9 years ago

merged branch core7 back to trunk

  • Property svn:eol-style set to native
File size: 11.6 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/**
30    @file
31    @brief Implementation of ConfigFile.
32*/
33
34#include "ConfigFile.h"
35
36#include <boost/filesystem.hpp>
37
38#include "util/Convert.h"
39#include "util/StringUtils.h"
40#include "core/ConfigurablePaths.h"
41#include "ConfigFileEntryComment.h"
42#include "ConfigFileEntryValue.h"
43
44namespace orxonox
45{
46    ////////////////
47    // ConfigFile //
48    ////////////////
49
50    const char* ConfigFile::DEFAULT_CONFIG_FOLDER = "defaultConfig";
51
52    /**
53        @brief Constructor: Initializes the config file.
54        @param filename The file-name of this config file
55        @param bCopyFallbackFile If true, the default config file is copied into the config-directory before loading the file
56    */
57    ConfigFile::ConfigFile(const std::string& filename, bool bCopyFallbackFile)
58        : filename_(filename)
59        , bCopyFallbackFile_(bCopyFallbackFile)
60        , bUpdated_(false)
61    {
62    }
63
64    /**
65        @brief Destructor: Deletes all sections and entries.
66    */
67    ConfigFile::~ConfigFile()
68    {
69        this->clear();
70    }
71
72    /**
73        @brief Loads the config file from the hard-disk and reads the sections and their values.
74    */
75    void ConfigFile::load()
76    {
77        // Be sure we start from new in the memory
78        this->clear();
79
80        boost::filesystem::path filepath(this->filename_);
81        if (!filepath.is_complete())
82        {
83            filepath = ConfigurablePaths::getConfigPath() / filepath;
84            if (this->bCopyFallbackFile_)
85            {
86                // Look for default file in the data folder
87                if (!boost::filesystem::exists(filepath))
88                {
89                    boost::filesystem::path defaultFilepath(ConfigurablePaths::getDataPath() / DEFAULT_CONFIG_FOLDER / this->filename_);
90                    if (boost::filesystem::exists(defaultFilepath))
91                    {
92                        // Try to copy default file from the data folder
93                        try
94                        {
95                            boost::filesystem::copy_file(defaultFilepath, filepath);
96                            orxout(internal_info, context::config) << "Copied " << this->filename_ << " from the default config folder." << endl;
97                        }
98                        catch (const boost::filesystem::filesystem_error& ex)
99                        { orxout(user_error, context::config) << "Error in ConfigFile: " << ex.what() << endl; }
100                    }
101                }
102            }
103        }
104
105        // Open the file
106        std::ifstream file;
107        file.open(filepath.string().c_str(), std::fstream::in);
108        if (file.is_open())
109        {
110            ConfigFileSection* newsection = 0;
111
112            while (file.good() && !file.eof())
113            {
114                std::string line;
115                std::getline(file, line);
116
117                const std::string& temp = getStripped(line);
118                if (!isEmpty(temp) && !isComment(temp))
119                {
120                    size_t   pos1 = temp.find('[');
121                    if (pos1 == 0) pos1 = line.find('['); else pos1 = std::string::npos;
122                    size_t   pos2 = line.find(']');
123
124                    if (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1)
125                    {
126                        // New section
127                        const std::string& comment = line.substr(pos2 + 1);
128                        if (isComment(comment))
129                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1), comment);
130                        else
131                            newsection = new ConfigFileSection(line.substr(pos1 + 1, pos2 - pos1 - 1));
132                        this->sections_.insert(this->sections_.end(), newsection);
133                        continue;
134                    }
135                }
136
137                if (newsection != 0)
138                {
139                    if (isComment(line))
140                    {
141                        // New comment
142                        newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryComment(removeTrailingWhitespaces(line)));
143                        continue;
144                    }
145                    else
146                    {
147                        size_t pos1 = line.find('=');
148
149                        if (pos1 != std::string::npos && pos1 > 0)
150                        {
151                            // New entry
152                            size_t pos2 = line.find('[');
153                            size_t pos3 = line.find(']');
154                            size_t commentposition = getNextCommentPosition(line, pos1 + 1);
155                            while (isBetweenQuotes(line, commentposition))
156                            {
157                                commentposition = getNextCommentPosition(line, commentposition + 1);
158                            }
159                            std::string value, comment;
160                            if (commentposition == std::string::npos)
161                            {
162                                value = line.substr(pos1 + 1);
163                            }
164                            else
165                            {
166                                value = line.substr(pos1 + 1, commentposition - pos1 - 1);
167                                comment = removeTrailingWhitespaces(line.substr(commentposition));
168                            }
169
170                            value = removeTrailingWhitespaces(value);
171                            value = removeSlashes(value);
172
173                            if (pos2 != std::string::npos && pos3 != std::string::npos && pos3 > pos2 + 1)
174                            {
175                                // There might be an array index
176                                unsigned int index = 0;
177                                if (convertValue(&index, line.substr(pos2 + 1, pos3 - pos2 - 1)))
178                                {
179                                    // New array
180                                    std::list<ConfigFileEntry*>::iterator it = newsection->getOrCreateEntryIterator(getStripped(line.substr(0, pos2)), index, value, false);
181                                    (*it)->setValue(value);
182                                    (*it)->setComment(comment);
183                                    continue;
184                                }
185                            }
186
187                            // New value
188                            newsection->getEntries().insert(newsection->getEntries().end(), new ConfigFileEntryValue(getStripped(line.substr(0, pos1)), value, false, comment));
189                            continue;
190                        }
191                    }
192                }
193            }
194
195            file.close();
196
197            orxout(internal_info, context::config) << "Loaded config file \"" << this->filename_ << "\"." << endl;
198
199            // DO NOT save the file --> we can open supposedly read only config files
200        } // end file.is_open()
201    }
202
203    /**
204        @brief Writes the sections and values to the hard-disk.
205    */
206    void ConfigFile::save() const
207    {
208        this->saveAs(this->filename_);
209    }
210
211    /**
212        @brief Writes the sections and values to a given file on the hard-disk.
213    */
214    void ConfigFile::saveAs(const std::string& filename) const
215    {
216        boost::filesystem::path filepath(filename);
217        if (!filepath.is_complete())
218            filepath = ConfigurablePaths::getConfigPath() / filename;
219        std::ofstream file;
220        file.open(filepath.string().c_str(), std::fstream::out);
221        file.setf(std::ios::fixed, std::ios::floatfield);
222        file.precision(6);
223
224        if (!file.is_open())
225        {
226            orxout(user_error, context::config) << "Couldn't open config-file \"" << filename << "\"." << endl;
227            return;
228        }
229
230        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
231        {
232            file << (*it)->getFileEntry() << endl;
233
234            for (std::list<ConfigFileEntry*>::const_iterator it_entries = (*it)->getEntriesBegin(); it_entries != (*it)->getEntriesEnd(); ++it_entries)
235                file << (*it_entries)->getFileEntry() << endl;
236
237            file << endl;
238        }
239
240        file.close();
241
242        orxout(verbose, context::config) << "Saved config file \"" << filename << "\"." << endl;
243    }
244
245    /**
246        @brief Deletes all sections (which again delete all their values) and clears the list of sections.
247    */
248    void ConfigFile::clear()
249    {
250        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); )
251            delete (*(it++));
252        this->sections_.clear();
253    }
254
255    /**
256        @brief Deletes all elements of a config vector if their index is greater or equal to @a startindex.
257
258        @param section      The name of the section
259        @param name         The name of the vector
260        @param startindex   The index of the first element that will be deleted
261    */
262    void ConfigFile::deleteVectorEntries(const std::string& section, const std::string& name, unsigned int startindex)
263    {
264        if (ConfigFileSection* sectionPtr = this->getSection(section))
265        {
266            sectionPtr->deleteVectorEntries(name, startindex);
267            this->save();
268        }
269    }
270
271    /**
272        @brief Returns a pointer to the section with given name (or NULL if the section doesn't exist).
273    */
274    ConfigFileSection* ConfigFile::getSection(const std::string& section) const
275    {
276        for (std::list<ConfigFileSection*>::const_iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
277            if ((*it)->getName() == section)
278                return (*it);
279        return NULL;
280    }
281
282    /**
283        @brief Returns a pointer to the section with given name. If it doesn't exist, the section is created.
284    */
285    ConfigFileSection* ConfigFile::getOrCreateSection(const std::string& section)
286    {
287        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
288            if ((*it)->getName() == section)
289                return (*it);
290
291        this->bUpdated_ = true;
292
293        return (*this->sections_.insert(this->sections_.end(), new ConfigFileSection(section)));
294    }
295
296    /**
297        @brief Saves the config file if it was updated (or if any of its sections were updated).
298    */
299    void ConfigFile::saveIfUpdated()
300    {
301        bool sectionsUpdated = false;
302
303        for (std::list<ConfigFileSection*>::iterator it = this->sections_.begin(); it != this->sections_.end(); ++it)
304        {
305            if ((*it)->bUpdated_)
306            {
307                sectionsUpdated = true;
308                (*it)->bUpdated_ = false;
309            }
310        }
311
312        if (this->bUpdated_ || sectionsUpdated)
313        {
314            this->bUpdated_ = false;
315            this->save();
316        }
317    }
318}
Note: See TracBrowser for help on using the repository browser.