Mercurial > code
annotate C++/Parser.cpp @ 209:706f861c4c6d
Little cleanup and rename getValue -> getOption
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 24 Jan 2014 09:17:06 +0100 |
parents | 1ffe6d4937b7 |
children | 3b0e276f0866 |
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 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
19 #include <cstring> |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
20 #include <cerrno> |
179 | 21 #include <iostream> |
22 #include <fstream> | |
23 | |
24 #include "Parser.h" | |
25 | |
26 /* -------------------------------------------------------- | |
27 * Section public members | |
28 * -------------------------------------------------------- */ | |
29 | |
30 Section::Section() | |
180 | 31 : m_allowed(true) |
179 | 32 { |
33 } | |
34 | |
209
706f861c4c6d
Little cleanup and rename getValue -> getOption
David Demelier <markand@malikania.fr>
parents:
203
diff
changeset
|
35 Section::Section(const std::string &name, bool allowed) |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
36 : m_name(name) |
209
706f861c4c6d
Little cleanup and rename getValue -> getOption
David Demelier <markand@malikania.fr>
parents:
203
diff
changeset
|
37 , m_allowed(allowed) |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
38 { |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
39 |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
40 } |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
41 |
180 | 42 const std::string &Section::getName() const |
179 | 43 { |
180 | 44 return m_name; |
179 | 45 } |
46 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
47 bool Section::hasOption(const std::string &name) const |
179 | 48 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
49 return m_options.count(name) >= 1; |
179 | 50 } |
51 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
52 Section::Map::iterator Section::begin() |
179 | 53 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
54 return m_options.begin(); |
179 | 55 } |
56 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
57 Section::Map::const_iterator Section::cbegin() const |
179 | 58 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
59 return m_options.cbegin(); |
179 | 60 } |
61 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
62 Section::Map::iterator Section::end() |
179 | 63 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
64 return m_options.end(); |
179 | 65 } |
66 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
67 Section::Map::const_iterator Section::cend() const |
179 | 68 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
69 return m_options.end(); |
179 | 70 } |
71 | |
72 /* -------------------------------------------------------- | |
73 * Parser private members | |
74 * -------------------------------------------------------- */ | |
75 | |
76 void Parser::addOption(const std::string &key, const std::string &value) | |
77 { | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
78 m_sections.back().m_options.insert(std::make_pair(key, value)); |
179 | 79 } |
80 | |
81 void Parser::readSection(int lineno, const std::string &line) | |
82 { | |
83 size_t end; | |
84 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
85 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
|
86 if (end > 1) { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
87 auto name = line.substr(1, end - 1); |
179 | 88 |
89 /* | |
90 * Check if we can add a section, if redefinition is | |
91 * disabled, we must disable the previous section so the | |
92 * further read options should not be enabled until | |
93 * a correct section is found again. | |
94 */ | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
95 if (hasSection(name) && (m_tuning & DisableRedefinition)) { |
179 | 96 if (!(m_tuning & DisableVerbosity)) |
97 log(lineno, name, "redefinition not allowed"); | |
98 m_sections.back().m_allowed = false; | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
99 } else { |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
100 m_sections.push_back(Section(name)); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
101 } |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
102 } else if (!(m_tuning & DisableVerbosity)) { |
179 | 103 /* |
104 * Do not add options at this step because it will | |
105 * corrupt the previous one. | |
106 */ | |
107 m_sections.back().m_allowed = false; | |
108 log(lineno, "", "empty section name"); | |
109 } | |
110 } | |
111 } | |
112 | |
113 void Parser::readOption(int lineno, const std::string &line) | |
114 { | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
115 auto ¤t = m_sections.back(); |
179 | 116 size_t epos; |
117 std::string key, value; | |
118 | |
119 // Error on last section? | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
120 if (!current.m_allowed) { |
179 | 121 /* |
122 * If it is the root section, this has been probably set by | |
123 * DisableRootSection flag, otherwise an error has occured | |
124 * so no need to log. | |
125 */ | |
126 if (current.m_name == "" && !(m_tuning == DisableVerbosity)) | |
127 log(lineno, "", "option not allowed in that scope"); | |
128 | |
129 return; | |
130 } | |
131 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
132 if ((epos = line.find_first_of('=')) == std::string::npos) { |
179 | 133 if (!(m_tuning & DisableVerbosity)) |
134 log(lineno, current.m_name, "missing `=' keyword"); | |
135 return; | |
136 } | |
137 | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
138 if (epos > 0) { |
179 | 139 size_t i, begin, last; |
140 char c; | |
141 | |
142 key = line.substr(0, epos - 1); | |
143 value = line.substr(epos + 1); | |
144 | |
145 // clean option key | |
146 for (i = 0; !isspace(key[i]) && i < key.length(); ++i) | |
147 continue; | |
148 key = key.substr(0, i); | |
149 | |
150 // clean option value | |
151 for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin) | |
152 continue; | |
153 value = value.substr(begin); | |
154 | |
155 c = value[0]; | |
156 begin = 0; | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
157 if (c == '\'' || c == '"') { |
179 | 158 for (last = begin = 1; value[last] != c && last < value.length(); ++last) |
159 continue; | |
160 if (value[last] != c && !(m_tuning & DisableVerbosity)) | |
161 if (!(m_tuning & DisableVerbosity)) | |
162 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
|
163 } else { |
179 | 164 for (last = begin; !isspace(value[last]) && last < value.length(); ++last) |
165 continue; | |
166 } | |
167 | |
168 if (last - begin > 0) | |
169 value = value.substr(begin, last - begin); | |
170 else | |
171 value.clear(); | |
172 | |
173 // Add the option if the key is not empty | |
174 if (key.length() > 0) | |
175 addOption(key, value); | |
176 } | |
177 } | |
178 | |
179 void Parser::readLine(int lineno, const std::string &line) | |
180 { | |
181 size_t i; | |
182 std::string buffer; | |
183 | |
184 // Skip default spaces | |
185 for (i = 0; isspace(line[i]) && i < line.length(); ++i) | |
186 continue; | |
187 | |
188 buffer = line.substr(i); | |
187
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
189 if (buffer.length() > 0) { |
600754c27c88
Update parser to style and remove useless stuff
David Demelier <markand@malikania.fr>
parents:
180
diff
changeset
|
190 if (buffer[0] != m_commentChar) { |
179 | 191 if (buffer[0] == '[') |
192 readSection(lineno, buffer); | |
193 else | |
194 readOption(lineno, buffer); | |
195 } | |
196 } | |
197 } | |
198 | |
199 /* -------------------------------------------------------- | |
200 * Parser public methods | |
201 * -------------------------------------------------------- */ | |
202 | |
203 const char Parser::DEFAULT_COMMENT_CHAR = '#'; | |
204 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
205 void Parser::open() |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
206 { |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
207 std::ifstream file; |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
208 std::string line; |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
209 int lineno = 1; |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
210 |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
211 file.open(m_path.c_str()); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
212 if (!file.is_open()) |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
213 throw std::runtime_error(m_path + std::string(std::strerror(errno))); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
214 |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
215 while (std::getline(file, line)) |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
216 readLine(lineno++, line); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
217 |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
218 file.close(); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
219 } |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
220 |
180 | 221 Parser::Parser() |
222 { | |
223 } | |
224 | |
179 | 225 Parser::Parser(const std::string &path, int tuning, char commentToken) |
180 | 226 : m_path(path) |
227 , m_tuning(tuning) | |
228 , m_commentChar(commentToken) | |
179 | 229 { |
209
706f861c4c6d
Little cleanup and rename getValue -> getOption
David Demelier <markand@malikania.fr>
parents:
203
diff
changeset
|
230 m_sections.push_back(Section("", (tuning & DisableRootSection) ? false : true)); |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
231 open(); |
179 | 232 } |
233 | |
234 Parser::~Parser() | |
235 { | |
236 } | |
237 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
238 Parser::List::iterator Parser::begin() |
179 | 239 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
240 return m_sections.begin(); |
179 | 241 } |
242 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
243 Parser::List::const_iterator Parser::cbegin() const |
179 | 244 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
245 return m_sections.cbegin(); |
179 | 246 } |
247 | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
248 Parser::List::iterator Parser::end() |
179 | 249 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
250 return m_sections.end(); |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
251 } |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
252 |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
253 Parser::List::const_iterator Parser::cend() const |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
254 { |
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
255 return m_sections.end(); |
179 | 256 } |
257 | |
180 | 258 void Parser::findSections(const std::string &name, FindFunc func) const |
179 | 259 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
260 for (const auto &s : m_sections) |
180 | 261 if (s.m_name == name) |
262 func(s); | |
179 | 263 } |
264 | |
265 bool Parser::hasSection(const std::string &name) const | |
266 { | |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
267 for (const auto &s : m_sections) |
179 | 268 if (s.m_name == name) |
269 return true; | |
270 | |
271 return false; | |
272 } | |
273 | |
180 | 274 const Section &Parser::getSection(const std::string &name) const |
179 | 275 { |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
276 for (const auto &s : m_sections) |
179 | 277 if (s.m_name == name) |
180 | 278 return s; |
179 | 279 |
203
1ffe6d4937b7
Update parser for more convenience and types security #224
David Demelier <markand@malikania.fr>
parents:
187
diff
changeset
|
280 throw std::out_of_range(name + " not found"); |
179 | 281 } |
282 | |
283 void Parser::log(int number, const std::string &, const std::string &message) | |
284 { | |
285 std::cout << "line " << number << ": " << message << std::endl; | |
286 } |