From ab9901f5530beac64373c880635486de12f6e717 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 8 Jan 2010 21:00:57 +0000 Subject: [PATCH] Merge from trunk. [SVN r58818] --- build/Jamfile.v2 | 13 +- example/multiple_sources.cpp | 20 +- example/regex.cpp | 4 +- example/response_file.cpp | 5 +- include/boost/program_options/cmdline.hpp | 15 +- .../boost/program_options/detail/cmdline.hpp | 6 +- .../program_options/detail/value_semantic.hpp | 6 +- include/boost/program_options/errors.hpp | 166 +++++++++++---- include/boost/program_options/option.hpp | 18 +- .../program_options/options_description.hpp | 25 ++- include/boost/program_options/parsers.hpp | 33 +++ .../boost/program_options/value_semantic.hpp | 19 +- .../boost/program_options/variables_map.hpp | 9 +- src/cmdline.cpp | 158 ++++++++++----- src/config_file.cpp | 10 +- src/convert.cpp | 2 +- src/options_description.cpp | 163 ++++++++++----- src/parsers.cpp | 35 +++- src/split.cpp | 62 ++++++ src/value_semantic.cpp | 154 ++++++++++---- src/variables_map.cpp | 80 +++++--- src/winmain.cpp | 8 +- test/Jamfile.v2 | 17 +- test/cmdline_test.cpp | 43 ++-- test/config_test.cfg | 9 + test/exception_test.cpp | 164 +++++++++++++++ test/options_description_test.cpp | 153 +++++++++++++- test/parsers_test.cpp | 58 ++++-- test/required_test.cfg | 1 + test/required_test.cpp | 97 +++++++++ test/split_test.cpp | 189 ++++++++++++++++++ test/test_convert.cpp | 5 +- test/unicode_test.cpp | 5 +- test/unrecognized_test.cpp | 88 ++++++++ test/variable_map_test.cpp | 26 +-- 35 files changed, 1544 insertions(+), 322 deletions(-) create mode 100644 src/split.cpp create mode 100644 test/config_test.cfg create mode 100644 test/exception_test.cpp create mode 100644 test/required_test.cfg create mode 100644 test/required_test.cpp create mode 100644 test/split_test.cpp create mode 100644 test/unrecognized_test.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 1b9d1e9..4d43ee0 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -1,20 +1,19 @@ project boost/program_options - : - source-location ../src + : source-location ../src ; SOURCES = cmdline config_file options_description parsers variables_map value_semantic positional_options utf8_codecvt_facet - convert winmain + convert winmain split ; lib boost_program_options + : $(SOURCES).cpp + : shared:BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's : - $(SOURCES).cpp - : - shared:BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's + : shared:BOOST_PROGRAM_OPTIONS_DYN_LINK=1 ; -boost-install boost_program_options ; \ No newline at end of file +boost-install boost_program_options ; diff --git a/example/multiple_sources.cpp b/example/multiple_sources.cpp index 8679f4e..cdc54d0 100644 --- a/example/multiple_sources.cpp +++ b/example/multiple_sources.cpp @@ -27,13 +27,16 @@ int main(int ac, char* av[]) { try { int opt; + string config_file; // Declare a group of options that will be // allowed only on command line po::options_description generic("Generic options"); generic.add_options() ("version,v", "print version string") - ("help", "produce help message") + ("help", "produce help message") + ("config,c", po::value(&config_file)->default_value("multiple_sources.cfg"), + "name of a file of a configuration.") ; // Declare a group of options that will be @@ -71,10 +74,19 @@ int main(int ac, char* av[]) po::variables_map vm; store(po::command_line_parser(ac, av). options(cmdline_options).positional(p).run(), vm); - - ifstream ifs("multiple_sources.cfg"); - store(parse_config_file(ifs, config_file_options), vm); notify(vm); + + ifstream ifs(config_file.c_str()); + if (!ifs) + { + cout << "can not open config file: " << config_file << "\n"; + return 0; + } + else + { + store(parse_config_file(ifs, config_file_options), vm); + notify(vm); + } if (vm.count("help")) { cout << visible << "\n"; diff --git a/example/regex.cpp b/example/regex.cpp index 17aec74..df98f77 100644 --- a/example/regex.cpp +++ b/example/regex.cpp @@ -43,7 +43,7 @@ public: */ void validate(boost::any& v, const std::vector& values, - magic_number* target_type, int) + magic_number*, int) { static regex r("\\d\\d\\d-(\\d\\d\\d)"); @@ -61,7 +61,7 @@ void validate(boost::any& v, if (regex_match(s, match, r)) { v = any(magic_number(lexical_cast(match[1]))); } else { - throw validation_error("invalid value"); + throw validation_error(validation_error::invalid_option_value); } } diff --git a/example/response_file.cpp b/example/response_file.cpp index 18876a6..ce80f76 100644 --- a/example/response_file.cpp +++ b/example/response_file.cpp @@ -70,7 +70,8 @@ int main(int ac, char* av[]) ss << ifs.rdbuf(); // Split the file content char_separator sep(" \n\r"); - tokenizer > tok(ss.str(), sep); + string sstr = ss.str(); + tokenizer > tok(sstr, sep); vector args; copy(tok.begin(), tok.end(), back_inserter(args)); // Parse the file and store the options @@ -87,7 +88,7 @@ int main(int ac, char* av[]) cout << "Magic value: " << vm["magic"].as() << "\n"; } } - catch(exception& e) { + catch (std::exception& e) { cout << e.what() << "\n"; } } diff --git a/include/boost/program_options/cmdline.hpp b/include/boost/program_options/cmdline.hpp index 976f5d3..8705e60 100644 --- a/include/boost/program_options/cmdline.hpp +++ b/include/boost/program_options/cmdline.hpp @@ -26,7 +26,7 @@ namespace boost { namespace program_options { namespace command_line_style { enum style_t { /// Allow "--long_name" style allow_long = 1, - /// Alow "--foo=10 */ - allow_long_disguise = case_insensitive << 1, + allow_long_disguise = short_case_insensitive << 1, /** The more-or-less traditional unix style. */ unix_style = (allow_short | short_allow_adjacent | short_allow_next | allow_long | long_allow_adjacent | long_allow_next diff --git a/include/boost/program_options/detail/cmdline.hpp b/include/boost/program_options/detail/cmdline.hpp index 0e67f6e..8d60ead 100644 --- a/include/boost/program_options/detail/cmdline.hpp +++ b/include/boost/program_options/detail/cmdline.hpp @@ -108,13 +108,15 @@ namespace boost { namespace program_options { namespace detail { void extra_style_parser(style_parser s); void check_style(int style) const; - + + bool is_style_active(style_t style) const; void init(const std::vector& args); void finish_option(option& opt, - std::vector& other_tokens); + std::vector& other_tokens, + const std::vector& style_parsers); // Copies of input. std::vector args; diff --git a/include/boost/program_options/detail/value_semantic.hpp b/include/boost/program_options/detail/value_semantic.hpp index e82d023..464e306 100644 --- a/include/boost/program_options/detail/value_semantic.hpp +++ b/include/boost/program_options/detail/value_semantic.hpp @@ -33,7 +33,7 @@ namespace boost { namespace program_options { void typed_value::notify(const boost::any& value_store) const { - const T* value = boost::any_cast(&value_store); + const T* value = boost::any_cast(&value_store); if (m_store_to) { *m_store_to = *value; } @@ -55,11 +55,11 @@ namespace boost { namespace program_options { { static std::basic_string empty; if (v.size() > 1) - boost::throw_exception(validation_error("multiple values not allowed")); + boost::throw_exception(validation_error(validation_error::multiple_values_not_allowed)); else if (v.size() == 1) return v.front(); else if (!allow_empty) - boost::throw_exception(validation_error("at least one value required")); + boost::throw_exception(validation_error(validation_error::at_least_one_value_required)); return empty; } diff --git a/include/boost/program_options/errors.hpp b/include/boost/program_options/errors.hpp index 11ff01b..116af58 100644 --- a/include/boost/program_options/errors.hpp +++ b/include/boost/program_options/errors.hpp @@ -25,25 +25,53 @@ namespace boost { namespace program_options { class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error { public: - invalid_syntax(const std::string& tokens, const std::string& msg) - : error(std::string(msg).append(" in '").append(tokens).append("'")), - tokens(tokens), msg(msg) - {} + enum kind_t { + long_not_allowed = 30, + long_adjacent_not_allowed, + short_adjacent_not_allowed, + empty_adjacent_parameter, + missing_parameter, + extra_parameter, + unrecognized_line + }; + + invalid_syntax(const std::string& tokens, kind_t kind); // gcc says that throw specification on dtor is loosened // without this line ~invalid_syntax() throw() {} + + kind_t kind() const; + + const std::string& tokens() const; + + protected: + /** Used to convert kind_t to a related error text */ + static std::string error_message(kind_t kind); + private: // TODO: copy ctor might throw - std::string tokens, msg; + std::string m_tokens; + + kind_t m_kind; }; /** Class thrown when option name is not recognized. */ class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error { public: unknown_option(const std::string& name) - : error(std::string("unknown option ").append(name)) + : error(std::string("unknown option ").append(name)), + m_option_name(name) {} + + // gcc says that throw specification on dtor is loosened + // without this line + ~unknown_option() throw() {} + + const std::string& get_option_name() const throw(); + + private: + std::string m_option_name; }; /** Class thrown when there's ambiguity amoung several possible options. */ @@ -51,21 +79,40 @@ namespace boost { namespace program_options { public: ambiguous_option(const std::string& name, const std::vector& alternatives) - : error(std::string("ambiguous option ").append(name)), - alternatives(alternatives) + : error(std::string("ambiguous option ").append(name)) + , m_alternatives(alternatives) + , m_option_name(name) {} ~ambiguous_option() throw() {} + + const std::string& get_option_name() const throw(); + + const std::vector& alternatives() const throw(); + private: // TODO: copy ctor might throw - std::vector alternatives; + std::vector m_alternatives; + std::string m_option_name; }; /** Class thrown when there are several option values, but user called a method which cannot return them all. */ class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error { public: - multiple_values(const std::string& what) : error(what) {} + multiple_values() + : error("multiple values") + , m_option_name() {} + + ~multiple_values() throw() {} + + void set_option_name(const std::string& option); + + const std::string& get_option_name() const throw(); + + private: + std::string m_option_name; // The name of the option which + // caused the exception. }; /** Class thrown when there are several occurrences of an @@ -73,23 +120,58 @@ namespace boost { namespace program_options { them all. */ class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error { public: - multiple_occurrences(const std::string& what) : error(what) {} + multiple_occurrences() + : error("multiple occurrences") + , m_option_name() {} + + ~multiple_occurrences() throw() {} + + void set_option_name(const std::string& option); + + const std::string& get_option_name() const throw(); + + private: + std::string m_option_name; // The name of the option which + // caused the exception. }; /** Class thrown when value of option is incorrect. */ class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error { public: - validation_error(const std::string& what) : error(what) {} + enum kind_t { + multiple_values_not_allowed = 30, + at_least_one_value_required, + invalid_bool_value, + invalid_option_value, + invalid_option + }; + + validation_error(kind_t kind, + const std::string& option_value = "", + const std::string& option_name = ""); + ~validation_error() throw() {} - void set_option_name(const std::string& option); + void set_option_name(const std::string& option); + + const std::string& get_option_name() const throw(); + const char* what() const throw(); + + protected: + /** Used to convert kind_t to a related error text */ + static std::string error_message(kind_t kind); + private: - mutable std::string m_message; // For on-demand formatting in 'what' + kind_t m_kind; std::string m_option_name; // The name of the option which // caused the exception. + std::string m_option_value; // Optional: value of the option m_options_name + mutable std::string m_message; // For on-demand formatting in 'what' + }; + /** Class thrown if there is an invalid option value givenn */ class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value : public validation_error { @@ -100,39 +182,23 @@ namespace boost { namespace program_options { #endif }; - /** Class thrown when there are too many positional options. */ + /** Class thrown when there are too many positional options. + This is a programming error. + */ class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error { public: - too_many_positional_options_error(const std::string& what) - : error(what) {} - }; - - /** Class thrown when there are too few positional options. */ - class BOOST_PROGRAM_OPTIONS_DECL too_few_positional_options_error : public error { - public: - too_few_positional_options_error(const std::string& what) - : error(what) {} + too_many_positional_options_error() + : error("too many positional options") + {} }; + /** Class thrown when there are syntax errors in given command line */ class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax { public: - enum kind_t { - long_not_allowed = 30, - long_adjacent_not_allowed, - short_adjacent_not_allowed, - empty_adjacent_parameter, - missing_parameter, - extra_parameter - }; - invalid_command_line_syntax(const std::string& tokens, kind_t kind); - kind_t kind() const; - protected: - static std::string error_message(kind_t kind); - private: - kind_t m_kind; }; + /** Class thrown when there are programming error related to style */ class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error { public: invalid_command_line_style(const std::string& msg) @@ -140,6 +206,30 @@ namespace boost { namespace program_options { {} }; + /** Class thrown if config file can not be read */ + class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error { + public: + reading_file(const char* filename) + : error(std::string("can not read file ").append(filename)) + {} + }; + + /** Class thrown when a required/mandatory option is missing */ + class BOOST_PROGRAM_OPTIONS_DECL required_option : public error { + public: + required_option(const std::string& name) + : error(std::string("missing required option ").append(name)) + , m_option_name(name) + {} + + ~required_option() throw() {} + + const std::string& get_option_name() const throw(); + + private: + std::string m_option_name; // The name of the option which + // caused the exception. + }; }} diff --git a/include/boost/program_options/option.hpp b/include/boost/program_options/option.hpp index 635f708..557c692 100644 --- a/include/boost/program_options/option.hpp +++ b/include/boost/program_options/option.hpp @@ -23,10 +23,17 @@ namespace boost { namespace program_options { template class basic_option { public: - basic_option() : position_key(-1), unregistered(false) {} + basic_option() + : position_key(-1) + , unregistered(false) + , case_insensitive(false) + {} basic_option(const std::string& string_key, - const std::vector< std::string> &value) - : string_key(string_key), value(value), unregistered(false) + const std::vector< std::string> &value) + : string_key(string_key) + , value(value) + , unregistered(false) + , case_insensitive(false) {} /** String key of this option. Intentionally independent of the template @@ -50,7 +57,10 @@ namespace boost { namespace program_options { recovered from the "original_tokens" member. */ bool unregistered; - + /** True if string_key has to be handled + case insensitive. + */ + bool case_insensitive; }; typedef basic_option option; typedef basic_option woption; diff --git a/include/boost/program_options/options_description.hpp b/include/boost/program_options/options_description.hpp index da92f5f..0486f02 100644 --- a/include/boost/program_options/options_description.hpp +++ b/include/boost/program_options/options_description.hpp @@ -83,7 +83,8 @@ namespace program_options { /** Given 'option', specified in the input source, return 'true' is 'option' specifies *this. */ - match_result match(const std::string& option, bool approx) const; + match_result match(const std::string& option, bool approx, + bool long_ignore_case, bool short_ignore_case) const; /** Return the key that should identify the option, in particular in the variables_map class. @@ -158,12 +159,18 @@ namespace program_options { static const unsigned m_default_line_length; /** Creates the instance. */ - options_description(unsigned line_length = m_default_line_length); + options_description(unsigned line_length = m_default_line_length, + unsigned min_description_length = m_default_line_length / 2); /** Creates the instance. The 'caption' parameter gives the name of this 'options_description' instance. Primarily useful for output. + The 'description_length' specifies the number of columns that + should be reserved for the description text; if the option text + encroaches into this, then the description will start on the next + line. */ options_description(const std::string& caption, - unsigned line_length = m_default_line_length); + unsigned line_length = m_default_line_length, + unsigned min_description_length = m_default_line_length / 2); /** Adds new variable description. Throws duplicate_variable_error if either short or long name matches that of already present one. */ @@ -185,11 +192,15 @@ namespace program_options { */ options_description_easy_init add_options(); - const option_description& find(const std::string& name, bool approx) - const; + const option_description& find(const std::string& name, + bool approx, + bool long_ignore_case = false, + bool short_ignore_case = false) const; const option_description* find_nothrow(const std::string& name, - bool approx) const; + bool approx, + bool long_ignore_case = false, + bool short_ignore_case = false) const; const std::vector< shared_ptr >& options() const; @@ -213,6 +224,8 @@ namespace program_options { std::string m_caption; const unsigned m_line_length; + const unsigned m_min_description_length; + // Data organization is chosen because: // - there could be two names for one option // - option_add_proxy needs to know the last added option diff --git a/include/boost/program_options/parsers.hpp b/include/boost/program_options/parsers.hpp index eca8c2e..e2a4467 100644 --- a/include/boost/program_options/parsers.hpp +++ b/include/boost/program_options/parsers.hpp @@ -147,6 +147,8 @@ namespace boost { namespace program_options { = ext_parser()); /** Parse a config file. + + Read from given stream. */ template #if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700)) @@ -156,6 +158,19 @@ namespace boost { namespace program_options { parse_config_file(std::basic_istream&, const options_description&, bool allow_unregistered = false); + /** Parse a config file. + + Read from file with the given name. The character type is + passed to the file stream. + */ + template +#if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700)) + BOOST_PROGRAM_OPTIONS_DECL +#endif + basic_parsed_options + parse_config_file(const char* filename, const options_description&, + bool allow_unregistered = false); + /** Controls if the 'collect_unregistered' function should include positional options, or not. */ enum collect_unrecognized_mode @@ -202,6 +217,24 @@ namespace boost { namespace program_options { BOOST_PROGRAM_OPTIONS_DECL parsed_options parse_environment(const options_description&, const char* prefix); + /** Splits a given string to a collection of single strings which + can be passed to command_line_parser. The second parameter is + used to specify a collection of possible seperator chars used + for splitting. The seperator is defaulted to space " ". + Splitting is done in a unix style way, with respect to quotes '"' + and escape characters '\' + */ + BOOST_PROGRAM_OPTIONS_DECL std::vector + split_unix(const std::string& cmdline, const std::string& seperator = " \t", + const std::string& quote = "'\"", const std::string& escape = "\\"); + +#ifndef BOOST_NO_STD_WSTRING + /** @overload */ + BOOST_PROGRAM_OPTIONS_DECL std::vector + split_unix(const std::wstring& cmdline, const std::wstring& seperator = L" \t", + const std::wstring& quote = L"'\"", const std::wstring& escape = L"\\"); +#endif + #ifdef _WIN32 /** Parses the char* string which is passed to WinMain function on windows. This function is provided for convenience, and because it's diff --git a/include/boost/program_options/value_semantic.hpp b/include/boost/program_options/value_semantic.hpp index 40132cd..033009e 100644 --- a/include/boost/program_options/value_semantic.hpp +++ b/include/boost/program_options/value_semantic.hpp @@ -43,6 +43,11 @@ namespace boost { namespace program_options { other sources are discarded. */ virtual bool is_composing() const = 0; + + /** Returns true if value must be given. Non-optional value + + */ + virtual bool is_required() const = 0; /** Parses a group of tokens that specify a value of option. Stores the result in 'value_store', using whatever representation @@ -131,6 +136,8 @@ namespace boost { namespace program_options { unsigned max_tokens() const; bool is_composing() const { return false; } + + bool is_required() const { return false; } /** If 'value_store' is already initialized, or new_tokens has more than one elements, throws. Otherwise, assigns @@ -177,7 +184,8 @@ namespace boost { namespace program_options { the value when it's known. The parameter can be NULL. */ typed_value(T* store_to) : m_store_to(store_to), m_composing(false), - m_multitoken(false), m_zero_tokens(false) + m_multitoken(false), m_zero_tokens(false), + m_required(false) {} /** Specifies default value, which will be used @@ -266,6 +274,12 @@ namespace boost { namespace program_options { return this; } + /** Specifies that the value must occur. */ + typed_value* required() + { + m_required = true; + return this; + } public: // value semantic overrides @@ -292,6 +306,7 @@ namespace boost { namespace program_options { } } + bool is_required() const { return m_required; } /** Creates an instance of the 'validator' class and calls its operator() to perform the actual conversion. */ @@ -335,7 +350,7 @@ namespace boost { namespace program_options { std::string m_default_value_as_text; boost::any m_implicit_value; std::string m_implicit_value_as_text; - bool m_composing, m_implicit, m_multitoken, m_zero_tokens; + bool m_composing, m_implicit, m_multitoken, m_zero_tokens, m_required; boost::function1 m_notifier; }; diff --git a/include/boost/program_options/variables_map.hpp b/include/boost/program_options/variables_map.hpp index cea4d7c..02c4af2 100644 --- a/include/boost/program_options/variables_map.hpp +++ b/include/boost/program_options/variables_map.hpp @@ -92,7 +92,8 @@ namespace boost { namespace program_options { friend BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options& options, variables_map& m, bool); - friend BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m); + + friend BOOST_PROGRAM_OPTIONS_DECL class variables_map; }; /** Implements string->string mapping with convenient value casting @@ -147,6 +148,8 @@ namespace boost { namespace program_options { // Resolve conflict between inherited operators. const variable_value& operator[](const std::string& name) const { return abstract_variables_map::operator[](name); } + + void notify(); private: /** Implementation of abstract_variables_map::get @@ -161,6 +164,10 @@ namespace boost { namespace program_options { void store(const basic_parsed_options& options, variables_map& xm, bool utf8); + + /** Names of required options, filled by parser which has + access to options_description. */ + std::set m_required; }; diff --git a/src/cmdline.cpp b/src/cmdline.cpp index 2d90bae..be31385 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -34,16 +34,18 @@ namespace boost { namespace program_options { using namespace std; using namespace boost::program_options::command_line_style; - invalid_command_line_syntax:: - invalid_command_line_syntax(const std::string& tokens, kind_t kind) - : invalid_syntax(tokens, error_message(kind)), m_kind(kind) + invalid_syntax:: + invalid_syntax(const string& tokens, kind_t kind) + : error(error_message(kind).append(" in '").append(tokens).append("'")) + , m_tokens(tokens) + , m_kind(kind) {} - - std::string - invalid_command_line_syntax::error_message(kind_t kind) + + string + invalid_syntax::error_message(kind_t kind) { // Initially, store the message in 'const char*' variable, - // to avoid conversion to std::string in all cases. + // to avoid conversion to string in all cases. const char* msg; switch(kind) { @@ -65,18 +67,31 @@ namespace boost { namespace program_options { case extra_parameter: msg = "extra parameter"; break; + case unrecognized_line: + msg = "unrecognized line"; + break; default: msg = "unknown error"; } return msg; } - invalid_command_line_syntax::kind_t - invalid_command_line_syntax::kind() const + invalid_syntax::kind_t + invalid_syntax::kind() const { return m_kind; } + + const string& + invalid_syntax::tokens() const + { + return m_tokens; + } + invalid_command_line_syntax:: + invalid_command_line_syntax(const string& tokens, kind_t kind) + : invalid_syntax(tokens, kind) + {} }} @@ -90,7 +105,7 @@ namespace boost { namespace program_options { namespace detail { #endif - cmdline::cmdline(const std::vector& args) + cmdline::cmdline(const vector& args) { init(args); } @@ -107,7 +122,7 @@ namespace boost { namespace program_options { namespace detail { } void - cmdline::init(const std::vector& args) + cmdline::init(const vector& args) { this->args = args; m_style = command_line_style::default_style; @@ -157,6 +172,12 @@ namespace boost { namespace program_options { namespace detail { // Need to check that if guessing and long disguise are enabled // -f will mean the same as -foo } + + bool + cmdline::is_style_active(style_t style) const + { + return ((m_style & style) ? true : false); + } void cmdline::set_options_description(const options_description& desc) @@ -230,12 +251,12 @@ namespace boost { namespace program_options { namespace detail { { vector e; for(unsigned k = 0; k < next.size()-1; ++k) { - finish_option(next[k], e); + finish_option(next[k], e, style_parsers); } // For the last option, pass the unparsed tokens // so that they can be added to next.back()'s values // if appropriate. - finish_option(next.back(), args); + finish_option(next.back(), args, style_parsers); for (unsigned j = 0; j < next.size(); ++j) result.push_back(next[j]); } @@ -269,7 +290,9 @@ namespace boost { namespace program_options { namespace detail { const option_description* xd = m_desc->find_nothrow(opt.string_key, - (m_style & allow_guessing)); + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive)); if (!xd) continue; @@ -326,29 +349,45 @@ namespace boost { namespace program_options { namespace detail { if (opt.position_key != -1) { if (position >= m_positional->max_total_count()) { - boost::throw_exception(too_many_positional_options_error( - "too many positional options")); + boost::throw_exception(too_many_positional_options_error()); } opt.string_key = m_positional->name_for_position(position); ++position; } } } + + // set case sensitive flag + for (unsigned i = 0; i < result.size(); ++i) { + if (result[i].string_key.size() > 2 || + (result[i].string_key.size() > 1 && result[i].string_key[0] != '-')) + { + // it is a long option + result[i].case_insensitive = is_style_active(long_case_insensitive); + } + else + { + // it is a short option + result[i].case_insensitive = is_style_active(short_case_insensitive); + } + } return result; } void cmdline::finish_option(option& opt, - vector& other_tokens) - { + vector& other_tokens, + const vector& style_parsers) + { if (opt.string_key.empty()) return; // First check that the option is valid, and get its description. - // TODO: case-sensitivity. const option_description* xd = m_desc->find_nothrow(opt.string_key, - (m_style & allow_guessing) ? true : false); + is_style_active(allow_guessing), + is_style_active(long_case_insensitive), + is_style_active(short_case_insensitive)); if (!xd) { @@ -379,16 +418,16 @@ namespace boost { namespace program_options { namespace detail { if (present_tokens >= min_tokens) { - if (!opt.value.empty() && max_tokens == 0) { + if (!opt.value.empty() && max_tokens == 0) + { boost::throw_exception(invalid_command_line_syntax(opt.string_key, invalid_command_line_syntax::extra_parameter)); } - // If an option wants, at minimum, N tokens, we grab them - // there and don't care if they look syntactically like an - // option. - - if (opt.value.size() <= min_tokens) + // If an option wants, at minimum, N tokens, we grab them there, + // when adding these tokens as values to current option we check + // if they look like options + if (opt.value.size() <= min_tokens) { min_tokens -= opt.value.size(); } @@ -398,7 +437,27 @@ namespace boost { namespace program_options { namespace detail { } // Everything's OK, move the values to the result. - for(;!other_tokens.empty() && min_tokens--; ) { + for(;!other_tokens.empty() && min_tokens--; ) + { + // check if extra parameter looks like a known option + // we use style parsers to check if it is syntactically an option, + // additionally we check if an option_description exists + vector