~skinny.moey/drizzle/innodb-replication

« back to all changes in this revision

Viewing changes to drizzled/program_options/config_file.h

  • Committer: Brian Aker
  • Date: 2010-11-08 22:35:57 UTC
  • mfrom: (1802.1.114 trunk)
  • Revision ID: brian@tangent.org-20101108223557-w3xzwp9hjjtjhtc1
MergeĀ inĀ trunk.

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
#include <boost/algorithm/string.hpp>
 
18
 
 
19
#include <boost/noncopyable.hpp>
 
20
 
 
21
#include <iosfwd>
 
22
#include <vector>
 
23
#include <utility>
 
24
#include <set>
 
25
 
 
26
namespace drizzled
 
27
{
 
28
namespace program_options
 
29
{
 
30
 
 
31
class invalid_syntax :
 
32
  public boost::program_options::error
 
33
{
 
34
public:
 
35
  enum kind_t {
 
36
    long_not_allowed = 30,
 
37
    long_adjacent_not_allowed,
 
38
    short_adjacent_not_allowed,
 
39
    empty_adjacent_parameter,
 
40
    missing_parameter,
 
41
    extra_parameter,
 
42
    unrecognized_line
 
43
  };
 
44
 
 
45
  invalid_syntax(const std::string& in_tokens, kind_t in_kind);
 
46
 
 
47
 
 
48
  // gcc says that throw specification on dtor is loosened
 
49
  // without this line
 
50
  ~invalid_syntax() throw() {}
 
51
 
 
52
  kind_t kind() const
 
53
  {
 
54
    return m_kind;
 
55
  }
 
56
 
 
57
 
 
58
  const std::string& tokens() const
 
59
  {
 
60
    return m_tokens;
 
61
  }
 
62
 
 
63
 
 
64
protected:
 
65
  /** Used to convert kind_t to a related error text */
 
66
  static std::string error_message(kind_t kind)
 
67
  {
 
68
    // Initially, store the message in 'const char*' variable, to avoid
 
69
    // conversion to string in all cases.
 
70
    const char* msg;
 
71
    switch(kind)
 
72
    {
 
73
    case long_not_allowed:
 
74
      msg = "long options are not allowed";
 
75
      break;
 
76
    case long_adjacent_not_allowed:
 
77
      msg = "parameters adjacent to long options not allowed";
 
78
      break;
 
79
    case short_adjacent_not_allowed:
 
80
      msg = "parameters adjust to short options are not allowed";
 
81
      break;
 
82
    case empty_adjacent_parameter:
 
83
      msg = "adjacent parameter is empty";
 
84
      break;
 
85
    case missing_parameter:
 
86
      msg = "required parameter is missing";
 
87
      break;
 
88
    case extra_parameter:
 
89
      msg = "extra parameter";
 
90
      break;
 
91
    case unrecognized_line:
 
92
      msg = "unrecognized line";
 
93
      break;
 
94
    default:
 
95
      msg = "unknown error";
 
96
    }
 
97
    return msg;
 
98
  }
 
99
 
 
100
private:
 
101
  // TODO: copy ctor might throw
 
102
  std::string m_tokens;
 
103
 
 
104
  kind_t m_kind;
 
105
};
 
106
 
 
107
invalid_syntax::invalid_syntax(const std::string& in_tokens,
 
108
                               invalid_syntax::kind_t in_kind) :
 
109
 boost::program_options::error(error_message(in_kind).append(" in '").append(in_tokens).append("'"))
 
110
, m_tokens(in_tokens)
 
111
  , m_kind(in_kind)
 
112
{}
 
113
 
 
114
namespace detail
 
115
{
 
116
 
 
117
/** Standalone parser for config files in ini-line format.
 
118
  The parser is a model of single-pass lvalue iterator, and
 
119
  default constructor creates past-the-end-iterator. The typical usage is:
 
120
  config_file_iterator i(is, ... set of options ...), e;
 
121
  for(; i !=e; ++i) {
 
122
 *i;
 
123
 }
 
124
 
 
125
 Syntax conventions:
 
126
 
 
127
 - config file can not contain positional options
 
128
 - '#' is comment character: it is ignored together with
 
129
 the rest of the line.
 
130
 - variable assignments are in the form
 
131
 name '=' value.
 
132
 spaces around '=' are trimmed.
 
133
 - Section names are given in brackets. 
 
134
 
 
135
 The actual option name is constructed by combining current section
 
136
 name and specified option name, with dot between. If section_name 
 
137
 already contains dot at the end, new dot is not inserted. For example:
 
138
 @verbatim
 
139
 [gui.accessibility]
 
140
 visual_bell=yes
 
141
 @endverbatim
 
142
 will result in option "gui.accessibility.visual_bell" with value
 
143
 "yes" been returned.
 
144
 
 
145
 */    
 
146
class common_config_file_iterator :
 
147
  public boost::eof_iterator<common_config_file_iterator,
 
148
                             boost::program_options::option>
 
149
{
 
150
public:
 
151
  common_config_file_iterator()
 
152
  {
 
153
    found_eof();
 
154
  }
 
155
 
 
156
  common_config_file_iterator(const std::set<std::string>& in_allowed_options,
 
157
                              bool allow_unregistered) :
 
158
    allowed_options(in_allowed_options),
 
159
    m_allow_unregistered(allow_unregistered)
 
160
  {
 
161
    for(std::set<std::string>::const_iterator i = allowed_options.begin();
 
162
        i != allowed_options.end(); 
 
163
        ++i)
 
164
    {
 
165
      add_option(i->c_str());
 
166
    }
 
167
  }
 
168
 
 
169
  virtual ~common_config_file_iterator() {}
 
170
 
 
171
public: // Method required by eof_iterator
 
172
 
 
173
  void get()
 
174
  {
 
175
    std::string s;
 
176
    std::string::size_type n;
 
177
    bool found = false;
 
178
 
 
179
    while(this->getline(s)) {
 
180
 
 
181
      // strip '#' comments and whitespace
 
182
      if ((n = s.find('#')) != std::string::npos)
 
183
        s = s.substr(0, n);
 
184
      boost::trim(s);
 
185
 
 
186
      if (!s.empty()) {
 
187
        // Handle section name
 
188
        if (*s.begin() == '[' && *s.rbegin() == ']') {
 
189
          m_prefix = s.substr(1, s.size()-2);
 
190
          if (*m_prefix.rbegin() != '.')
 
191
            m_prefix += '.';
 
192
        }
 
193
        else if ((n = s.find('=')) != std::string::npos) {
 
194
 
 
195
          std::string name = m_prefix + boost::trim_copy(s.substr(0, n));
 
196
          std::string option_value = boost::trim_copy(s.substr(n+1));
 
197
 
 
198
          bool registered = allowed_option(name);
 
199
          if (!registered && !m_allow_unregistered)
 
200
            boost::throw_exception(boost::program_options::unknown_option(name));
 
201
 
 
202
          found = true;
 
203
          this->value().string_key = name;
 
204
          this->value().value.clear();
 
205
          this->value().value.push_back(option_value);
 
206
          this->value().unregistered = !registered;
 
207
          this->value().original_tokens.clear();
 
208
          this->value().original_tokens.push_back(name);
 
209
          this->value().original_tokens.push_back(option_value);
 
210
          break;
 
211
 
 
212
        } else {
 
213
          boost::throw_exception(invalid_syntax(s, invalid_syntax::unrecognized_line));
 
214
        }
 
215
      }
 
216
    }
 
217
    if (!found)
 
218
      found_eof();
 
219
  }
 
220
 
 
221
protected: // Stubs for derived classes
 
222
 
 
223
  // Obtains next line from the config file
 
224
  // Note: really, this design is a bit ugly
 
225
  // The most clean thing would be to pass 'line_iterator' to
 
226
  // constructor of this class, but to avoid templating this class
 
227
  // we'd need polymorphic iterator, which does not exist yet.
 
228
  virtual bool getline(std::string&) { return false; }
 
229
 
 
230
private:
 
231
  /** Adds another allowed option. If the 'name' ends with
 
232
    '*', then all options with the same prefix are
 
233
    allowed. For example, if 'name' is 'foo*', then 'foo1' and
 
234
    'foo_bar' are allowed. */
 
235
  void add_option(const char* name)
 
236
  {
 
237
    std::string s(name);
 
238
    assert(!s.empty());
 
239
    if (*s.rbegin() == '*') {
 
240
      s.resize(s.size()-1);
 
241
      bool bad_prefixes(false);
 
242
      // If 's' is a prefix of one of allowed suffix, then
 
243
      // lower_bound will return that element.
 
244
      // If some element is prefix of 's', then lower_bound will
 
245
      // return the next element.
 
246
      std::set<std::string>::iterator i = allowed_prefixes.lower_bound(s);
 
247
      if (i != allowed_prefixes.end()) {
 
248
        if (i->find(s) == 0)
 
249
          bad_prefixes = true;                    
 
250
      }
 
251
      if (i != allowed_prefixes.begin()) {
 
252
        --i;
 
253
        if (s.find(*i) == 0)
 
254
          bad_prefixes = true;
 
255
      }
 
256
      if (bad_prefixes)
 
257
        boost::throw_exception(boost::program_options::error("bad prefixes"));
 
258
      allowed_prefixes.insert(s);
 
259
    }
 
260
  }
 
261
 
 
262
 
 
263
  // Returns true if 's' is a registered option name.
 
264
  bool allowed_option(const std::string& s) const
 
265
  {
 
266
    std::set<std::string>::const_iterator i = allowed_options.find(s);
 
267
    if (i != allowed_options.end())
 
268
      return true;        
 
269
    // If s is "pa" where "p" is allowed prefix then
 
270
    // lower_bound should find the element after "p". 
 
271
    // This depends on 'allowed_prefixes' invariant.
 
272
    i = allowed_prefixes.lower_bound(s);
 
273
    if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
 
274
      return true;
 
275
    return false;
 
276
  }
 
277
 
 
278
 
 
279
  // That's probably too much data for iterator, since
 
280
  // it will be copied, but let's not bother for now.
 
281
  std::set<std::string> allowed_options;
 
282
  // Invariant: no element is prefix of other element.
 
283
  std::set<std::string> allowed_prefixes;
 
284
  std::string m_prefix;
 
285
  bool m_allow_unregistered;
 
286
};
 
287
 
 
288
template<class charT>
 
289
class basic_config_file_iterator :
 
290
  public common_config_file_iterator
 
291
{
 
292
public:
 
293
 
 
294
  basic_config_file_iterator()
 
295
  {
 
296
    found_eof();
 
297
  }
 
298
 
 
299
  /** Creates a config file parser for the specified stream. */
 
300
  basic_config_file_iterator(std::basic_istream<charT>& is, 
 
301
                             const std::set<std::string>& allowed_options,
 
302
                             bool allow_unregistered = false); 
 
303
 
 
304
private: // base overrides
 
305
 
 
306
  bool getline(std::string&);
 
307
 
 
308
private: // internal data
 
309
  boost::shared_ptr<std::basic_istream<charT> > is;
 
310
};
 
311
 
 
312
typedef basic_config_file_iterator<char> config_file_iterator;
 
313
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
 
314
 
 
315
struct null_deleter
 
316
{
 
317
  void operator()(void const *) const {}
 
318
};
 
319
 
 
320
 
 
321
template<class charT>
 
322
basic_config_file_iterator<charT>::
 
323
basic_config_file_iterator(std::basic_istream<charT>& in_is, 
 
324
                           const std::set<std::string>& in_allowed_options,
 
325
                           bool in_allow_unregistered) :
 
326
  common_config_file_iterator(in_allowed_options, in_allow_unregistered)
 
327
{
 
328
  this->is.reset(&in_is, null_deleter());                 
 
329
  get();
 
330
}
 
331
 
 
332
 
 
333
// Specializing this function for wchar_t causes problems on
 
334
// borland and vc7, as well as on metrowerks. On the first two
 
335
// I don't know a workaround, so make use of 'to_internal' to
 
336
// avoid specialization.
 
337
template<class charT>
 
338
bool
 
339
basic_config_file_iterator<charT>::getline(std::string& s)
 
340
{
 
341
  if (std::getline(*is, s)) {
 
342
    return true;
 
343
  } else {
 
344
    return false;
 
345
  }
 
346
}
 
347
 
 
348
} /* namespace detail */
 
349
 
 
350
/** Parse a config file. 
 
351
 
 
352
  Read from given stream.
 
353
*/
 
354
template<class charT>
 
355
boost::program_options::basic_parsed_options<charT>
 
356
parse_config_file(std::basic_istream<charT>& is,
 
357
                  const boost::program_options::options_description& desc,
 
358
                  bool allow_unregistered = false)
 
359
{    
 
360
  std::set<std::string> allowed_options;
 
361
 
 
362
  const std::vector<boost::shared_ptr<boost::program_options::option_description> >& options = desc.options();
 
363
  for (unsigned i = 0; i < options.size(); ++i)
 
364
  {
 
365
    const boost::program_options::option_description& d= *options[i];
 
366
 
 
367
    if (d.long_name().empty())
 
368
      boost::throw_exception(
 
369
                             boost::program_options::error("long name required for config file"));
 
370
 
 
371
    allowed_options.insert(d.long_name());
 
372
  }
 
373
 
 
374
  // Parser return char strings
 
375
  boost::program_options::parsed_options result(&desc);        
 
376
  std::copy(detail::basic_config_file_iterator<charT>(
 
377
                                                      is, allowed_options, allow_unregistered), 
 
378
       detail::basic_config_file_iterator<charT>(), 
 
379
       std::back_inserter(result.options));
 
380
  // Convert char strings into desired type.
 
381
  return boost::program_options::basic_parsed_options<charT>(result);
 
382
}
 
383
 
 
384
/** Parse a config file. 
 
385
 
 
386
  Read from file with the given name. The character type is
 
387
  passed to the file stream. 
 
388
*/
 
389
template<class charT>
 
390
boost::program_options::basic_parsed_options<charT>
 
391
parse_config_file(const char* filename,
 
392
                  const boost::program_options::options_description& desc,
 
393
                  bool allow_unregistered = false)
 
394
 
395
  // Parser return char strings
 
396
  std::basic_ifstream< charT > strm(filename);
 
397
  if (!strm) 
 
398
  {
 
399
    boost::throw_exception("Couldn't open file");
 
400
  }
 
401
  return parse_config_file(strm, desc, allow_unregistered);
 
402
}
 
403
 
 
404
} /* namespace program_options */
 
405
} /* namespace drizzled */
 
406
 
 
407
#endif /* DRIZZLED_PROGRAM_OPTIONS_CONFIG_FILE_H */
 
408