Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11481 was 11071, checked in by landauf, 9 years ago

merged branch cpp11_v3 back to trunk

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