~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/program_options/config_file.h

  • Committer: Monty Taylor
  • Date: 2010-04-15 19:14:53 UTC
  • mto: This revision was merged to the branch mainline in revision 1476.
  • Revision ID: mordred@inaugust.com-20100415191453-ril2x8qdo78fny9w
Replaced test_authz with a plugin implementing a hard-coded simple
multi-tennancy policy. The policy describes:
- A root user exists which can do anything
- A user may only see a schema that is named the same has his user name
- A user may see data_dictionary and information_schema (data_dictionary
  required for show databases to work)

This way, we can more clearly test the results of the authorization
interface while providing an optional plugin that is actually useful to some
human.

Show diffs side-by-side

added added

removed removed

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