Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11/src/libraries/core/config/ConfigFile.cc @ 10441

Last change on this file since 10441 was 10441, checked in by bknecht, 9 years ago

Turned on c++11 flag in compiler and fixed a bug where boost couldn't handle it.

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