Mercurial > code
annotate C++/Parser.cpp @ 187:600754c27c88
Update parser to style and remove useless stuff
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 26 Nov 2013 20:20:59 +0100 |
parents | 2bcdee0fe8d4 |
children | 1ffe6d4937b7 |
rev | line source |
---|---|
179 | 1 /* |
2 * Parser.h -- config file parser | |
3 * | |
4 * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <iostream> | |
20 #include <fstream> | |
21 | |
22 #include "Parser.h" | |
23 | |
24 /* -------------------------------------------------------- | |
25 * Option public members | |
26 * -------------------------------------------------------- */ | |
27 | |
28 bool operator==(const Option &o1, const Option &o2) | |
29 { | |
30 return o1.m_key == o2.m_key && | |
31 o1.m_value == o2.m_value; | |
32 } | |
33 | |
34 /* -------------------------------------------------------- | |
35 * Section public members | |
36 * -------------------------------------------------------- */ | |
37 | |
38 Section::Section() | |
180 | 39 : m_allowed(true) |
179 | 40 { |
41 } | |
42 | |
180 | 43 const std::string &Section::getName() const |
179 | 44 { |
180 | 45 return m_name; |
179 | 46 } |
47 | |
48 const std::string Section::findOption(const std::string &name) const | |
49 { | |
50 std::string ret; | |
51 | |
52 for (const Option &o : m_options) | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
53 if (o.m_key == name) { |
179 | 54 ret = o.m_value; |
55 break; | |
56 } | |
57 | |
58 return ret; | |
59 } | |
60 | |
180 | 61 template <> |
62 bool Section::getValue(const std::string &name) const | |
179 | 63 { |
64 bool result = false; | |
65 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
66 if (hasOption(name)) { |
179 | 67 std::string value = findOption(name); |
68 | |
69 if (value == "yes" || value == "true"|| value == "1") | |
70 result = true; | |
71 else if (value == "no" || value == "false" || value == "0") | |
72 result = false; | |
73 } | |
74 | |
75 return result; | |
76 } | |
77 | |
180 | 78 template <> |
79 int Section::getValue(const std::string &name) const | |
179 | 80 { |
81 int result = -1; | |
82 | |
180 | 83 if (hasOption(name)) |
179 | 84 result = atoi(findOption(name).c_str()); |
85 | |
86 return result; | |
87 } | |
88 | |
180 | 89 template <> |
90 std::string Section::getValue(const std::string &name) const | |
179 | 91 { |
92 std::string result; | |
93 | |
94 if (hasOption(name)) | |
95 result = findOption(name); | |
96 | |
97 return result; | |
98 } | |
99 | |
180 | 100 const std::vector<Option> &Section::getOptions() const |
179 | 101 { |
102 return m_options; | |
103 } | |
104 | |
105 bool Section::hasOption(const std::string &name) const | |
106 { | |
107 for (const Option &o : m_options) | |
108 if (o.m_key == name) | |
109 return true; | |
110 | |
111 return false; | |
112 } | |
113 | |
114 bool operator==(const Section &s1, const Section &s2) | |
115 { | |
116 if (s1.m_name != s2.m_name) | |
117 return false; | |
118 | |
119 return s1.m_options == s2.m_options; | |
120 } | |
121 | |
122 /* -------------------------------------------------------- | |
123 * Parser private members | |
124 * -------------------------------------------------------- */ | |
125 | |
126 void Parser::addSection(const std::string &name) | |
127 { | |
128 Section section; | |
129 | |
130 section.m_name = name; | |
131 section.m_allowed = true; | |
132 | |
133 m_sections.push_back(section); | |
134 } | |
135 | |
136 void Parser::addOption(const std::string &key, const std::string &value) | |
137 { | |
138 Option option; | |
139 Section ¤t = m_sections.back(); | |
140 | |
141 option.m_key = key; | |
142 option.m_value = value; | |
143 | |
144 current.m_options.push_back(option); | |
145 } | |
146 | |
147 void Parser::readSection(int lineno, const std::string &line) | |
148 { | |
149 size_t end; | |
150 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
151 if ((end = line.find_first_of(']')) != std::string::npos) { |
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
152 if (end > 1) { |
179 | 153 std::string name = line.substr(1, end - 1); |
154 | |
155 /* | |
156 * Check if we can add a section, if redefinition is | |
157 * disabled, we must disable the previous section so the | |
158 * further read options should not be enabled until | |
159 * a correct section is found again. | |
160 */ | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
161 if (hasSection(name) && (m_tuning & DisableRedefinition)) { |
179 | 162 if (!(m_tuning & DisableVerbosity)) |
163 log(lineno, name, "redefinition not allowed"); | |
164 m_sections.back().m_allowed = false; | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
165 } else |
179 | 166 addSection(name); |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
167 } else if (!(m_tuning & DisableVerbosity)) { |
179 | 168 /* |
169 * Do not add options at this step because it will | |
170 * corrupt the previous one. | |
171 */ | |
172 m_sections.back().m_allowed = false; | |
173 log(lineno, "", "empty section name"); | |
174 } | |
175 } | |
176 } | |
177 | |
178 void Parser::readOption(int lineno, const std::string &line) | |
179 { | |
180 size_t epos; | |
181 std::string key, value; | |
182 Section ¤t = m_sections.back(); | |
183 | |
184 // Error on last section? | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
185 if (!current.m_allowed) { |
179 | 186 /* |
187 * If it is the root section, this has been probably set by | |
188 * DisableRootSection flag, otherwise an error has occured | |
189 * so no need to log. | |
190 */ | |
191 if (current.m_name == "" && !(m_tuning == DisableVerbosity)) | |
192 log(lineno, "", "option not allowed in that scope"); | |
193 | |
194 return; | |
195 } | |
196 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
197 if ((epos = line.find_first_of('=')) == std::string::npos) { |
179 | 198 if (!(m_tuning & DisableVerbosity)) |
199 log(lineno, current.m_name, "missing `=' keyword"); | |
200 return; | |
201 } | |
202 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
203 if (epos > 0) { |
179 | 204 size_t i, begin, last; |
205 char c; | |
206 | |
207 key = line.substr(0, epos - 1); | |
208 value = line.substr(epos + 1); | |
209 | |
210 // clean option key | |
211 for (i = 0; !isspace(key[i]) && i < key.length(); ++i) | |
212 continue; | |
213 key = key.substr(0, i); | |
214 | |
215 // clean option value | |
216 for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin) | |
217 continue; | |
218 value = value.substr(begin); | |
219 | |
220 c = value[0]; | |
221 begin = 0; | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
222 if (c == '\'' || c == '"') { |
179 | 223 for (last = begin = 1; value[last] != c && last < value.length(); ++last) |
224 continue; | |
225 if (value[last] != c && !(m_tuning & DisableVerbosity)) | |
226 if (!(m_tuning & DisableVerbosity)) | |
227 log(lineno, current.m_name, "undisclosed std::string"); | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
228 } else { |
179 | 229 for (last = begin; !isspace(value[last]) && last < value.length(); ++last) |
230 continue; | |
231 } | |
232 | |
233 if (last - begin > 0) | |
234 value = value.substr(begin, last - begin); | |
235 else | |
236 value.clear(); | |
237 | |
238 // Add the option if the key is not empty | |
239 if (key.length() > 0) | |
240 addOption(key, value); | |
241 } | |
242 } | |
243 | |
244 void Parser::readLine(int lineno, const std::string &line) | |
245 { | |
246 size_t i; | |
247 std::string buffer; | |
248 | |
249 // Skip default spaces | |
250 for (i = 0; isspace(line[i]) && i < line.length(); ++i) | |
251 continue; | |
252 | |
253 buffer = line.substr(i); | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
254 if (buffer.length() > 0) { |
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
255 if (buffer[0] != m_commentChar) { |
179 | 256 if (buffer[0] == '[') |
257 readSection(lineno, buffer); | |
258 else | |
259 readOption(lineno, buffer); | |
260 } | |
261 } | |
262 } | |
263 | |
264 /* -------------------------------------------------------- | |
265 * Parser public methods | |
266 * -------------------------------------------------------- */ | |
267 | |
268 const char Parser::DEFAULT_COMMENT_CHAR = '#'; | |
269 | |
180 | 270 Parser::Parser() |
271 { | |
272 } | |
273 | |
179 | 274 Parser::Parser(const std::string &path, int tuning, char commentToken) |
180 | 275 : m_path(path) |
276 , m_tuning(tuning) | |
277 , m_commentChar(commentToken) | |
179 | 278 { |
279 Section root; | |
280 | |
281 // Add a default root section | |
282 root.m_name = ""; | |
283 root.m_allowed = (tuning & DisableRootSection) ? false : true; | |
284 | |
285 m_sections.push_back(root); | |
286 } | |
287 | |
288 Parser::~Parser() | |
289 { | |
290 } | |
291 | |
292 bool Parser::open() | |
293 { | |
294 std::ifstream file; | |
295 std::string line; | |
296 int lineno = 1; | |
297 | |
298 file.open(m_path.c_str()); | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
299 if (!file.is_open()) { |
179 | 300 m_error = "could not open file " + m_path; // XXX: add a real error |
301 return false; | |
302 } | |
303 | |
304 // Avoid use of C getline | |
180 | 305 while (std::getline(file, line)) |
179 | 306 readLine(lineno++, line); |
307 | |
308 file.close(); | |
309 | |
310 return true; | |
311 } | |
312 | |
180 | 313 const std::string &Parser::getError() const |
179 | 314 { |
315 return m_error; | |
316 } | |
317 | |
180 | 318 const std::vector<Section> &Parser::getSections() const |
179 | 319 { |
320 return m_sections; | |
321 } | |
322 | |
180 | 323 void Parser::findSections(const std::string &name, FindFunc func) const |
179 | 324 { |
180 | 325 for (const Section &s : m_sections) |
326 if (s.m_name == name) | |
327 func(s); | |
179 | 328 } |
329 | |
330 bool Parser::hasSection(const std::string &name) const | |
331 { | |
332 for (const Section &s : m_sections) | |
333 if (s.m_name == name) | |
334 return true; | |
335 | |
336 return false; | |
337 } | |
338 | |
180 | 339 const Section &Parser::getSection(const std::string &name) const |
179 | 340 { |
341 for (const Section &s : m_sections) | |
342 if (s.m_name == name) | |
180 | 343 return s; |
179 | 344 |
180 | 345 throw NotFoundException(name); |
179 | 346 } |
347 | |
348 void Parser::log(int number, const std::string &, const std::string &message) | |
349 { | |
350 std::cout << "line " << number << ": " << message << std::endl; | |
351 } | |
352 | |
353 void Parser::dump() | |
354 { | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
355 for (auto s : m_sections) { |
179 | 356 dumpSection(s); |
180 | 357 |
179 | 358 for (auto o : s.m_options) |
359 dumpOption(o); | |
360 } | |
361 } | |
362 | |
363 void Parser::dumpSection(const Section §ion) | |
364 { | |
365 std::cout << "Section " << section.m_name << std::endl; | |
366 } | |
367 | |
368 void Parser::dumpOption(const Option &option) | |
369 { | |
370 std::cout << " Option " << option.m_key << " = " << option.m_value << std::endl; | |
371 } |