Drizzled Public API Documentation

config_file.h
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 #pragma once
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>
16 #include <boost/algorithm/string.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 namespace program_options {
27 
28 typedef std::pair<std::string, std::string> option_result_pair;
29 std::string parse_suffix(const std::string& arg_val);
30 option_result_pair parse_size_suffixes(std::string s);
31 option_result_pair parse_size_arg(std::string s);
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  }
64  catch (std::exception&)
65  { }
66 
67  return arg_val;
68 }
69 
70 option_result_pair parse_size_suffixes(std::string s)
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 
87 option_result_pair parse_size_arg(std::string s)
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 
97  public boost::program_options::error
98 {
99 public:
100  enum kind_t
101  {
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:
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) :
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 { }
179 
180 namespace detail
181 {
182 
213  public boost::eof_iterator<common_config_file_iterator,
214  boost::program_options::option>
215 {
216 public:
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);
250  boost::trim(s);
251 
252  if (!s.empty()) {
253  // Handle section name
254  if (*s.begin() == '[' && *s.rbegin() == ']')
255  {
256  m_prefix = s.substr(1, s.size()-2);
257  if (*m_prefix.rbegin() != '.')
258  m_prefix += '.';
259  }
260  else
261  {
262 
263  std::string name;
264  std::string option_value("true");
265 
266  if ((n = s.find('=')) != std::string::npos)
267  {
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  }
273  else
274  {
275  name = m_prefix + boost::trim_copy(s);
276  }
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:
313  void add_option(const char* name)
314  {
315  std::string s(name);
316  assert(!s.empty());
317  if (*s.rbegin() == '*')
318  {
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);
326  if (i != allowed_prefixes.end())
327  {
328  if (i->find(s) == 0)
329  bad_prefixes = true;
330  }
331  if (i != allowed_prefixes.begin())
332  {
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>
372 {
373 public:
374 
376  {
377  found_eof();
378  }
379 
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 
395 
397 {
398  void operator()(void const *) const {}
399 };
400 
401 
402 template<class 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
421 {
422  if (std::getline(*is, s))
423  {
424  return true;
425  }
426  else
427  {
428  return false;
429  }
430 }
431 
432 } /* namespace detail */
433 
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())
452  boost::throw_exception(boost::program_options::error("long name required for config file"));
453 
454  allowed_options.insert(d.long_name());
455  }
456 
457  // Parser return char strings
458  boost::program_options::parsed_options result(&desc);
459  std::copy(detail::basic_config_file_iterator<charT>(is,
460  allowed_options,
461  allow_unregistered),
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 
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 */
Definition: vertex.h:34
static std::string error_message(kind_t kind)
Definition: config_file.h:132
TODO: Rename this file - func.h is stupid.