1 /*
2  *  This file is part of Dune Legacy.
3  *
4  *  Dune Legacy is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Dune Legacy is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Dune Legacy.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INIFILE_H
19 #define INIFILE_H
20 
21 #include <string>
22 #include <list>
23 #include <algorithm>
24 #include <SDL_rwops.h>
25 #include <SDL.h>
26 
27 #define INVALID_LINE (-1)
28 
29 //!  A class for reading and writing *.ini configuration files.
30 /*!
31     This class can be used to read or write to a *.ini file. An ini-File has a very simple format.<br>
32     Example:<br>
33         <br>
34         ; Comments start with ; or #<br>
35         ; start of the first section with name ""<br>
36         key1 = value1<br>
37         key2 = value2<br>
38         ; start of a section with name "Section1"<br>
39         [Section1]<br>
40         key3 = value3<br>
41         key4 = value4<br>
42     <br>
43     The section names and key names are treated case insensitive.
44 */
45 class INIFile
46 {
47 public:
48 
49     //\cond
50     class INIFileLine;
51     class Key;
52     class KeyIterator;
53     class Section;
54     class SectionIterator;
55 
56 
57     class INIFileLine
58     {
59     public:
60         INIFileLine(const std::string& completeLine, int lineNumber);
61 
getLineNumber()62         inline int getLineNumber() const { return line; };
63 
64         friend class INIFile;
65         friend class INIFile::Section;
66 
67     protected:
shiftLineNumber(int shift)68         inline void shiftLineNumber(int shift) {
69             INIFileLine* pCurrentLine = this;
70             while(pCurrentLine != nullptr) {
71                 pCurrentLine->line += shift;
72                 pCurrentLine = pCurrentLine->nextLine;
73             }
74         }
75 
76         std::string completeLine;
77         int line;
78         INIFileLine* nextLine;
79         INIFileLine* prevLine;
80     };
81 
82 
83     class Key : public INIFileLine
84     {
85     public:
86         Key(const std::string& completeLine, int lineNumber, int keystringbegin, int keystringlength, int valuestringbegin, int valuestringlength);
87         Key(const std::string& keyname, const std::string& value, bool bEscapeIfNeeded = true, bool bWhitespace = true);
88 
89         std::string getKeyName() const;
90         std::string getStringValue() const;
91         int getIntValue(int defaultValue = 0) const;
92         bool getBoolValue(bool defaultValue = false) const;
93         float getFloatValue(float defaultValue = 0.0f) const;
94         double getDoubleValue(double defaultValue = 0.0) const;
95 
96         void setStringValue(const std::string& newValue, bool bEscapeIfNeeded = true);
97         void setIntValue(int newValue);
98         void setBoolValue(bool newValue);
99         void setDoubleValue(double newValue);
100 
101         friend class INIFile;
102         friend class INIFile::KeyIterator;
103         friend class INIFile::Section;
104         friend class INIFile::SectionIterator;
105 
106     protected:
107         static bool escapingValueNeeded(const std::string& value);
108         static std::string escapeValue(const std::string& value);
109 
110         int keyStringBegin;
111         int keyStringLength;
112         int valueStringBegin;
113         int valueStringLength;
114         Key* nextKey;
115         Key* prevKey;
116     };
117 
118 
119     class KeyIterator
120     {
121     public:
KeyIterator()122         KeyIterator() : key(nullptr) {
123         }
124 
KeyIterator(Key * pKey)125         explicit KeyIterator(Key* pKey) : key(pKey) {
126         }
127 
128         Key& operator*() const {
129             return *key;
130         }
131 
132         Key* operator->() const {
133             return key;
134         }
135 
136         bool operator==(const KeyIterator& other) const {
137             return (key == other.key);
138         }
139 
140         bool operator!=(const KeyIterator& other) const {
141             return !(operator==(other));
142         }
143 
144         void operator++() {
145             if(key != nullptr) {
146                 key = key->nextKey;
147             }
148         }
149 
150     private:
151         Key* key;
152     };
153 
154 
155     class Section : public INIFileLine
156     {
157     public:
158         Section(const std::string& completeLine, int lineNumber, int sectionstringbegin, int sectionstringlength, bool bWhitespace = true);
159         Section(const std::string& sectionname, bool bWhitespace = true);
160 
161         std::string getSectionName() const;
162         KeyIterator begin() const;
163         KeyIterator end() const;
164 
165         bool hasKey(const std::string& key) const;
166         Key* getKey(const std::string& keyname) const;
167 
168         void setStringValue(const std::string& key, const std::string& newValue, bool bEscapeIfNeeded = true);
169         void setIntValue(const std::string& key, int newValue);
170         void setBoolValue(const std::string& key, bool newValue);
171         void setDoubleValue(const std::string& key, double newValue);
172 
173         friend class INIFile;
174         friend class INIFile::SectionIterator;
175 
176     protected:
177         void insertKey(Key* newKey);
178 
179         int sectionStringBegin;
180         int sectionStringLength;
181         Section* nextSection;
182         Section* prevSection;
183         Key* keyRoot;
184         bool bWhitespace;
185     };
186 
187 
188     class SectionIterator
189     {
190     public:
SectionIterator()191         SectionIterator() : section(nullptr) {
192         }
193 
SectionIterator(Section * pSection)194         explicit SectionIterator(Section* pSection) : section(pSection) {
195         }
196 
197         Section& operator*() const {
198             return *section;
199         }
200 
201         Section* operator->() const {
202             return section;
203         }
204 
205         bool operator==(const SectionIterator& other) const {
206             return (section == other.section);
207         }
208 
209         bool operator!=(const SectionIterator& other) const {
210             return !(operator==(other));
211         }
212 
213         void operator++() {
214             if(section != nullptr) {
215                 section = section->nextSection;
216             }
217         }
218 
219     private:
220         Section* section;
221     };
222     //\endcond
223 
224 
225 
226 public:
227 
228     INIFile(bool bWhitespace, const std::string& firstLineComment);
229     INIFile(const std::string& filename, bool bWhitespace = true);
230     INIFile(SDL_RWops * RWopsFile, bool bWhitespace = true);
231     INIFile(const INIFile& o) = delete;
232     ~INIFile();
233 
234     bool hasSection(const std::string& section) const;
235     const Section& getSection(const std::string& sectionname) const;
236     bool removeSection(const std::string& sectionname);
237     bool clearSection(const std::string& sectionname, bool bBlankLineAtSectionEnd = true);
238     bool hasKey(const std::string& section, const std::string& key) const;
239     const Key* getKey(const std::string& sectionname, const std::string& keyname) const;
240     bool removeKey(const std::string& section, const std::string& key);
241 
242     std::string getStringValue(const std::string& section, const std::string& key, const std::string& defaultValue = "") const;
243     int getIntValue(const std::string& section, const std::string& key, int defaultValue = 0) const;
244     bool getBoolValue(const std::string& section, const std::string& key, bool defaultValue = false) const;
245     float getFloatValue(const std::string& section, const std::string& key, float defaultValue = 0.0f) const;
246     double getDoubleValue(const std::string& section, const std::string& key, double defaultValue = 0.0) const;
247 
248     void setStringValue(const std::string& section, const std::string& key, const std::string& value, bool bEscapeIfNeeded = true);
249     void setIntValue(const std::string& section, const std::string& key, int value);
250     void setBoolValue(const std::string& section, const std::string& key, bool value);
251     void setDoubleValue(const std::string& section, const std::string& key, double value);
252 
253     SectionIterator begin() const;
254     SectionIterator end() const;
255 
256     KeyIterator begin(const std::string& section) const;
257     KeyIterator end(const std::string& section) const;
258 
259     bool saveChangesTo(const std::string& filename, bool bDOSLineEnding = false) const;
260     bool saveChangesTo(SDL_RWops * file, bool bDOSLineEnding = false) const;
261 
262 private:
263     INIFileLine* firstLine;
264     Section* sectionRoot;
265     bool bWhitespace;
266 
267     void flush() const;
268     void readfile(SDL_RWops * file);
269 
270     void insertSection(Section* newSection);
271 
272     const Section* getSectionInternal(const std::string& sectionname) const;
273     Section* getSectionOrCreate(const std::string& sectionname);
274 
275     static bool isValidSectionName(const std::string& sectionname);
276     static bool isValidKeyName(const std::string& keyname);
277 
278     static int getNextChar(const unsigned char* line, int startpos);
279     static int skipName(const unsigned char* line,int startpos);
280     static int skipValue(const unsigned char* line,int startpos);
281     static int skipKey(const unsigned char* line,int startpos);
282     static int getNextQuote(const unsigned char* line,int startpos);
283 
284     static bool isWhitespace(unsigned char s);
285     static bool isNormalChar(unsigned char s);
286 
287     static int strncicmp(const char *s1, const char *s2, size_t n);
288 };
289 
290 #endif // INIFILE_H
291 
292