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