comparison C++/Parser.cpp @ 179:3648e9e6935b

Add parser
author David Demelier <markand@malikania.fr>
date Sun, 22 Sep 2013 19:57:59 +0200
parents
children 2bcdee0fe8d4
comparison
equal deleted inserted replaced
178:2bfe43b85d7f 179:3648e9e6935b
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()
39 :m_allowed(true)
40 {
41 }
42
43 Section::~Section()
44 {
45 }
46
47 Section::Section(const Section &s)
48 {
49 m_name = s.m_name;
50 m_options = s.m_options;
51 m_allowed = s.m_allowed;
52 }
53
54 const std::string Section::findOption(const std::string &name) const
55 {
56 std::string ret;
57
58 for (const Option &o : m_options)
59 if (o.m_key == name) {
60 ret = o.m_value;
61 break;
62 }
63
64 return ret;
65 }
66
67 template <> bool Section::getOption(const std::string &name) const
68 {
69 bool result = false;
70
71 if (hasOption(name)) {
72 std::string value = findOption(name);
73
74 if (value == "yes" || value == "true"|| value == "1")
75 result = true;
76 else if (value == "no" || value == "false" || value == "0")
77 result = false;
78 }
79
80 return result;
81 }
82
83 template <> int Section::getOption(const std::string &name) const
84 {
85 int result = -1;
86
87 if (hasOption(name)) {
88 result = atoi(findOption(name).c_str());
89 }
90
91 return result;
92 }
93
94 template <> std::string Section::getOption(const std::string &name) const
95 {
96 std::string result;
97
98 if (hasOption(name))
99 result = findOption(name);
100
101 return result;
102 }
103
104 const std::string & Section::getName() const
105 {
106 return m_name;
107 }
108
109 const std::vector<Option> & Section::getOptions() const
110 {
111 return m_options;
112 }
113
114 bool Section::hasOption(const std::string &name) const
115 {
116 for (const Option &o : m_options)
117 if (o.m_key == name)
118 return true;
119
120 return false;
121 }
122
123 bool operator==(const Section &s1, const Section &s2)
124 {
125 if (s1.m_name != s2.m_name)
126 return false;
127
128 return s1.m_options == s2.m_options;
129 }
130
131 /* --------------------------------------------------------
132 * Parser private members
133 * -------------------------------------------------------- */
134
135 void Parser::addSection(const std::string &name)
136 {
137 Section section;
138
139 section.m_name = name;
140 section.m_allowed = true;
141
142 m_sections.push_back(section);
143 }
144
145 void Parser::addOption(const std::string &key, const std::string &value)
146 {
147 Option option;
148 Section &current = m_sections.back();
149
150 option.m_key = key;
151 option.m_value = value;
152
153 current.m_options.push_back(option);
154 }
155
156 void Parser::readSection(int lineno, const std::string &line)
157 {
158 size_t end;
159
160 if ((end = line.find_first_of(']')) != std::string::npos) {
161 if (end > 1) {
162 std::string name = line.substr(1, end - 1);
163
164 /*
165 * Check if we can add a section, if redefinition is
166 * disabled, we must disable the previous section so the
167 * further read options should not be enabled until
168 * a correct section is found again.
169 */
170 if (hasSection(name) && (m_tuning & DisableRedefinition)) {
171 if (!(m_tuning & DisableVerbosity))
172 log(lineno, name, "redefinition not allowed");
173 m_sections.back().m_allowed = false;
174 } else {
175 addSection(name);
176 }
177 } else if (!(m_tuning & DisableVerbosity)) {
178 /*
179 * Do not add options at this step because it will
180 * corrupt the previous one.
181 */
182 m_sections.back().m_allowed = false;
183 log(lineno, "", "empty section name");
184 }
185 }
186 }
187
188 void Parser::readOption(int lineno, const std::string &line)
189 {
190 size_t epos;
191 std::string key, value;
192 Section &current = m_sections.back();
193
194 // Error on last section?
195 if (!current.m_allowed) {
196 /*
197 * If it is the root section, this has been probably set by
198 * DisableRootSection flag, otherwise an error has occured
199 * so no need to log.
200 */
201 if (current.m_name == "" && !(m_tuning == DisableVerbosity))
202 log(lineno, "", "option not allowed in that scope");
203
204 return;
205 }
206
207 if ((epos = line.find_first_of('=')) == std::string::npos) {
208 if (!(m_tuning & DisableVerbosity))
209 log(lineno, current.m_name, "missing `=' keyword");
210 return;
211 }
212
213 if (epos > 0) {
214 size_t i, begin, last;
215 char c;
216
217 key = line.substr(0, epos - 1);
218 value = line.substr(epos + 1);
219
220 // clean option key
221 for (i = 0; !isspace(key[i]) && i < key.length(); ++i)
222 continue;
223 key = key.substr(0, i);
224
225 // clean option value
226 for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin)
227 continue;
228 value = value.substr(begin);
229
230 c = value[0];
231 begin = 0;
232 if (c == '\'' || c == '"') {
233 for (last = begin = 1; value[last] != c && last < value.length(); ++last)
234 continue;
235 if (value[last] != c && !(m_tuning & DisableVerbosity))
236 if (!(m_tuning & DisableVerbosity))
237 log(lineno, current.m_name, "undisclosed std::string");
238 } else {
239 for (last = begin; !isspace(value[last]) && last < value.length(); ++last)
240 continue;
241 }
242
243 if (last - begin > 0)
244 value = value.substr(begin, last - begin);
245 else
246 value.clear();
247
248 // Add the option if the key is not empty
249 if (key.length() > 0)
250 addOption(key, value);
251 }
252 }
253
254 void Parser::readLine(int lineno, const std::string &line)
255 {
256 size_t i;
257 std::string buffer;
258
259 // Skip default spaces
260 for (i = 0; isspace(line[i]) && i < line.length(); ++i)
261 continue;
262
263 buffer = line.substr(i);
264 if (buffer.length() > 0) {
265 if (buffer[0] != m_commentChar) {
266 if (buffer[0] == '[')
267 readSection(lineno, buffer);
268 else
269 readOption(lineno, buffer);
270 }
271 }
272 }
273
274 /* --------------------------------------------------------
275 * Parser public methods
276 * -------------------------------------------------------- */
277
278 const char Parser::DEFAULT_COMMENT_CHAR = '#';
279
280 Parser::Parser(const std::string &path, int tuning, char commentToken)
281 :m_path(path), m_tuning(tuning), m_commentChar(commentToken)
282 {
283 Section root;
284
285 // Add a default root section
286 root.m_name = "";
287 root.m_allowed = (tuning & DisableRootSection) ? false : true;
288
289 m_sections.push_back(root);
290 }
291
292 Parser::Parser()
293 {
294 }
295
296 Parser::~Parser()
297 {
298 }
299
300 bool Parser::open()
301 {
302 std::ifstream file;
303 std::string line;
304 int lineno = 1;
305
306 file.open(m_path.c_str());
307 if (!file.is_open()) {
308 m_error = "could not open file " + m_path; // XXX: add a real error
309 return false;
310 }
311
312 // Avoid use of C getline
313 while (std::getline(file, line)) {
314 readLine(lineno++, line);
315 }
316
317 file.close();
318
319 return true;
320 }
321
322 const std::string & Parser::getError() const
323 {
324 return m_error;
325 }
326
327 const std::vector<Section> & Parser::getSections() const
328 {
329 return m_sections;
330 }
331
332 std::vector<Section> Parser::findSections(const std::string &name) const
333 {
334 std::vector<Section> list;
335
336 for (const Section &s : m_sections) {
337 if (s.m_name == name) {
338 Section copy = s;
339 list.push_back(copy);
340 }
341 }
342
343 return list;
344 }
345
346 bool Parser::hasSection(const std::string &name) const
347 {
348 for (const Section &s : m_sections)
349 if (s.m_name == name)
350 return true;
351
352 return false;
353 }
354
355 Section Parser::getSection(const std::string &name) const
356 {
357 Section ret;
358
359 for (const Section &s : m_sections)
360 if (s.m_name == name)
361 ret = s;
362
363 return ret;
364 }
365
366 Section Parser::requireSection(const std::string &name) const
367 {
368 if (!hasSection(name))
369 throw NotFoundException(name);
370
371 return getSection(name);
372 }
373
374 void Parser::log(int number, const std::string &, const std::string &message)
375 {
376 std::cout << "line " << number << ": " << message << std::endl;
377 }
378
379 void Parser::dump()
380 {
381 for (auto s : m_sections) {
382 dumpSection(s);
383 for (auto o : s.m_options)
384 dumpOption(o);
385 }
386 }
387
388 void Parser::dumpSection(const Section &section)
389 {
390 std::cout << "Section " << section.m_name << std::endl;
391 }
392
393 void Parser::dumpOption(const Option &option)
394 {
395 std::cout << " Option " << option.m_key << " = " << option.m_value << std::endl;
396 }