Compare commits

...

48 Commits

Author SHA1 Message Date
Beman Dawes
133c5c702e Boost 1.42.0
[SVN r59432]
2010-02-02 20:03:43 +00:00
Vladimir Prus
ab9901f553 Merge from trunk.
[SVN r58818]
2010-01-08 21:00:57 +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
nobody
198b29d107 This commit was manufactured by cvs2svn to create tag
'Version_1_34_1'.

[SVN r38286]
2007-07-24 19:28:14 +00:00
Thomas Witt
f407b6ce47 Applying patch to fix example build.
[SVN r38153]
2007-07-06 19:20:51 +00:00
Vladimir Prus
87938cfa8e Merge: Add missing include, to try to fix compilation on sun
[SVN r37006]
2007-02-19 19:28:49 +00:00
Hartmut Kaiser
9a73a1c412 Trying to fix sun-5.8 error.
[SVN r36943]
2007-02-15 01:25:10 +00:00
Daniel James
37143a449d Fix a broken link
[SVN r36653]
2007-01-07 22:51:27 +00:00
Vladimir Prus
2e0e9fd30b Merge: Fix dynamic linking
[SVN r35992]
2006-11-10 20:31:43 +00:00
Vladimir Prus
1bd588d677 Merge from HEAD.
Allow building of shared versions of some Boost.Test libraries.
Adjust tests to use always use static linking to Boost.Test, since
linking to the shared version requires test changes.

Patch from Juergen Hunold.


[SVN r35990]
2006-11-10 19:59:52 +00:00
Beman Dawes
d83e0dea37 Merged copyright and license addition
[SVN r35907]
2006-11-07 19:27:00 +00:00
Rene Rivera
54daca4c09 Remove obsolete Boost.Build v1 files.
[SVN r35880]
2006-11-06 17:10:46 +00:00
John Maddock
f4eac99310 Fix for Borland compilers.
[SVN r35652]
2006-10-18 12:33:54 +00:00
Vladimir Prus
a367a1b021 Make intel happy
[SVN r35034]
2006-09-07 08:06:16 +00:00
Gennaro Prota
720d0455dd avoid bogus detection of min/max guideline violation (Inspect tool)
[SVN r34784]
2006-07-29 21:00:31 +00:00
Hartmut Kaiser
aad1a60172 Just another fix for the Intel DLL issue.
[SVN r34196]
2006-06-06 14:30:28 +00:00
Hartmut Kaiser
1e4d1dee3d Fixed intel 9.1 dll export problem.
[SVN r34168]
2006-06-04 15:50:47 +00:00
Hartmut Kaiser
e718d0a8a5 Try to fix Intel dll issue on Windows.
[SVN r34104]
2006-05-26 16:52:54 +00:00
Hartmut Kaiser
ab30ec28eb Fixed a dllimport/dllexport problem.
[SVN r34049]
2006-05-20 22:14:41 +00:00
Vladimir Prus
682f1b7670 Merge from trunk
[SVN r33992]
2006-05-18 06:06:53 +00:00
Vladimir Prus
43577d0ca8 Merge from trunk
[SVN r33970]
2006-05-15 14:06:24 +00:00
Vladimir Prus
d05b400b13 Merge from trunk
[SVN r33787]
2006-04-24 09:51:01 +00:00
Vladimir Prus
4863727509 Merge from trunk
[SVN r33785]
2006-04-24 09:41:50 +00:00
Vladimir Prus
7d90a1b1b2 Merge from trunk
[SVN r33783]
2006-04-24 09:15:27 +00:00
Vladimir Prus
ac9830625b Merge from trunk
[SVN r33781]
2006-04-24 08:52:43 +00:00
Vladimir Prus
252a3f9ebd Merge from trunk
[SVN r33779]
2006-04-24 08:29:05 +00:00
Vladimir Prus
b1dc87da3c Merge from trunk
[SVN r33777]
2006-04-24 08:02:07 +00:00
nobody
1fbf955272 This commit was manufactured by cvs2svn to create branch 'RC_1_34_0'.
[SVN r33417]
2006-03-21 02:26:31 +00:00
57 changed files with 2088 additions and 610 deletions

View File

@@ -1,49 +0,0 @@
subproject libs/program_options/build ;
SOURCES = cmdline config_file options_description parsers variables_map
value_semantic positional_options utf8_codecvt_facet convert
winmain
;
lib boost_program_options
: ../src/$(SOURCES).cpp
: # build requirements
[ common-names ] # magic for install and auto-link features
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
std::locale-support
: debug release # build variants
;
dll boost_program_options
: ../src/$(SOURCES).cpp
: # build requirements
[ common-names ] # magic for install and auto-link features
<define>BOOST_ALL_DYN_LINK=1 # tell source we're building dll's
<runtime-link>dynamic # build only for dynamic runtimes
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
# The following really turns on static runtime linking
# which leads to runtime crashes when using DLL, so
# seem DLL is not usable on Metrowerks 8
# std::facet-support std::locale-support
: debug release # build variants
;
install program_options lib
: <lib>boost_program_options <dll>boost_program_options
;
stage stage/lib : <lib>boost_program_options <dll>boost_program_options
:
# copy to a path rooted at BOOST_ROOT:
<locate>$(BOOST_ROOT)
# make sure the names of the libraries are correctly named:
[ common-names ]
# add this target to the "stage" and "all" psuedo-targets:
<target>stage
<target>all
:
debug release
;
# end

View File

@@ -1,28 +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
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
:
$(SOURCES).cpp
:
<link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
;
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

@@ -4,6 +4,11 @@
</head>
<body>
Automatic redirection failed, please go to
<a href="../../../doc/html/program_options.html">../../../doc/html/program_options.html</a>
<a href="../../../doc/html/program_options.html">../../../doc/html/program_options.html</a>
&nbsp;<hr>
<p>© Copyright Beman Dawes, 2001</p>
<p>Distributed under the Boost Software License, Version 1.0. (See accompanying
file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy
at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p>
</body>
</html>

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>
@@ -348,7 +348,7 @@ positional_options_description pd; pd.add("input-file", 1);
given the same name.
<programlisting>
positional_options_description pd;
pd.add("output-file", 2).add_optional("input-file", -1);
pd.add("output-file", 2).add("input-file", -1);
</programlisting>
In the above example, the first two positional options will be associated
with name "output-file", and any others with the name "input-file".
@@ -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

@@ -96,7 +96,7 @@ Compression level was set to 10.
<para>An option value, surely, can have other types than <code>int</code>, and
can have other interesting properties, which we'll discuss right now. The
complete version of the code snipped below can be found in
"example/options_description.cpp".</para>
<filename>example/options_description.cpp</filename>.</para>
<para>Imagine we're writing a compiler. It should take the optimization
level, a number of include paths, and a number of input files, and perform some
@@ -115,21 +115,29 @@ desc.add_options()
</programlisting>
</para>
<para>The "--help" option should be familiar from the previous example.
It's a good idea to have this option in all cases.</para>
<para>The <literal>"help"</literal> option should be familiar from
the previous example. It's a good idea to have this option in all cases.
</para>
<para>The "optimization" option shows two new features. First, we specify
<para>The <literal>"optimization"</literal> option shows two new features. First, we specify
the address of the variable(<code>&amp;opt</code>). After storing values, that
variable will have the value of the option. Second, we specify a default
value of 10, which will be used if no value is specified by the user.
</para>
<para>The "include-path" option is an example of the only case where
the interface of the <code>options_description</code> class serves only one
<para>The <literal>"include-path"</literal> option is an example of the
only case where the interface of the <code>options_description</code>
class serves only one
source -- the command line. Users typically like to use short option names
for common options, and the "include-path,I" name specifies that short
option name is "I". So, both "--include-path" and "-I" can be used.
</para>
<para>Note also that the type of the <literal>"include-path"</literal>
option is <type>std::vector</type>. The library provides special
support for vectors -- it will be possible to specify the option several
times, and all specified values will be collected in one vector.
</para>
<para>The "input-file" option specifies the list of files to
process. That's okay for a start, but, of course, writing something like:
@@ -337,7 +345,7 @@ Optimization level is 4
<!--
Local Variables:
mode: xml
mode: nxml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t

View File

@@ -1,23 +0,0 @@
subproject libs/program_options/example ;
rule program-options-example ( name extra-sources * )
{
exe $(name) : $(name).cpp <lib>../build/boost_program_options $(extra-sources)
: <include>$(BOOST_ROOT) ;
}
program-options-example first ;
program-options-example options_description ;
program-options-example multiple_sources ;
program-options-example custom_syntax ;
program-options-example response_file ;
program-options-example option_groups ;
program-options-example real ;
program-options-example regex <lib>../../regex/build/boost_regex ;
#program-options-example prefix ;

View File

@@ -1,6 +1,6 @@
project
: requirements <library>../build//program_options
: requirements <library>../build//boost_program_options
<hardcode-dll-paths>true
;
@@ -9,7 +9,5 @@ exe options_description : options_description.cpp ;
exe multiple_sources : multiple_sources.cpp ;
exe custom_syntax : custom_syntax.cpp ;
exe a : a.cpp ;
#exe real : real.cpp ;
#exe regex : regex.cpp /boost/regex//boost_regex ;
exe real : real.cpp ;
exe regex : regex.cpp /boost/regex//boost_regex ;

View File

@@ -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<string>(&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";

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

@@ -43,7 +43,7 @@ public:
*/
void validate(boost::any& v,
const std::vector<std::string>& 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<int>(match[1])));
} else {
throw validation_error("invalid value");
throw validation_error(validation_error::invalid_option_value);
}
}
@@ -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

@@ -70,7 +70,8 @@ int main(int ac, char* av[])
ss << ifs.rdbuf();
// Split the file content
char_separator<char> sep(" \n\r");
tokenizer<char_separator<char> > tok(ss.str(), sep);
string sstr = ss.str();
tokenizer<char_separator<char> > tok(sstr, sep);
vector<string> 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<int>() << "\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

@@ -12,13 +12,13 @@ namespace boost { namespace program_options { namespace command_line_style {
There are "long" options, which start with "--" and "short",
which start with either "-" or "/". Both kinds can be allowed or
disallowed, see allow_long and allow_short. The allowed character
for short option is also configurable.
for short options is also configurable.
Option's value can be specified in the same token as value
Option's value can be specified in the same token as name
("--foo=bar"), or in the next token.
It's possible to introduce long option by the same character as
long option, see allow_long_disguise.
It's possible to introduce long options by the same character as
short options, see allow_long_disguise.
Finally, guessing (specifying only prefix of option) and case
insensitive processing are supported.
@@ -26,7 +26,7 @@ namespace boost { namespace program_options { namespace command_line_style {
enum style_t {
/// Allow "--long_name" style
allow_long = 1,
/// Alow "-<single character" style
/// Allow "-<single character" style
allow_short = allow_long << 1,
/// Allow "-" in short options
allow_dash_for_short = allow_short << 1,
@@ -39,7 +39,7 @@ namespace boost { namespace program_options { namespace command_line_style {
@endverbatim
*/
long_allow_adjacent = allow_slash_for_short << 1,
/** Allow option parameter in the same token for
/** Allow option parameter in the next token for
long options. */
long_allow_next = long_allow_adjacent << 1,
/** Allow option parameter in the same token for
@@ -59,17 +59,22 @@ namespace boost { namespace program_options { namespace command_line_style {
/** Allow abbreviated spellings for long options,
if they unambiguously identify long option.
No long option name should be prefix of other
long option name is guessing is in effect.
long option name if guessing is in effect.
*/
allow_guessing = allow_sticky << 1,
/** Ignore the difference in case for options.
@todo Should this apply to long options only?
/** Ignore the difference in case for long options.
*/
case_insensitive = allow_guessing << 1,
long_case_insensitive = allow_guessing << 1,
/** Ignore the difference in case for short options.
*/
short_case_insensitive = long_case_insensitive << 1,
/** Ignore the difference in case for all options.
*/
case_insensitive = (long_case_insensitive | short_case_insensitive),
/** Allow long options with single option starting character,
e.g <tt>-foo=10</tt>
*/
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

View File

@@ -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<std::string>& args);
void
finish_option(option& opt,
std::vector<std::string>& other_tokens);
std::vector<std::string>& other_tokens,
const std::vector<style_parser>& style_parsers);
// Copies of input.
std::vector<std::string> args;

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;
@@ -27,7 +33,7 @@ namespace boost { namespace program_options {
void
typed_value<T, charT>::notify(const boost::any& value_store) const
{
const T* value = boost::any_cast<const T>(&value_store);
const T* value = boost::any_cast<T>(&value_store);
if (m_store_to) {
*m_store_to = *value;
}
@@ -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(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(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

@@ -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<std::string>& 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<std::string>& alternatives() const throw();
private:
// TODO: copy ctor might throw
std::vector<std::string> alternatives;
std::vector<std::string> 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,22 +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);
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.
const char* what() const throw();
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
{
@@ -99,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)
@@ -139,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.
};
}}

View File

@@ -23,10 +23,17 @@ namespace boost { namespace program_options {
template<class charT>
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<char> option;
typedef basic_option<wchar_t> woption;

View File

@@ -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.
@@ -155,15 +156,21 @@ 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);
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<option_description> >& 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

View File

@@ -147,13 +147,29 @@ namespace boost { namespace program_options {
= ext_parser());
/** Parse a config file.
Read from given stream.
*/
template<class charT>
#if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700))
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);
/** Parse a config file.
Read from file with the given name. The character type is
passed to the file stream.
*/
template<class charT>
#if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700))
BOOST_PROGRAM_OPTIONS_DECL
#endif
basic_parsed_options<charT>
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. */
@@ -201,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<std::string>
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<std::wstring>
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

View File

@@ -37,10 +37,11 @@ namespace boost { namespace program_options {
No calls to 'add' can be made after call with 'max_value' equal to
'-1'.
*/
void add(const char* name, int max_count);
positional_options_description&
add(const char* name, int max_count);
/** Returns the maximum number of positional options that can
be present. Can return numeric_limits<unsigned>::max() to
be present. Can return (numeric_limits<unsigned>::max)() to
indicate unlimited number. */
unsigned max_total_count() const;

View File

@@ -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
@@ -204,6 +212,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)
@@ -234,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
@@ -243,7 +289,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;
@@ -260,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. */
@@ -301,7 +348,9 @@ namespace boost { namespace program_options {
// as boost::optional to avoid unnecessary instantiations.
boost::any m_default_value;
std::string m_default_value_as_text;
bool m_composing, m_implicit, m_multitoken, m_zero_tokens;
boost::any m_implicit_value;
std::string m_implicit_value_as_text;
bool m_composing, m_implicit, m_multitoken, m_zero_tokens, m_required;
boost::function1<void, const T&> m_notifier;
};

View File

@@ -24,6 +24,29 @@ namespace boost { namespace program_options {
class value_semantic;
class variables_map;
// forward declaration
/** Stores in 'm' all options that are defined in '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,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
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,
variables_map& m);
/** Runs all 'notify' function for options in 'm'. */
BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
/** Class holding value of option. Contains details about how the
value is set and allows to conveniently obtain the value.
*/
@@ -36,10 +59,15 @@ namespace boost { namespace program_options {
/** If stored value if of type T, returns that value. Otherwise,
throws boost::bad_any_cast exception. */
template<class T> const T& as() const;
/** @overload */
template<class T> T& as();
template<class T>
const T& as() const {
return boost::any_cast<const T&>(v);
}
/** @overload */
template<class T>
T& as() {
return boost::any_cast<T&>(v);
}
/// Returns true if no value is stored.
bool empty() const;
@@ -61,10 +89,11 @@ 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 BOOST_PROGRAM_OPTIONS_DECL
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 BOOST_PROGRAM_OPTIONS_DECL notify(variables_map& m);
friend BOOST_PROGRAM_OPTIONS_DECL class variables_map;
};
/** Implements string->string mapping with convenient value casting
@@ -104,7 +133,11 @@ namespace boost { namespace program_options {
const abstract_variables_map* m_next;
};
/** Concrete variables map which store variables in real map. */
/** Concrete variables map which store variables in real map.
This class is derived from std::map<std::string, variable_value>,
so you can use all map operators to examine its content.
*/
class BOOST_PROGRAM_OPTIONS_DECL variables_map : public abstract_variables_map,
public std::map<std::string, variable_value>
{
@@ -115,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
@@ -125,30 +160,16 @@ 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);
/** Names of required options, filled by parser which has
access to options_description. */
std::set<std::string> m_required;
};
/** Stores in 'm' all options that are defined in '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,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
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,
variables_map& m);
/** Runs all 'notify' function for options in 'm'. */
BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
/*
* Templates/inlines
@@ -180,18 +201,6 @@ namespace boost { namespace program_options {
return v;
}
template<class T>
const T&
variable_value::as() const {
return boost::any_cast<const T&>(v);
}
template<class T>
T&
variable_value::as() {
return boost::any_cast<T&>(v);
}
}}
#endif

View File

@@ -4,6 +4,11 @@
</head>
<body>
Automatic redirection failed, please go to
<a href="../../doc/html/program_options.html">../../doc/html/program_options.html</a>
<a href="../../doc/html/program_options.html">../../doc/html/program_options.html</a>
&nbsp;<hr>
<p>© Copyright Beman Dawes, 2001</p>
<p>Distributed under the Boost Software License, Version 1.0. (See accompanying
file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy
at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p>
</body>
</html>
</html>

View File

@@ -23,6 +23,7 @@
#include <cassert>
#include <cstring>
#include <cctype>
#include <climits>
#include <cstdio>
@@ -33,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)
{
@@ -64,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)
{}
}}
@@ -89,7 +105,7 @@ namespace boost { namespace program_options { namespace detail {
#endif
cmdline::cmdline(const std::vector<std::string>& args)
cmdline::cmdline(const vector<string>& args)
{
init(args);
}
@@ -98,15 +114,15 @@ 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
}
void
cmdline::init(const std::vector<std::string>& args)
cmdline::init(const vector<string>& args)
{
this->args = args;
m_style = command_line_style::default_style;
@@ -151,11 +167,17 @@ 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
}
bool
cmdline::is_style_active(style_t style) const
{
return ((m_style & style) ? true : false);
}
void
cmdline::set_options_description(const options_description& desc)
@@ -195,24 +217,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())
@@ -229,12 +251,12 @@ namespace boost { namespace program_options { namespace detail {
{
vector<string> 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]);
}
@@ -254,6 +276,64 @@ 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,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
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,29 +349,45 @@ 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());
}
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<string>& other_tokens)
{
vector<string>& other_tokens,
const vector<style_parser>& 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));
const option_description* xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (!xd)
{
@@ -322,15 +418,46 @@ 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);
if (!opt.value.empty() && max_tokens == 0)
{
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,
// 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();
}
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--; )
{
// 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<option> followed_option;
vector<string> next_token(1, other_tokens[0]);
for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
{
followed_option = style_parsers[i](next_token);
}
if (!followed_option.empty())
{
const option_description* od = m_desc->find_nothrow(other_tokens[0],
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (od)
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
invalid_command_line_syntax::missing_parameter));
}
opt.value.push_back(other_tokens[0]);
opt.original_tokens.push_back(other_tokens[0]);
other_tokens.erase(other_tokens.begin());
@@ -338,17 +465,17 @@ 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));
}
}
std::vector<option>
cmdline::parse_long_option(std::vector<string>& args)
vector<option>
cmdline::parse_long_option(vector<string>& args)
{
vector<option> result;
const std::string& tok = args[0];
const string& tok = args[0];
if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
{
string name, adjacent;
@@ -359,8 +486,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
{
@@ -378,10 +505,10 @@ namespace boost { namespace program_options { namespace detail {
}
std::vector<option>
cmdline::parse_short_option(std::vector<string>& args)
vector<option>
cmdline::parse_short_option(vector<string>& args)
{
const std::string& tok = args[0];
const string& tok = args[0];
if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-')
{
vector<option> result;
@@ -397,7 +524,8 @@ namespace boost { namespace program_options { namespace detail {
// option.
for(;;) {
const option_description* d
= m_desc->find_nothrow(name, false);
= m_desc->find_nothrow(name, false, false,
is_style_active(short_case_insensitive));
// FIXME: check for 'allow_sticky'.
if (d && (m_style & allow_sticky) &&
@@ -429,14 +557,14 @@ namespace boost { namespace program_options { namespace detail {
}
return result;
}
return std::vector<option>();
return vector<option>();
}
std::vector<option>
cmdline::parse_dos_option(std::vector<string>& args)
vector<option>
cmdline::parse_dos_option(vector<string>& args)
{
vector<option> result;
const std::string& tok = args[0];
const string& tok = args[0];
if (tok.size() >= 2 && tok[0] == '/')
{
string name = "-" + tok.substr(1,1);
@@ -453,16 +581,19 @@ namespace boost { namespace program_options { namespace detail {
return result;
}
std::vector<option>
cmdline::parse_disguised_long_option(std::vector<string>& args)
vector<option>
cmdline::parse_disguised_long_option(vector<string>& args)
{
const std::string& tok = args[0];
const string& tok = args[0];
if (tok.size() >= 2 &&
((tok[0] == '-' && tok[1] != '-') ||
((m_style & allow_slash_for_short) && tok[0] == '/')))
{
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
m_style & allow_guessing)) {
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive)))
{
args[0].insert(0, "-");
if (args[0][1] == '/')
args[0][1] = '-';
@@ -472,17 +603,19 @@ namespace boost { namespace program_options { namespace detail {
return vector<option>();
}
std::vector<option>
cmdline::parse_terminator(std::vector<std::string>& args)
vector<option>
cmdline::parse_terminator(vector<string>& args)
{
vector<option> result;
const std::string& tok = args[0];
const string& tok = args[0];
if (tok == "--")
{
for(unsigned i = 1; i < args.size(); ++i)
{
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();
@@ -490,15 +623,16 @@ namespace boost { namespace program_options { namespace detail {
return result;
}
std::vector<option>
cmdline::handle_additional_parser(std::vector<std::string>& args)
vector<option>
cmdline::handle_additional_parser(vector<string>& args)
{
vector<option> result;
pair<string, string> r = m_additional_parser(args[0]);
if (!r.first.empty()) {
option next;
next.string_key = r.first;
next.value.push_back(r.second);
if (!r.second.empty())
next.value.push_back(r.second);
result.push_back(next);
args.erase(args.begin());
}

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,20 +102,22 @@ 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())
boost::throw_exception(invalid_syntax(s, "no value given"));
found = true;
this->value().string_key = name;
this->value().value.clear();
this->value().value.push_back(value);
this->value().unregistered = !registered;
this->value().original_tokens.clear();
this->value().original_tokens.push_back(name);
this->value().original_tokens.push_back(value);
break;
} else {
boost::throw_exception(invalid_syntax(s, "unrecognized line"));
boost::throw_exception(invalid_syntax(s, invalid_syntax::unrecognized_line));
}
}
}

View File

@@ -43,7 +43,7 @@ namespace boost { namespace detail {
{
std::basic_string<ToChar> result;
std::mbstate_t state = {0};
std::mbstate_t state = std::mbstate_t();
const FromChar* from = s.data();
const FromChar* from_end = s.data() + s.size();

View File

@@ -8,7 +8,7 @@
#define BOOST_PROGRAM_OPTIONS_SOURCE
#include <boost/program_options/config.hpp>
#include <boost/program_options/options_description.hpp>
// FIXME: this is only to get multiple_occureces class
// FIXME: this is only to get multiple_occurences class
// should move that to a separate headers.
#include <boost/program_options/parsers.hpp>
@@ -23,10 +23,27 @@
#include <cstring>
#include <cstdarg>
#include <sstream>
#include <iterator>
using namespace std;
namespace boost { namespace program_options {
namespace {
template< class charT >
std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
{
std::basic_string< charT > result;
for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
{
result.append(1, static_cast< charT >(std::tolower(str[i])));
}
return result;
}
} // unnamed namespace
option_description::option_description()
{
}
@@ -54,37 +71,51 @@ namespace boost { namespace program_options {
}
option_description::match_result
option_description::match(const std::string& option, bool approx) const
option_description::match(const std::string& option,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
match_result result = no_match;
if (!m_long_name.empty()) {
match_result result = no_match;
std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
if (!local_long_name.empty()) {
std::string local_option = (long_ignore_case ? tolower_(option) : option);
if (*m_long_name.rbegin() == '*')
if (*local_long_name.rbegin() == '*')
{
// The name ends with '*'. Any specified name with the given
// prefix is OK.
if (option.find(m_long_name.substr(0, m_long_name.length()-1))
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
== 0)
result = approximate_match;
}
if (approx)
if (local_long_name == local_option)
{
if (m_long_name.find(option) == 0)
if (m_long_name == option)
result = full_match;
else
result = approximate_match;
result = full_match;
}
else
else if (approx)
{
if (m_long_name == option)
result = full_match;
if (local_long_name.find(local_option) == 0)
{
result = approximate_match;
}
}
}
if (m_short_name == option)
result = full_match;
if (result != full_match)
{
std::string local_option(short_ignore_case ? tolower_(option) : option);
std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
if (local_short_name == local_option)
{
result = full_match;
}
}
return result;
}
@@ -200,15 +231,28 @@ namespace boost { namespace program_options {
return *this;
}
options_description::options_description(unsigned line_length)
const unsigned options_description::m_default_line_length = 80;
options_description::options_description(unsigned line_length,
unsigned min_description_length)
: m_line_length(line_length)
{}
options_description::options_description(const string& caption,
unsigned line_length)
: m_caption(caption), m_line_length(line_length)
{}
, m_min_description_length(min_description_length)
{
// we require a space between the option and description parts, so add 1.
assert(m_min_description_length < m_line_length - 1);
}
options_description::options_description(const std::string& caption,
unsigned line_length,
unsigned min_description_length)
: m_caption(caption)
, m_line_length(line_length)
, m_min_description_length(min_description_length)
{
// we require a space between the option and description parts, so add 1.
assert(m_min_description_length < m_line_length - 1);
}
void
options_description::add(shared_ptr<option_description> desc)
{
@@ -237,9 +281,13 @@ namespace boost { namespace program_options {
}
const option_description&
options_description::find(const std::string& name, bool approx) const
options_description::find(const std::string& name,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
const option_description* d = find_nothrow(name, approx);
const option_description* d = find_nothrow(name, approx,
long_ignore_case, short_ignore_case);
if (!d)
boost::throw_exception(unknown_option(name));
return *d;
@@ -251,55 +299,54 @@ 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
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
int found = -1;
shared_ptr<option_description> found;
vector<string> approximate_matches;
vector<string> full_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.
for(unsigned i = 0; i < m_options.size(); ++i)
{
option_description::match_result r =
m_options[i]->match(name, approx);
m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
if (r == option_description::no_match)
continue;
// If we have a full patch, and an approximate match,
// ignore approximate match instead of reporting error.
// Say, if we have options "all" and "all-chroots", then
// "--all" on the command line should select the first one,
// without ambiguity.
//
// 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();
}
if (found != -1)
{
vector<string> alts;
{
full_matches.push_back(m_options[i]->key(name));
}
else
{
// 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;
approximate_matches.push_back(m_options[i]->key(name));
}
found = m_options[i];
}
if (found != -1) {
return m_options[found].get();
} else {
return 0;
}
if (full_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, full_matches));
// If we have a full match, and an approximate match,
// ignore approximate match instead of reporting error.
// Say, if we have options "all" and "all-chroots", then
// "--all" on the command line should select the first one,
// without ambiguity.
if (full_matches.empty() && approximate_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, approximate_matches));
return found.get();
}
BOOST_PROGRAM_OPTIONS_DECL
@@ -390,13 +437,12 @@ namespace boost { namespace program_options {
}
}
string::const_iterator line_end;
line_end = line_begin + line_length;
if (line_end > par_end)
{
line_end = par_end;
}
// Take care to never increment the iterator past
// the end, since MSVC 8.0 (brokenly), assumes that
// doing that, even if no access happens, is a bug.
unsigned remaining = distance(line_begin, par_end);
string::const_iterator line_end = line_begin +
((remaining < line_length) ? remaining : line_length);
// prevent chopped words
// Is line_end between two non-space characters?
@@ -414,8 +460,8 @@ namespace boost { namespace program_options {
{
// is last_space within the second half ot the
// current line
if (unsigned(distance(last_space, line_end)) <
(line_length - indent) / 2)
if (static_cast<unsigned>(distance(last_space, line_end)) <
(line_length / 2))
{
line_end = last_space;
}
@@ -428,6 +474,7 @@ namespace boost { namespace program_options {
if (first_line)
{
indent += par_indent;
line_length -= par_indent; // there's less to work with now
first_line = false;
}
@@ -508,11 +555,18 @@ namespace boost { namespace program_options {
if (!opt.description().empty())
{
for(unsigned pad = first_column_width - ss.str().size();
pad > 0;
--pad)
if (ss.str().size() >= first_column_width)
{
os.put(' ');
os.put('\n'); // first column is too long, lets put description in new line
for (unsigned pad = first_column_width; pad > 0; --pad)
{
os.put(' ');
}
} else {
for(unsigned pad = first_column_width - ss.str().size(); pad > 0; --pad)
{
os.put(' ');
}
}
format_description(os, opt.description(),
@@ -537,6 +591,11 @@ namespace boost { namespace program_options {
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
width = (max)(width, static_cast<unsigned>(ss.str().size()));
}
/* this is the column were description should start, if first
column is longer, we go to a new line */
const unsigned start_of_description_column = m_line_length - m_min_description_length;
width = (min)(width, start_of_description_column-1);
/* add an additional space to improve readability */
++width;

View File

@@ -20,6 +20,7 @@
#include <boost/throw_exception.hpp>
#include <cctype>
#include <fstream>
#if !defined(__GNUC__) || __GNUC__ < 3
#include <iostream>
@@ -67,10 +68,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 +95,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 +114,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,14 +125,46 @@ 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
template<class charT>
basic_parsed_options<charT>
parse_config_file(const char* filename,
const options_description& desc,
bool allow_unregistered)
{
// Parser return char strings
std::basic_ifstream< charT > strm(filename);
if (!strm)
{
boost::throw_exception(reading_file(filename));
}
return parse_config_file(strm, desc, allow_unregistered);
}
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
parse_config_file(const char* filename,
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(const char* filename,
const options_description& desc,
bool allow_unregistered);
#endif
// This versio, which accepts any options without validation, is disabled,
// in the hope that nobody will need it and we cant drop it altogether.
@@ -160,7 +201,7 @@ namespace boost { namespace program_options {
return result;
}
namespace {
namespace detail {
class prefix_name_mapper {
public:
prefix_name_mapper(const std::string& prefix)
@@ -189,7 +230,7 @@ namespace boost { namespace program_options {
parse_environment(const options_description& desc,
const std::string& prefix)
{
return parse_environment(desc, prefix_name_mapper(prefix));
return parse_environment(desc, detail::prefix_name_mapper(prefix));
}
BOOST_PROGRAM_OPTIONS_DECL parsed_options

View File

@@ -17,7 +17,7 @@ namespace boost { namespace program_options {
positional_options_description::positional_options_description()
{}
void
positional_options_description&
positional_options_description::add(const char* name, int max_count)
{
assert(max_count != -1 || m_trailing.empty());
@@ -27,7 +27,7 @@ namespace boost { namespace program_options {
else {
m_names.resize(m_names.size() + max_count, name);
}
return *this;
}
unsigned

62
src/split.cpp Normal file
View File

@@ -0,0 +1,62 @@
// Copyright Sascha Ochsenknecht 2009.
// 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/parsers.hpp>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
namespace boost { namespace program_options { namespace detail {
template< class charT >
std::vector<std::basic_string<charT> >
split_unix(
const std::basic_string<charT>& cmdline,
const std::basic_string<charT>& seperator,
const std::basic_string<charT>& quote,
const std::basic_string<charT>& escape)
{
typedef boost::tokenizer< boost::escaped_list_separator<charT>,
typename std::basic_string<charT>::const_iterator,
std::basic_string<charT> > tokenizerT;
tokenizerT tok(cmdline.begin(), cmdline.end(),
boost::escaped_list_separator< charT >(escape, seperator, quote));
std::vector< std::basic_string<charT> > result;
for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) {
if (!cur_token->empty())
result.push_back(*cur_token);
}
return result;
}
}}} // namespace
namespace boost { namespace program_options {
// Take a command line string and splits in into tokens, according
// to the given collection of seperators chars.
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::string>
split_unix(const std::string& cmdline, const std::string& seperator,
const std::string& quote, const std::string& escape)
{
return detail::split_unix< char >(cmdline, seperator, quote, escape);
}
#ifndef BOOST_NO_STD_WSTRING
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
split_unix(const std::wstring& cmdline, const std::wstring& seperator,
const std::wstring& quote, const std::wstring& escape)
{
return detail::split_unix< wchar_t >(cmdline, seperator, quote, escape);
}
#endif
}} // namespace

View File

@@ -97,9 +97,9 @@ namespace boost { namespace program_options {
{
if (!value_store.empty())
boost::throw_exception(
multiple_occurrences("multiple_occurrences"));
multiple_occurrences());
if (new_tokens.size() > 1)
boost::throw_exception(multiple_values("multiple_values"));
boost::throw_exception(multiple_values());
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
}
@@ -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)
@@ -139,8 +139,7 @@ namespace boost { namespace program_options {
else if (s == "off" || s == "no" || s == "0" || s == "false")
v = any(false);
else
boost::throw_exception(validation_error(
"'" + s + "' doesn't look like a bool value."));
boost::throw_exception(validation_error(validation_error::invalid_bool_value, s));
}
// This is blatant copy-paste. However, templating this will cause a problem,
@@ -162,19 +161,14 @@ namespace boost { namespace program_options {
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
v = any(false);
else
boost::throw_exception(validation_error("invalid bool value"));
boost::throw_exception(validation_error(validation_error::invalid_bool_value));
}
#endif
BOOST_PROGRAM_OPTIONS_DECL
void validate(any& v, const vector<string>& xs, std::string*, int)
{
check_first_occurrence(v);
string s(get_single_string(xs));
if (*s.begin() == '\'' && *s.rbegin() == '\'' ||
*s.begin() == '"' && *s.rbegin() == '"')
v = any(s.substr(1, s.size()-2));
else
v = any(s);
v = any(get_single_string(xs));
}
#if !defined(BOOST_NO_STD_WSTRING)
@@ -182,12 +176,7 @@ namespace boost { namespace program_options {
void validate(any& v, const vector<wstring>& xs, std::string*, int)
{
check_first_occurrence(v);
wstring s(get_single_string(xs));
if (*s.begin() == L'\'' && *s.rbegin() == L'\'' ||
*s.begin() == L'"' && *s.rbegin() == L'"')
v = any(s.substr(1, s.size()-2));
else
v = any(s);
v = any(get_single_string(xs));
}
#endif
@@ -198,19 +187,17 @@ namespace boost { namespace program_options {
{
if (!value.empty())
boost::throw_exception(
multiple_occurrences("multiple_occurrences"));
multiple_occurrences());
}
}
invalid_option_value::
invalid_option_value(const std::string& bad_value)
: validation_error(string("invalid option value '")
.append(bad_value).append("'"))
: validation_error(validation_error::invalid_option_value, bad_value)
{}
#ifndef BOOST_NO_STD_WSTRING
namespace
{
std::string convert_value(const std::wstring& s)
@@ -226,35 +213,123 @@ namespace boost { namespace program_options {
invalid_option_value::
invalid_option_value(const std::wstring& bad_value)
: validation_error(string("invalid option value '")
.append(convert_value(bad_value))
.append("'"))
: validation_error(validation_error::invalid_option_value, convert_value(bad_value))
{}
#endif
#endif
const std::string&
unknown_option::get_option_name() const throw()
{
return m_option_name;
}
const std::string&
ambiguous_option::get_option_name() const throw()
{
return m_option_name;
}
const std::vector<std::string>&
ambiguous_option::alternatives() const throw()
{
return m_alternatives;
}
void validation_error::set_option_name(const std::string& option_name)
void
multiple_values::set_option_name(const std::string& option_name)
{
m_option_name = option_name;
}
const char* validation_error::what() const throw()
const std::string&
multiple_values::get_option_name() const throw()
{
return m_option_name;
}
void
multiple_occurrences::set_option_name(const std::string& option_name)
{
m_option_name = option_name;
}
const std::string&
multiple_occurrences::get_option_name() const throw()
{
return m_option_name;
}
validation_error::
validation_error(kind_t kind,
const std::string& option_value,
const std::string& option_name)
: error("")
, m_kind(kind)
, m_option_name(option_name)
, m_option_value(option_value)
, m_message(error_message(kind))
{
if (!option_value.empty())
{
m_message.append(std::string("'") + option_value + std::string("'"));
}
}
void
validation_error::set_option_name(const std::string& option_name)
{
m_option_name = option_name;
}
const std::string&
validation_error::get_option_name() const throw()
{
return m_option_name;
}
std::string
validation_error::error_message(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 multiple_values_not_allowed:
msg = "multiple values not allowed";
break;
case at_least_one_value_required:
msg = "at least one value required";
break;
case invalid_bool_value:
msg = "invalid bool value";
break;
case invalid_option_value:
msg = "invalid option value";
break;
case invalid_option:
msg = "invalid option";
break;
default:
msg = "unknown error";
}
return msg;
}
const char*
validation_error::what() const throw()
{
if (!m_option_name.empty())
{
m_message = "in option '" + m_option_name + "': "
+ logic_error::what();
return m_message.c_str();
}
else
{
return logic_error::what();
+ error_message(m_kind);
}
return m_message.c_str();
}
const std::string&
required_option::get_option_name() const throw()
{
return m_option_name;
}
}}

View File

@@ -35,8 +35,11 @@ namespace boost { namespace program_options {
std::set<std::string> new_final;
// Declared once, to please Intel in VC++ mode;
unsigned i;
// First, convert/store all given options
for (size_t i = 0; i < options.options.size(); ++i) {
for (i = 0; i < options.options.size(); ++i) {
const string& name = options.options[i].string_key;
// Skip positional options without name
@@ -55,12 +58,8 @@ namespace boost { namespace program_options {
if (xm.m_final.count(name))
continue;
// Ignore options which are not described
//TODO: consider this.
//if (desc.count(name) == 0)
// continue;
const option_description& d = desc.find(name, false);
const option_description& d = desc.find(name, false,
false, false);
variable_value& v = m[name];
if (v.defaulted()) {
@@ -71,11 +70,23 @@ 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;
}
catch(multiple_occurrences& e)
{
e.set_option_name(name);
throw;
}
catch(multiple_values& e)
{
e.set_option_name(name);
throw;
}
#endif
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
@@ -90,9 +101,9 @@ namespace boost { namespace program_options {
// Second, apply default values.
// Second, apply default values and store required options.
const vector<shared_ptr<option_description> >& all = desc.options();
for(unsigned i = 0; i < all.size(); ++i)
for(i = 0; i < all.size(); ++i)
{
const option_description& d = *all[i];
string key = d.key("");
@@ -112,7 +123,12 @@ namespace boost { namespace program_options {
m[key] = variable_value(def, true);
m[key].m_value_semantic = d.semantic();
}
}
}
// add empty value if this is an required option
if (d.semantic()->is_required()) {
xm.m_required.insert(key);
}
}
}
@@ -125,13 +141,7 @@ namespace boost { namespace program_options {
BOOST_PROGRAM_OPTIONS_DECL
void notify(variables_map& vm)
{
// Lastly, run notify actions.
for (map<string, variable_value>::iterator k = vm.begin();
k != vm.end();
++k)
{
k->second.m_value_semantic->notify(k->second.value());
}
vm.notify();
}
abstract_variables_map::abstract_variables_map()
@@ -182,4 +192,40 @@ namespace boost { namespace program_options {
else
return i->second;
}
void
variables_map::notify()
{
// This checks if all required options occur
for (set<string>::const_iterator r = m_required.begin();
r != m_required.end();
++r)
{
const string& opt = *r;
map<string, variable_value>::const_iterator iter = find(opt);
if (iter == end() || iter->second.empty())
{
boost::throw_exception(required_option(opt));
}
}
// Lastly, run notify actions.
for (map<string, variable_value>::iterator k = begin();
k != end();
++k)
{
/* 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

@@ -10,8 +10,6 @@
#ifdef _WIN32
namespace boost { namespace program_options {
using namespace std;
// Take a command line string and splits in into tokens, according
// to the rules windows command line processor uses.
//
@@ -23,7 +21,7 @@ namespace boost { namespace program_options {
{
std::vector<std::string> result;
string::const_iterator i = input.begin(), e = input.end();
std::string::const_iterator i = input.begin(), e = input.end();
for(;i != e; ++i)
if (!isspace((unsigned char)*i))
break;
@@ -86,8 +84,8 @@ namespace boost { namespace program_options {
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
split_winmain(const std::wstring& cmdline)
{
vector<wstring> result;
vector<string> aux = split_winmain(to_internal(cmdline));
std::vector<std::wstring> result;
std::vector<std::string> aux = split_winmain(to_internal(cmdline));
for (unsigned i = 0, e = aux.size(); i < e; ++i)
result.push_back(from_utf8(aux[i]));
return result;

View File

@@ -1,47 +0,0 @@
subproject libs/program_options/test ;
import testing ;
rule program-options-test ( name )
{
return [
run $(name).cpp <lib>../build/boost_program_options
<lib>../../test/build/boost_test_exec_monitor : :
: <include>$(BOOST_ROOT)
std::locale-support ]
;
}
rule program-options-dll-test ( name )
{
return [
run $(name).cpp <dll>../build/boost_program_options
<lib>../../test/build/boost_test_exec_monitor : :
: <include>$(BOOST_ROOT)
<define>BOOST_ALL_DYN_LINK=1
<runtime-link>dynamic
: $(name)_dll ]
;
}
test-suite program_options :
[ program-options-test options_description_test ]
[ program-options-test parsers_test ]
[ program-options-test variable_map_test ]
[ program-options-test cmdline_test ]
[ program-options-test positional_options_test ]
[ program-options-test unicode_test ]
[ program-options-test winmain ]
[ program-options-dll-test options_description_test ]
[ program-options-dll-test parsers_test ]
[ program-options-dll-test variable_map_test ]
[ program-options-dll-test cmdline_test ]
[ program-options-dll-test positional_options_test ]
[ program-options-dll-test unicode_test ]
[ program-options-dll-test winmain ]
;

View File

@@ -2,31 +2,36 @@
project
: requirements
<library>../build//boost_program_options
<library>/boost/test//boost_test_exec_monitor
<link>static
<variant>debug
# <define>_GLIBCXX_CONCEPT_CHECKS
# <define>_GLIBCXX_DEBUG
;
rule po-test ( source )
rule po-test ( source : input-file ? )
{
return
[ run $(source) ]
[ run $(source) : : : <link>shared <define>BOOST_ALL_DYN_LINK=1
: $(source:B)_dll ]
;
[ run $(source) : : $(input-file) ]
[ run $(source) : : $(input-file)
: <link>shared <define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
: $(source:B)_dll ]
;
}
test-suite program_options :
[ po-test options_description_test.cpp ]
[ po-test parsers_test.cpp ]
[ po-test parsers_test.cpp : config_test.cfg ]
[ po-test variable_map_test.cpp ]
[ po-test cmdline_test.cpp ]
[ po-test positional_options_test.cpp ]
[ po-test unicode_test.cpp ]
[ po-test winmain.cpp ]
[ po-test exception_test.cpp ]
[ po-test split_test.cpp ]
[ po-test unrecognized_test.cpp ]
[ po-test required_test.cpp : required_test.cfg ]
;
exe test_convert : test_convert.cpp ;

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.
*/
@@ -31,6 +30,7 @@ const int s_short_adjacent_not_allowed = 5;
const int s_empty_adjacent_parameter = 6;
const int s_missing_parameter = 7;
const int s_extra_parameter = 8;
const int s_unrecognized_line = 9;
int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
{
@@ -41,6 +41,7 @@ int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
invalid_command_line_syntax::empty_adjacent_parameter,
invalid_command_line_syntax::missing_parameter,
invalid_command_line_syntax::extra_parameter,
invalid_command_line_syntax::unrecognized_line
};
invalid_command_line_syntax::kind_t *b, *e, *i;
b = table;
@@ -184,7 +185,7 @@ void test_long_options()
{"--bar", s_missing_parameter, ""},
{"--bar=123", s_success, "bar:123"},
{0}
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases1);
@@ -199,7 +200,7 @@ void test_long_options()
// considered a value, even though it looks like
// an option.
{"--bar --foo", s_success, "bar:--foo"},
{0}
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases2);
style = cmdline::style_t(
@@ -209,7 +210,7 @@ void test_long_options()
test_case test_cases3[] = {
{"--bar=10", s_success, "bar:10"},
{"--bar 11", s_success, "bar:11"},
{0}
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases3);
@@ -217,8 +218,6 @@ void test_long_options()
allow_long | long_allow_adjacent
| long_allow_next | case_insensitive);
// FIXME: restore
#if 0
// Test case insensitive style.
// Note that option names are normalized to lower case.
test_case test_cases4[] = {
@@ -227,10 +226,9 @@ void test_long_options()
{"--bar=Ab", s_success, "bar:Ab"},
{"--Bar=ab", s_success, "bar:ab"},
{"--giz", s_success, "Giz:"},
{0}
{0, 0, 0}
};
test_cmdline("foo bar= baz? Giz", style, test_cases4);
#endif
}
void test_short_options()
@@ -250,7 +248,7 @@ void test_short_options()
{"-f14", s_success, "-f:14"},
{"-g -f1", s_success, "-g: -f:1"},
{"-f", s_missing_parameter, ""},
{0}
{0, 0, 0}
};
test_cmdline(",d ,f= ,g", style, test_cases1);
@@ -263,8 +261,8 @@ void test_short_options()
{"-f -13", s_success, "-f:-13"},
{"-f", s_missing_parameter, ""},
{"-f /foo", s_success, "-f:/foo"},
{"-f -d", s_success, "-f:-d"},
{0}
{"-f -d", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases2);
@@ -275,8 +273,8 @@ void test_short_options()
test_case test_cases3[] = {
{"-f10", s_success, "-f:10"},
{"-f 10", s_success, "-f:10"},
{"-f -d", s_success, "-f:-d"},
{0}
{"-f -d", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases3);
@@ -292,7 +290,7 @@ void test_short_options()
//{"-d12", s_extra_parameter, ""},
{"-f12", s_success, "-f:12"},
{"-fe", s_success, "-f:e"},
{0}
{0, 0, 0}
};
test_cmdline(",d ,f= ,e", style, test_cases4);
@@ -314,7 +312,7 @@ void test_dos_options()
{"/d13", s_extra_parameter, ""},
{"/f14", s_success, "-f:14"},
{"/f", s_missing_parameter, ""},
{0}
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases1);
@@ -326,7 +324,7 @@ void test_dos_options()
test_case test_cases2[] = {
{"/de", s_extra_parameter, ""},
{"/fe", s_success, "-f:e"},
{0}
{0, 0, 0}
};
test_cmdline(",d ,f= ,e", style, test_cases2);
@@ -348,7 +346,7 @@ void test_disguised_long()
{"-foo -f", s_success, "foo: foo:"},
{"-goo=x -gy", s_success, "goo:x goo:y"},
{"-bee=x -by", s_success, "bee:x bee:y"},
{0}
{0, 0, 0}
};
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
@@ -356,7 +354,7 @@ void test_disguised_long()
test_case test_cases2[] = {
{"/foo -f", s_success, "foo: foo:"},
{"/goo=x", s_success, "goo:x"},
{0}
{0, 0, 0}
};
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
}
@@ -377,7 +375,7 @@ void test_guessing()
{"--opt", s_ambiguous_option, ""},
{"--f=1", s_success, "foo:1"},
{"-far", s_success, "foo:ar"},
{0}
{0, 0, 0}
};
test_cmdline("opt123 opt56 foo,f=", style, test_cases1);
}
@@ -395,7 +393,7 @@ void test_arguments()
test_case test_cases1[] = {
{"-f file -gx file2", s_success, "-f: file -g:x file2"},
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
{0}
{0, 0, 0}
};
test_cmdline(",f ,g= ,e", style, test_cases1);
@@ -408,7 +406,7 @@ void test_arguments()
test_case test_cases2[] = {
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
{0}
{0, 0, 0}
};
test_cmdline(",f ,g= ,e", style, test_cases2);
}
@@ -426,7 +424,7 @@ void test_prefix()
test_case test_cases1[] = {
{"--foo.bar=12", s_success, "foo.bar:12"},
{0}
{0, 0, 0}
};
test_cmdline("foo*=", style, test_cases1);
@@ -600,7 +598,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();

9
test/config_test.cfg Normal file
View File

@@ -0,0 +1,9 @@
gv1 = 0#asd
empty_value =
plug3 = 7
b = true
[m1]
v1 = 1
v2 = 2

164
test/exception_test.cpp Normal file
View File

@@ -0,0 +1,164 @@
// Copyright Sascha Ochsenknecht 2009.
// 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)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
void test_ambiguous()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,c", value<string>(), "the output file")
("output,o", value<string>(), "the output file")
;
const char* cmdline[] = {"program", "-c", "file", "-o", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (ambiguous_option& e)
{
BOOST_CHECK_EQUAL(e.alternatives().size(), 2);
BOOST_CHECK_EQUAL(e.get_option_name(), "-c");
BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}
void test_unknown_option()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "-c", "file", "-f", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (unknown_option& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "-f");
BOOST_CHECK_EQUAL(string(e.what()), "unknown option -f");
}
}
void test_multiple_values()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,o", value<string>(), "the output file")
;
const char* cmdline[] = { "program", "-o", "fritz", "hugo", "--cfgfile", "file", "c", "-o", "text.out" };
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (validation_error& e)
{
// TODO: this is currently validation_error, shouldn't it be multiple_values ???
//
// multiple_values is thrown only at one place untyped_value::xparse(),
// but I think this can never be reached
// because: untyped_value always has one value and this is filtered before reach specific
// validation and parsing
//
BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "in option 'cfgfile': multiple values not allowed");
}
}
void test_multiple_occurrences()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "--cfgfile", "file", "-c", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "multiple occurrences");
}
}
void test_missing_value()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,o", value<string>(), "the output file")
;
// missing value for option '-c'
const char* cmdline[] = { "program", "-c", "-c", "output.txt"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (invalid_command_line_syntax& e)
{
BOOST_CHECK_EQUAL(e.kind(), invalid_syntax::missing_parameter);
BOOST_CHECK_EQUAL(e.tokens(), "cfgfile");
}
}
int main(int /*ac*/, char** /*av*/)
{
test_ambiguous();
test_unknown_option();
test_multiple_values();
test_multiple_occurrences();
test_missing_value();
return 0;
}

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,19 +10,19 @@ 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;
desc.add_options()
("foo", value<int>(), "")
("bar", value<std::string>(), "")
("bar", value<string>(), "")
;
const typed_value_base* b = dynamic_cast<const typed_value_base*>
@@ -33,7 +33,7 @@ void test_type()
const typed_value_base* b2 = dynamic_cast<const typed_value_base*>
(desc.find("bar", false).semantic().get());
BOOST_CHECK(b2);
BOOST_CHECK(b2->value_type() == typeid(std::string));
BOOST_CHECK(b2->value_type() == typeid(string));
}
void test_approximation()
@@ -43,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");
@@ -61,9 +61,172 @@ void test_approximation()
// BOOST_CHECK(*(++a.begin()) == "foo");
}
int test_main(int, char* [])
void test_formatting()
{
// Long option descriptions used to crash on MSVC-8.0.
options_description desc;
desc.add_options()(
"test", new untyped_value(),
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo")
("list", new untyped_value(),
"a list:\n \t"
"item1, item2, item3, item4, item5, item6, item7, item8, item9, "
"item10, item11, item12, item13, item14, item15, item16, item17, item18")
("well_formated", new untyped_value(),
"As you can see this is a very well formatted option description.\n"
"You can do this for example:\n\n"
"Values:\n"
" Value1: \tdoes this and that, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n"
" Value2: \tdoes something else, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n\n"
" This paragraph has a first line indent only, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --test arg foo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foo\n"
" --list arg a list:\n"
" item1, item2, item3, item4, item5, item6, item7, \n"
" item8, item9, item10, item11, item12, item13, \n"
" item14, item15, item16, item17, item18\n"
" --well_formated arg As you can see this is a very well formatted option \n"
" description.\n"
" You can do this for example:\n"
" \n"
" Values:\n"
" Value1: does this and that, bla bla bla bla bla bla \n"
" bla bla bla bla bla bla bla bla bla\n"
" Value2: does something else, bla bla bla bla bla bla \n"
" bla bla bla bla bla bla bla bla bla\n"
" \n"
" This paragraph has a first line indent only, bla \n"
" bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n"
);
}
void test_formatting_description_length()
{
{
options_description desc("",
options_description::m_default_line_length,
options_description::m_default_line_length / 2U);
desc.add_options()
("an-option-that-sets-the-max", new untyped_value(), // > 40 available for desc
"this description sits on the same line, but wrapping should still work correctly")
("a-long-option-that-would-leave-very-little-space-for-description", new untyped_value(),
"the description of the long opt, but placed on the next line\n"
" \talso ensure that the tabulation works correctly when a"
" description size has been set");
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --an-option-that-sets-the-max arg this description sits on the same line,\n"
" but wrapping should still work \n"
" correctly\n"
" --a-long-option-that-would-leave-very-little-space-for-description arg\n"
" the description of the long opt, but \n"
" placed on the next line\n"
" also ensure that the tabulation \n"
" works correctly when a description \n"
" size has been set\n");
}
{
// the default behaviour reserves 23 (+1 space) characters for the
// option column; this shows that the min_description_length does not
// breach that.
options_description desc("",
options_description::m_default_line_length,
options_description::m_default_line_length - 10U); // leaves < 23 (default option space)
desc.add_options()
("an-option-that-encroaches-description", new untyped_value(),
"this description should always be placed on the next line, and wrapping should continue as normal");
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --an-option-that-encroaches-description arg\n"
//123456789_123456789_
" this description should always be placed on the next line, and \n"
" wrapping should continue as normal\n");
}
}
void test_long_default_value()
{
options_description desc;
desc.add_options()
("cfgfile,c",
value<string>()->default_value("/usr/local/etc/myprogramXXXXXXXXX/configuration.conf"),
"the configfile")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" -c [ --cfgfile ] arg (=/usr/local/etc/myprogramXXXXXXXXX/configuration.conf)\n"
" the configfile\n"
);
}
void test_word_wrapping()
{
options_description desc("Supported options");
desc.add_options()
("help", "this is a sufficiently long text to require word-wrapping")
("prefix", value<string>()->default_value("/h/proj/tmp/dispatch"), "root path of the dispatch installation")
("opt1", "this_is_a_sufficiently_long_text_to_require_word-wrapping_but_cannot_be_wrapped")
("opt2", "this_is_a_sufficiently long_text_to_require_word-wrapping")
("opt3", "this_is_a sufficiently_long_text_to_require_word-wrapping_but_will_not_be_wrapped")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --help this is a sufficiently long text to \n"
" require word-wrapping\n"
" --prefix arg (=/h/proj/tmp/dispatch) root path of the dispatch installation\n"
" --opt1 this_is_a_sufficiently_long_text_to_requ\n"
" ire_word-wrapping_but_cannot_be_wrapped\n"
" --opt2 this_is_a_sufficiently \n"
" long_text_to_require_word-wrapping\n"
" --opt3 this_is_a sufficiently_long_text_to_requ\n"
" ire_word-wrapping_but_will_not_be_wrappe\n"
" d\n"
);
}
void test_default_values()
{
options_description desc("Supported options");
desc.add_options()
("maxlength", value<double>()->default_value(.1, "0.1"), "Maximum edge length to keep.")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --maxlength arg (=0.1) Maximum edge length to keep.\n"
);
}
int main(int, char* [])
{
test_type();
test_approximation();
test_formatting();
test_formatting_description_length();
test_long_default_value();
test_word_wrapping();
test_default_values();
return 0;
}

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 \
@@ -53,7 +57,7 @@ void check_value(const option& option, const char* name, const char* value)
BOOST_CHECK(option.value.front() == value);
}
vector<string> sv(char* array[], unsigned size)
vector<string> sv(const char* array[], unsigned size)
{
vector<string> r;
for (unsigned i = 0; i < size; ++i)
@@ -109,10 +113,10 @@ void test_command_line()
("baz", new untyped_value())
("plug*", new untyped_value())
;
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
"--plug3=10"};
vector<string> cmdline3 = sv(cmdline3_,
sizeof(cmdline3_)/sizeof(cmdline3_[0]));
sizeof(cmdline3_)/sizeof(const char*));
vector<option> a3 =
command_line_parser(cmdline3).options(desc).run().options;
@@ -127,21 +131,78 @@ void test_command_line()
// Regression test: check that '0' as style is interpreted as
// 'default_style'
vector<option> a4 =
parse_command_line(5, cmdline3_, desc, 0, additional_parser).options;
parse_command_line(sizeof(cmdline3_)/sizeof(const char*), const_cast<char**>(cmdline3_),
desc, 0, additional_parser).options;
BOOST_CHECK_EQUAL(a4.size(), 4u);
check_value(a4[0], "foo", "4");
check_value(a4[1], "bar", "11");
// Check that we don't crash on empty values of type 'string'
const char* cmdline4[] = {"", "--open", ""};
options_description desc2;
desc2.add_options()
("open", po::value<string>())
;
variables_map vm;
po::store(po::parse_command_line(sizeof(cmdline4)/sizeof(const char*), const_cast<char**>(cmdline4), desc2), vm);
const 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(sizeof(cmdline5)/sizeof(const char*), const_cast<char**>(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 );
const char* cmdline6[] = {"", "-m", "token1", "token2", "--", "some_file"};
vector<option> a6 =
command_line_parser(sizeof(cmdline6)/sizeof(const char*), const_cast<char**>(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()
void test_config_file(const char* config_file)
{
options_description desc;
desc.add_options()
("gv1", new untyped_value)
("gv2", new untyped_value)
("empty_value", new untyped_value)
("plug*", new untyped_value)
("m1.v1", new untyped_value)
("m1.v2", new untyped_value)
@@ -150,6 +211,7 @@ void test_config_file()
const char content1[] =
" gv1 = 0#asd\n"
"empty_value = \n"
"plug3 = 7\n"
"b = true\n"
"[m1]\n"
@@ -160,13 +222,23 @@ void test_config_file()
stringstream ss(content1);
vector<option> a1 = parse_config_file(ss, desc).options;
BOOST_REQUIRE(a1.size() == 5);
BOOST_REQUIRE(a1.size() == 6);
check_value(a1[0], "gv1", "0");
check_value(a1[1], "plug3", "7");
check_value(a1[2], "b", "true");
check_value(a1[3], "m1.v1", "1");
check_value(a1[4], "m1.v2", "2");
check_value(a1[1], "empty_value", "");
check_value(a1[2], "plug3", "7");
check_value(a1[3], "b", "true");
check_value(a1[4], "m1.v1", "1");
check_value(a1[5], "m1.v2", "2");
// same test, but now options come from file
vector<option> a2 = parse_config_file<char>(config_file, desc).options;
BOOST_REQUIRE(a2.size() == 6);
check_value(a2[0], "gv1", "0");
check_value(a2[1], "empty_value", "");
check_value(a2[2], "plug3", "7");
check_value(a2[3], "b", "true");
check_value(a2[4], "m1.v1", "1");
check_value(a2[5], "m1.v2", "2");
}
void test_environment()
@@ -180,7 +252,7 @@ void test_environment()
#if defined(_WIN32) && ! defined(__BORLANDC__)
_putenv("PO_TEST_FOO=1");
#else
putenv("PO_TEST_FOO=1");
putenv(const_cast<char*>("PO_TEST_FOO=1"));
#endif
parsed_options p = parse_environment(desc, "PO_TEST_");
@@ -197,9 +269,9 @@ void test_unregistered()
{
options_description desc;
char* cmdline1_[] = { "--foo=12", "--bar", "1"};
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
vector<string> cmdline1 = sv(cmdline1_,
sizeof(cmdline1_)/sizeof(cmdline1_[0]));
sizeof(cmdline1_)/sizeof(const char*));
vector<option> a1 =
command_line_parser(cmdline1).options(desc).allow_unregistered().run()
.options;
@@ -228,12 +300,26 @@ 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* av[])
{
test_command_line();
test_config_file();
test_config_file(av[1]);
test_environment();
test_unregistered();
return 0;

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;
@@ -49,28 +50,31 @@ void test_parsing()
("first", po::value<int>())
("second", po::value<int>())
("input-file", po::value< vector<string> >())
("some-other", po::value<string>())
;
positional_options_description p;
p.add("input-file", 2);
p.add("input-file", 2).add("some-other", 1);
vector<string> args;
args.push_back("--first=10");
args.push_back("file1");
args.push_back("--second=10");
args.push_back("file2");
args.push_back("file3");
// Check that positional options are handled.
parsed_options parsed =
command_line_parser(args).options(desc).positional(p).run();
BOOST_REQUIRE(parsed.options.size() == 4);
BOOST_REQUIRE(parsed.options.size() == 5);
BOOST_CHECK_EQUAL(parsed.options[1].string_key, "input-file");
BOOST_CHECK_EQUAL(parsed.options[1].value[0], "file1");
BOOST_CHECK_EQUAL(parsed.options[3].string_key, "input-file");
BOOST_CHECK_EQUAL(parsed.options[3].value[0], "file2");
BOOST_CHECK_EQUAL(parsed.options[4].value[0], "file3");
args.push_back("file3");
args.push_back("file4");
// Check that excessive number of positional options is detected.
BOOST_CHECK_THROW(command_line_parser(args).options(desc).positional(p)
@@ -78,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();

1
test/required_test.cfg Normal file
View File

@@ -0,0 +1 @@
cfgfile = file.cfg

97
test/required_test.cpp Normal file
View File

@@ -0,0 +1,97 @@
// Copyright Sascha Ochsenknecht 2009.
// 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)
#include <boost/program_options.hpp>
using namespace boost::program_options;
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
#include "minitest.hpp"
void required_throw_test()
{
options_description opts;
opts.add_options()
("cfgfile,c", value<string>()->required(), "the configfile")
("fritz,f", value<string>()->required(), "the output file")
;
variables_map vm;
bool throwed = false;
{
// This test must throw exception
string cmdline = "prg -f file.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
try {
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
throwed = true;
}
BOOST_CHECK(throwed);
}
{
// This test mustn't throw exception
string cmdline = "prg -c config.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
try {
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
throwed = true;
}
BOOST_CHECK(!throwed);
}
}
void simple_required_test(const char* config_file)
{
options_description opts;
opts.add_options()
("cfgfile,c", value<string>()->required(), "the configfile")
("fritz,f", value<string>()->required(), "the output file")
;
variables_map vm;
bool throwed = false;
{
// This test must throw exception
string cmdline = "prg -f file.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
try {
// options coming from different sources
store(command_line_parser(tokens).options(opts).run(), vm);
store(parse_config_file<char>(config_file, opts), vm);
notify(vm);
}
catch (required_option& e) {
throwed = true;
}
BOOST_CHECK(!throwed);
}
}
int main(int /*argc*/, char* av[])
{
required_throw_test();
simple_required_test(av[1]);
return 0;
}

189
test/split_test.cpp Normal file
View File

@@ -0,0 +1,189 @@
// Copyright Sascha Ochsenknecht 2009.
// 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)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
void check_value(const string& option, const string& value)
{
BOOST_CHECK(option == value);
}
void split_whitespace(const options_description& description)
{
const char* cmdline = "prg --input input.txt \r --optimization 4 \t --opt \n option";
vector< string > tokens = split_unix(cmdline, " \t\n\r");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_equalsign(const options_description& description)
{
const char* cmdline = "prg --input=input.txt --optimization=4 --opt=option";
vector< string > tokens = split_unix(cmdline, "= ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_semi(const options_description& description)
{
const char* cmdline = "prg;--input input.txt;--optimization 4;--opt option";
vector< string > tokens = split_unix(cmdline, "; ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_quotes(const options_description& description)
{
const char* cmdline = "prg --input \"input.txt input.txt\" --optimization 4 --opt \"option1 option2\"";
vector< string > tokens = split_unix(cmdline, " ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option1 option2");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_escape(const options_description& description)
{
const char* cmdline = "prg --input \\\"input.txt\\\" --optimization 4 --opt \\\"option1\\ option2\\\"";
vector< string > tokens = split_unix(cmdline, " ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "\"input.txt\"");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "\"option1 option2\"");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_single_quote(const options_description& description)
{
const char* cmdline = "prg --input 'input.txt input.txt' --optimization 4 --opt 'option1 option2'";
vector< string > tokens = split_unix(cmdline, " ", "'");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option1 option2");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_defaults(const options_description& description)
{
const char* cmdline = "prg --input \t \'input file.txt\' \t --optimization 4 --opt \\\"option1\\ option2\\\"";
vector< string > tokens = split_unix(cmdline);
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input file.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "\"option1 option2\"");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
int main(int /*ac*/, char** /*av*/)
{
options_description desc;
desc.add_options()
("input,i", value<string>(), "the input file")
("optimization,O", value<unsigned>(), "optimization level")
("opt,o", value<string>(), "misc option")
;
split_whitespace(desc);
split_equalsign(desc);
split_semi(desc);
split_quotes(desc);
split_escape(desc);
split_single_quote(desc);
split_defaults(desc);
return 0;
}

View File

@@ -3,6 +3,8 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <cstring>
#include <cassert>
#include <string>
#include <fstream>
#include <sstream>
@@ -14,6 +16,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)
@@ -35,8 +39,8 @@ std::wstring from_8_bit_2(const std::string& s,
std::wstring result;
std::mbstate_t state = {0};
std::mbstate_t state = std::mbstate_t();
const char* from = s.data();
const char* from_end = s.data() + s.size();
// The interace of cvt is not really iterator-like, and it's
@@ -74,7 +78,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 +122,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
@@ -85,8 +88,7 @@ void test_native_to_unicode()
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
}
vector<wstring> sv(wchar_t* array[], unsigned size)
vector<wstring> sv(const wchar_t* array[], unsigned size)
{
vector<wstring> r;
for (unsigned i = 0; i < size; ++i)
@@ -112,7 +114,7 @@ void test_command_line()
("plug*", new untyped_value())
;
wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11",
const wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11",
L"-b4", L"--plug3=10"};
vector<wstring> cmdline4 = sv(cmdline4_,
sizeof(cmdline4_)/sizeof(cmdline4_[0]));
@@ -149,7 +151,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

@@ -0,0 +1,88 @@
// Copyright Sascha Ochsenknecht 2009.
// 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)
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/detail/cmdline.hpp>
using namespace boost::program_options;
using boost::program_options::detail::cmdline;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
// Test free function collect_unrecognized()
//
// it collects the tokens of all not registered options. It can be used
// to pass them to an own parser implementation
void test_unrecognize_cmdline()
{
options_description desc;
string content = "prg --input input.txt --optimization 4 --opt option";
vector< string > tokens = split_unix(content);
cmdline cmd(tokens);
cmd.set_options_description(desc);
cmd.allow_unregistered();
vector< option > opts = cmd.run();
vector< string > result = collect_unrecognized(opts, include_positional);
BOOST_CHECK_EQUAL(result.size(), 7);
BOOST_CHECK_EQUAL(result[0], "prg");
BOOST_CHECK_EQUAL(result[1], "--input");
BOOST_CHECK_EQUAL(result[2], "input.txt");
BOOST_CHECK_EQUAL(result[3], "--optimization");
BOOST_CHECK_EQUAL(result[4], "4");
BOOST_CHECK_EQUAL(result[5], "--opt");
BOOST_CHECK_EQUAL(result[6], "option");
}
void test_unrecognize_config()
{
options_description desc;
string content =
" input = input.txt\n"
" optimization = 4\n"
" opt = option\n"
;
stringstream ss(content);
vector< option > opts = parse_config_file(ss, desc, true).options;
vector< string > result = collect_unrecognized(opts, include_positional);
BOOST_CHECK_EQUAL(result.size(), 6);
BOOST_CHECK_EQUAL(result[0], "input");
BOOST_CHECK_EQUAL(result[1], "input.txt");
BOOST_CHECK_EQUAL(result[2], "optimization");
BOOST_CHECK_EQUAL(result[3], "4");
BOOST_CHECK_EQUAL(result[4], "opt");
BOOST_CHECK_EQUAL(result[5], "option");
}
int main(int /*ac*/, char** /*av*/)
{
test_unrecognize_cmdline();
test_unrecognize_config();
return 0;
}

View File

@@ -15,24 +15,12 @@ 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)
vector<string> sv(const char* array[], unsigned size)
{
vector<string> r;
for (unsigned i = 0; i < size; ++i)
@@ -50,9 +38,9 @@ void test_variable_map()
("baz", new untyped_value())
("output,o", new untyped_value(), "")
;
char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" };
const char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" };
vector<string> cmdline3 = sv(cmdline3_,
sizeof(cmdline3_)/sizeof(cmdline3_[0]));
sizeof(cmdline3_)/sizeof(const char*));
parsed_options a3 = command_line_parser(cmdline3).options(desc).run();
variables_map vm;
store(a3, vm);
@@ -70,9 +58,9 @@ void test_variable_map()
("zak", po::value<int>(&i), "")
("opt", bool_switch(), "");
char* cmdline4_[] = { "--zee", "--zak=13" };
const char* cmdline4_[] = { "--zee", "--zak=13" };
vector<string> cmdline4 = sv(cmdline4_,
sizeof(cmdline4_)/sizeof(cmdline4_[0]));
sizeof(cmdline4_)/sizeof(const char*));
parsed_options a4 = command_line_parser(cmdline4).options(desc).run();
variables_map vm2;
@@ -90,9 +78,9 @@ void test_variable_map()
("voo", po::value<string>())
("iii", po::value<int>()->default_value(123))
;
char* cmdline5_[] = { "--voo=1" };
const char* cmdline5_[] = { "--voo=1" };
vector<string> cmdline5 = sv(cmdline5_,
sizeof(cmdline5_)/sizeof(cmdline5_[0]));
sizeof(cmdline5_)/sizeof(const char*));
parsed_options a5 = command_line_parser(cmdline5).options(desc2).run();
variables_map vm3;
@@ -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. */
const char* cmdline6_[] = { "--imp=1", "-m", "--foo=1" };
vector<string> cmdline6 = sv(cmdline6_,
sizeof(cmdline6_)/sizeof(const char*));
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;
@@ -184,15 +194,15 @@ void test_priority()
("include", po::value< vector<int> >()->composing())
;
char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" };
const char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" };
vector<string> cmdline1 = sv(cmdline1_,
sizeof(cmdline1_)/sizeof(cmdline1_[0]));
sizeof(cmdline1_)/sizeof(const char*));
parsed_options p1 = command_line_parser(cmdline1).options(desc).run();
char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" };
const char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" };
vector<string> cmdline2 = sv(cmdline2_,
sizeof(cmdline2_)/sizeof(cmdline2_[0]));
sizeof(cmdline2_)/sizeof(const char*));
parsed_options p2 = command_line_parser(cmdline2).options(desc).run();
@@ -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;
}