2
* Copyright (c) 2002-2004 Vladimir Prus.
4
* Distributed under the Boost Software License, Version 1.0.
5
* (See accompanying file LICENSE_1_0.txt or copy at
6
* http://www.boost.org/LICENSE_1_0.txt)
9
#ifndef DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H
10
#define DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H
12
#include <boost/program_options.hpp>
13
#include <boost/program_options/eof_iterator.hpp>
14
#include <boost/static_assert.hpp>
15
#include <boost/type_traits/is_same.hpp>
16
#include <boost/shared_ptr.hpp>
18
#include <boost/noncopyable.hpp>
27
namespace program_options
35
std::string trim_ws(const std::string& s)
37
std::string::size_type n, n2;
38
n = s.find_first_not_of(" \t\r\n");
39
if (n == std::string::npos)
42
n2 = s.find_last_not_of(" \t\r\n");
43
return s.substr(n, n2-n+1);
48
/** Standalone parser for config files in ini-line format.
49
The parser is a model of single-pass lvalue iterator, and
50
default constructor creates past-the-end-iterator. The typical usage is:
51
config_file_iterator i(is, ... set of options ...), e;
58
- config file can not contain positional options
59
- '#' is comment character: it is ignored together with
61
- variable assignments are in the form
63
spaces around '=' are trimmed.
64
- Section names are given in brackets.
66
The actual option name is constructed by combining current section
67
name and specified option name, with dot between. If section_name
68
already contains dot at the end, new dot is not inserted. For example:
73
will result in option "gui.accessibility.visual_bell" with value
77
class common_config_file_iterator :
78
public boost::eof_iterator<common_config_file_iterator,
79
boost::program_options::option>
82
common_config_file_iterator()
87
common_config_file_iterator(const std::set<std::string>& in_allowed_options,
88
bool allow_unregistered) :
89
allowed_options(in_allowed_options),
90
m_allow_unregistered(allow_unregistered)
92
for(std::set<std::string>::const_iterator i = allowed_options.begin();
93
i != allowed_options.end();
96
add_option(i->c_str());
100
virtual ~common_config_file_iterator() {}
102
public: // Method required by eof_iterator
107
std::string::size_type n;
110
while(this->getline(s)) {
112
// strip '#' comments and whitespace
113
if ((n = s.find('#')) != std::string::npos)
118
// Handle section name
119
if (*s.begin() == '[' && *s.rbegin() == ']') {
120
m_prefix = s.substr(1, s.size()-2);
121
if (*m_prefix.rbegin() != '.')
124
else if ((n = s.find('=')) != std::string::npos) {
126
std::string name = m_prefix + trim_ws(s.substr(0, n));
127
std::string option_value = trim_ws(s.substr(n+1));
129
bool registered = allowed_option(name);
130
if (!registered && !m_allow_unregistered)
131
boost::throw_exception(boost::program_options::unknown_option(name));
134
this->value().string_key = name;
135
this->value().value.clear();
136
this->value().value.push_back(option_value);
137
this->value().unregistered = !registered;
138
this->value().original_tokens.clear();
139
this->value().original_tokens.push_back(name);
140
this->value().original_tokens.push_back(option_value);
144
boost::throw_exception(boost::program_options::invalid_syntax(s, "Unrecognized line"));
152
protected: // Stubs for derived classes
154
// Obtains next line from the config file
155
// Note: really, this design is a bit ugly
156
// The most clean thing would be to pass 'line_iterator' to
157
// constructor of this class, but to avoid templating this class
158
// we'd need polymorphic iterator, which does not exist yet.
159
virtual bool getline(std::string&) { return false; }
162
/** Adds another allowed option. If the 'name' ends with
163
'*', then all options with the same prefix are
164
allowed. For example, if 'name' is 'foo*', then 'foo1' and
165
'foo_bar' are allowed. */
166
void add_option(const char* name)
170
if (*s.rbegin() == '*') {
171
s.resize(s.size()-1);
172
bool bad_prefixes(false);
173
// If 's' is a prefix of one of allowed suffix, then
174
// lower_bound will return that element.
175
// If some element is prefix of 's', then lower_bound will
176
// return the next element.
177
std::set<std::string>::iterator i = allowed_prefixes.lower_bound(s);
178
if (i != allowed_prefixes.end()) {
182
if (i != allowed_prefixes.begin()) {
188
boost::throw_exception(boost::program_options::error("bad prefixes"));
189
allowed_prefixes.insert(s);
194
// Returns true if 's' is a registered option name.
195
bool allowed_option(const std::string& s) const
197
std::set<std::string>::const_iterator i = allowed_options.find(s);
198
if (i != allowed_options.end())
200
// If s is "pa" where "p" is allowed prefix then
201
// lower_bound should find the element after "p".
202
// This depends on 'allowed_prefixes' invariant.
203
i = allowed_prefixes.lower_bound(s);
204
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
210
// That's probably too much data for iterator, since
211
// it will be copied, but let's not bother for now.
212
std::set<std::string> allowed_options;
213
// Invariant: no element is prefix of other element.
214
std::set<std::string> allowed_prefixes;
215
std::string m_prefix;
216
bool m_allow_unregistered;
219
template<class charT>
220
class basic_config_file_iterator :
221
public common_config_file_iterator
225
basic_config_file_iterator()
230
/** Creates a config file parser for the specified stream. */
231
basic_config_file_iterator(std::basic_istream<charT>& is,
232
const std::set<std::string>& allowed_options,
233
bool allow_unregistered = false);
235
private: // base overrides
237
bool getline(std::string&);
239
private: // internal data
240
boost::shared_ptr<std::basic_istream<charT> > is;
243
typedef basic_config_file_iterator<char> config_file_iterator;
244
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
248
void operator()(void const *) const {}
252
template<class charT>
253
basic_config_file_iterator<charT>::
254
basic_config_file_iterator(std::basic_istream<charT>& in_is,
255
const std::set<std::string>& in_allowed_options,
256
bool in_allow_unregistered) :
257
common_config_file_iterator(in_allowed_options, in_allow_unregistered)
259
this->is.reset(&in_is, null_deleter());
264
// Specializing this function for wchar_t causes problems on
265
// borland and vc7, as well as on metrowerks. On the first two
266
// I don't know a workaround, so make use of 'to_internal' to
267
// avoid specialization.
268
template<class charT>
270
basic_config_file_iterator<charT>::getline(std::string& s)
272
if (std::getline(*is, s)) {
279
} /* namespace detail */
281
/** Parse a config file.
283
Read from given stream.
285
template<class charT>
286
boost::program_options::basic_parsed_options<charT>
287
parse_config_file(std::basic_istream<charT>& is,
288
const boost::program_options::options_description& desc,
289
bool allow_unregistered = false)
291
std::set<std::string> allowed_options;
293
const std::vector<boost::shared_ptr<boost::program_options::option_description> >& options = desc.options();
294
for (unsigned i = 0; i < options.size(); ++i)
296
const boost::program_options::option_description& d= *options[i];
298
if (d.long_name().empty())
299
boost::throw_exception(
300
boost::program_options::error("long name required for config file"));
302
allowed_options.insert(d.long_name());
305
// Parser return char strings
306
boost::program_options::parsed_options result(&desc);
307
std::copy(detail::basic_config_file_iterator<charT>(
308
is, allowed_options, allow_unregistered),
309
detail::basic_config_file_iterator<charT>(),
310
std::back_inserter(result.options));
311
// Convert char strings into desired type.
312
return boost::program_options::basic_parsed_options<charT>(result);
315
/** Parse a config file.
317
Read from file with the given name. The character type is
318
passed to the file stream.
320
template<class charT>
321
boost::program_options::basic_parsed_options<charT>
322
parse_config_file(const char* filename,
323
const boost::program_options::options_description& desc,
324
bool allow_unregistered = false)
326
// Parser return char strings
327
std::basic_ifstream< charT > strm(filename);
330
boost::throw_exception("Couldn't open file");
332
return parse_config_file(strm, desc, allow_unregistered);
335
} /* namespace program_options */
336
} /* namespace drizzled */
338
#endif /* DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H */