00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #pragma once
00011
00012 #include <boost/program_options.hpp>
00013 #include <boost/program_options/eof_iterator.hpp>
00014 #include <boost/static_assert.hpp>
00015 #include <boost/type_traits/is_same.hpp>
00016 #include <boost/shared_ptr.hpp>
00017 #include <boost/algorithm/string.hpp>
00018
00019 #include <boost/noncopyable.hpp>
00020
00021 #include <iosfwd>
00022 #include <vector>
00023 #include <utility>
00024 #include <set>
00025
00026 namespace drizzled
00027 {
00028 namespace program_options
00029 {
00030
00031 typedef std::pair<std::string, std::string> option_result_pair;
00032 std::string parse_suffix(const std::string& arg_val);
00033 option_result_pair parse_size_suffixes(std::string s);
00034 option_result_pair parse_size_arg(std::string s);
00035
00036 std::string parse_suffix(const std::string& arg_val)
00037 {
00038 try
00039 {
00040 size_t size_suffix_pos= arg_val.find_last_of("kmgKMG");
00041 if (size_suffix_pos == arg_val.size()-1)
00042 {
00043 char suffix= arg_val[size_suffix_pos];
00044 std::string size_val(arg_val.substr(0, size_suffix_pos));
00045
00046 uint64_t base_size= boost::lexical_cast<uint64_t>(size_val);
00047 uint64_t new_size= 0;
00048
00049 switch (suffix)
00050 {
00051 case 'K':
00052 case 'k':
00053 new_size= base_size * 1024;
00054 break;
00055 case 'M':
00056 case 'm':
00057 new_size= base_size * 1024 * 1024;
00058 break;
00059 case 'G':
00060 case 'g':
00061 new_size= base_size * 1024 * 1024 * 1024;
00062 break;
00063 }
00064 return boost::lexical_cast<std::string>(new_size);
00065 }
00066 }
00067 catch (std::exception&)
00068 { }
00069
00070 return arg_val;
00071 }
00072
00073 option_result_pair parse_size_suffixes(std::string s)
00074 {
00075 size_t equal_pos= s.find("=");
00076 if (equal_pos != std::string::npos)
00077 {
00078 std::string arg_key(s.substr(0, equal_pos));
00079 std::string arg_val(parse_suffix(s.substr(equal_pos+1)));
00080
00081 if (arg_val != s.substr(equal_pos+1))
00082 {
00083 return std::make_pair(arg_key, arg_val);
00084 }
00085 }
00086
00087 return std::make_pair(std::string(""), std::string(""));
00088 }
00089
00090 option_result_pair parse_size_arg(std::string s)
00091 {
00092 if (s.find("--") == 0)
00093 {
00094 return parse_size_suffixes(s.substr(2));
00095 }
00096 return make_pair(std::string(""), std::string(""));
00097 }
00098
00099 class invalid_syntax :
00100 public boost::program_options::error
00101 {
00102 public:
00103 enum kind_t
00104 {
00105 long_not_allowed = 30,
00106 long_adjacent_not_allowed,
00107 short_adjacent_not_allowed,
00108 empty_adjacent_parameter,
00109 missing_parameter,
00110 extra_parameter,
00111 unrecognized_line
00112 };
00113
00114 invalid_syntax(const std::string& in_tokens, kind_t in_kind);
00115
00116
00117
00118
00119 ~invalid_syntax() throw() {}
00120
00121 kind_t kind() const
00122 {
00123 return m_kind;
00124 }
00125
00126
00127 const std::string& tokens() const
00128 {
00129 return m_tokens;
00130 }
00131
00132
00133 protected:
00135 static std::string error_message(kind_t kind)
00136 {
00137
00138
00139 const char* msg;
00140 switch(kind)
00141 {
00142 case long_not_allowed:
00143 msg = "long options are not allowed";
00144 break;
00145 case long_adjacent_not_allowed:
00146 msg = "parameters adjacent to long options not allowed";
00147 break;
00148 case short_adjacent_not_allowed:
00149 msg = "parameters adjust to short options are not allowed";
00150 break;
00151 case empty_adjacent_parameter:
00152 msg = "adjacent parameter is empty";
00153 break;
00154 case missing_parameter:
00155 msg = "required parameter is missing";
00156 break;
00157 case extra_parameter:
00158 msg = "extra parameter";
00159 break;
00160 case unrecognized_line:
00161 msg = "unrecognized line";
00162 break;
00163 default:
00164 msg = "unknown error";
00165 }
00166 return msg;
00167 }
00168
00169 private:
00170
00171 std::string m_tokens;
00172
00173 kind_t m_kind;
00174 };
00175
00176 invalid_syntax::invalid_syntax(const std::string& in_tokens,
00177 invalid_syntax::kind_t in_kind) :
00178 boost::program_options::error(error_message(in_kind).append(" in '").append(in_tokens).append("'")),
00179 m_tokens(in_tokens),
00180 m_kind(in_kind)
00181 { }
00182
00183 namespace detail
00184 {
00185
00215 class common_config_file_iterator :
00216 public boost::eof_iterator<common_config_file_iterator,
00217 boost::program_options::option>
00218 {
00219 public:
00220 common_config_file_iterator()
00221 {
00222 found_eof();
00223 }
00224
00225 common_config_file_iterator(const std::set<std::string>& in_allowed_options,
00226 bool allow_unregistered) :
00227 allowed_options(in_allowed_options),
00228 m_allow_unregistered(allow_unregistered)
00229 {
00230 for(std::set<std::string>::const_iterator i = allowed_options.begin();
00231 i != allowed_options.end();
00232 ++i)
00233 {
00234 add_option(i->c_str());
00235 }
00236 }
00237
00238 virtual ~common_config_file_iterator() {}
00239
00240 public:
00241
00242 void get()
00243 {
00244 std::string s;
00245 std::string::size_type n;
00246 bool found = false;
00247
00248 while(this->getline(s)) {
00249
00250
00251 if ((n = s.find('#')) != std::string::npos)
00252 s = s.substr(0, n);
00253 boost::trim(s);
00254
00255 if (!s.empty()) {
00256
00257 if (*s.begin() == '[' && *s.rbegin() == ']')
00258 {
00259 m_prefix = s.substr(1, s.size()-2);
00260 if (*m_prefix.rbegin() != '.')
00261 m_prefix += '.';
00262 }
00263 else
00264 {
00265
00266 std::string name;
00267 std::string option_value("true");
00268
00269 if ((n = s.find('=')) != std::string::npos)
00270 {
00271
00272 name = m_prefix + boost::trim_copy(s.substr(0, n));
00273 option_value = boost::trim_copy(parse_suffix(s.substr(n+1)));
00274
00275 }
00276 else
00277 {
00278 name = m_prefix + boost::trim_copy(s);
00279 }
00280
00281 bool registered = allowed_option(name);
00282 if (!registered && !m_allow_unregistered)
00283 boost::throw_exception(boost::program_options::unknown_option(name));
00284
00285 found = true;
00286 this->value().string_key = name;
00287 this->value().value.clear();
00288 this->value().value.push_back(option_value);
00289 this->value().unregistered = !registered;
00290 this->value().original_tokens.clear();
00291 this->value().original_tokens.push_back(name);
00292 this->value().original_tokens.push_back(option_value);
00293 break;
00294
00295 }
00296 }
00297 }
00298 if (!found)
00299 found_eof();
00300 }
00301
00302 protected:
00303
00304
00305
00306
00307
00308
00309 virtual bool getline(std::string&) { return false; }
00310
00311 private:
00316 void add_option(const char* name)
00317 {
00318 std::string s(name);
00319 assert(!s.empty());
00320 if (*s.rbegin() == '*')
00321 {
00322 s.resize(s.size()-1);
00323 bool bad_prefixes(false);
00324
00325
00326
00327
00328 std::set<std::string>::iterator i = allowed_prefixes.lower_bound(s);
00329 if (i != allowed_prefixes.end())
00330 {
00331 if (i->find(s) == 0)
00332 bad_prefixes = true;
00333 }
00334 if (i != allowed_prefixes.begin())
00335 {
00336 --i;
00337 if (s.find(*i) == 0)
00338 bad_prefixes = true;
00339 }
00340 if (bad_prefixes)
00341 boost::throw_exception(boost::program_options::error("bad prefixes"));
00342 allowed_prefixes.insert(s);
00343 }
00344 }
00345
00346
00347
00348 bool allowed_option(const std::string& s) const
00349 {
00350 std::set<std::string>::const_iterator i = allowed_options.find(s);
00351 if (i != allowed_options.end())
00352 return true;
00353
00354
00355
00356 i = allowed_prefixes.lower_bound(s);
00357 if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
00358 return true;
00359 return false;
00360 }
00361
00362
00363
00364
00365 std::set<std::string> allowed_options;
00366
00367 std::set<std::string> allowed_prefixes;
00368 std::string m_prefix;
00369 bool m_allow_unregistered;
00370 };
00371
00372 template<class charT>
00373 class basic_config_file_iterator :
00374 public common_config_file_iterator
00375 {
00376 public:
00377
00378 basic_config_file_iterator()
00379 {
00380 found_eof();
00381 }
00382
00384 basic_config_file_iterator(std::basic_istream<charT>& is,
00385 const std::set<std::string>& allowed_options,
00386 bool allow_unregistered = false);
00387
00388 private:
00389
00390 bool getline(std::string&);
00391
00392 private:
00393 boost::shared_ptr<std::basic_istream<charT> > is;
00394 };
00395
00396 typedef basic_config_file_iterator<char> config_file_iterator;
00397 typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
00398
00399 struct null_deleter
00400 {
00401 void operator()(void const *) const {}
00402 };
00403
00404
00405 template<class charT>
00406 basic_config_file_iterator<charT>::
00407 basic_config_file_iterator(std::basic_istream<charT>& in_is,
00408 const std::set<std::string>& in_allowed_options,
00409 bool in_allow_unregistered) :
00410 common_config_file_iterator(in_allowed_options, in_allow_unregistered)
00411 {
00412 this->is.reset(&in_is, null_deleter());
00413 get();
00414 }
00415
00416
00417
00418
00419
00420
00421 template<class charT>
00422 bool
00423 basic_config_file_iterator<charT>::getline(std::string& s)
00424 {
00425 if (std::getline(*is, s))
00426 {
00427 return true;
00428 }
00429 else
00430 {
00431 return false;
00432 }
00433 }
00434
00435 }
00436
00441 template<class charT>
00442 boost::program_options::basic_parsed_options<charT>
00443 parse_config_file(std::basic_istream<charT>& is,
00444 const boost::program_options::options_description& desc,
00445 bool allow_unregistered = false)
00446 {
00447 std::set<std::string> allowed_options;
00448
00449 const std::vector<boost::shared_ptr<boost::program_options::option_description> >& options = desc.options();
00450 for (unsigned i = 0; i < options.size(); ++i)
00451 {
00452 const boost::program_options::option_description& d= *options[i];
00453
00454 if (d.long_name().empty())
00455 boost::throw_exception(boost::program_options::error("long name required for config file"));
00456
00457 allowed_options.insert(d.long_name());
00458 }
00459
00460
00461 boost::program_options::parsed_options result(&desc);
00462 std::copy(detail::basic_config_file_iterator<charT>(is,
00463 allowed_options,
00464 allow_unregistered),
00465 detail::basic_config_file_iterator<charT>(),
00466 std::back_inserter(result.options));
00467
00468 return boost::program_options::basic_parsed_options<charT>(result);
00469 }
00470
00476 template<class charT>
00477 boost::program_options::basic_parsed_options<charT>
00478 parse_config_file(const char* filename,
00479 const boost::program_options::options_description& desc,
00480 bool allow_unregistered = false)
00481 {
00482
00483 std::basic_ifstream< charT > strm(filename);
00484 if (!strm)
00485 {
00486 boost::throw_exception("Couldn't open file");
00487 }
00488 return parse_config_file(strm, desc, allow_unregistered);
00489 }
00490
00491 }
00492 }
00493
00494