1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
1 |
/*
|
1999.6.1
by kalebral at gmail
update Copyright strings to a more common format to help with creating the master debian copyright file |
2 |
* Copyright (C) 2002-2004 Vladimir Prus.
|
3 |
* Copyright (C) 2010 Monty Taylor
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
4 |
*
|
5 |
* Distributed under the Boost Software License, Version 1.0.
|
|
6 |
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
7 |
* http://www.boost.org/LICENSE_1_0.txt)
|
|
8 |
*/
|
|
9 |
||
2234
by Brian Aker
Mass removal of ifdef/endif in favor of pragma once. |
10 |
#pragma once
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
11 |
|
12 |
#include <boost/program_options.hpp> |
|
13 |
#include <boost/program_options/eof_iterator.hpp> |
|
14 |
#include <boost/type_traits/is_same.hpp> |
|
15 |
#include <boost/shared_ptr.hpp> |
|
1815.1.4
by Monty Taylor
Removed private copy of trim and use the one from boost::algorithm. |
16 |
#include <boost/algorithm/string.hpp> |
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
17 |
|
18 |
#include <boost/noncopyable.hpp> |
|
19 |
||
20 |
#include <iosfwd> |
|
21 |
#include <vector> |
|
22 |
#include <utility> |
|
23 |
#include <set> |
|
24 |
||
2318.6.98
by Olaf van der Spek
Refactor |
25 |
namespace drizzled { |
26 |
namespace program_options { |
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
27 |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
28 |
typedef std::pair<std::string, std::string> option_result_pair; |
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
29 |
std::string parse_suffix(const std::string& arg_val); |
1926.2.3
by Monty Taylor
Cleaned up stylewq |
30 |
option_result_pair parse_size_suffixes(std::string s); |
31 |
option_result_pair parse_size_arg(std::string s); |
|
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
32 |
|
33 |
std::string parse_suffix(const std::string& arg_val) |
|
34 |
{
|
|
35 |
try
|
|
36 |
{
|
|
37 |
size_t size_suffix_pos= arg_val.find_last_of("kmgKMG"); |
|
38 |
if (size_suffix_pos == arg_val.size()-1) |
|
39 |
{
|
|
40 |
char suffix= arg_val[size_suffix_pos]; |
|
41 |
std::string size_val(arg_val.substr(0, size_suffix_pos)); |
|
42 |
||
43 |
uint64_t base_size= boost::lexical_cast<uint64_t>(size_val); |
|
44 |
uint64_t new_size= 0; |
|
45 |
||
46 |
switch (suffix) |
|
47 |
{
|
|
48 |
case 'K': |
|
49 |
case 'k': |
|
50 |
new_size= base_size * 1024; |
|
51 |
break; |
|
52 |
case 'M': |
|
53 |
case 'm': |
|
54 |
new_size= base_size * 1024 * 1024; |
|
55 |
break; |
|
56 |
case 'G': |
|
57 |
case 'g': |
|
58 |
new_size= base_size * 1024 * 1024 * 1024; |
|
59 |
break; |
|
60 |
}
|
|
61 |
return boost::lexical_cast<std::string>(new_size); |
|
62 |
}
|
|
63 |
}
|
|
1966.3.1
by Monty Taylor
Use std::exception instead of catch(...) |
64 |
catch (std::exception&) |
1926.2.3
by Monty Taylor
Cleaned up stylewq |
65 |
{ } |
66 |
||
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
67 |
return arg_val; |
68 |
}
|
|
69 |
||
1926.2.3
by Monty Taylor
Cleaned up stylewq |
70 |
option_result_pair parse_size_suffixes(std::string s) |
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
71 |
{
|
72 |
size_t equal_pos= s.find("="); |
|
73 |
if (equal_pos != std::string::npos) |
|
74 |
{
|
|
75 |
std::string arg_key(s.substr(0, equal_pos)); |
|
76 |
std::string arg_val(parse_suffix(s.substr(equal_pos+1))); |
|
77 |
||
78 |
if (arg_val != s.substr(equal_pos+1)) |
|
79 |
{
|
|
80 |
return std::make_pair(arg_key, arg_val); |
|
81 |
}
|
|
82 |
}
|
|
83 |
||
84 |
return std::make_pair(std::string(""), std::string("")); |
|
85 |
}
|
|
86 |
||
1926.2.3
by Monty Taylor
Cleaned up stylewq |
87 |
option_result_pair parse_size_arg(std::string s) |
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
88 |
{
|
89 |
if (s.find("--") == 0) |
|
90 |
{
|
|
91 |
return parse_size_suffixes(s.substr(2)); |
|
92 |
}
|
|
93 |
return make_pair(std::string(""), std::string("")); |
|
94 |
}
|
|
95 |
||
1815.1.2
by Monty Taylor
Deal with differencs in surrounding boost program_options infrastructure. |
96 |
class invalid_syntax : |
97 |
public boost::program_options::error |
|
98 |
{
|
|
99 |
public: |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
100 |
enum kind_t |
101 |
{
|
|
1815.1.2
by Monty Taylor
Deal with differencs in surrounding boost program_options infrastructure. |
102 |
long_not_allowed = 30, |
103 |
long_adjacent_not_allowed, |
|
104 |
short_adjacent_not_allowed, |
|
105 |
empty_adjacent_parameter, |
|
106 |
missing_parameter, |
|
107 |
extra_parameter, |
|
108 |
unrecognized_line
|
|
109 |
};
|
|
110 |
||
111 |
invalid_syntax(const std::string& in_tokens, kind_t in_kind); |
|
112 |
||
113 |
||
114 |
// gcc says that throw specification on dtor is loosened
|
|
115 |
// without this line
|
|
116 |
~invalid_syntax() throw() {} |
|
117 |
||
118 |
kind_t kind() const |
|
119 |
{
|
|
120 |
return m_kind; |
|
121 |
}
|
|
122 |
||
123 |
||
124 |
const std::string& tokens() const |
|
125 |
{
|
|
126 |
return m_tokens; |
|
127 |
}
|
|
128 |
||
129 |
||
130 |
protected: |
|
131 |
/** Used to convert kind_t to a related error text */
|
|
132 |
static std::string error_message(kind_t kind) |
|
133 |
{
|
|
134 |
// Initially, store the message in 'const char*' variable, to avoid
|
|
135 |
// conversion to string in all cases.
|
|
136 |
const char* msg; |
|
137 |
switch(kind) |
|
138 |
{
|
|
139 |
case long_not_allowed: |
|
140 |
msg = "long options are not allowed"; |
|
141 |
break; |
|
142 |
case long_adjacent_not_allowed: |
|
143 |
msg = "parameters adjacent to long options not allowed"; |
|
144 |
break; |
|
145 |
case short_adjacent_not_allowed: |
|
146 |
msg = "parameters adjust to short options are not allowed"; |
|
147 |
break; |
|
148 |
case empty_adjacent_parameter: |
|
149 |
msg = "adjacent parameter is empty"; |
|
150 |
break; |
|
151 |
case missing_parameter: |
|
152 |
msg = "required parameter is missing"; |
|
153 |
break; |
|
154 |
case extra_parameter: |
|
155 |
msg = "extra parameter"; |
|
156 |
break; |
|
157 |
case unrecognized_line: |
|
158 |
msg = "unrecognized line"; |
|
159 |
break; |
|
160 |
default: |
|
161 |
msg = "unknown error"; |
|
162 |
}
|
|
163 |
return msg; |
|
164 |
}
|
|
165 |
||
166 |
private: |
|
167 |
// TODO: copy ctor might throw
|
|
168 |
std::string m_tokens; |
|
169 |
||
170 |
kind_t m_kind; |
|
171 |
};
|
|
172 |
||
173 |
invalid_syntax::invalid_syntax(const std::string& in_tokens, |
|
174 |
invalid_syntax::kind_t in_kind) : |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
175 |
boost::program_options::error(error_message(in_kind).append(" in '").append(in_tokens).append("'")), |
176 |
m_tokens(in_tokens), |
|
177 |
m_kind(in_kind) |
|
178 |
{ } |
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
179 |
|
180 |
namespace detail |
|
181 |
{
|
|
182 |
||
183 |
/** Standalone parser for config files in ini-line format.
|
|
184 |
The parser is a model of single-pass lvalue iterator, and
|
|
185 |
default constructor creates past-the-end-iterator. The typical usage is:
|
|
186 |
config_file_iterator i(is, ... set of options ...), e;
|
|
187 |
for(; i !=e; ++i) {
|
|
188 |
*i;
|
|
189 |
}
|
|
190 |
||
191 |
Syntax conventions:
|
|
192 |
||
193 |
- config file can not contain positional options
|
|
194 |
- '#' is comment character: it is ignored together with
|
|
195 |
the rest of the line.
|
|
196 |
- variable assignments are in the form
|
|
197 |
name '=' value.
|
|
198 |
spaces around '=' are trimmed.
|
|
199 |
- Section names are given in brackets.
|
|
200 |
||
201 |
The actual option name is constructed by combining current section
|
|
202 |
name and specified option name, with dot between. If section_name
|
|
203 |
already contains dot at the end, new dot is not inserted. For example:
|
|
204 |
@verbatim
|
|
205 |
[gui.accessibility]
|
|
206 |
visual_bell=yes
|
|
207 |
@endverbatim
|
|
208 |
will result in option "gui.accessibility.visual_bell" with value
|
|
209 |
"yes" been returned.
|
|
210 |
||
211 |
*/
|
|
212 |
class common_config_file_iterator : |
|
213 |
public boost::eof_iterator<common_config_file_iterator, |
|
214 |
boost::program_options::option> |
|
215 |
{
|
|
216 |
public: |
|
217 |
common_config_file_iterator() |
|
218 |
{
|
|
219 |
found_eof(); |
|
220 |
}
|
|
221 |
||
222 |
common_config_file_iterator(const std::set<std::string>& in_allowed_options, |
|
223 |
bool allow_unregistered) : |
|
224 |
allowed_options(in_allowed_options), |
|
225 |
m_allow_unregistered(allow_unregistered) |
|
226 |
{
|
|
227 |
for(std::set<std::string>::const_iterator i = allowed_options.begin(); |
|
228 |
i != allowed_options.end(); |
|
229 |
++i) |
|
230 |
{
|
|
231 |
add_option(i->c_str()); |
|
232 |
}
|
|
233 |
}
|
|
234 |
||
235 |
virtual ~common_config_file_iterator() {} |
|
236 |
||
237 |
public: // Method required by eof_iterator |
|
238 |
||
239 |
void get() |
|
240 |
{
|
|
241 |
std::string s; |
|
242 |
std::string::size_type n; |
|
243 |
bool found = false; |
|
244 |
||
245 |
while(this->getline(s)) { |
|
246 |
||
247 |
// strip '#' comments and whitespace
|
|
248 |
if ((n = s.find('#')) != std::string::npos) |
|
249 |
s = s.substr(0, n); |
|
1815.1.4
by Monty Taylor
Removed private copy of trim and use the one from boost::algorithm. |
250 |
boost::trim(s); |
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
251 |
|
252 |
if (!s.empty()) { |
|
253 |
// Handle section name
|
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
254 |
if (*s.begin() == '[' && *s.rbegin() == ']') |
255 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
256 |
m_prefix = s.substr(1, s.size()-2); |
257 |
if (*m_prefix.rbegin() != '.') |
|
258 |
m_prefix += '.'; |
|
259 |
}
|
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
260 |
else
|
261 |
{
|
|
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
262 |
|
263 |
std::string name; |
|
264 |
std::string option_value("true"); |
|
265 |
||
1926.2.3
by Monty Taylor
Cleaned up stylewq |
266 |
if ((n = s.find('=')) != std::string::npos) |
267 |
{
|
|
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
268 |
|
269 |
name = m_prefix + boost::trim_copy(s.substr(0, n)); |
|
270 |
option_value = boost::trim_copy(parse_suffix(s.substr(n+1))); |
|
271 |
||
272 |
}
|
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
273 |
else
|
274 |
{
|
|
1926.2.1
by Monty Taylor
Add better boolean parsing and size suffix parsing. |
275 |
name = m_prefix + boost::trim_copy(s); |
276 |
}
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
277 |
|
278 |
bool registered = allowed_option(name); |
|
279 |
if (!registered && !m_allow_unregistered) |
|
280 |
boost::throw_exception(boost::program_options::unknown_option(name)); |
|
281 |
||
282 |
found = true; |
|
283 |
this->value().string_key = name; |
|
284 |
this->value().value.clear(); |
|
285 |
this->value().value.push_back(option_value); |
|
286 |
this->value().unregistered = !registered; |
|
287 |
this->value().original_tokens.clear(); |
|
288 |
this->value().original_tokens.push_back(name); |
|
289 |
this->value().original_tokens.push_back(option_value); |
|
290 |
break; |
|
291 |
||
292 |
}
|
|
293 |
}
|
|
294 |
}
|
|
295 |
if (!found) |
|
296 |
found_eof(); |
|
297 |
}
|
|
298 |
||
299 |
protected: // Stubs for derived classes |
|
300 |
||
301 |
// Obtains next line from the config file
|
|
302 |
// Note: really, this design is a bit ugly
|
|
303 |
// The most clean thing would be to pass 'line_iterator' to
|
|
304 |
// constructor of this class, but to avoid templating this class
|
|
305 |
// we'd need polymorphic iterator, which does not exist yet.
|
|
306 |
virtual bool getline(std::string&) { return false; } |
|
307 |
||
308 |
private: |
|
309 |
/** Adds another allowed option. If the 'name' ends with
|
|
310 |
'*', then all options with the same prefix are
|
|
311 |
allowed. For example, if 'name' is 'foo*', then 'foo1' and
|
|
312 |
'foo_bar' are allowed. */
|
|
313 |
void add_option(const char* name) |
|
314 |
{
|
|
315 |
std::string s(name); |
|
316 |
assert(!s.empty()); |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
317 |
if (*s.rbegin() == '*') |
318 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
319 |
s.resize(s.size()-1); |
320 |
bool bad_prefixes(false); |
|
321 |
// If 's' is a prefix of one of allowed suffix, then
|
|
322 |
// lower_bound will return that element.
|
|
323 |
// If some element is prefix of 's', then lower_bound will
|
|
324 |
// return the next element.
|
|
325 |
std::set<std::string>::iterator i = allowed_prefixes.lower_bound(s); |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
326 |
if (i != allowed_prefixes.end()) |
327 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
328 |
if (i->find(s) == 0) |
329 |
bad_prefixes = true; |
|
330 |
}
|
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
331 |
if (i != allowed_prefixes.begin()) |
332 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
333 |
--i; |
334 |
if (s.find(*i) == 0) |
|
335 |
bad_prefixes = true; |
|
336 |
}
|
|
337 |
if (bad_prefixes) |
|
338 |
boost::throw_exception(boost::program_options::error("bad prefixes")); |
|
339 |
allowed_prefixes.insert(s); |
|
340 |
}
|
|
341 |
}
|
|
342 |
||
343 |
||
344 |
// Returns true if 's' is a registered option name.
|
|
345 |
bool allowed_option(const std::string& s) const |
|
346 |
{
|
|
347 |
std::set<std::string>::const_iterator i = allowed_options.find(s); |
|
348 |
if (i != allowed_options.end()) |
|
349 |
return true; |
|
350 |
// If s is "pa" where "p" is allowed prefix then
|
|
351 |
// lower_bound should find the element after "p".
|
|
352 |
// This depends on 'allowed_prefixes' invariant.
|
|
353 |
i = allowed_prefixes.lower_bound(s); |
|
354 |
if (i != allowed_prefixes.begin() && s.find(*--i) == 0) |
|
355 |
return true; |
|
356 |
return false; |
|
357 |
}
|
|
358 |
||
359 |
||
360 |
// That's probably too much data for iterator, since
|
|
361 |
// it will be copied, but let's not bother for now.
|
|
362 |
std::set<std::string> allowed_options; |
|
363 |
// Invariant: no element is prefix of other element.
|
|
364 |
std::set<std::string> allowed_prefixes; |
|
365 |
std::string m_prefix; |
|
366 |
bool m_allow_unregistered; |
|
367 |
};
|
|
368 |
||
369 |
template<class charT> |
|
370 |
class basic_config_file_iterator : |
|
371 |
public common_config_file_iterator |
|
372 |
{
|
|
373 |
public: |
|
374 |
||
375 |
basic_config_file_iterator() |
|
376 |
{
|
|
377 |
found_eof(); |
|
378 |
}
|
|
379 |
||
380 |
/** Creates a config file parser for the specified stream. */
|
|
381 |
basic_config_file_iterator(std::basic_istream<charT>& is, |
|
382 |
const std::set<std::string>& allowed_options, |
|
383 |
bool allow_unregistered = false); |
|
384 |
||
385 |
private: // base overrides |
|
386 |
||
387 |
bool getline(std::string&); |
|
388 |
||
389 |
private: // internal data |
|
390 |
boost::shared_ptr<std::basic_istream<charT> > is; |
|
391 |
};
|
|
392 |
||
393 |
typedef basic_config_file_iterator<char> config_file_iterator; |
|
394 |
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator; |
|
395 |
||
396 |
struct null_deleter |
|
397 |
{
|
|
398 |
void operator()(void const *) const {} |
|
399 |
};
|
|
400 |
||
401 |
||
402 |
template<class charT> |
|
403 |
basic_config_file_iterator<charT>:: |
|
404 |
basic_config_file_iterator(std::basic_istream<charT>& in_is, |
|
405 |
const std::set<std::string>& in_allowed_options, |
|
406 |
bool in_allow_unregistered) : |
|
407 |
common_config_file_iterator(in_allowed_options, in_allow_unregistered) |
|
408 |
{
|
|
409 |
this->is.reset(&in_is, null_deleter()); |
|
410 |
get(); |
|
411 |
}
|
|
412 |
||
413 |
||
414 |
// Specializing this function for wchar_t causes problems on
|
|
415 |
// borland and vc7, as well as on metrowerks. On the first two
|
|
416 |
// I don't know a workaround, so make use of 'to_internal' to
|
|
417 |
// avoid specialization.
|
|
418 |
template<class charT> |
|
419 |
bool
|
|
420 |
basic_config_file_iterator<charT>::getline(std::string& s) |
|
421 |
{
|
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
422 |
if (std::getline(*is, s)) |
423 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
424 |
return true; |
1926.2.3
by Monty Taylor
Cleaned up stylewq |
425 |
}
|
426 |
else
|
|
427 |
{
|
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
428 |
return false; |
429 |
}
|
|
430 |
}
|
|
431 |
||
432 |
} /* namespace detail */ |
|
433 |
||
434 |
/** Parse a config file.
|
|
435 |
||
436 |
Read from given stream.
|
|
437 |
*/
|
|
438 |
template<class charT> |
|
439 |
boost::program_options::basic_parsed_options<charT> |
|
440 |
parse_config_file(std::basic_istream<charT>& is, |
|
441 |
const boost::program_options::options_description& desc, |
|
442 |
bool allow_unregistered = false) |
|
443 |
{
|
|
444 |
std::set<std::string> allowed_options; |
|
445 |
||
446 |
const std::vector<boost::shared_ptr<boost::program_options::option_description> >& options = desc.options(); |
|
447 |
for (unsigned i = 0; i < options.size(); ++i) |
|
448 |
{
|
|
449 |
const boost::program_options::option_description& d= *options[i]; |
|
450 |
||
451 |
if (d.long_name().empty()) |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
452 |
boost::throw_exception(boost::program_options::error("long name required for config file")); |
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
453 |
|
454 |
allowed_options.insert(d.long_name()); |
|
455 |
}
|
|
456 |
||
457 |
// Parser return char strings
|
|
458 |
boost::program_options::parsed_options result(&desc); |
|
1926.2.3
by Monty Taylor
Cleaned up stylewq |
459 |
std::copy(detail::basic_config_file_iterator<charT>(is, |
460 |
allowed_options, |
|
461 |
allow_unregistered), |
|
1815.1.1
by Monty Taylor
Embed a modified version of parse_config_file. There are several more bugs |
462 |
detail::basic_config_file_iterator<charT>(), |
463 |
std::back_inserter(result.options)); |
|
464 |
// Convert char strings into desired type.
|
|
465 |
return boost::program_options::basic_parsed_options<charT>(result); |
|
466 |
}
|
|
467 |
||
468 |
/** Parse a config file.
|
|
469 |
||
470 |
Read from file with the given name. The character type is
|
|
471 |
passed to the file stream.
|
|
472 |
*/
|
|
473 |
template<class charT> |
|
474 |
boost::program_options::basic_parsed_options<charT> |
|
475 |
parse_config_file(const char* filename, |
|
476 |
const boost::program_options::options_description& desc, |
|
477 |
bool allow_unregistered = false) |
|
478 |
{
|
|
479 |
// Parser return char strings
|
|
480 |
std::basic_ifstream< charT > strm(filename); |
|
481 |
if (!strm) |
|
482 |
{
|
|
483 |
boost::throw_exception("Couldn't open file"); |
|
484 |
}
|
|
485 |
return parse_config_file(strm, desc, allow_unregistered); |
|
486 |
}
|
|
487 |
||
488 |
} /* namespace program_options */ |
|
489 |
} /* namespace drizzled */ |