~dshrews/drizzle/bug746580_elliott

« back to all changes in this revision

Viewing changes to drizzled/program_options/config_file.h

  • Committer: Monty Taylor
  • Date: 2010-10-02 05:07:25 UTC
  • mto: (1817.1.3 build)
  • mto: This revision was merged to the branch mainline in revision 1818.
  • Revision ID: mordred@inaugust.com-20101002050725-h1b30b0nr3leeoh1
Embed a modified version of parse_config_file. There are several more bugs
that we'll want to fix in it, and then submit upstream. Eventually we should
be able to remove this- but for now the version on lucid is completely
broken.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2002-2004 Vladimir Prus.
 
3
 *
 
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)
 
7
 */
 
8
 
 
9
#ifndef DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H
 
10
#define DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H
 
11
 
 
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>
 
17
 
 
18
#include <boost/noncopyable.hpp>
 
19
 
 
20
#include <iosfwd>
 
21
#include <vector>
 
22
#include <utility>
 
23
#include <set>
 
24
 
 
25
namespace drizzled
 
26
{
 
27
namespace program_options
 
28
{
 
29
 
 
30
 
 
31
namespace detail
 
32
{
 
33
 
 
34
namespace {
 
35
  std::string trim_ws(const std::string& s)
 
36
  {
 
37
    std::string::size_type n, n2;
 
38
    n = s.find_first_not_of(" \t\r\n");
 
39
    if (n == std::string::npos)
 
40
      return std::string();
 
41
    else {
 
42
      n2 = s.find_last_not_of(" \t\r\n");
 
43
      return s.substr(n, n2-n+1);
 
44
    }
 
45
  }
 
46
}
 
47
 
 
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;
 
52
  for(; i !=e; ++i) {
 
53
 *i;
 
54
 }
 
55
 
 
56
 Syntax conventions:
 
57
 
 
58
 - config file can not contain positional options
 
59
 - '#' is comment character: it is ignored together with
 
60
 the rest of the line.
 
61
 - variable assignments are in the form
 
62
 name '=' value.
 
63
 spaces around '=' are trimmed.
 
64
 - Section names are given in brackets. 
 
65
 
 
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:
 
69
 @verbatim
 
70
 [gui.accessibility]
 
71
 visual_bell=yes
 
72
 @endverbatim
 
73
 will result in option "gui.accessibility.visual_bell" with value
 
74
 "yes" been returned.
 
75
 
 
76
 */    
 
77
class common_config_file_iterator :
 
78
  public boost::eof_iterator<common_config_file_iterator,
 
79
                             boost::program_options::option>
 
80
{
 
81
public:
 
82
  common_config_file_iterator()
 
83
  {
 
84
    found_eof();
 
85
  }
 
86
 
 
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)
 
91
  {
 
92
    for(std::set<std::string>::const_iterator i = allowed_options.begin();
 
93
        i != allowed_options.end(); 
 
94
        ++i)
 
95
    {
 
96
      add_option(i->c_str());
 
97
    }
 
98
  }
 
99
 
 
100
  virtual ~common_config_file_iterator() {}
 
101
 
 
102
public: // Method required by eof_iterator
 
103
 
 
104
  void get()
 
105
  {
 
106
    std::string s;
 
107
    std::string::size_type n;
 
108
    bool found = false;
 
109
 
 
110
    while(this->getline(s)) {
 
111
 
 
112
      // strip '#' comments and whitespace
 
113
      if ((n = s.find('#')) != std::string::npos)
 
114
        s = s.substr(0, n);
 
115
      s = trim_ws(s);
 
116
 
 
117
      if (!s.empty()) {
 
118
        // Handle section name
 
119
        if (*s.begin() == '[' && *s.rbegin() == ']') {
 
120
          m_prefix = s.substr(1, s.size()-2);
 
121
          if (*m_prefix.rbegin() != '.')
 
122
            m_prefix += '.';
 
123
        }
 
124
        else if ((n = s.find('=')) != std::string::npos) {
 
125
 
 
126
          std::string name = m_prefix + trim_ws(s.substr(0, n));
 
127
          std::string option_value = trim_ws(s.substr(n+1));
 
128
 
 
129
          bool registered = allowed_option(name);
 
130
          if (!registered && !m_allow_unregistered)
 
131
            boost::throw_exception(boost::program_options::unknown_option(name));
 
132
 
 
133
          found = true;
 
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);
 
141
          break;
 
142
 
 
143
        } else {
 
144
          boost::throw_exception(boost::program_options::invalid_syntax(s, "Unrecognized line"));
 
145
        }
 
146
      }
 
147
    }
 
148
    if (!found)
 
149
      found_eof();
 
150
  }
 
151
 
 
152
protected: // Stubs for derived classes
 
153
 
 
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; }
 
160
 
 
161
private:
 
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)
 
167
  {
 
168
    std::string s(name);
 
169
    assert(!s.empty());
 
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()) {
 
179
        if (i->find(s) == 0)
 
180
          bad_prefixes = true;                    
 
181
      }
 
182
      if (i != allowed_prefixes.begin()) {
 
183
        --i;
 
184
        if (s.find(*i) == 0)
 
185
          bad_prefixes = true;
 
186
      }
 
187
      if (bad_prefixes)
 
188
        boost::throw_exception(boost::program_options::error("bad prefixes"));
 
189
      allowed_prefixes.insert(s);
 
190
    }
 
191
  }
 
192
 
 
193
 
 
194
  // Returns true if 's' is a registered option name.
 
195
  bool allowed_option(const std::string& s) const
 
196
  {
 
197
    std::set<std::string>::const_iterator i = allowed_options.find(s);
 
198
    if (i != allowed_options.end())
 
199
      return true;        
 
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)
 
205
      return true;
 
206
    return false;
 
207
  }
 
208
 
 
209
 
 
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;
 
217
};
 
218
 
 
219
template<class charT>
 
220
class basic_config_file_iterator :
 
221
  public common_config_file_iterator
 
222
{
 
223
public:
 
224
 
 
225
  basic_config_file_iterator()
 
226
  {
 
227
    found_eof();
 
228
  }
 
229
 
 
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); 
 
234
 
 
235
private: // base overrides
 
236
 
 
237
  bool getline(std::string&);
 
238
 
 
239
private: // internal data
 
240
  boost::shared_ptr<std::basic_istream<charT> > is;
 
241
};
 
242
 
 
243
typedef basic_config_file_iterator<char> config_file_iterator;
 
244
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
 
245
 
 
246
struct null_deleter
 
247
{
 
248
  void operator()(void const *) const {}
 
249
};
 
250
 
 
251
 
 
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)
 
258
{
 
259
  this->is.reset(&in_is, null_deleter());                 
 
260
  get();
 
261
}
 
262
 
 
263
 
 
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>
 
269
bool
 
270
basic_config_file_iterator<charT>::getline(std::string& s)
 
271
{
 
272
  if (std::getline(*is, s)) {
 
273
    return true;
 
274
  } else {
 
275
    return false;
 
276
  }
 
277
}
 
278
 
 
279
} /* namespace detail */
 
280
 
 
281
/** Parse a config file. 
 
282
 
 
283
  Read from given stream.
 
284
*/
 
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)
 
290
{    
 
291
  std::set<std::string> allowed_options;
 
292
 
 
293
  const std::vector<boost::shared_ptr<boost::program_options::option_description> >& options = desc.options();
 
294
  for (unsigned i = 0; i < options.size(); ++i)
 
295
  {
 
296
    const boost::program_options::option_description& d= *options[i];
 
297
 
 
298
    if (d.long_name().empty())
 
299
      boost::throw_exception(
 
300
                             boost::program_options::error("long name required for config file"));
 
301
 
 
302
    allowed_options.insert(d.long_name());
 
303
  }
 
304
 
 
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);
 
313
}
 
314
 
 
315
/** Parse a config file. 
 
316
 
 
317
  Read from file with the given name. The character type is
 
318
  passed to the file stream. 
 
319
*/
 
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)
 
325
 
326
  // Parser return char strings
 
327
  std::basic_ifstream< charT > strm(filename);
 
328
  if (!strm) 
 
329
  {
 
330
    boost::throw_exception("Couldn't open file");
 
331
  }
 
332
  return parse_config_file(strm, desc, allow_unregistered);
 
333
}
 
334
 
 
335
} /* namespace program_options */
 
336
} /* namespace drizzled */
 
337
 
 
338
#endif /* DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H */
 
339