diff --git a/doc/howto.xml b/doc/howto.xml index b45b9d2..16b9d7e 100644 --- a/doc/howto.xml +++ b/doc/howto.xml @@ -398,13 +398,49 @@ $ export LC_CTYPE=ru_RU.KOI8-R +
+ Allowing Unknown Options + + Usually, the library throws an exception on unknown option names. This + behaviour can be changed. For example, only some part of your application uses + Program_options, and you wish to pass unrecognized options to another part of + the program, or even to another application. + + To allow unregistered options on the command line, you need to use + the &basic_command_line_parser; class for parsing (not &parse_command_line;) + and call the allow_unregistered + method of that class: + +parsed_options parsed = + command_line_parser(argv, argc).options(desc).allow_unregistered().run(); + + + For each token that looks like an option, but does not have a known name, + an instance of &basic_option; will be added to the result. + The string_key and value fields of the instance will contain results + of syntactic parsing of the token, the unregistered field will be set to true, + and the original_tokens field will contain the token as it appeared on the command line. + + + If you want to pass the unrecognized options further, the + collect_unrecognized function can be used. + The function will collect original tokens for all unrecognized values, and optionally, all found positional options. + Say, if your code handles a few options, but does not handles positional options at all, you can use the function like this: + +vector<string> to_pass_further = collect_arguments(parsed.option, include_positional); + + + + +
+ diff --git a/doc/program_options.ent b/doc/program_options.ent index 4d53cd8..084f605 100644 --- a/doc/program_options.ent +++ b/doc/program_options.ent @@ -37,3 +37,10 @@ command_line_parser"> +basic_command_line_parser"> + + +basic_option"> + diff --git a/doc/todo.txt b/doc/todo.txt index beab9b8..4ee42e1 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,4 +1,6 @@ +Make parse_config_file("foo.cfg", desc) work. + Document handling of positional options which depends on precedding options. I.e scanning the parsed options and creating new variables_map when we see a positional option. (Email from Tony). diff --git a/include/boost/program_options/detail/parsers.hpp b/include/boost/program_options/detail/parsers.hpp index 0328025..fb97ff6 100644 --- a/include/boost/program_options/detail/parsers.hpp +++ b/include/boost/program_options/detail/parsers.hpp @@ -76,6 +76,15 @@ namespace boost { namespace program_options { return *this; } + template + basic_command_line_parser& + basic_command_line_parser::allow_unregistered() + { + detail::cmdline::allow_unregistered(); + return *this; + } + + template basic_parsed_options basic_command_line_parser::run() @@ -101,6 +110,26 @@ namespace boost { namespace program_options { style(style).extra_parser(ext).run(); } + template + std::vector< std::basic_string > + collect_unrecognized(const std::vector< basic_option >& options, + enum collect_unrecognized_mode mode) + { + std::vector< std::basic_string > result; + for(unsigned i = 0; i < options.size(); ++i) + { + if (options[i].unregistered || + (mode == include_positional && options[i].position_key != -1)) + { + copy(options[i].original_tokens.begin(), + options[i].original_tokens.end(), + back_inserter(result)); + } + } + return result; + } + + }} #endif diff --git a/include/boost/program_options/option.hpp b/include/boost/program_options/option.hpp index e5eb0ad..635f708 100644 --- a/include/boost/program_options/option.hpp +++ b/include/boost/program_options/option.hpp @@ -41,10 +41,16 @@ namespace boost { namespace program_options { int position_key; /** Option's value */ std::vector< std::basic_string > value; + /** The original unchanged tokens this option was + created from. */ + std::vector< std::basic_string > original_tokens; /** True if option was not recognized. In that case, 'string_key' and 'value' are results of purely - syntactic parsing of source. */ + syntactic parsing of source. The original tokens can be + recovered from the "original_tokens" member. + */ bool unregistered; + }; typedef basic_option option; typedef basic_option woption; diff --git a/include/boost/program_options/parsers.hpp b/include/boost/program_options/parsers.hpp index e59c18e..10b98a9 100644 --- a/include/boost/program_options/parsers.hpp +++ b/include/boost/program_options/parsers.hpp @@ -73,13 +73,16 @@ namespace boost { namespace program_options { /** Command line parser. The class allows one to specify all the information needed for parsing - and to parser the parse the command line. It is primarily needed to + and to parse the command line. It is primarily needed to emulate named function parameters -- a regular function with 5 parameters will be hard to use and creating overloads with a smaller nuber of parameters will be confusing. For the most common case, the function parse_command_line is a better alternative. + + There are two typedefs -- command_line_parser and wcommand_line_parser, + for charT == char and charT == wchar_t cases. */ template class basic_command_line_parser : private detail::cmdline { @@ -90,7 +93,7 @@ namespace boost { namespace program_options { basic_command_line_parser(const std::vector< std::basic_string >& args); /** Creates a command line parser for the specified arguments - list. The parameter should be the same as passes to 'main'. + list. The parameters should be the same as passed to 'main'. */ basic_command_line_parser(int argc, charT* argv[]); @@ -104,8 +107,21 @@ namespace boost { namespace program_options { basic_command_line_parser& style(int); /** Sets the extra parsers. */ basic_command_line_parser& extra_parser(ext_parser); - + + /** Parses the options and returns the result of parsing. + Throws on error. + */ basic_parsed_options run(); + + /** Specifies that unregistered options are allowed and should + be passed though. For each command like token that looks + like an option but does not contain a recognized name, an + instance of basic_option will be added to result, + with 'unrecognized' field set to 'true'. It's possible to + collect all unrecognized options with the 'collect_unrecognized' + funciton. + */ + basic_command_line_parser& allow_unregistered(); private: const options_description* m_desc; }; @@ -134,6 +150,22 @@ namespace boost { namespace program_options { basic_parsed_options parse_config_file(std::basic_istream&, const options_description&); + /** Controls if the 'collect_unregistered' function should + include positional options, or not. */ + enum collect_unrecognized_mode + { include_positional, exclude_positional }; + + /** Collects the original tokens for all named options with + 'unregistered' flag set. If 'mode' is 'include_positional' + also collects all positional options. + Returns the vector of origianl tokens for all collected + options. + */ + template + std::vector< std::basic_string > + collect_unrecognized(const std::vector< basic_option >& options, + enum collect_unrecognized_mode mode); + /** Parse environment. For each environment variable, the 'name_mapper' function is called to @@ -180,6 +212,7 @@ namespace boost { namespace program_options { split_winmain(const std::wstring& cmdline); #endif #endif + }} diff --git a/src/cmdline.cpp b/src/cmdline.cpp index 246677c..e4cae7f 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -248,6 +248,7 @@ namespace boost { namespace program_options { namespace detail { if (!ok) { option opt; opt.value.push_back(args[0]); + opt.original_tokens.push_back(args[0]); result.push_back(opt); args.erase(args.begin()); } @@ -331,6 +332,7 @@ namespace boost { namespace program_options { namespace detail { // Everything's OK, move the values to the result. for(;!other_tokens.empty() && max_tokens--; ) { opt.value.push_back(other_tokens[0]); + opt.original_tokens.push_back(other_tokens[0]); other_tokens.erase(other_tokens.begin()); } } @@ -368,6 +370,7 @@ namespace boost { namespace program_options { namespace detail { opt.string_key = name; if (!adjacent.empty()) opt.value.push_back(adjacent); + opt.original_tokens.push_back(tok); result.push_back(opt); args.erase(args.begin()); } @@ -416,6 +419,7 @@ namespace boost { namespace program_options { namespace detail { option opt; opt.string_key = name; + opt.original_tokens.push_back(tok); if (!adjacent.empty()) opt.value.push_back(adjacent); result.push_back(opt); @@ -442,6 +446,7 @@ namespace boost { namespace program_options { namespace detail { opt.string_key = name; if (!adjacent.empty()) opt.value.push_back(adjacent); + opt.original_tokens.push_back(tok); result.push_back(opt); args.erase(args.begin()); }