Compare commits

...

22 Commits

Author SHA1 Message Date
Beman Dawes
730b5f2169 Release 1.41.0
[SVN r57747]
2009-11-18 14:58:26 +00:00
Troy D. Straszheim
5820ee9f7f rm cmake from the release branch before it goes out broken. Policy dictates that you never commit to release, you commit to trunk and merge to release.
[SVN r56941]
2009-10-17 01:10:45 +00:00
Troy D. Straszheim
67ba23b8d9 Add basic copyright/license to keep cmake out of the inspection report
[SVN r55095]
2009-07-22 21:51:01 +00:00
Vladimir Prus
c3cb7cf05d Merge from trunk
[SVN r54884]
2009-07-11 12:00:18 +00:00
Jeremiah Willcock
bdbd3dfc42 Fixed almost all tab and min/max issues found by inspect tool
[SVN r53142]
2009-05-20 19:41:20 +00:00
Douglas Gregor
15c990b7af Fix CMake build of Program Options DLL tests
[SVN r52847]
2009-05-08 06:13:08 +00:00
Douglas Gregor
1a884cef74 Fix CMake test builds for ProgramOptions and Units libraries.
[SVN r52843]
2009-05-07 23:53:56 +00:00
Vladimir Prus
3c6401b19a Prevent multitoken options from eating tokens after '--' separator.
Thanks to Gevorg Voskanyan for the bug report and the testcase.


[SVN r52440]
2009-04-17 09:45:00 +00:00
Vladimir Prus
ed4847e0a7 Fix warnings
[SVN r52439]
2009-04-17 09:35:10 +00:00
Vladimir Prus
6eef67f5ba Merge from trunk
[SVN r52211]
2009-04-06 09:43:03 +00:00
Vladimir Prus
91d9cabb51 When processing value multitoken options, don't eat futher options.
Fixes #469.


[SVN r52154]
2009-04-03 13:42:29 +00:00
John Maddock
22e8d11888 Merge PDF build changes from Trunk.
[SVN r51417]
2009-02-23 18:39:32 +00:00
Troy D. Straszheim
b55001a061 merge of cmake build files from trunk per beman
[SVN r50756]
2009-01-24 18:57:20 +00:00
Vladimir Prus
2a16575f98 Merge from trunk. No code changes, only properties and docs and examples
[SVN r47336]
2008-07-12 09:03:18 +00:00
Vladimir Prus
f14a369077 Merge r43992
[SVN r47335]
2008-07-12 08:58:04 +00:00
Vladimir Prus
9df5124fed Don't hardcode cout.
Suggested by Guido Ziliotti.


[SVN r47285]
2008-07-10 09:40:04 +00:00
Vladimir Prus
86487c7b63 Add example of zero_tokens
[SVN r45450]
2008-05-17 07:28:17 +00:00
Vladimir Prus
6cd68a2fd9 Clarify docs. Fixes #1530.
[SVN r45448]
2008-05-17 07:06:59 +00:00
Vladimir Prus
f0eae2ccfe Merge from trunk:
Tolerate argc being zero.

Patch from C. K. Jester-Young.


[SVN r43331]
2008-02-20 14:55:25 +00:00
Beman Dawes
b2a01d9405 Full merge from trunk at revision 41356 of entire boost-root tree.
[SVN r41370]
2007-11-25 18:38:02 +00:00
Beman Dawes
2d0c627d34 Full merge from trunk at revision 41356 of entire boost-root tree.
[SVN r41369]
2007-11-25 18:07:19 +00:00
Beman Dawes
2cc29f6dfa Starting point for releases
[SVN r39706]
2007-10-05 14:25:06 +00:00
33 changed files with 466 additions and 144 deletions

View File

@@ -17,12 +17,4 @@ lib boost_program_options
<link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
;
install dist-lib
:
boost_program_options
:
<install-type>LIB
<location>../../../dist/lib
;
explicit dist-lib ;
boost-install boost_program_options ;

View File

@@ -2,7 +2,11 @@
import toolset ;
toolset.using doxygen ;
boostbook program_option : program_options.xml ;
boostbook program_option
: program_options.xml
: <implicit-dependency>autodoc
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/doc/html
;
doxygen autodoc
: [ glob ../../../boost/program_options/*.hpp ] ;

View File

@@ -412,7 +412,7 @@ $ export LC_CTYPE=ru_RU.KOI8-R
method of that class:
<programlisting>
parsed_options parsed =
command_line_parser(argv, argc).options(desc).allow_unregistered().run();
command_line_parser(argc, argv).options(desc).allow_unregistered().run();
</programlisting>
For each token that looks like an option, but does not have a known name,
@@ -427,7 +427,7 @@ parsed_options parsed =
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:
<programlisting>
vector&lt;string&gt; to_pass_further = collect_arguments(parsed.option, include_positional);
vector&lt;string&gt; to_pass_further = collect_unrecognized(parsed.options, include_positional);
</programlisting>
</para>

View File

@@ -181,7 +181,7 @@ options_description desc;
desc.add_options()
("help", "produce help message")
("compression", value&lt;string&gt;(), "compression level")
("verbose", value&lt;string&gt;()->zero_token(), "verbosity level")
("verbose", value&lt;string&gt;()->zero_tokens(), "verbosity level")
("email", value&lt;string&gt;()->multitoken(), "email to send to")
;
</programlisting>
@@ -478,7 +478,45 @@ notify(vm);
<title>Specific parsers</title>
<section>
<title>Environment variables</title>
<title>Configuration file parser</title>
<para>The &parse_config_file; function implements parsing
of simple INI-like configuration files. Configuration file
syntax is line based:
</para>
<itemizedlist>
<listitem><para>A line in the form:</para>
<screen>
<replaceable>name</replaceable>=<replaceable>value</replaceable>
</screen>
<para>gives a value to an option.</para>
</listitem>
<listitem><para>A line in the form:</para>
<screen>
[<replaceable>section name</replaceable>]
</screen>
<para>introduces a new section in the configuration file.</para>
</listitem>
<listitem><para>The <literal>#</literal> character introduces a
comment that spans until the end of the line.</para>
</listitem>
</itemizedlist>
<para>The option names are relative to the section names, so
the following configuration file part:</para>
<screen>
[gui.accessibility]
visual_bell=yes
</screen>
<para>is equivalent to</para>
<screen>
gui.accessibility.visual_bell=yes
</screen>
</section>
<section>
<title>Environment variables parser</title>
<para><firstterm>Environment variables</firstterm> are string variables
which are available to all programs via the <code>getenv</code> function

View File

@@ -83,5 +83,5 @@
<xi:include href="design.xml"/>
<xi:include href="acknowledgements.xml"/>
<xi:include href="autodoc.boostbook"/>
<xi:include href="autodoc.xml"/>
</library>

View File

@@ -1,4 +1,6 @@
Say that variables_map is actually derived from std::map.
Make parse_config_file("foo.cfg", desc) work.
Document handling of positional options which depends on precedding options.

View File

@@ -26,11 +26,17 @@ int main(int ac, char* av[])
{
try {
int opt;
int portnum;
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("optimization", po::value<int>(&opt)->default_value(10),
"optimization level")
("verbose,v", po::value<int>()->implicit_value(1),
"enable verbosity (optionally specify level)")
("listen,l", po::value<int>(&portnum)->implicit_value(1001)
->default_value(0,"no"),
"listen on a port.")
("include-path,I", po::value< vector<string> >(),
"include path")
("input-file", po::value< vector<string> >(), "input file")
@@ -62,9 +68,16 @@ int main(int ac, char* av[])
<< vm["input-file"].as< vector<string> >() << "\n";
}
if (vm.count("verbose")) {
cout << "Verbosity enabled. Level is " << vm["verbose"].as<int>()
<< "\n";
}
cout << "Optimization level is " << opt << "\n";
cout << "Listen port is " << portnum << "\n";
}
catch(exception& e)
catch(std::exception& e)
{
cout << e.what() << "\n";
return 1;

View File

@@ -94,7 +94,7 @@ int main(int ac, char* av[])
<< vm["magic"].as<magic_number>().n << "\"\n";
}
}
catch(exception& e)
catch(std::exception& e)
{
cout << e.what() << "\n";
}

View File

@@ -3,6 +3,8 @@
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See www.boost.org/libs/program_options for documentation.
#ifndef PROGRAM_OPTIONS_VP_2003_05_19
#define PROGRAM_OPTIONS_VP_2003_05_19

View File

@@ -70,7 +70,8 @@ namespace boost { namespace program_options { namespace detail {
public:
common_config_file_iterator() { found_eof(); }
common_config_file_iterator(
const std::set<std::string>& allowed_options);
const std::set<std::string>& allowed_options,
bool allow_unregistered = false);
virtual ~common_config_file_iterator() {}
@@ -103,6 +104,7 @@ namespace boost { namespace program_options { namespace detail {
// Invariant: no element is prefix of other element.
std::set<std::string> allowed_prefixes;
std::string m_prefix;
bool m_allow_unregistered;
};
template<class charT>
@@ -116,7 +118,8 @@ namespace boost { namespace program_options { namespace detail {
/** Creates a config file parser for the specified stream.
*/
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options);
const std::set<std::string>& allowed_options,
bool allow_unregistered = false);
private: // base overrides
@@ -139,8 +142,9 @@ namespace boost { namespace program_options { namespace detail {
template<class charT>
basic_config_file_iterator<charT>::
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options)
: common_config_file_iterator(allowed_options)
const std::set<std::string>& allowed_options,
bool allow_unregistered)
: common_config_file_iterator(allowed_options, allow_unregistered)
{
this->is.reset(&is, null_deleter());
get();

View File

@@ -8,6 +8,8 @@
#include <boost/program_options/detail/convert.hpp>
#include <iterator>
namespace boost { namespace program_options {
namespace detail {
@@ -38,7 +40,7 @@ namespace boost { namespace program_options {
: detail::cmdline(
// Explicit template arguments are required by gcc 3.3.1
// (at least mingw version), and do no harm on other compilers.
to_internal(detail::make_vector<charT, charT**>(argv+1, argv+argc)))
to_internal(detail::make_vector<charT, charT**>(argv+1, argv+argc+!argc)))
{}

View File

@@ -1,4 +1,4 @@
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). Permission to copy,
// use, modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided "as is"

View File

@@ -16,7 +16,13 @@ namespace boost { namespace program_options {
std::string
typed_value<T, charT>::name() const
{
if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
if (!m_implicit_value.empty() && !m_implicit_value_as_text.empty()) {
std::string msg = "[=arg(=" + m_implicit_value_as_text + ")]";
if (!m_default_value.empty() && !m_default_value_as_text.empty())
msg += " (=" + m_default_value_as_text + ")";
return msg;
}
else if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
return arg + " (=" + m_default_value_as_text + ")";
} else {
return arg;
@@ -49,13 +55,12 @@ namespace boost { namespace program_options {
{
static std::basic_string<charT> empty;
if (v.size() > 1)
throw validation_error("multiple values not allowed");
if (v.size() == 1)
boost::throw_exception(validation_error("multiple values not allowed"));
else if (v.size() == 1)
return v.front();
else if (allow_empty)
return empty;
else
throw validation_error("at least one value required");
else if (!allow_empty)
boost::throw_exception(validation_error("at least one value required"));
return empty;
}
/* Throws multiple_occurrences if 'value' is not empty. */
@@ -134,7 +139,14 @@ namespace boost { namespace program_options {
for (unsigned i = 0; i < s.size(); ++i)
{
try {
tv->push_back(boost::lexical_cast<T>(s[i]));
/* We call validate so that if user provided
a validator for class T, we use it even
when parsing vector<T>. */
boost::any a;
std::vector<std::basic_string<charT> > v;
v.push_back(s[i]);
validate(a, v, (T*)0, 0);
tv->push_back(boost::any_cast<T>(a));
}
catch(const bad_lexical_cast& /*e*/) {
boost::throw_exception(invalid_option_value(s[i]));
@@ -148,7 +160,13 @@ namespace boost { namespace program_options {
xparse(boost::any& value_store,
const std::vector<std::basic_string<charT> >& new_tokens) const
{
validate(value_store, new_tokens, (T*)0, 0);
// If no tokens were given, and the option accepts an implicit
// value, then assign the implicit value as the stored value;
// otherwise, validate the user-provided token(s).
if (new_tokens.empty() && !m_implicit_value.empty())
value_store = m_implicit_value;
else
validate(value_store, new_tokens, (T*)0, 0);
}
template<class T>

View File

@@ -155,7 +155,7 @@ namespace program_options {
*/
class BOOST_PROGRAM_OPTIONS_DECL options_description {
public:
static const unsigned m_default_line_length = 80;
static const unsigned m_default_line_length;
/** Creates the instance. */
options_description(unsigned line_length = m_default_line_length);

View File

@@ -153,7 +153,8 @@ namespace boost { namespace program_options {
BOOST_PROGRAM_OPTIONS_DECL
#endif
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>&, const options_description&);
parse_config_file(std::basic_istream<charT>&, const options_description&,
bool allow_unregistered = false);
/** Controls if the 'collect_unregistered' function should
include positional options, or not. */

View File

@@ -204,6 +204,38 @@ namespace boost { namespace program_options {
return this;
}
/** Specifies an implicit value, which will be used
if the option is given, but without an adjacent value.
Using this implies that an explicit value is optional, but if
given, must be strictly adjacent to the option, i.e.: '-ovalue'
or '--option=value'. Giving '-o' or '--option' will cause the
implicit value to be applied.
*/
typed_value* implicit_value(const T &v)
{
m_implicit_value = boost::any(v);
m_implicit_value_as_text =
boost::lexical_cast<std::string>(v);
return this;
}
/** Specifies an implicit value, which will be used
if the option is given, but without an adjacent value.
Using this implies that an explicit value is optional, but if
given, must be strictly adjacent to the option, i.e.: '-ovalue'
or '--option=value'. Giving '-o' or '--option' will cause the
implicit value to be applied.
Unlike the above overload, the type 'T' need not provide
operator<< for ostream, but textual representation of default
value must be provided by the user.
*/
typed_value* implicit_value(const T &v, const std::string& textual)
{
m_implicit_value = boost::any(v);
m_implicit_value_as_text = textual;
return this;
}
/** Specifies a function to be called when the final value
is determined. */
typed_value* notifier(function1<void, const T&> f)
@@ -243,7 +275,7 @@ namespace boost { namespace program_options {
unsigned min_tokens() const
{
if (m_zero_tokens) {
if (m_zero_tokens || !m_implicit_value.empty()) {
return 0;
} else {
return 1;
@@ -301,6 +333,8 @@ namespace boost { namespace program_options {
// as boost::optional to avoid unnecessary instantiations.
boost::any m_default_value;
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;
boost::function1<void, const T&> m_notifier;
};

View File

@@ -30,7 +30,8 @@ namespace boost { namespace program_options {
If 'm' already has a non-defaulted value of an option, that value
is not changed, even if 'options' specify some value.
*/
BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<char>& options, variables_map& m,
BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options, variables_map& m,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
@@ -38,7 +39,8 @@ namespace boost { namespace program_options {
is not changed, even if 'options' specify some value.
This is wide character variant.
*/
BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<wchar_t>& options,
BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<wchar_t>& options,
variables_map& m);
@@ -87,9 +89,10 @@ namespace boost { namespace program_options {
// be easily accessible, so we need to store semantic here.
shared_ptr<const value_semantic> m_value_semantic;
friend void store(const basic_parsed_options<char>& options,
friend BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options,
variables_map& m, bool);
friend void notify(variables_map& m);
friend BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
};
/** Implements string->string mapping with convenient value casting
@@ -154,7 +157,8 @@ namespace boost { namespace program_options {
be changed by subsequence assignments. */
std::set<std::string> m_final;
friend void store(const basic_parsed_options<char>& options,
friend BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options,
variables_map& xm,
bool utf8);
};

View File

@@ -23,6 +23,7 @@
#include <cassert>
#include <cstring>
#include <cctype>
#include <climits>
#include <cstdio>
@@ -98,10 +99,10 @@ namespace boost { namespace program_options { namespace detail {
{
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
vector<string> args;
copy(argv+1, argv+argc, inserter(args, args.end()));
copy(argv+1, argv+argc+!argc, inserter(args, args.end()));
init(args);
#else
init(vector<string>(argv+1, argv+argc));
init(vector<string>(argv+1, argv+argc+!argc));
#endif
}
@@ -151,7 +152,7 @@ namespace boost { namespace program_options { namespace detail {
error = "style disallows all characters for short options";
if (error)
throw invalid_command_line_style(error);
boost::throw_exception(invalid_command_line_style(error));
// Need to check that if guessing and long disguise are enabled
// -f will mean the same as -foo
@@ -195,24 +196,24 @@ namespace boost { namespace program_options { namespace detail {
if (m_additional_parser)
style_parsers.push_back(
bind(&cmdline::handle_additional_parser, this, _1));
boost::bind(&cmdline::handle_additional_parser, this, _1));
if (m_style & allow_long)
style_parsers.push_back(
bind(&cmdline::parse_long_option, this, _1));
boost::bind(&cmdline::parse_long_option, this, _1));
if ((m_style & allow_long_disguise))
style_parsers.push_back(
bind(&cmdline::parse_disguised_long_option, this, _1));
boost::bind(&cmdline::parse_disguised_long_option, this, _1));
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
style_parsers.push_back(
bind(&cmdline::parse_short_option, this, _1));
boost::bind(&cmdline::parse_short_option, this, _1));
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
style_parsers.push_back(bind(&cmdline::parse_dos_option, this, _1));
style_parsers.push_back(boost::bind(&cmdline::parse_dos_option, this, _1));
style_parsers.push_back(bind(&cmdline::parse_terminator, this, _1));
style_parsers.push_back(boost::bind(&cmdline::parse_terminator, this, _1));
vector<option> result;
while(!args.empty())
@@ -254,6 +255,62 @@ namespace boost { namespace program_options { namespace detail {
}
}
/* If an key option is followed by a positional option,
can can consume more tokens (e.g. it's multitoke option),
give those tokens to it. */
vector<option> result2;
for (unsigned i = 0; i < result.size(); ++i)
{
result2.push_back(result[i]);
option& opt = result2.back();
if (opt.string_key.empty())
continue;
const option_description* xd =
m_desc->find_nothrow(opt.string_key,
(m_style & allow_guessing));
if (!xd)
continue;
unsigned min_tokens = xd->semantic()->min_tokens();
unsigned max_tokens = xd->semantic()->max_tokens();
if (min_tokens < max_tokens && opt.value.size() < max_tokens)
{
// This option may grab some more tokens.
// We only allow to grab tokens that are not already
// recognized as key options.
int can_take_more = max_tokens - opt.value.size();
unsigned j = i+1;
for (; can_take_more && j < result.size(); --can_take_more, ++j)
{
option& opt2 = result[j];
if (!opt2.string_key.empty())
break;
if (opt2.position_key == INT_MAX)
{
// We use INT_MAX to mark positional options that
// were found after the '--' terminator and therefore
// should stay positional forever.
break;
}
assert(opt2.value.size() == 1);
opt.value.push_back(opt2.value[0]);
assert(opt2.original_tokens.size() == 1);
opt.original_tokens.push_back(opt2.original_tokens[0]);
}
i = j-1;
}
}
result.swap(result2);
// Assign position keys to positional options.
int position_key = 0;
for(unsigned i = 0; i < result.size(); ++i) {
@@ -269,8 +326,8 @@ namespace boost { namespace program_options { namespace detail {
if (opt.position_key != -1) {
if (position >= m_positional->max_total_count())
{
throw too_many_positional_options_error(
"too many positional options");
boost::throw_exception(too_many_positional_options_error(
"too many positional options"));
}
opt.string_key = m_positional->name_for_position(position);
++position;
@@ -290,8 +347,8 @@ namespace boost { namespace program_options { namespace detail {
// 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));
const option_description* xd = m_desc->find_nothrow(opt.string_key,
(m_style & allow_guessing) ? true : false);
if (!xd)
{
@@ -323,14 +380,25 @@ namespace boost { namespace program_options { namespace detail {
if (present_tokens >= min_tokens)
{
if (!opt.value.empty() && max_tokens == 0) {
throw invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::extra_parameter);
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::extra_parameter));
}
max_tokens -= opt.value.size();
// 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)
{
min_tokens -= opt.value.size();
}
else
{
min_tokens = 0;
}
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && max_tokens--; ) {
for(;!other_tokens.empty() && min_tokens--; ) {
opt.value.push_back(other_tokens[0]);
opt.original_tokens.push_back(other_tokens[0]);
other_tokens.erase(other_tokens.begin());
@@ -338,8 +406,8 @@ namespace boost { namespace program_options { namespace detail {
}
else
{
throw invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::missing_parameter);
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::missing_parameter));
}
}
@@ -359,8 +427,8 @@ namespace boost { namespace program_options { namespace detail {
name = tok.substr(2, p-2);
adjacent = tok.substr(p+1);
if (adjacent.empty())
throw invalid_command_line_syntax(name,
invalid_command_line_syntax::empty_adjacent_parameter);
boost::throw_exception( invalid_command_line_syntax(name,
invalid_command_line_syntax::empty_adjacent_parameter));
}
else
{
@@ -462,7 +530,8 @@ namespace boost { namespace program_options { namespace detail {
((m_style & allow_slash_for_short) && tok[0] == '/')))
{
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
m_style & allow_guessing)) {
(m_style & allow_guessing) ? true : false))
{
args[0].insert(0, "-");
if (args[0][1] == '/')
args[0][1] = '-';
@@ -483,6 +552,8 @@ namespace boost { namespace program_options { namespace detail {
{
option opt;
opt.value.push_back(args[i]);
opt.original_tokens.push_back(args[i]);
opt.position_key = INT_MAX;
result.push_back(opt);
}
args.clear();

View File

@@ -21,8 +21,10 @@ namespace boost { namespace program_options { namespace detail {
using namespace std;
common_config_file_iterator::common_config_file_iterator(
const std::set<std::string>& allowed_options)
: allowed_options(allowed_options)
const std::set<std::string>& allowed_options,
bool allow_unregistered)
: allowed_options(allowed_options),
m_allow_unregistered(allow_unregistered)
{
for(std::set<std::string>::const_iterator i = allowed_options.begin();
i != allowed_options.end();
@@ -100,7 +102,8 @@ namespace boost { namespace program_options { namespace detail {
string name = m_prefix + trim_ws(s.substr(0, n));
string value = trim_ws(s.substr(n+1));
if (!allowed_option(name))
bool registered = allowed_option(name);
if (!registered && !m_allow_unregistered)
boost::throw_exception(unknown_option(name));
if (value.empty())
@@ -110,6 +113,7 @@ namespace boost { namespace program_options { namespace detail {
this->value().string_key = name;
this->value().value.clear();
this->value().value.push_back(value);
this->value().unregistered = !registered;
break;
} else {

View File

@@ -201,6 +201,8 @@ namespace boost { namespace program_options {
return *this;
}
const unsigned options_description::m_default_line_length = 80;
options_description::options_description(unsigned line_length)
: m_line_length(line_length)
{}
@@ -252,11 +254,12 @@ namespace boost { namespace program_options {
return m_options;
}
const option_description*
const option_description*
options_description::find_nothrow(const std::string& name,
bool approx) const
{
int found = -1;
shared_ptr<option_description> found;
vector<string> approximate_matches;
// We use linear search because matching specified option
// name with the declared option name need to take care about
// case sensitivity and trailing '*' and so we can't use simple map.
@@ -276,31 +279,22 @@ namespace boost { namespace program_options {
//
// For now, we don't check the situation when there are
// two full matches.
if (r == option_description::full_match)
{
return m_options[i].get();
return m_options[i].get();
}
if (found != -1)
{
vector<string> alts;
// FIXME: the use of 'key' here might not
// be the best approach.
alts.push_back(m_options[found]->key(name));
alts.push_back(m_options[i]->key(name));
boost::throw_exception(ambiguous_option(name, alts));
}
else
{
found = i;
}
}
if (found != -1) {
return m_options[found].get();
} else {
return 0;
found = m_options[i];
// FIXME: the use of 'key' here might not
// be the best approach.
approximate_matches.push_back(m_options[i]->key(name));
}
if (approximate_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, approximate_matches));
return found.get();
}
BOOST_PROGRAM_OPTIONS_DECL
@@ -412,9 +406,9 @@ namespace boost { namespace program_options {
if (last_space != line_begin)
{
// is last_space within the second half of the
// is last_space within the second half ot the
// current line
if ((unsigned)std::distance(last_space, line_end) <
if ((unsigned)distance(last_space, line_end) <
(line_length - indent) / 2)
{
line_end = last_space;

View File

@@ -67,10 +67,16 @@ namespace boost { namespace program_options {
woption result;
result.string_key = opt.string_key;
result.position_key = opt.position_key;
result.unregistered = opt.unregistered;
std::transform(opt.value.begin(), opt.value.end(),
back_inserter(result.value),
bind(from_utf8, _1));
boost::bind(from_utf8, _1));
std::transform(opt.original_tokens.begin(),
opt.original_tokens.end(),
back_inserter(result.original_tokens),
boost::bind(from_utf8, _1));
return result;
}
}
@@ -88,7 +94,8 @@ namespace boost { namespace program_options {
template<class charT>
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>& is,
const options_description& desc)
const options_description& desc,
bool allow_unregistered)
{
set<string> allowed_options;
@@ -106,7 +113,8 @@ namespace boost { namespace program_options {
// Parser return char strings
parsed_options result(&desc);
copy(detail::basic_config_file_iterator<charT>(is, allowed_options),
copy(detail::basic_config_file_iterator<charT>(
is, allowed_options, allow_unregistered),
detail::basic_config_file_iterator<charT>(),
back_inserter(result.options));
// Convert char strings into desired type.
@@ -116,13 +124,15 @@ namespace boost { namespace program_options {
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
parse_config_file(std::basic_istream<char>& is,
const options_description& desc);
const options_description& desc,
bool allow_unregistered);
#ifndef BOOST_NO_STD_WSTRING
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
parse_config_file(std::basic_istream<wchar_t>& is,
const options_description& desc);
const options_description& desc,
bool allow_unregistered);
#endif
// This versio, which accepts any options without validation, is disabled,

View File

@@ -122,8 +122,8 @@ namespace boost { namespace program_options {
/* 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. Regardless of name passed, parameter will always
be optional.
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)

View File

@@ -74,11 +74,13 @@ namespace boost { namespace program_options {
try {
d.semantic()->parse(v.value(), options.options[i].value, utf8);
}
#ifndef BOOST_NO_EXCEPTIONS
catch(validation_error& e)
{
e.set_option_name(name);
throw;
}
#endif
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
@@ -133,7 +135,16 @@ namespace boost { namespace program_options {
k != vm.end();
++k)
{
k->second.m_value_semantic->notify(k->second.value());
/* Users might wish to use variables_map to store their own values
that are not parsed, and therefore will not have value_semantics
defined. Do no crash on such values. In multi-module programs,
one module might add custom values, and the 'notify' function
will be called after that, so we check that value_sematics is
not NULL. See:
https://svn.boost.org/trac/boost/ticket/2782
*/
if (k->second.m_value_semantic)
k->second.m_value_semantic->notify(k->second.value());
}
}

View File

@@ -2,8 +2,8 @@
project
: requirements
<library>../build//boost_program_options
<library>/boost/test//boost_test_exec_monitor/<link>static
<link>static
<variant>debug
# <define>_GLIBCXX_CONCEPT_CHECKS
# <define>_GLIBCXX_DEBUG

View File

@@ -9,15 +9,14 @@
using namespace boost::program_options;
using boost::program_options::detail::cmdline;
#include <boost/test/test_tools.hpp>
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
/* To facilitate testing, declare a number of error codes. Otherwise,
we'd have to specify the type of exception that should be thrown.
*/
@@ -600,7 +599,7 @@ void test_unregistered()
// It's not clear yet, so I'm leaving the decision till later.
}
int test_main(int ac, char* av[])
int main(int ac, char* av[])
{
test_long_options();
test_short_options();

25
test/minitest.hpp Normal file
View File

@@ -0,0 +1,25 @@
#ifndef BOOST_PROGRAM_OPTIONS_MINITEST
#define BOOST_PROGRAM_OPTIONS_MINITEST
#include <assert.h>
#include <iostream>
#include <stdlib.h>
#define BOOST_REQUIRE(b) assert(b)
#define BOOST_CHECK(b) assert(b)
#define BOOST_CHECK_EQUAL(a, b) assert(a == b)
#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n"; abort();
#define BOOST_CHECK_THROW(expression, exception) \
try \
{ \
expression; \
BOOST_ERROR("expected exception not thrown");\
throw 10; \
} \
catch(exception &) \
{ \
}
#endif

View File

@@ -10,14 +10,13 @@ using namespace boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
#include <boost/test/test_tools.hpp>
#include <utility>
#include <string>
#include <sstream>
using namespace std;
#include "minitest.hpp"
void test_type()
{
options_description desc;
@@ -44,9 +43,9 @@ void test_approximation()
("foo", new untyped_value())
("fee", new untyped_value())
("baz", new untyped_value())
("all", new untyped_value())
("all-chroots", new untyped_value())
("all-sessions", new untyped_value())
("all", new untyped_value())
;
BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "foo");
@@ -77,7 +76,7 @@ void test_formatting()
ss << desc;
}
int test_main(int, char* [])
int main(int, char* [])
{
test_type();
test_approximation();

View File

@@ -14,13 +14,17 @@ namespace po = boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
#include <boost/test/test_tools.hpp>
#include <sstream>
#include <iostream>
using namespace std;
#if defined(__sun)
#include <stdlib.h> // for putenv on solaris
#else
#include <cstdlib> // for putenv
#endif
#include "minitest.hpp"
#define TEST_CHECK_THROW(expression, exception, description) \
try \
@@ -142,8 +146,52 @@ void test_command_line()
variables_map vm;
po::store(po::parse_command_line(3, cmdline4, desc2), vm);
char* cmdline5[] = {"", "-p7", "-o", "1", "2", "3", "-x8"};
options_description desc3;
desc3.add_options()
(",p", po::value<string>())
(",o", po::value<string>()->multitoken())
(",x", po::value<string>())
;
vector<option> a5 =
parse_command_line(7, cmdline5, desc3, 0, additional_parser).options;
BOOST_CHECK_EQUAL(a5.size(), 3u);
check_value(a5[0], "-p", "7");
BOOST_REQUIRE(a5[1].value.size() == 3);
BOOST_CHECK_EQUAL(a5[1].string_key, "-o");
BOOST_CHECK_EQUAL(a5[1].value[0], "1");
BOOST_CHECK_EQUAL(a5[1].value[1], "2");
BOOST_CHECK_EQUAL(a5[1].value[2], "3");
check_value(a5[2], "-x", "8");
po::options_description desc4( "" );
desc4.add_options()
( "multitoken,m",
po::value< std::vector< std::string > >()->multitoken(),
"values"
)
( "file",
po::value< std::string >(),
"the file to process"
)
;
po::positional_options_description p;
p.add( "file", 1 );
char* cmdline6[] = {"", "-m", "token1", "token2", "--", "some_file"};
vector<option> a6 =
command_line_parser(6, cmdline6).options(desc4).positional(p)
.run().options;
BOOST_CHECK_EQUAL(a6.size(), 2u);
BOOST_REQUIRE(a6[0].value.size() == 2);
BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken");
BOOST_CHECK_EQUAL(a6[0].value[0], "token1");
BOOST_CHECK_EQUAL(a6[0].value[1], "token2");
BOOST_CHECK_EQUAL(a6[1].string_key, "file");
BOOST_REQUIRE(a6[1].value.size() == 1);
BOOST_CHECK_EQUAL(a6[1].value[0], "some_file");
}
void test_config_file()
@@ -238,9 +286,23 @@ void test_unregistered()
vm);
BOOST_CHECK_EQUAL(vm.size(), 0u);
const char content1[] =
"gv1 = 0\n"
"[m1]\n"
"v1 = 1\n"
;
stringstream ss(content1);
vector<option> a3 = parse_config_file(ss, desc, true).options;
BOOST_REQUIRE(a3.size() == 2);
cout << "XXX" << a3[0].value.front() << "\n";
check_value(a3[0], "gv1", "0");
check_value(a3[1], "m1.v1", "1");
}
int test_main(int, char* [])
int main(int, char* [])
{
test_command_line();
test_config_file();

View File

@@ -12,7 +12,8 @@ namespace po = boost::program_options;
#include <boost/limits.hpp>
#include <boost/test/test_tools.hpp>
#include "minitest.hpp"
#include <vector>
using namespace std;
@@ -81,7 +82,7 @@ void test_parsing()
too_many_positional_options_error);
}
int test_main(int, char* [])
int main(int, char* [])
{
test_positional_options();
test_parsing();

View File

@@ -3,6 +3,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <cassert>
#include <string>
#include <fstream>
#include <sstream>
@@ -14,6 +15,8 @@
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include "minitest.hpp"
using namespace std;
string file_content(const string& filename)
@@ -74,7 +77,7 @@ std::wstring from_8_bit_2(const std::string& s,
void test_convert(const std::string& input,
const std::string& expected_output)
{
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char> facet;
boost::program_options::detail::utf8_codecvt_facet facet;
std::wstring output;
{
@@ -118,7 +121,7 @@ void test_convert(const std::string& input,
BOOST_CHECK(ref == input);
}
int test_main(int ac, char* av[])
int main(int ac, char* av[])
{
std::string input = file_content("utf8.txt");
std::string expected = file_content("ucs2.txt");

View File

@@ -15,12 +15,11 @@ namespace po = boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
#include <boost/test/test_tools.hpp>
#include <sstream>
using namespace std;
#include "minitest.hpp"
// Test that unicode input is forwarded to unicode option without
// problems.
void test_unicode_to_unicode()
@@ -35,9 +34,13 @@ void test_unicode_to_unicode()
args.push_back(L"--foo=\x044F");
variables_map vm;
store(wcommand_line_parser(args).options(desc).run(), vm);
basic_parsed_options<wchar_t> parsed =
wcommand_line_parser(args).options(desc).run();
store(parsed, vm);
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
BOOST_CHECK(parsed.options[0].original_tokens.size() == 1);
BOOST_CHECK(parsed.options[0].original_tokens[0] == L"--foo=\x044F");
}
// Test that unicode input is property converted into
@@ -149,7 +152,7 @@ void test_config_file()
BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F");
}
int test_main(int, char* [])
int main(int, char* [])
{
test_unicode_to_unicode();
test_unicode_to_native();

View File

@@ -15,22 +15,10 @@ namespace po = boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
#include <boost/test/test_tools.hpp>
#include <sstream>
using namespace std;
#define TEST_CHECK_THROW(expression, exception, description) \
try \
{ \
expression; \
BOOST_ERROR(description);\
throw 10; \
} \
catch(exception &) \
{ \
}
#include "minitest.hpp"
vector<string> sv(char* array[], unsigned size)
{
@@ -102,6 +90,28 @@ void test_variable_map()
BOOST_CHECK(vm3["vee"].as<string>() == "42");
BOOST_CHECK(vm3["voo"].as<string>() == "1");
BOOST_CHECK(vm3["iii"].as<int>() == 123);
options_description desc3;
desc3.add_options()
("imp", po::value<int>()->implicit_value(100))
("iim", po::value<int>()->implicit_value(200)->default_value(201))
("mmp,m", po::value<int>()->implicit_value(123)->default_value(124))
("foo", po::value<int>())
;
/* The -m option is implicit. It does not have value in inside the token,
and we should not grab the next token. */
char* cmdline6_[] = { "--imp=1", "-m", "--foo=1" };
vector<string> cmdline6 = sv(cmdline6_,
sizeof(cmdline6_)/sizeof(cmdline6_[0]));
parsed_options a6 = command_line_parser(cmdline6).options(desc3).run();
variables_map vm4;
store(a6, vm4);
notify(vm4);
BOOST_REQUIRE(vm4.size() == 4);
BOOST_CHECK(vm4["imp"].as<int>() == 1);
BOOST_CHECK(vm4["iim"].as<int>() == 201);
BOOST_CHECK(vm4["mmp"].as<int>() == 123);
}
int stored_value;
@@ -269,7 +279,7 @@ void test_multiple_assignments_with_different_option_description()
}
int test_main(int, char* [])
int main(int, char* [])
{
test_variable_map();
test_semantic_values();

View File

@@ -7,26 +7,42 @@
#include <string>
#include <vector>
#include <cctype>
#include <iostream>
#include <stdlib.h>
using namespace std;
#include <boost/program_options/parsers.hpp>
using namespace boost::program_options;
#include <boost/test/test_tools.hpp>
void check_equal(const std::vector<string>& actual, char **expected, int n)
{
if (actual.size() != n)
{
std::cerr << "Size mismatch between expected and actual data\n";
abort();
}
for (int i = 0; i < n; ++i)
{
if (actual[i] != expected[i])
{
std::cerr << "Unexpected content\n";
abort();
}
}
}
#include <boost/preprocessor/cat.hpp>
void test_winmain()
{
using namespace std;
#define C ,
#define TEST(input, expected) \
char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\
BOOST_CHECK_EQUAL_COLLECTIONS(BOOST_PP_CAT(v, __LINE__).begin(),\
BOOST_PP_CAT(v, __LINE__).end(),\
BOOST_PP_CAT(e, __LINE__),\
BOOST_PP_CAT(e, __LINE__) + \
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));
check_equal(BOOST_PP_CAT(v, __LINE__), BOOST_PP_CAT(e, __LINE__),\
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));
// The following expectations were obtained in Win2000 shell:
TEST("1 ", {"1"});
@@ -45,13 +61,13 @@ void test_winmain()
TEST("1\\\\1 ", {"1\\\\1"});
}
int test_main(int, char*[])
int main(int, char*[])
{
test_winmain();
return 0;
}
#else
int test_main(int, char*[])
int main(int, char*[])
{
return 0;
}