// Copyright Vladimir Prus 2004. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #define BOOST_PROGRAM_OPTIONS_SOURCE #include <boost/program_options/config.hpp> #include <boost/program_options/value_semantic.hpp> #include <boost/program_options/detail/convert.hpp> #include <boost/program_options/detail/cmdline.hpp> #include <set> #include <cctype> namespace boost { namespace program_options { using namespace std; #ifndef BOOST_NO_STD_WSTRING namespace { std::string convert_value(const std::wstring& s) { try { return to_local_8_bit(s); } catch(const std::exception&) { return "<unrepresentable unicode string>"; } } } #endif void value_semantic_codecvt_helper<char>:: parse(boost::any& value_store, const std::vector<std::string>& new_tokens, bool utf8) const { if (utf8) { #ifndef BOOST_NO_STD_WSTRING // Need to convert to local encoding. std::vector<string> local_tokens; for (unsigned i = 0; i < new_tokens.size(); ++i) { std::wstring w = from_utf8(new_tokens[i]); local_tokens.push_back(to_local_8_bit(w)); } xparse(value_store, local_tokens); #else boost::throw_exception( std::runtime_error("UTF-8 conversion not supported.")); #endif } else { // Already in local encoding, pass unmodified xparse(value_store, new_tokens); } } #ifndef BOOST_NO_STD_WSTRING void value_semantic_codecvt_helper<wchar_t>:: parse(boost::any& value_store, const std::vector<std::string>& new_tokens, bool utf8) const { std::vector<wstring> tokens; if (utf8) { // Convert from utf8 for (unsigned i = 0; i < new_tokens.size(); ++i) { tokens.push_back(from_utf8(new_tokens[i])); } } else { // Convert from local encoding for (unsigned i = 0; i < new_tokens.size(); ++i) { tokens.push_back(from_local_8_bit(new_tokens[i])); } } xparse(value_store, tokens); } #endif BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg"); std::string untyped_value::name() const { return arg; } unsigned untyped_value::min_tokens() const { if (m_zero_tokens) return 0; else return 1; } unsigned untyped_value::max_tokens() const { if (m_zero_tokens) return 0; else return 1; } void untyped_value::xparse(boost::any& value_store, const std::vector<std::string>& new_tokens) const { if (!value_store.empty()) boost::throw_exception( multiple_occurrences()); if (new_tokens.size() > 1) boost::throw_exception(multiple_values()); value_store = new_tokens.empty() ? std::string("") : new_tokens.front(); } BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>* bool_switch() { return bool_switch(0); } BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>* bool_switch(bool* v) { typed_value<bool>* r = new typed_value<bool>(v); r->default_value(0); r->zero_tokens(); return r; } /* Validates bool value. Any of "1", "true", "yes", "on" will be converted to "1".<br> Any of "0", "false", "no", "off" will be converted to "0".<br> Case is ignored. The 'xs' vector can either be empty, in which case the value is 'true', or can contain explicit value. */ BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs, bool*, int) { check_first_occurrence(v); string s(get_single_string(xs, true)); for (size_t i = 0; i < s.size(); ++i) s[i] = char(tolower(s[i])); if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true") v = any(true); else if (s == "off" || s == "no" || s == "0" || s == "false") v = any(false); else boost::throw_exception(invalid_bool_value(s)); } // This is blatant copy-paste. However, templating this will cause a problem, // since wstring can't be constructed/compared with char*. We'd need to // create auxiliary 'widen' routine to convert from char* into // needed string type, and that's more work. #if !defined(BOOST_NO_STD_WSTRING) BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<wstring>& xs, bool*, int) { check_first_occurrence(v); wstring s(get_single_string(xs, true)); for (size_t i = 0; i < s.size(); ++i) s[i] = wchar_t(tolower(s[i])); if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true") v = any(true); else if (s == L"off" || s == L"no" || s == L"0" || s == L"false") v = any(false); else boost::throw_exception(invalid_bool_value(convert_value(s))); } #endif BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs, std::string*, int) { check_first_occurrence(v); v = any(get_single_string(xs)); } #if !defined(BOOST_NO_STD_WSTRING) BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<wstring>& xs, std::string*, int) { check_first_occurrence(v); v = any(get_single_string(xs)); } #endif namespace validators { BOOST_PROGRAM_OPTIONS_DECL void check_first_occurrence(const boost::any& value) { if (!value.empty()) boost::throw_exception( multiple_occurrences()); } } invalid_option_value:: invalid_option_value(const std::string& bad_value) : validation_error(validation_error::invalid_option_value) { set_substitute("value", bad_value); } #ifndef BOOST_NO_STD_WSTRING invalid_option_value:: invalid_option_value(const std::wstring& bad_value) : validation_error(validation_error::invalid_option_value) { set_substitute("value", convert_value(bad_value)); } #endif invalid_bool_value:: invalid_bool_value(const std::string& bad_value) : validation_error(validation_error::invalid_bool_value) { set_substitute("value", bad_value); } error_with_option_name::error_with_option_name( const std::string& template_, const std::string& option_name, const std::string& original_token, int option_style) : error(template_), m_option_style(option_style), m_error_template(template_) { // parameter | placeholder | value // --------- | ----------- | ----- set_substitute_default("canonical_option", "option '%canonical_option%'", "option"); set_substitute_default("value", "argument ('%value%')", "argument"); set_substitute_default("prefix", "%prefix%", ""); m_substitutions["option"] = option_name; m_substitutions["original_token"] = original_token; } const char* error_with_option_name::what() const throw() { // will substitute tokens each time what is run() substitute_placeholders(m_error_template); return m_message.c_str(); } void error_with_option_name::replace_token(const string& from, const string& to) const { for (;;) { std::size_t pos = m_message.find(from.c_str(), 0, from.length()); // not found: all replaced if (pos == std::string::npos) return; m_message.replace(pos, from.length(), to); } } string error_with_option_name::get_canonical_option_prefix() const { switch (m_option_style) { case command_line_style::allow_dash_for_short: return "-"; case command_line_style::allow_slash_for_short: return "/"; case command_line_style::allow_long_disguise: return "-"; case command_line_style::allow_long: return "--"; case 0: return ""; } throw std::logic_error("error_with_option_name::m_option_style can only be " "one of [0, allow_dash_for_short, allow_slash_for_short, " "allow_long_disguise or allow_long]"); } string error_with_option_name::get_canonical_option_name() const { if (!m_substitutions.find("option")->second.length()) return m_substitutions.find("original_token")->second; string original_token = strip_prefixes(m_substitutions.find("original_token")->second); string option_name = strip_prefixes(m_substitutions.find("option")->second); // For long options, use option name if (m_option_style == command_line_style::allow_long || m_option_style == command_line_style::allow_long_disguise) return get_canonical_option_prefix() + option_name; // For short options use first letter of original_token if (m_option_style && original_token.length()) return get_canonical_option_prefix() + original_token[0]; // no prefix return option_name; } void error_with_option_name::substitute_placeholders(const string& error_template) const { m_message = error_template; std::map<std::string, std::string> substitutions(m_substitutions); substitutions["canonical_option"] = get_canonical_option_name(); substitutions["prefix"] = get_canonical_option_prefix(); // // replace placeholder with defaults if values are missing // for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin(); iter != m_substitution_defaults.end(); ++iter) { // missing parameter: use default if (substitutions.count(iter->first) == 0 || substitutions[iter->first].length() == 0) replace_token(iter->second.first, iter->second.second); } // // replace placeholder with values // placeholder are denoted by surrounding '%' // for (map<string, string>::iterator iter = substitutions.begin(); iter != substitutions.end(); ++iter) replace_token('%' + iter->first + '%', iter->second); } void ambiguous_option::substitute_placeholders(const string& original_error_template) const { // For short forms, all alternatives must be identical, by // definition, to the specified option, so we don't need to // display alternatives if (m_option_style == command_line_style::allow_dash_for_short || m_option_style == command_line_style::allow_slash_for_short) { error_with_option_name::substitute_placeholders(original_error_template); return; } string error_template = original_error_template; // remove duplicates using std::set std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end()); std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end()); error_template += " and matches "; // Being very cautious: should be > 1 alternative! if (alternatives_vec.size() > 1) { for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i) error_template += "'%prefix%" + alternatives_vec[i] + "', "; error_template += "and "; } // there is a programming error if multiple options have the same name... if (m_alternatives.size() > 1 && alternatives_vec.size() == 1) error_template += "different versions of "; error_template += "'%prefix%" + alternatives_vec.back() + "'"; // use inherited logic error_with_option_name::substitute_placeholders(error_template); } string validation_error::get_template(kind_t kind) { // Initially, store the message in 'const char*' variable, // to avoid conversion to std::string in all cases. const char* msg; switch(kind) { case invalid_bool_value: msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'"; break; case invalid_option_value: msg = "the argument ('%value%') for option '%canonical_option%' is invalid"; break; case multiple_values_not_allowed: msg = "option '%canonical_option%' only takes a single argument"; break; case at_least_one_value_required: msg = "option '%canonical_option%' requires at least one argument"; break; // currently unused case invalid_option: msg = "option '%canonical_option%' is not valid"; break; default: msg = "unknown error"; } return msg; } }}