Compare commits

..

54 Commits

Author SHA1 Message Date
nobody
767cacbced This commit was manufactured by cvs2svn to create tag
'Version_1_33_0'.

[SVN r30532]
2005-08-12 03:25:34 +00:00
nobody
3ae92db179 This commit was manufactured by cvs2svn to create branch 'RC_1_33_0'.
[SVN r30300]
2005-07-28 18:22:24 +00:00
Douglas Gregor
e6e1ef29db Fixup uses of max
[SVN r30059]
2005-07-13 16:32:41 +00:00
Markus Schöpflin
78c3d90280 Fixed previous checkin.
[SVN r29621]
2005-06-16 13:23:17 +00:00
Markus Schöpflin
2f17c3b9b9 Added workaround for Tru64/CXX, getline() is not defined in <string> when
compiling in strict ansi mode. This is a bug in the string header file.


[SVN r29617]
2005-06-16 12:49:32 +00:00
Vladimir Prus
a78a1f0d76 Unroll 'make parse_command_line' const-correct patch, because it causes too
much troubles. Basically, consider;

    char* cmdline3_[1] = {};

    template<class charT>
    void func(const charT* const argv[]) {}

    int main()
    {
        func(cmdline3_);
        char** p = cmdline3_;
        func(p);
    }

EDG compilers can't deduce template argument in the first call. See
http://thread.gmane.org/gmane.comp.lib.boost.devel/125396 for details.
Some other compilers (borland, vc 7.0) see to be have this issue too.


[SVN r29615]
2005-06-16 10:44:23 +00:00
Vladimir Prus
5af27a78e7 Use boost::throw_exception instead just throw_exception. Otherwise, borland
"forgets" to generate code for the function call.


[SVN r29614]
2005-06-16 10:29:49 +00:00
Vladimir Prus
de511c601a Make install target explicit. Otherwise top-level Jamfile will try to
install both results of 'boost_program_options' and of the install target,
resulting in duplicate target error.


[SVN r29604]
2005-06-16 07:51:56 +00:00
Vladimir Prus
f88e0a9572 Attempt to fix VC7 parse error.
[SVN r29400]
2005-06-03 10:38:24 +00:00
Vladimir Prus
1fec99d686 Fix library target naming.
Patch from Jurgen Hunold.


[SVN r29246]
2005-05-27 15:15:05 +00:00
John Maddock
da4baad235 Added qualifier to cmdline type to fix Borland failures.
[SVN r28987]
2005-05-17 11:44:44 +00:00
Rene Rivera
0c1332a0d3 Some compilers don't support, or make it hard to use, source relative includes. So avoid them.
[SVN r28920]
2005-05-15 06:32:18 +00:00
Markus Schöpflin
4fab784453 Fixed failure for 64 bit platforms.
[SVN r28860]
2005-05-13 11:48:09 +00:00
Vladimir Prus
274cd2e682 Fix ambiguity between 'std::detail' and 'program_options::detail' on cw.
[SVN r28692]
2005-05-06 08:13:32 +00:00
Vladimir Prus
b5bc8b4fcb Add test for unregisted options.
[SVN r28691]
2005-05-06 08:12:27 +00:00
Vladimir Prus
6b57600a81 Finally make 'allow_unregistered' method of cmdline work.
[SVN r28689]
2005-05-06 07:48:45 +00:00
Vladimir Prus
9a149beb76 Fix 'unknown option' error for two successive calls to 'store'.
The bug triggered if
- we store two parsed_option object into variables_map
- the options descriptions associated with those parsed_option objects
  are different
- an option present in first parser_option object is not declared in
  second options_description.

The problem was that on the second 'store' call we went over all
stored options, trying to get their description from the *second*
options description object.

Thanks to Hartmut Kaiser for the bug report.


[SVN r28685]
2005-05-06 06:40:39 +00:00
Hartmut Kaiser
dc9097c3d0 Make the True64 compiler happy.
[SVN r28663]
2005-05-05 16:50:41 +00:00
Vladimir Prus
d4748e8153 Fix a regression -- the if the 'style' argument to parse_command_line
function was zero, it was interpreted as no style is suppored. Previously,
it was interpreted as default_styles. Also, remove 'style' argument from
'cmdline' constructors.


[SVN r28500]
2005-04-27 07:02:39 +00:00
Vladimir Prus
efc9712f70 Check for invalid options in vector<option> returned by extra style
parser or additional parser.


[SVN r28468]
2005-04-25 09:06:44 +00:00
Vladimir Prus
239deeb456 Fix positional options.
[SVN r28467]
2005-04-25 06:39:34 +00:00
Rene Rivera
2476e5b265 Fix use of *DYN_LIB=1 defines.
Add dist-lib target to put results in a convenient place.


[SVN r28461]
2005-04-24 17:17:09 +00:00
Rene Rivera
f5bba0a918 Add missing define to make the code export symbols when creating a DLL.
[SVN r28440]
2005-04-23 16:37:48 +00:00
Vladimir Prus
94d186836e Derive basic_command_line_parser from cmdline.
Eliminate 'common_command_line_parser'.


[SVN r28419]
2005-04-22 15:32:23 +00:00
Vladimir Prus
6565cbc334 Revive 'additional parser'.
[SVN r28415]
2005-04-22 14:05:17 +00:00
Vladimir Prus
c984d59de1 Refactor the command line parser so that it uses options_description,
as opposed to having his own data structures. Clean up option description
classes a bit, removing unneeded methods. Remove support for 'implicit'
options.


[SVN r28413]
2005-04-22 13:35:45 +00:00
Vladimir Prus
07778adab7 Use direct cast from any to reference, recently introduced in CVS.
[SVN r28411]
2005-04-22 12:56:15 +00:00
Vladimir Prus
d78ebf5f0e Don't force static linking on NT.
[SVN r28334]
2005-04-20 11:25:16 +00:00
Hartmut Kaiser
7b23670e4d Improved readability in certain cases.
[SVN r27896]
2005-03-31 06:55:01 +00:00
Vladimir Prus
cb9bd037d9 Fix 64-bit portability problem.
Patch from Jonathan Wakely. Original explanation follows:

   The attached patch fixes a 64 bit portability problem where
   std::string::size_type is assigned to unsigned, which is shorter
   than size_t on x86-64 and so will be truncated. This means the
   following comparison to std::string::npos is always false.


[SVN r27772]
2005-03-23 11:11:55 +00:00
Vladimir Prus
c2442fcad6 Remove a couple of unused option_description members.
[SVN r27494]
2005-02-24 12:57:07 +00:00
Vladimir Prus
e5a143e2c5 Update BOOST_CHECK_EQUAL_COLLECTIONS usage to the current version of
Boost.Test.


[SVN r27417]
2005-02-18 11:11:11 +00:00
Vladimir Prus
58d35a27b9 Change "throw" to "throw_exception".
[SVN r27398]
2005-02-16 09:03:19 +00:00
Vladimir Prus
69d2f8fb21 Use "const char * const []" for parameters which take command line.
[SVN r27397]
2005-02-16 08:09:36 +00:00
Vladimir Prus
cedd6570fd Patches for IRIX MIPSPro. See
https://sourceforge.net/tracker/?func=detail&atid=307586&aid=1115576&group_id=7586


[SVN r27269]
2005-02-09 09:06:45 +00:00
Vladimir Prus
065b0a4a9d Correct example usage of positional_options_description::add.
[SVN r27218]
2005-02-07 14:27:20 +00:00
Stefan Slapeta
fc423bf6bc Replaced BOOST_TEST*
[SVN r27052]
2005-02-03 12:45:59 +00:00
Vladimir Prus
a9c3f21021 Remove a couple of potentially confusing sentences.
[SVN r26839]
2005-01-24 16:03:26 +00:00
Vladimir Prus
f27dce4ed4 Initial docs for text formatting rules.
[SVN r26785]
2005-01-21 16:11:26 +00:00
Vladimir Prus
c3e02a2b0a Merge the utf8 workaround in program_options and serialization and
put the result to boost/detail and libs/detail.


[SVN r26758]
2005-01-20 08:49:13 +00:00
Vladimir Prus
934e96dcef Workaround for borland
[SVN r26757]
2005-01-20 07:29:17 +00:00
Vladimir Prus
c184748325 Typo fixes
[SVN r26694]
2005-01-14 07:10:19 +00:00
Vladimir Prus
87558bfe7c Added wordwrapping and improved intentation for options_description
output.

Patch from Bertolt Mildner.


[SVN r26679]
2005-01-12 12:05:47 +00:00
Vladimir Prus
eca947a1ab Add a new option to response_file.cpp to illustrate that options in
response files are overriden by options on the command line.


[SVN r26678]
2005-01-12 10:56:29 +00:00
Vladimir Prus
87b4fff3e2 Fix missing includes.
Patch from Graham Bennett.


[SVN r26677]
2005-01-12 10:50:32 +00:00
Vladimir Prus
11946c4461 Some doc updates
[SVN r26676]
2005-01-12 10:00:25 +00:00
Vladimir Prus
1e12dd69ff Markup user input in screens
[SVN r26281]
2004-11-24 14:56:28 +00:00
Vladimir Prus
1f57064c70 Fix title capitalization
[SVN r26280]
2004-11-24 14:40:17 +00:00
Vladimir Prus
0c9cfb5825 Fix order of value and description parameters in the docs.
Thanks to Charles Brockman for the report.


[SVN r26279]
2004-11-24 14:39:14 +00:00
Vladimir Prus
a00373eec5 Better way to supress the warning
[SVN r26276]
2004-11-23 12:25:20 +00:00
Vladimir Prus
4394406aeb Suppress gcc warning in release mode.
Thanks to Jody Hagins for the report.


[SVN r26274]
2004-11-23 07:31:13 +00:00
John Maddock
a122bb502b Fixed and activated auto-linking code.
[SVN r26145]
2004-11-08 12:21:52 +00:00
Vladimir Prus
9bd7193660 Make program_options compile on IBM compiler.
Patch from Matthias Troyer.


[SVN r26096]
2004-11-03 07:11:45 +00:00
Vladimir Prus
e7fd9b25ad Add comments
[SVN r26081]
2004-11-02 07:41:35 +00:00
36 changed files with 1589 additions and 2083 deletions

View File

@@ -1,20 +1,28 @@
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
;
import os ;
if [ os.name ] = NT
{
linkage = <link>static ;
}
lib program_options
: $(SOURCES).cpp
: $(linkage)
SOURCES =
cmdline config_file options_description parsers variables_map
value_semantic positional_options utf8_codecvt_facet
convert winmain
;
lib boost_program_options
:
$(SOURCES).cpp
:
<link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
;
install dist-lib
:
boost_program_options
:
<install-type>LIB
<location>../../../dist/lib
;
explicit dist-lib ;

View File

@@ -138,7 +138,7 @@ if (vm.count("response-file")) {
compilers, it is possible to obtain
the split command line, but it's not clear if all compilers support the
same mechanism on all versions of the operating system. The
<code>split_command_line</code> function is a portable mechanism provided
<code>split_winmain</code> function is a portable mechanism provided
by the library.</para>
<para>Here's an example of use:

View File

@@ -148,44 +148,150 @@ desc.add_options()
</para>
<section>
<title>Syntactic information</title>
<title>Syntactic Information</title>
<para>The syntactic information is provided by the
<classname>boost::program_options::options_description</classname> class
and some methods of the
<classname>boost::program_options::value_semantic</classname> class.
The simplest usage is illustrated below:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help message")
;
</programlisting>
This declares one option named "help" and associates a description with
it. The user is not allowed to specify any value.
<classname>boost::program_options::value_semantic</classname> class
and includes:
<itemizedlist>
<listitem>
<para>
name of the option, used to identify the option inside the
program,
</para>
</listitem>
<listitem>
<para>
description of the option, which can be presented to the user,
</para>
</listitem>
<listitem>
<para>
the allowed number of source tokens that comprise options's
value, which is used during parsing.
</para>
</listitem>
</itemizedlist>
</para>
<para>To make an option accept a value, you'd need the
<code>value</code> function mentioned above:
<para>Consider the following example:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help message")
("compression", value&lt;string&gt;(), "compression level")
("verbose", value&lt;string&gt;()->implicit(), "verbosity level")
("email", value&lt;string&gt;()->multitoken(), "email to send to")
;
</programlisting>
For the first parameter, we specify only the name and the
description. No value can be specified in the parsed source.
For the first option, the user must specify a value, using a single
token. For the third option, the user may either provide a single token
for the value, or no token at all. For the last option, the value can
span several tokens. For example, the following command line is OK:
<screen>
test --help --compression 10 --verbose --email beadle@mars beadle2@mars
</screen>
</para>
<section>
<title>Description formatting</title>
<para>
Sometimes the description can get rather long, for example, when
several option's values need separate documentation. Below we
describe some simple formatting mechanisms you can use.
</para>
<para>The description string has one or more paragraphs, separated by
the newline character ('\n'). When an option is output, the library
will compute the indentation for options's description. Each of the
paragraph is output as a separate line with that intentation. If
a paragraph does not fit on one line it is spanned over multiple
lines (which will have the same indentation).
</para>
<para>You may specify additional indent for the first specified by
inserting spaces at the beginning of a paragraph. For example:
<programlisting>
options_description desc;
desc.add_options()
("compression", "compression level", value&lt;string&gt;())
("verbose", "verbosity level", value&lt;string&gt;()->implicit())
("email", "email to send to", value&lt;string&gt;()->multitoken());
options.add_options()
("help", " A long help msg a long help msg a long help msg a long help
msg a long help msg a long help msg a long help msg a long help msg ")
;
</programlisting>
With these declarations, the user must specify a value for
the first option, using a single token. For the second option, the user
may either provide a single token for the value, or no token at
all. For the last option, the value can span several tokens. For
example, the following command line is OK:
will specify a four-space indent for the first line. The output will
look like:
<screen>
test --compression 10 --verbose --email beadle@mars beadle2@mars
</screen>
</para>
--help A long help msg a long
help msg a long help msg
a long help msg a long
help msg a long help msg
a long help msg a long
help msg
</screen>
</para>
<para>For the case where line is wrapped, you can want an additional
indent for wrapped text. This can be done by
inserting a tabulator character ('\t') at the desired position. For
example:
<programlisting>
options.add_options()
("well_formated", "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");
</programlisting>
will produce:
<screen>
--well_formated As you can see this is a
very well formatted
option description.
You can do this for
example:
Values:
Value1: does this and
that, bla bla
bla bla bla bla
bla bla bla bla
bla bla bla bla
bla
Value2: does something
else, bla bla
bla bla bla bla
bla bla bla bla
bla bla bla bla
bla
This paragraph has a
first line indent only,
bla bla bla bla bla bla
bla bla bla bla bla bla
bla bla bla
</screen>
The tab character is removed before output. Only one tabulator per
paragraph is allowed, otherwisee an exception of type
program_options::error is thrown. Finally, the tabulator is ignored if
it's is not on the first line of the paragraph or is on the last
possible position of the first line.
</para>
</section>
</section>
<section>
<title>Semantic information</title>
<title>Semantic Information</title>
<para>The semantic information is completely provided by the
<classname>boost::program_options::value_semantic</classname> class. For
@@ -193,9 +299,10 @@ desc.add_options()
<programlisting>
options_description desc;
desc.add_options()
("compression", "compression level", value&lt;int&gt;()->default(10));
("email", "email", value&lt; vector&lt;string&gt; &gt;()
->composing()->notify(&amp;your_function);
("compression", value&lt;int&gt;()->default_value(10), "compression level")
("email", value&lt; vector&lt;string&gt; &gt;()
->composing()->notifier(&amp;your_function), "email")
;
</programlisting>
These declarations specify that default value of the first option is 10,
that the second option can appear several times and all instances should
@@ -206,7 +313,7 @@ desc.add_options()
</section>
<section>
<title>Positional options</title>
<title>Positional Options</title>
<para>Our definition of option as (name, value) pairs is simple and
useful, but in one special case of the command line, there's a
@@ -231,18 +338,18 @@ desc.add_options()
<para>The &positional_options_desc; class allows the command line
parser to assign the names. The class specifies how many positional options
are allowed, and for each allowed option, specifies the name. For example:
<programlisting>
positional_options_description pd; pd.add("input-file", 1, 1);
</programlisting> specifies that for exactly one, first, positional
<programlisting>
positional_options_description pd; pd.add("input-file", 1);
</programlisting> specifies that for exactly one, first, positional
option the name will be "input-file".
</para>
<para>It's possible to specify that a number, or even all positional options, be
given the same name.
<programlisting>
positional_options_description pd;
pd.add("output-file", 2, 2).add_optional("input-file", 0, -1);
</programlisting>
<programlisting>
positional_options_description pd;
pd.add("output-file", 2).add_optional("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".
</para>
@@ -334,12 +441,12 @@ desc.add_options()
</para>
<para>Let's consider an example:
<programlisting>
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
store(parse_config_file("example.cfg", desc), vm);
notify(vm);
</programlisting>
<programlisting>
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
store(parse_config_file("example.cfg", desc), vm);
notify(vm);
</programlisting>
The <code>variables_map</code> class is used to store the option
values. The two calls to the <code>store</code> function add values
found on the command line and in the config file. Finally the call to
@@ -360,6 +467,59 @@ desc.add_options()
</section>
<section>
<title>Specific parsers</title>
<section>
<title>Environment variables</title>
<para><firstterm>Environment variables</firstterm> are string variables
which are available to all programs via the <code>getenv</code> function
of C runtime library. The operating system allows to set initial values
for a given user, and the values can be further changed on the command
line. For example, on Windows one can use the
<filename>autoexec.bat</filename> file or (on recent versions) the
<filename>Control Panel/System/Advanced/Environment Variables</filename>
dialog, and on Unix &#x2014;, the <filename>/etc/profile</filename>,
<filename>~/profile</filename> and <filename>~/bash_profile</filename>
files. Because environment variables can be set for the entire system,
they are particularly suitable for options which apply to all programs.
</para>
<para>The environment variables can be parsed with the
&parse_environment; function. The function have several overloaded
versions. The first parameter is always an &options_description;
instance, and the second specifies what variables must be processed, and
what option names must correspond to it. To describe the second
parameter we need to consider naming conventions for environment
variables.</para>
<para>If you have an option that should be specified via environment
variable, you need make up the variable's name. To avoid name clashes,
we suggest that you use a sufficiently unique prefix for environment
variables. Also, while option names are most likely in lower case,
environment variables conventionally use upper case. So, for an option
name <literal>proxy</literal> the environment variable might be called
<envar>BOOST_PROXY</envar>. During parsing, we need to perform reverse
conversion of the names. This is accomplished by passing the choosen
prefix as the second parameter of the &parse_environment; function.
Say, if you pass <literal>BOOST_</literal> as the prefix, and there are
two variables, <envar>CVSROOT</envar> and <envar>BOOST_PROXY</envar>, the
first variable will be ignored, and the second one will be converted to
option <literal>proxy</literal>.
</para>
<para>The above logic is sufficient in many cases, but it is also
possible to pass, as the second parameter of the &parse_environment;
function, any function taking a <code>std::string</code> and returning
<code>std::string</code>. That function will be called for each
environment variable and should return either the name of the option, or
empty string if the variable should be ignored.
</para>
</section>
</section>
<section>
<title>Annotated List of Symbols</title>
@@ -432,7 +592,7 @@ desc.add_options()
<!--
Local Variables:
mode: xml
mode: nxml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t

View File

@@ -151,7 +151,7 @@
- test additional parser
- Show default values in help output
- Adaptive field width
- Mandatory options
- Mandatory options (2 votes (second Jonathan Graehl))
- (new) return vector from parsers by auto_ptr, not by value?
- (new) rename value_semantic into value_description
- (new) output for positional_options_description

View File

@@ -1,4 +1,16 @@
Document handling of positional options which depends on precedding options.
I.e scanning the parsed options and creating new variables_map when we see
a positional option. (Email from Tony).
> My instinctive reaction is to provide both via an options argument to
> split_command_line (a name that would now be more appropriate). But I
> haven't devoted much time to thinking this through, so I may be wrong. :-)
>
> In any event, the tokenization isn't much fun. I'd expect the library to
> provide a convenient mechanism for parsing a response file.
> Similarly, is there some easy to use hook for customizing the "arg" to
> indicate the type of the data (similar to how the textual representation
> of the default argument can be changed, e.g.
@@ -201,3 +213,22 @@ More visibility for bool_switch.
> as what can be achieved with the po::command_line_style::style_t enum. I
> think most users will need this information sooner or later. A few
> examples would be fine... But then again time is such a precious thing
> Does the library supports sections in config files
> What about the combination of (if some user-settable switch is thrown,
> but not by default):
>
> * allowing unknown options -- these are considered positional parameters
> * rearranging the argument list such that all positional parameters
> are moved to the end
>
> This way:
>
> program --unknown 42 --known-flag --known-arg value
>
> is handled as if it were (in standard UNIX command-line-ese):
>
> program --known-flag --known-arg value -- --unknown 42

View File

@@ -77,14 +77,13 @@ if (vm.count(&quot;compression&quot;)) {
<para>It's now a good time to try compiling the code yourself, but if
you're not yet ready, here's an example session:
<screen>
$bin/gcc/debug/first
$<userinput>bin/gcc/debug/first</userinput>
Compression level was not set.
$bin/gcc/debug/first --help
$<userinput>bin/gcc/debug/first --help</userinput>
Allowed options:
--help : produce help message
--compression arg : set compression level
$bin/gcc/debug/first --compression 10
$<userinput>bin/gcc/debug/first --compression 10</userinput>
Compression level was set to 10.
</screen>
</para>
@@ -135,11 +134,11 @@ desc.add_options()
<para>The "input-file" option specifies the list of files to
process. That's okay for a start, but, of course, writing something like:
<screen>
compiler --input-file=a.cpp
<userinput>compiler --input-file=a.cpp</userinput>
</screen>
is a little non-standard, compared with
<screen>
compiler a.cpp
<userinput>compiler a.cpp</userinput>
</screen>
We'll address this in a moment.
</para>
@@ -192,7 +191,7 @@ cout &lt;&lt; &quot;Optimization level is &quot; &lt;&lt; opt &lt;&lt; &quot;\n&
<para>Here's an example session:
<screen>
$bin/gcc/debug/options_description --help
$<userinput>bin/gcc/debug/options_description --help</userinput>
Usage: options_description [options]
Allowed options:
--help : produce help message
@@ -201,7 +200,7 @@ Allowed options:
--input-file arg : input file
$bin/gcc/debug/options_description
Optimization level is 10
$bin/gcc/debug/options_description --optimization 4 -I foo a.cpp
$<userinput>bin/gcc/debug/options_description --optimization 4 -I foo a.cpp</userinput>
Include paths are: foo
Input files are: a.cpp
Optimization level is 4
@@ -301,10 +300,10 @@ visible.add(generic).add(config);
<para>Here's an example session:
<screen>
$bin/gcc/debug/multiple_sources
$<userinput>bin/gcc/debug/multiple_sources</userinput>
Include paths are: /opt
Optimization level is 1
$bin/gcc/debug/multiple_sources --help
$<userinput>bin/gcc/debug/multiple_sources --help</userinput>
Allows options:
Generic options:
@@ -315,7 +314,7 @@ Configuration:
--optimization n : optimization level
-I [ --include-path ] path : include path
$bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp
$<userinput>bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp</userinput>
Include paths are: foo /opt
Input files are: a.cpp b.cpp
Optimization level is 4

View File

@@ -46,6 +46,7 @@ int main(int ac, char* av[])
("help", "produce a help message")
("include-path,I", value< vector<string> >()->composing(),
"include path")
("magic", value<int>(), "magic value")
("response-file", value<string>(),
"can be specified with '@name', too")
;
@@ -82,6 +83,9 @@ int main(int ac, char* av[])
copy(s.begin(), s.end(), ostream_iterator<string>(cout, " "));
cout << "\n";
}
if (vm.count("magic")) {
cout << "Magic value: " << vm["magic"].as<int>() << "\n";
}
}
catch(exception& e) {
cout << e.what() << "\n";

View File

@@ -1,2 +1,3 @@
-I bar
-I biz
-I biz
--magic 10

View File

@@ -10,6 +10,10 @@
#include <boost/program_options/config.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/detail/workaround.hpp>
@@ -55,6 +59,9 @@ namespace boost { namespace program_options { namespace detail {
typedef function1<std::pair<std::string, std::string>,
const std::string&>
additional_parser;
typedef function1<std::vector<option>, std::vector<std::string>&>
style_parser;
/** Constructs a command line parser for (argc, argv) pair. Uses
style options passed in 'style', which should be binary or'ed values
@@ -63,12 +70,30 @@ namespace boost { namespace program_options { namespace detail {
unregistered options. They will be assigned index 1 and are
assumed to have optional parameter.
*/
cmdline(const std::vector<std::string>& args, int style,
bool allow_unregistered = false);
cmdline(const std::vector<std::string>& args);
/** @overload */
cmdline(int argc, const char*const * argv, int style,
bool allow_unregistered = false);
cmdline(int argc, const char*const * argv);
void style(int style);
void allow_unregistered();
void set_options_description(const options_description& desc);
void set_positional_options(
const positional_options_description& m_positional);
std::vector<option> run();
std::vector<option> parse_long_option(std::vector<std::string>& args);
std::vector<option> parse_short_option(std::vector<std::string>& args);
std::vector<option> parse_dos_option(std::vector<std::string>& args);
std::vector<option> parse_disguised_long_option(
std::vector<std::string>& args);
std::vector<option> parse_terminator(
std::vector<std::string>& args);
std::vector<option> handle_additional_parser(
std::vector<std::string>& args);
/** Set additional parser. This will be called for each token
of command line. If first string in pair is not empty,
@@ -80,203 +105,27 @@ namespace boost { namespace program_options { namespace detail {
*/
void set_additional_parser(additional_parser p);
/** Registers a new option.
@param long_name Long name to use. When ending '*', symbols up to
it give an allowed prefix -- all options starting with it will be
allowed. The first character may not be '-'. Empty string means no
long name.
@param short_name Short name to use. Value of '\0' means no short
name.
@param properties Tell about possible parameters
'|' -- no parameter
'?' -- optional parameter
':' -- required parameter
'*' -- 0 or more parameters
'+' -- 1 or more parameters
@param index A distinguishing value for the option.
The index will be returned by 'option_index' member function.
Indices need not be unique -- e.g. client can set all indices to 0,
and use the string value to recognize options.
*/
void add_option(const std::string& long_name, char short_name,
char properties = '|', int index = 0);
void extra_style_parser(style_parser s);
/** @overload */
void add_option(const char* long_name, char short_name,
char properties = '|', int index = 0);
/** Returns false when nothing more can be extracted */
operator bool() const;
/** Advances to the next element on command line.
When called for the first time, positions on the first element. */
cmdline& operator++();
/// Tells if the current element is option.
bool at_option() const;
/// Tells if the current element is argument.
bool at_argument() const;
/** Returns the option name. If there's long option name associated with
this option, it is returned, even if short name was used in command
line.Otherwise, the short name given to 'add_option' is returned
with a '-' prefix.
For purposes of simplicity, '-' is used even when dos-style short
option was found.
*/
const std::string& option_name() const;
/** Returns the index for the current option. */
int option_index() const;
/** Returns the option name as found on the command line. Any symbols
that introduce the option, or delimit its parameter will be
stripped. This function allows to work with allowed prefixes, in
which case 'option_name' will return the prefix specification, and
full option name should be queried explicitly.
*/
const std::string& raw_option_name() const;
/** Returns the first of option values. If there's more than one option
throws multiple_values. If there are no options, returns empty
string. */
const std::string& option_value() const;
/** Returns all option values. */
const std::vector<std::string>& option_values() const;
/** Returns the argument. */
const std::string& argument() const;
/** Returns all arguments read by this command line parser. */
const std::vector<std::string>& arguments() const;
/** Returns the token that was current when 'operator++' was
last invoked. */
const std::string& last() const;
private:
// Properties of an option.
enum properties_t {
no_parameter = 1,
/// 0 or 1 parameter
allow_parameter,
/// exactly 1 parameter
require_parameter,
/// 0 or more parameters
allow_parameters,
/// 1 or more parameters
require_parameters
};
enum element_kind_t {
ek_option = 0,
ek_argument
};
// General error status.
enum error_type_t {
no_error = 0,
unknown_option,
ambiguous_option,
invalid_syntax
};
// Detailed error status.
enum error_description_t {
ed_success = 0,
ed_long_not_allowed,
ed_long_adjacent_not_allowed,
ed_short_adjacent_not_allowed,
ed_empty_adjacent_parameter,
ed_missing_parameter,
ed_extra_parameter,
ed_unknown_option,
ed_ambiguous_option
};
// The standard say that nested classes has no access to
// private member of enclosing class. However, most compilers
// allow that and it's likely be to allowed in future:
// http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#45
// For Sun compiler, try using friend declaration.
#if BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x560))
struct option;
friend struct option;
#endif
struct option {
option(const std::string& long_name, char short_name,
properties_t properties, int index)
: long_name(long_name), short_name(short_name), index(index),
properties(properties)
{}
std::string long_name;
char short_name;
int index;
properties_t properties;
};
std::vector<option> options;
void init(const std::vector<std::string>& args, int style,
bool allow_unregistered);
void check_style(int style) const;
void next();
const option* find_long_option(const char* name);
const option* find_short_option(char name);
enum option_kind { error_option, no_option, long_option, short_option,
dos_option };
option_kind is_option(const char* s);
// All handle_* member functions take string without any "--" or "-" or "/"
// They return true and success and set m_num_tokens to the number of
// tokens that were consumed.
bool handle_long_option(const char* s);
bool handle_short_option(const char* s, bool ignore_sticky = false);
bool handle_dos_option(const char* s);
// Attempts to apply additional parser to 's'.
bool handle_additional_parser(const std::pair<std::string, std::string>& p);
void init(const std::vector<std::string>& args);
bool process_parameter(const option* opt, bool adjacent_parameter,
bool next_parameter);
void advance(int count);
/// Converts parameter property character into enum value.
properties_t translate_property(char p);
/** Clears the error state. If there were an error, throws appripriate
exception. */
void clear_error();
void
finish_option(option& opt,
std::vector<std::string>& other_tokens);
// Copies of input.
std::vector<std::string> args;
style_t style;
bool allow_unregistered;
style_t m_style;
bool m_allow_unregistered;
// Current state of parsing.
unsigned index;
const char* m_current;
const char* m_next;
const char* pending_short_option;
bool m_no_more_options;
error_description_t m_error_description;
element_kind_t m_element_kind;
int m_option_index;
// Results of parsing the last option
std::string m_last;
const option* m_opt;
std::string m_option_name, m_raw_option_name, m_argument;
std::vector<std::string> m_option_values;
int m_num_tokens;
bool m_disguised_long;
std::vector<std::string> m_arguments;
const options_description* m_desc;
const positional_options_description* m_positional;
additional_parser m_additional_parser;
style_parser m_style_parser;
};
void test_cmdline_detail();

View File

@@ -21,6 +21,10 @@
#include <boost/program_options/detail/convert.hpp>
#endif
#if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
#include <istream> // std::getline
#endif
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/shared_ptr.hpp>
@@ -160,7 +164,8 @@ namespace boost { namespace program_options { namespace detail {
}
// Specialization is needed to workaround getline bug on Comeau.
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303))
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \
(defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741)))
template<>
bool
basic_config_file_iterator<wchar_t>::getline(std::string& s);

View File

@@ -19,7 +19,7 @@
#include <cwchar>
#include <stdexcept>
#if BOOST_WORKAROUND(__ICL, <= 700) || BOOST_WORKAROUND(_MSC_VER, <= 1200)
#if defined(BOOST_NO_STDC_NAMESPACE)
#include <wchar.h>
namespace std
{

View File

@@ -28,14 +28,14 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>::
basic_command_line_parser(const std::vector<
std::basic_string<charT> >& args)
: common_command_line_parser(to_internal(args))
: detail::cmdline(to_internal(args))
{}
template<class charT>
basic_command_line_parser<charT>::
basic_command_line_parser(int argc, charT* argv[])
: common_command_line_parser(
: 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)))
@@ -46,6 +46,7 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::options(const options_description& desc)
{
detail::cmdline::set_options_description(desc);
m_desc = &desc;
return *this;
}
@@ -55,7 +56,7 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>::positional(
const positional_options_description& desc)
{
m_positional = &desc;
detail::cmdline::set_positional_options(desc);
return *this;
}
@@ -63,7 +64,7 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::style(int style)
{
m_style = style;
detail::cmdline::style(style);
return *this;
}
@@ -71,18 +72,20 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::extra_parser(ext_parser ext)
{
m_ext = ext;
detail::cmdline::set_additional_parser(ext);
return *this;
}
template<class charT>
basic_parsed_options<charT>
basic_command_line_parser<charT>::run() const
basic_command_line_parser<charT>::run()
{
parsed_options result(m_desc);
result.options = detail::cmdline::run();
// Presense of parsed_options -> wparsed_options conversion
// does the trick.
return basic_parsed_options<charT>(
common_command_line_parser::run());
return basic_parsed_options<charT>(result);
}

View File

@@ -1,14 +1,3 @@
#ifndef BOOST_UTF8_CODECVT_FACET_HPP
#define BOOST_UTF8_CODECVT_FACET_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.hpp
// Copyright © 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
@@ -16,206 +5,21 @@
// without express or implied warranty, and with no claim as to its suitability
// for any purpose.
// Note:(Robert Ramey). I have made the following alterations in the original
// code.
// a) Rendered utf8_codecvt<wchar_t, char> with using templates
// b) Move longer functions outside class definition to prevent inlining
// and make code smaller
// c) added on a derived class to permit translation to/from current
// locale to utf8
// See http://www.boost.org for updates, documentation, and revision history.
// archives stored as text - note these ar templated on the basic
// stream templates to accommodate wide (and other?) kind of characters
//
// note the fact that on libraries without wide characters, ostream is
// is not a specialization of basic_ostream which in fact is not defined
// in such cases. So we can't use basic_ostream<OStream::char_type> but rather
// use two template parameters
//
// utf8_codecvt_facet
// This is an implementation of a std::codecvt facet for translating
// from UTF-8 externally to UCS-4. Note that this is not tied to
// any specific types in order to allow customization on platforms
// where wchar_t is not big enough.
//
// NOTES: The current implementation jumps through some unpleasant hoops in
// order to deal with signed character types. As a std::codecvt_base::result,
// it is necessary for the ExternType to be convertible to unsigned char.
// I chose not to tie the extern_type explicitly to char. But if any combination
// of types other than <wchar_t,char_t> is used, then std::codecvt must be
// specialized on those types for this to work.
#include <locale>
// for mbstate_t
#include <wchar.h>
#ifndef BOOST_PROGRAM_OPTIONS_UTF8_CODECVT_FACET_HPP
#define BOOST_PROGRAM_OPTIONS_UTF8_CODECVT_FACET_HPP
#include <boost/program_options/config.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(__BORLANDC__,BOOST_TESTED_AT(0x551))
#ifndef _RWSTD_NO_NAMESPACE
using std::codecvt;
using std::min;
#ifdef _RWSTD_NO_MBSTATE_T
using std::mbstate_t;
#endif
#endif
#elif defined(__COMO__) || defined(_MSC_VER) && _MSC_VER <= 1300
typedef ::mbstate_t mbstate_t;
#elif defined(BOOST_NO_STDC_NAMESPACE)
typedef std::mbstate_t mbstate_t;
namespace std{
using ::codecvt;
} // namespace std
#define BOOST_UTF8_BEGIN_NAMESPACE \
namespace boost { namespace program_options { namespace detail {
#define BOOST_UTF8_END_NAMESPACE }}}
#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/detail/utf8_codecvt_facet.hpp>
#undef BOOST_UTF8_BEGIN_NAMESPACE
#undef BOOST_UTF8_END_NAMESPACE
#undef BOOST_UTF8_DECL
#endif
// maximum lenght of a multibyte string
#define MB_LENGTH_MAX 8
namespace boost { namespace program_options { namespace detail {
struct BOOST_PROGRAM_OPTIONS_DECL utf8_codecvt_facet_wchar_t :
public std::codecvt<wchar_t, char, mbstate_t>
{
public:
explicit utf8_codecvt_facet_wchar_t(std::size_t no_locale_manage=0)
: std::codecvt<wchar_t, char, mbstate_t>(no_locale_manage)
{}
protected:
virtual std::codecvt_base::result do_in(
mbstate_t& state,
const char * from,
const char * from_end,
const char * & from_next,
wchar_t * to,
wchar_t * to_end,
wchar_t*& to_next
) const;
virtual std::codecvt_base::result do_out(
mbstate_t & state, const wchar_t * from,
const wchar_t * from_end, const wchar_t* & from_next,
char * to, char * to_end, char * & to_next
) const;
bool invalid_continuing_octet(unsigned char octet_1) const {
return (octet_1 < 0x80|| 0xbf< octet_1);
}
bool invalid_leading_octet(unsigned char octet_1) const {
return (0x7f < octet_1 && octet_1 < 0xc0) ||
(octet_1 > 0xfd);
}
// continuing octets = octets except for the leading octet
static unsigned int get_cont_octet_count(unsigned char lead_octet) {
return get_octet_count(lead_octet) - 1;
}
static unsigned int get_octet_count(unsigned char lead_octet);
// How many "continuing octets" will be needed for this word
// == total octets - 1.
int get_cont_octet_out_count(wchar_t word) const ;
virtual bool do_always_noconv() const throw() { return false; }
// UTF-8 isn't really stateful since we rewind on partial conversions
virtual std::codecvt_base::result do_unshift(
mbstate_t&,
char * from,
char * to,
char * & next
) const
{
next = from;
return ok;
}
virtual int do_encoding() const throw() {
const int variable_byte_external_encoding=0;
return variable_byte_external_encoding;
}
// How many char objects can I process to get <= max_limit
// wchar_t objects?
virtual int do_length(
mbstate_t &,
const char * from,
const char * from_end,
std::size_t max_limit
#if BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600))
) const throw();
#else
) const;
#endif
// Largest possible value do_length(state,from,from_end,1) could return.
virtual int do_max_length() const throw () {
return 6; // largest UTF-8 encoding of a UCS-4 character
}
};
#if 0 // not used - incorrect in any case
// Robert Ramey - use the above to make a code converter from multi-byte
// char strings to utf8 encoding
struct utf8_codecvt_facet_char : public utf8_codecvt_facet_wchar_t
{
typedef utf8_codecvt_facet_wchar_t base_class;
public:
explicit utf8_codecvt_facet_char(size_t no_locale_manage=0)
: base_class(no_locale_manage)
{}
protected:
virtual std::codecvt_base::result do_in(
mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const;
virtual std::codecvt_base::result do_out(
mbstate_t & state,
const char * from,
const char * from_end,
const char* & from_next,
char * to,
char * to_end,
char * & to_next
) const;
// How many char objects can I process to get <= max_limit
// char objects?
virtual int do_length(
const mbstate_t&,
const char * from,
const char * from_end,
size_t max_limit
) const;
};
#endif
template<class Internal, class External>
struct utf8_codecvt_facet
{};
template<>
struct BOOST_PROGRAM_OPTIONS_DECL utf8_codecvt_facet<wchar_t, char>
: public utf8_codecvt_facet_wchar_t
{};
#if 0
template<>
struct utf8_codecvt_facet<char, char>
: public utf8_codecvt_facet_char
{};
#endif
}}}
#endif // BOOST_UTF8_CODECVT_FACET_HPP

View File

@@ -6,6 +6,8 @@
// This file defines template functions that are declared in
// ../value_semantic.hpp.
#include <boost/throw_exception.hpp>
namespace boost { namespace program_options {
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
@@ -81,7 +83,7 @@ namespace boost { namespace program_options {
v = any(lexical_cast<T>(s));
}
catch(const bad_lexical_cast&) {
throw invalid_option_value(s);
boost::throw_exception(invalid_option_value(s));
}
}
@@ -128,14 +130,14 @@ namespace boost { namespace program_options {
v = boost::any(std::vector<T>());
}
std::vector<T>* tv = boost::any_cast< std::vector<T> >(&v);
assert(tv);
assert(NULL != tv);
for (unsigned i = 0; i < s.size(); ++i)
{
try {
tv->push_back(boost::lexical_cast<T>(s[i]));
}
catch(const bad_lexical_cast& /*e*/) {
throw invalid_option_value(s[i]);
boost::throw_exception(invalid_option_value(s[i]));
}
}
}

View File

@@ -23,10 +23,10 @@ namespace boost { namespace program_options {
template<class charT>
class basic_option {
public:
basic_option() : position_key(-1) {}
basic_option() : position_key(-1), unregistered(false) {}
basic_option(const std::string& string_key,
const std::vector< std::string> &value)
: string_key(string_key), value(value)
: string_key(string_key), value(value), unregistered(false)
{}
/** String key of this option. Intentionally independent of the template
@@ -41,6 +41,10 @@ namespace boost { namespace program_options {
int position_key;
/** Option's value */
std::vector< std::basic_string<charT> > value;
/** True if option was not recognized. In that case,
'string_key' and 'value' are results of purely
syntactic parsing of source. */
bool unregistered;
};
typedef basic_option<char> option;
typedef basic_option<wchar_t> woption;

View File

@@ -1,4 +1,5 @@
// Copyright Vladimir Prus 2002-2004.
// Copyright Bertolt Mildner 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -77,11 +78,23 @@ namespace program_options {
virtual ~option_description();
/// Name to be used with short-style option ("-w").
const std::string& short_name() const;
/** Given 'option', specified in the input source,
return 'true' is 'option' specifies *this.
*/
bool match(const std::string& option, bool approx) const;
/** Return the key that should identify the option, in
particular in the variables_map class.
The 'option' parameter is the option spelling from the
input source.
If option name contains '*', returns 'option'.
If long name was specified, it's the long name, otherwise
it's a short name with prepended '-'.
*/
const std::string& key(const std::string& option) const;
/// Name to be used with long-style option ("--whatever").
const std::string& long_name() const;
/// Explanation of this option
const std::string& description() const;
@@ -96,11 +109,10 @@ namespace program_options {
std::string format_parameter() const;
private:
option_description& name(const char* name);
option_description& set_name(const char* name);
std::string m_short_name, m_long_name, m_description;
std::string m_default_value, m_default_parameter;
// shared_ptr is needed to simplify memory management in
// copy ctor and destructor.
shared_ptr<const value_semantic> m_value_semantic;
@@ -141,13 +153,15 @@ namespace program_options {
*/
class BOOST_PROGRAM_OPTIONS_DECL options_description {
public:
static const unsigned m_default_line_length = 80;
/** Creates the instance. */
options_description();
options_description(unsigned line_length = m_default_line_length);
/** Creates the instance. The 'caption' parameter gives the name of
this 'options_description' instance. Primarily useful for output.
*/
options_description(const std::string& caption);
options_description(const std::string& caption,
unsigned line_length = m_default_line_length);
/** Adds new variable description. Throws duplicate_variable_error if
either short or long name matches that of already present one.
*/
@@ -169,32 +183,14 @@ namespace program_options {
*/
options_description_easy_init add_options();
/** Count the number of option descriptions with given name.
Returns 0 or 1.
The 'name' parameter can be either name of long option, and short
option prefixed by '-'.
*/
unsigned count(const std::string& name) const;
/** Count the number of descriptions having the given string as
prefix. This makes sense only for long options.
*/
unsigned count_approx(const std::string& prefix) const;
/** Returns description given a name.
@pre count(name) == 1
*/
const option_description& find(const std::string& name) const;
/** Returns description given a prefix. Throws
@pre count_approx(name) == 1
*/
const option_description& find_approx(const std::string& prefix) const;
/// Returns all such strings x for which count(x) == 1
std::set<std::string> keys() const;
/** For each option description stored, contains long name if not empty,
if it is empty, short name is returned.
*/
std::set<std::string> primary_keys() const;
/// Returns all such long options for which 'prefix' is prefix
std::set<std::string> approximations(const std::string& prefix) const;
const option_description& find(const std::string& name, bool approx)
const;
const option_description* find_nothrow(const std::string& name,
bool approx) const;
const std::vector< shared_ptr<option_description> >& options() const;
/** Produces a human readable output of 'desc', listing options,
their descriptions and allowed parameters. Other options_description
@@ -211,15 +207,15 @@ namespace program_options {
typedef std::pair<name2index_iterator, name2index_iterator>
approximation_range;
approximation_range find_approximation(const std::string& prefix) const;
//approximation_range find_approximation(const std::string& prefix) const;
std::string m_caption;
const unsigned m_line_length;
// Data organization is chosen because:
// - there could be two names for one option
// - option_add_proxy needs to know the last added option
std::vector< shared_ptr<option_description> > options;
std::map<std::string, int> name2index;
std::vector< shared_ptr<option_description> > m_options;
// Whether the option comes from one of declared groups.
#if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, BOOST_TESTED_AT(313))
// vector<bool> is buggy there, see

View File

@@ -9,6 +9,7 @@
#include <boost/program_options/config.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/detail/cmdline.hpp>
#include <boost/function/function1.hpp>
@@ -69,24 +70,6 @@ namespace boost { namespace program_options {
typedef function1<std::pair<std::string, std::string>, const std::string&> ext_parser;
/** Character-type independent command line parser. */
class BOOST_PROGRAM_OPTIONS_DECL common_command_line_parser {
public:
/// Creates the parsers. The arguments must be in internal encoding.
common_command_line_parser(const std::vector<std::string>& args);
/// Parses the command line and returns parsed options in internal
/// encoding.
parsed_options run() const;
protected:
int m_style;
const options_description* m_desc;
const positional_options_description* m_positional;
function1<std::pair<std::string, std::string>, const std::string&> m_ext;
// Intentionally independent from charT
std::vector<std::string> m_args;
};
/** Command line parser.
The class allows one to specify all the information needed for parsing
@@ -99,7 +82,7 @@ namespace boost { namespace program_options {
alternative.
*/
template<class charT>
class basic_command_line_parser : private common_command_line_parser {
class basic_command_line_parser : private detail::cmdline {
public:
/** Creates a command line parser for the specified arguments
list. The 'args' parameter should not include program name.
@@ -122,8 +105,9 @@ namespace boost { namespace program_options {
/** Sets the extra parsers. */
basic_command_line_parser& extra_parser(ext_parser);
basic_parsed_options<charT> run() const;
basic_parsed_options<charT> run();
private:
const options_description* m_desc;
};
typedef basic_command_line_parser<char> command_line_parser;
@@ -201,6 +185,6 @@ namespace boost { namespace program_options {
#undef DECL
#include "detail/parsers.hpp"
#include "boost/program_options/detail/parsers.hpp"
#endif

View File

@@ -30,24 +30,19 @@ namespace boost { namespace program_options {
*/
virtual std::string name() const = 0;
/** Returns true if value cannot be specified in source at all.
Other methods can still set the value somehow, but
user can't affect it.
*/
virtual bool is_zero_tokens() const = 0;
/** The minimum number of tokens for this option that
should be present on the command line. */
virtual unsigned min_tokens() const = 0;
/** The maximum number of tokens for this option that
should be present on the command line. */
virtual unsigned max_tokens() const = 0;
/** Returns true if values from different sources should be composed.
Otherwise, value from the first source is used and values from
other sources are discarded.
*/
virtual bool is_composing() const = 0;
/** Returns true if explicit value of an option can be omitted.
*/
virtual bool is_implicit() const = 0;
/** Returns true if value can span several token in input source. */
virtual bool is_multitoken() const = 0;
/** Parses a group of tokens that specify a value of option.
Stores the result in 'value_store', using whatever representation
@@ -117,10 +112,10 @@ namespace boost { namespace program_options {
std::string name() const;
bool is_zero_tokens() const { return m_zero_tokens; }
unsigned min_tokens() const;
unsigned max_tokens() const;
bool is_composing() const { return false; }
bool is_implicit() const { return false; }
bool is_multitoken() const { return false; }
/** If 'value_store' is already initialized, or new_tokens
has more than one elements, throws. Otherwise, assigns
@@ -147,7 +142,7 @@ 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_implicit(false), m_multitoken(false), m_zero_tokens(false)
m_multitoken(false), m_zero_tokens(false)
{}
/** Specifies default value, which will be used
@@ -191,13 +186,6 @@ namespace boost { namespace program_options {
return this;
}
/** Specifies that the value is implicit. */
typed_value* implicit()
{
m_implicit = true;
return this;
}
/** Specifies that the value can span multiple tokens. */
typed_value* multitoken()
{
@@ -216,10 +204,26 @@ namespace boost { namespace program_options {
std::string name() const;
bool is_zero_tokens() const { return m_zero_tokens; }
bool is_composing() const { return m_composing; }
bool is_implicit() const { return m_implicit; }
bool is_multitoken() const { return m_multitoken; }
unsigned min_tokens() const
{
if (m_zero_tokens) {
return 0;
} else {
return 1;
}
}
unsigned max_tokens() const {
if (m_multitoken) {
return 32000;
} else if (m_zero_tokens) {
return 0;
} else {
return 1;
}
}
/** Creates an instance of the 'validator' class and calls
@@ -304,7 +308,7 @@ namespace boost { namespace program_options {
}}
#include "detail/value_semantic.hpp"
#include "boost/program_options/detail/value_semantic.hpp"
#endif

View File

@@ -14,6 +14,7 @@
#include <string>
#include <map>
#include <set>
namespace boost { namespace program_options {
@@ -119,6 +120,14 @@ namespace boost { namespace program_options {
/** Implementation of abstract_variables_map::get
which does 'find' in *this. */
const variable_value& get(const std::string& name) const;
/** Names of option with 'final' values -- which should not
be changed by subsequence assignments. */
std::set<std::string> m_final;
friend void store(const basic_parsed_options<char>& options,
variables_map& xm,
bool utf8);
};
/** Stores in 'm' all options that are defined in 'options'.
@@ -175,19 +184,13 @@ namespace boost { namespace program_options {
template<class T>
const T&
variable_value::as() const {
const T* r = boost::any_cast<T>(&v);
if (!r)
throw boost::bad_any_cast();
return *r;
return boost::any_cast<const T&>(v);
}
template<class T>
T&
variable_value::as() {
T* r = boost::any_cast<T>(&v);
if (!r)
throw boost::bad_any_cast();
return *r;
return boost::any_cast<T&>(v);
}
}}

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/throw_exception.hpp>
#include <iostream>
#include <fstream>
@@ -54,7 +55,7 @@ namespace boost { namespace program_options { namespace detail {
bad_prefixes = true;
}
if (bad_prefixes)
throw error("bad prefixes");
boost::throw_exception(error("bad prefixes"));
allowed_prefixes.insert(s);
}
}
@@ -100,10 +101,10 @@ namespace boost { namespace program_options { namespace detail {
string value = trim_ws(s.substr(n+1));
if (!allowed_option(name))
throw unknown_option(name);
boost::throw_exception(unknown_option(name));
if (value.empty())
throw invalid_syntax(s, "no value given");
boost::throw_exception(invalid_syntax(s, "no value given"));
found = true;
this->value().string_key = name;
@@ -112,7 +113,7 @@ namespace boost { namespace program_options { namespace detail {
break;
} else {
throw invalid_syntax(s, "unrecognized line");
boost::throw_exception(invalid_syntax(s, "unrecognized line"));
}
}
}
@@ -136,9 +137,8 @@ namespace boost { namespace program_options { namespace detail {
return false;
}
// On Metrowerks, the function is defined inline.
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303))
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \
(defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741)))
template<>
bool
basic_config_file_iterator<wchar_t>::getline(std::string& s)

View File

@@ -17,6 +17,7 @@
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/throw_exception.hpp>
#include <boost/bind.hpp>
@@ -62,14 +63,16 @@ namespace boost { namespace detail {
fun(state, from, from_end, from, buffer, to_end, to_next);
if (r == std::codecvt_base::error)
throw std::logic_error("character conversion failed");
boost::throw_exception(
std::logic_error("character conversion failed"));
// 'partial' is not an error, it just means not all source
// characters were converted. However, we need to check that at
// least one new target character was produced. If not, it means
// the source data is incomplete, and since we don't have extra
// data to add to source, it's error.
if (to_next == buffer)
throw std::logic_error("character conversion failed");
boost::throw_exception(
std::logic_error("character conversion failed"));
// Add converted characters
result.append(buffer, to_next);
@@ -106,7 +109,7 @@ namespace boost {
namespace {
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>
boost::program_options::detail::utf8_codecvt_facet
utf8_facet;
}

View File

@@ -1,4 +1,5 @@
// Copyright Vladimir Prus 2002-2004.
// Copyright Bertolt Mildner 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,9 +12,11 @@
// should move that to a separate headers.
#include <boost/program_options/parsers.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/throw_exception.hpp>
#include <cassert>
#include <climits>
@@ -33,7 +36,7 @@ namespace boost { namespace program_options {
const value_semantic* s)
: m_value_semantic(s)
{
this->name(name);
this->set_name(name);
}
@@ -43,32 +46,61 @@ namespace boost { namespace program_options {
const char* description)
: m_description(description), m_value_semantic(s)
{
this->name(name);
this->set_name(name);
}
option_description::~option_description()
{
}
option_description&
option_description::name(const char* _name)
bool
option_description::match(const std::string& option, bool approx) const
{
std::string name(_name);
string::size_type n = name.find(',');
if (n != string::npos) {
assert(n == name.size()-2);
m_long_name = name.substr(0, n);
m_short_name = name.substr(n+1,1);
} else {
m_long_name = name;
bool result = false;
if (!m_long_name.empty()) {
if (*m_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))
== 0)
result = true;
}
if (approx)
{
if (m_long_name.find(option) == 0)
result = true;
}
else
{
if (m_long_name == option)
result = true;
}
}
return *this;
if (m_short_name == option)
result = true;
return result;
}
const std::string&
option_description::short_name() const
{
return m_short_name;
const std::string&
option_description::key(const std::string& option) const
{
if (!m_long_name.empty())
if (m_long_name.find('*') != string::npos)
// The '*' character means we're long_name
// matches only part of the input. So, returning
// long name will remove some of the information,
// and we have to return the option as specified
// in the source.
return option;
else
return m_long_name;
else
return m_short_name;
}
const std::string&
@@ -77,6 +109,21 @@ namespace boost { namespace program_options {
return m_long_name;
}
option_description&
option_description::set_name(const char* _name)
{
std::string name(_name);
string::size_type n = name.find(',');
if (n != string::npos) {
assert(n == name.size()-2);
m_long_name = name.substr(0, n);
m_short_name = '-' + name.substr(n+1,1);
} else {
m_long_name = name;
}
return *this;
}
const std::string&
option_description::description() const
{
@@ -92,17 +139,17 @@ namespace boost { namespace program_options {
std::string
option_description::format_name() const
{
if (!short_name().empty())
return string("-").append(short_name()).append(" [ --").
append(long_name()).append(" ]");
if (!m_short_name.empty())
return string(m_short_name).append(" [ --").
append(m_long_name).append(" ]");
else
return string("--").append(long_name());
return string("--").append(m_long_name);
}
std::string
option_description::format_parameter() const
{
if (!m_value_semantic->is_zero_tokens())
if (m_value_semantic->max_tokens() != 0)
return m_value_semantic->name();
else
return "";
@@ -150,30 +197,19 @@ namespace boost { namespace program_options {
return *this;
}
options_description::options_description()
options_description::options_description(unsigned line_length)
: m_line_length(line_length)
{}
options_description::options_description(const string& caption)
: m_caption(caption)
options_description::options_description(const string& caption,
unsigned line_length)
: m_caption(caption), m_line_length(line_length)
{}
void
options_description::add(shared_ptr<option_description> desc)
{
const string& s = desc->short_name();
const string& l = desc->long_name();
assert(!s.empty() || !l.empty());
if (!s.empty())
if (name2index.count("-" + s) != 0)
throw duplicate_option_error("Short name '" + s + "' is already present");
else
name2index["-" + s] = options.size();
if (!l.empty())
if (name2index.count(s) != 0)
throw duplicate_option_error("Long name '" + s + "' is already present");
else
name2index[l] = options.size();
options.push_back(desc);
m_options.push_back(desc);
belong_to_group.push_back(false);
}
@@ -183,8 +219,8 @@ namespace boost { namespace program_options {
shared_ptr<options_description> d(new options_description(desc));
groups.push_back(d);
for (size_t i = 0; i < desc.options.size(); ++i) {
add(desc.options[i]);
for (size_t i = 0; i < desc.m_options.size(); ++i) {
add(desc.m_options[i]);
belong_to_group.back() = true;
}
@@ -197,76 +233,55 @@ namespace boost { namespace program_options {
return options_description_easy_init(this);
}
unsigned
options_description::count(const std::string& name) const
{
return name2index.count(name);
}
unsigned
options_description::count_approx(const std::string& prefix) const
{
approximation_range er = find_approximation(prefix);
return distance(er.first, er.second);
}
const option_description&
options_description::find(const std::string& name) const
options_description::find(const std::string& name, bool approx) const
{
assert(this->count(name) != 0);
return *options[name2index.find(name)->second];
const option_description* d = find_nothrow(name, approx);
if (!d)
boost::throw_exception(unknown_option(name));
return *d;
}
const option_description&
options_description::find_approx(const std::string& prefix) const
const std::vector< shared_ptr<option_description> >&
options_description::options() const
{
approximation_range er = find_approximation(prefix);
assert(distance(er.first, er.second) == 1);
return *options[er.first->second];
return m_options;
}
std::set<std::string>
options_description::keys() const
const option_description*
options_description::find_nothrow(const std::string& name,
bool approx) const
{
set<string> result;
for (map<string, int>::const_iterator i = name2index.begin();
i != name2index.end();
++i)
result.insert(i->first);
return result;
int found = -1;
// 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)
{
if (m_options[i]->match(name, approx))
{
if (found != -1)
{
vector<string> alts;
// FIXME: the use of 'key' here might not
// be the best approach.
alts.push_back(m_options[found]->key(name));
alts.push_back(m_options[i]->key(name));
boost::throw_exception(ambiguous_option(name, alts));
}
else
{
found = i;
}
}
}
if (found != -1) {
return m_options[found].get();
} else {
return 0;
}
}
std::set<std::string>
options_description::primary_keys() const
{
set<string> result;
for (size_t i = 0; i < options.size(); ++i)
if (options[i]->long_name().empty())
result.insert("-" + options[i]->short_name());
else
result.insert(options[i]->long_name());
return result;
}
std::set<std::string>
options_description::approximations(const std::string& prefix) const
{
approximation_range er = find_approximation(prefix);
set<string> result;
for (name2index_iterator i = er.first; i != er.second; ++i)
result.insert(i->first);
return result;
}
options_description::approximation_range
options_description::find_approximation(const std::string& prefix) const
{
name2index_iterator b = name2index.lower_bound(prefix);
name2index_iterator e = name2index.upper_bound(prefix + char(CHAR_MAX));
return make_pair(b, e);
}
BOOST_PROGRAM_OPTIONS_DECL
std::ostream& operator<<(std::ostream& os, const options_description& desc)
{
@@ -275,21 +290,203 @@ namespace boost { namespace program_options {
}
namespace {
void format_paragraph(std::ostream& os,
std::string par,
unsigned first_column_width,
unsigned line_length)
{
// index of tab (if present) is used as additional indent relative
// to first_column_width if paragrapth is spanned over multiple
// lines if tab is not on first line it is ignored
string::size_type par_indent = par.find('\t');
if (par_indent == string::npos)
{
par_indent = 0;
}
else
{
// only one tab per paragraph allowed
if (count(par.begin(), par.end(), '\t') > 1)
{
boost::throw_exception(program_options::error(
"Only one tab per paragraph is allowed"));
}
// erase tab from string
par.erase(par_indent, 1);
// this assert may fail due to user error or
// environment conditions!
assert(par_indent < (line_length - first_column_width));
// ignore tab if not on first line
if (par_indent >= (line_length - first_column_width))
{
par_indent = 0;
}
}
if (par.size() < (line_length - first_column_width))
{
os << par;
}
else
{
string::const_iterator line_begin = par.begin();
const string::const_iterator par_end = par.end();
bool first_line = true; // of current paragraph!
unsigned indent = first_column_width;
while (line_begin < par_end) // paragraph lines
{
if (!first_line)
{
// trimm leading single spaces
// if (firstchar == ' ') &&
// ((exists(firstchar + 1) && (firstchar + 1 != ' '))
if ((*line_begin == ' ') &&
((line_begin + 1 < par_end) &&
(*(line_begin + 1) != ' ')))
{
line_begin += 1; // line_begin != line_end
}
}
string::const_iterator line_end;
if (line_begin + (line_length - indent) > par_end)
{
line_end = par_end;
}
else
{
line_end = line_begin + (line_length - indent);
}
// prevent chopped words
// if (lastchar != ' ') &&
// ((exists(lastchar + 1) && (lastchar + 1 != ' '))
if ((*(line_end - 1) != ' ') &&
((line_end < par_end) && (*line_end != ' ')))
{
// find last ' ' in the second half of the current paragraph line
string::const_iterator last_space =
find(reverse_iterator<string::const_iterator>(line_end - 1),
reverse_iterator<string::const_iterator>(line_begin - 1),
' ')
.base();
if (last_space != line_begin - 1)
{
// is last_space within the second half ot the
// current line
if (unsigned(distance(last_space, line_end)) <
(line_length - indent) / 2)
{
line_end = last_space;
}
}
} // prevent chopped words
// write line to stream
copy(line_begin, line_end, ostream_iterator<char>(os));
if (first_line)
{
indent = first_column_width + par_indent;
first_line = false;
}
// more lines to follow?
if (line_end != par_end)
{
os << '\n';
for(unsigned pad = indent; pad > 0; --pad)
{
os.put(' ');
}
}
// next line starts after of this line
line_begin = line_end;
} // paragraph lines
}
}
void format_description(std::ostream& os,
const std::string& desc,
unsigned first_column_width,
unsigned line_length)
{
// we need to use one char less per line to work correctly if actual
// console has longer lines
assert(line_length > 1);
if (line_length > 1)
{
--line_length;
}
// line_length must be larger than first_column_width
// this assert may fail due to user error or environment conditions!
assert(line_length > first_column_width);
// Note: can't use 'tokenizer' as name of typedef -- borland
// will consider subsequence uses of 'tokenizer' as uses of
// boost::tokenizer, not typedef.
typedef boost::tokenizer<boost::char_separator<char> > tok;
tok paragraphs(
desc,
char_separator<char>("\n", "", boost::keep_empty_tokens));
tok::const_iterator par_iter = paragraphs.begin();
const tok::const_iterator par_end = paragraphs.end();
while (par_iter != par_end) // paragraphs
{
format_paragraph(os, *par_iter, first_column_width,
line_length);
++par_iter;
// prepair next line if any
if (par_iter != par_end)
{
os << '\n';
for(unsigned pad = first_column_width; pad > 0; --pad)
{
os.put(' ');
}
}
} // paragraphs
}
void format_one(std::ostream& os, const option_description& opt,
unsigned first_column_width)
unsigned first_column_width, unsigned line_length)
{
stringstream ss;
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
// Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
os << ss.str();
if (!opt.description().empty()) {
for(int pad = first_column_width - ss.str().size(); pad > 0; --pad) {
if (!opt.description().empty())
{
for(unsigned pad = first_column_width - ss.str().size();
pad > 0;
--pad)
{
os.put(' ');
}
os << " : " << opt.description();
format_description(os, opt.description(),
first_column_width, line_length);
}
}
}
@@ -301,25 +498,28 @@ namespace boost { namespace program_options {
os << m_caption << ":\n";
/* Find the maximum width of the option column */
unsigned width(24);
unsigned width(23);
unsigned i; // vc6 has broken for loop scoping
for (i = 0; i < options.size(); ++i)
for (i = 0; i < m_options.size(); ++i)
{
const option_description& opt = *options[i];
const option_description& opt = *m_options[i];
stringstream ss;
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
width = max(width, static_cast<unsigned>(ss.str().size()));
width = (max)(width, static_cast<unsigned>(ss.str().size()));
}
/* add an additional space to improve readability */
++width;
/* The options formatting style is stolen from Subversion. */
for (i = 0; i < options.size(); ++i)
for (i = 0; i < m_options.size(); ++i)
{
if (belong_to_group[i])
continue;
const option_description& opt = *options[i];
const option_description& opt = *m_options[i];
format_one(os, opt, width);
format_one(os, opt, width, m_line_length);
os << "\n";
}

View File

@@ -17,7 +17,9 @@
#include <boost/program_options/detail/convert.hpp>
#include <boost/bind.hpp>
#include <boost/throw_exception.hpp>
#include <cctype>
#if !defined(__GNUC__) || __GNUC__ < 3
#include <iostream>
@@ -83,111 +85,21 @@ namespace boost { namespace program_options {
}
#endif
namespace detail
{
void
parse_command_line(cmdline& cmd, parsed_options& result);
}
common_command_line_parser::
common_command_line_parser(const std::vector<std::string>& args)
: m_style(0), m_desc(0), m_positional(0), m_args(args)
{}
parsed_options
common_command_line_parser::run() const
{
parsed_options result(m_desc);
detail::cmdline cmd(m_args, m_style);
cmd.set_additional_parser(m_ext);
if (m_desc) {
set<string> keys = m_desc->primary_keys();
for (set<string>::iterator i = keys.begin(); i != keys.end(); ++i) {
const option_description& d = m_desc->find(*i);
char s = d.short_name().empty() ? '\0' : d.short_name()[0];
shared_ptr<const value_semantic> vs = d.semantic();
char flags;
if (vs->is_zero_tokens())
flags = '|';
else
if (vs->is_implicit())
if (vs->is_multitoken())
flags = '*';
else
flags = '?';
else if (vs->is_multitoken())
flags = '+';
else flags = ':';
cmd.add_option(d.long_name(), s, flags, 1);
}
}
detail::parse_command_line(cmd, result);
if (m_positional)
{
unsigned position = 0;
for (unsigned i = 0; i < result.options.size(); ++i) {
option& opt = result.options[i];
if (opt.position_key != -1) {
if (position >= m_positional->max_total_count())
{
throw too_many_positional_options_error(
"too many positional options");
}
opt.string_key = m_positional->name_for_position(position);
++position;
}
}
}
return result;
}
namespace detail {
void
parse_command_line(cmdline& cmd, parsed_options& result)
{
int position(0);
while(++cmd) {
option n;
if (cmd.at_option()) {
if (*cmd.option_name().rbegin() != '*') {
n.string_key = cmd.option_name();
}
else {
n.string_key = cmd.raw_option_name();
}
n.value = cmd.option_values();
} else {
n.position_key = position++;
n.value.clear();
n.value.push_back(cmd.argument());
}
result.options.push_back(n);
}
}
}
template<class charT>
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>& is,
const options_description& desc)
{
set<string> allowed_options;
set<string> pm = desc.primary_keys();
for (set<string>::iterator i = pm.begin(); i != pm.end(); ++i) {
const option_description& d = desc.find(*i);
const vector<shared_ptr<option_description> >& options = desc.options();
for (unsigned i = 0; i < options.size(); ++i)
{
const option_description& d = *options[i];
if (d.long_name().empty())
throw error("long name required for config file");
boost::throw_exception(
error("long name required for config file"));
allowed_options.insert(d.long_name());
}

View File

@@ -34,7 +34,7 @@ namespace boost { namespace program_options {
positional_options_description::max_total_count() const
{
return m_trailing.empty() ?
m_names.size() : std::numeric_limits<unsigned>::max();
m_names.size() : (std::numeric_limits<unsigned>::max)();
}
const std::string&

View File

@@ -1,364 +1,21 @@
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.cpp
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
// Use, modification and distribution is subject to 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 <cstdlib> // for multi-byte converson routines
#include <cassert>
#define BOOST_PROGRAM_OPTIONS_SOURCE
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/program_options/config.hpp>
#include <boost/limits.hpp>
#include <boost/config.hpp>
#define BOOST_UTF8_BEGIN_NAMESPACE \
namespace boost { namespace program_options { namespace detail {
// If we don't have wstring, then Unicode support
// is not available anyway, so we don't need to even
// compiler this file. This also fixes the problem
// with mingw, which can compile this file, but will
// generate link error when building DLL.
#ifndef BOOST_NO_STD_WSTRING
#define BOOST_UTF8_END_NAMESPACE }}}
#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL
namespace boost { namespace program_options { namespace detail {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// implementation for wchar_t
#include "../../detail/utf8_codecvt_facet.cpp"
// Translate incoming UTF-8 into UCS-4
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_in(
mbstate_t& state,
const char * from,
const char * from_end,
const char * & from_next,
wchar_t * to,
wchar_t * to_end,
wchar_t * & to_next
) const {
// Basic algorithm: The first octet determines how many
// octets total make up the UCS-4 character. The remaining
// "continuing octets" all begin with "10". To convert, subtract
// the amount that specifies the number of octets from the first
// octet. Subtract 0x80 (1000 0000) from each continuing octet,
// then mash the whole lot together. Note that each continuing
// octet only uses 6 bits as unique values, so only shift by
// multiples of 6 to combine.
while (from != from_end && to != to_end) {
// Error checking on the first octet
if (invalid_leading_octet(*from)){
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
#undef BOOST_UTF8_BEGIN_NAMESPACE
#undef BOOST_UTF8_END_NAMESPACE
#undef BOOST_UTF8_DECL
// The first octet is adjusted by a value dependent upon
// the number of "continuing octets" encoding the character
const int cont_octet_count = get_cont_octet_count(*from);
const wchar_t octet1_modifier_table[] = {
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
};
// The unsigned char conversion is necessary in case char is
// signed (I learned this the hard way)
wchar_t ucs_result =
(unsigned char)(*from++) - octet1_modifier_table[cont_octet_count];
// Invariants :
// 1) At the start of the loop, 'i' continuing characters have been
// processed
// 2) *from points to the next continuing character to be processed.
int i = 0;
while(i != cont_octet_count && from != from_end) {
// Error checking on continuing characters
if (invalid_continuing_octet(*from)) {
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
ucs_result *= (1 << 6);
// each continuing character has an extra (10xxxxxx)b attached to
// it that must be removed.
ucs_result += (unsigned char)(*from++) - 0x80;
++i;
}
// If the buffer ends with an incomplete unicode character...
if (from == from_end && i != cont_octet_count) {
// rewind "from" to before the current character translation
from_next = from - (i+1);
to_next = to;
return std::codecvt_base::partial;
}
*to++ = ucs_result;
}
from_next = from;
to_next = to;
// Were we done converting or did we run out of destination space?
if(from == from_end) return std::codecvt_base::ok;
else return std::codecvt_base::partial;
}
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_out(
mbstate_t& state,
const wchar_t * from,
const wchar_t * from_end,
const wchar_t * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
// RG - consider merging this table with the other one
const wchar_t octet1_modifier_table[] = {
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
};
wchar_t max_wchar = std::numeric_limits<wchar_t>::max();
while (from != from_end && to != to_end) {
// Check for invalid UCS-4 character
if (*from > max_wchar) {
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
int cont_octet_count = get_cont_octet_out_count(*from);
// RG - comment this formula better
int shift_exponent = (cont_octet_count) * 6;
// Process the first character
*to++ = octet1_modifier_table[cont_octet_count] +
(unsigned char)(*from / (1 << shift_exponent));
// Process the continuation characters
// Invariants: At the start of the loop:
// 1) 'i' continuing octets have been generated
// 2) '*to' points to the next location to place an octet
// 3) shift_exponent is 6 more than needed for the next octet
int i = 0;
while (i != cont_octet_count && to != to_end) {
shift_exponent -= 6;
*to++ = 0x80 + ((*from / (1 << shift_exponent)) % (1 << 6));
++i;
}
// If we filled up the out buffer before encoding the character
if(to == to_end && i != cont_octet_count) {
from_next = from;
to_next = to - (i+1);
return std::codecvt_base::partial;
}
*from++;
}
from_next = from;
to_next = to;
// Were we done or did we run out of destination space
if(from == from_end) return std::codecvt_base::ok;
else return std::codecvt_base::partial;
}
// How many char objects can I process to get <= max_limit
// wchar_t objects?
int utf8_codecvt_facet_wchar_t::do_length(
mbstate_t &,
const char * from,
const char * from_end,
std::size_t max_limit
#if BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600))
) const throw()
#else
) const
#endif
{
// RG - this code is confusing! I need a better way to express it.
// and test cases.
// Invariants:
// 1) last_octet_count has the size of the last measured character
// 2) char_count holds the number of characters shown to fit
// within the bounds so far (no greater than max_limit)
// 3) from_next points to the octet 'last_octet_count' before the
// last measured character.
int last_octet_count=0;
std::size_t char_count = 0;
const char* from_next = from;
// Use "<" because the buffer may represent incomplete characters
while (from_next+last_octet_count <= from_end && char_count <= max_limit) {
from_next += last_octet_count;
last_octet_count = (get_octet_count(*from_next));
++char_count;
}
return from_next-from_end;
}
unsigned int utf8_codecvt_facet_wchar_t::get_octet_count(
unsigned char lead_octet
){
// if the 0-bit (MSB) is 0, then 1 character
if (lead_octet <= 0x7f) return 1;
// Otherwise the count number of consecutive 1 bits starting at MSB
// assert(0xc0 <= lead_octet && lead_octet <= 0xfd);
if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2;
else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3;
else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4;
else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5;
else return 6;
}
}}}
namespace {
template<std::size_t s>
int get_cont_octet_out_count_impl(wchar_t word){
if (word < 0x80) {
return 0;
}
if (word < 0x800) {
return 1;
}
return 2;
}
template<>
int get_cont_octet_out_count_impl<4>(wchar_t word){
if (word < 0x80) {
return 0;
}
if (word < 0x800) {
return 1;
}
if (word < 0x10000) {
return 2;
}
if (word < 0x200000) {
return 3;
}
if (word < 0x4000000) {
return 4;
}
return 5;
}
} // namespace anonymous
namespace boost { namespace program_options { namespace detail {
// How many "continuing octets" will be needed for this word
// == total octets - 1.
int utf8_codecvt_facet_wchar_t::get_cont_octet_out_count(
wchar_t word
) const {
return get_cont_octet_out_count_impl<sizeof(wchar_t)>(word);
}
}}}
#if 0 // not used?
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// implementation for char
std::codecvt_base::result utf8_codecvt_facet_char::do_in(
mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
while(from_next < from_end){
wchar_t w;
wchar_t *wnext = & w;
utf8_codecvt_facet_wchar_t::result ucs4_result;
ucs4_result = base_class::do_in(
state,
from, from_end, from_next,
wnext, wnext + 1, wnext
);
if(codecvt_base::ok != ucs4_result)
return ucs4_result;
// if the conversion succeeds.
int length = std::wctomb(to_next, w);
// assert(-1 != length);
to_next += length;
}
return codecvt_base::ok;
}
std::codecvt_base::result utf8_codecvt_facet_char::do_out(
std::mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
while(from_next < from_end){
wchar_t w;
int result = std::mbtowc(&w, from_next, MB_LENGTH_MAX);
// assert(-1 != result);
from_next += result;
utf8_codecvt_facet_wchar_t::result ucs4_result;
const wchar_t *wptr = & w;
ucs4_result = base_class::do_out(
state,
wptr, wptr+1, wptr,
to_next, to_end, to_next
);
if(codecvt_base::ok != ucs4_result)
return ucs4_result;
}
return codecvt_base::ok;
}
// How many bytes objects can I process to get <= max_limit
// char objects?
int utf8_codecvt_facet_char::do_length(
const mbstate_t & initial_state,
const char * from_next,
const char * from_end,
std::size_t max_limit
) const
{
int total_length = 0;
const char *from = from_next;
mbstate_t state = initial_state;
while(from_next < from_end){
wchar_t w;
wchar_t *wnext = & w;
utf8_codecvt_facet_wchar_t::result ucs4_result;
ucs4_result = base_class::do_in(
state,
from_next, from_end, from_next,
wnext, wnext + 1, wnext
);
if(codecvt_base::ok != ucs4_result)
break;
char carray[MB_LENGTH_MAX];
std::size_t count = wctomb(carray, w);
if(count > max_limit)
break;
max_limit -= count;
total_length = from_next - from;
}
return total_length;
}
}
#endif
#endif

View File

@@ -8,6 +8,8 @@
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <cctype>
namespace boost { namespace program_options {
using namespace std;
@@ -28,7 +30,8 @@ namespace boost { namespace program_options {
}
xparse(value_store, local_tokens);
#else
throw std::runtime_error("UTF-8 conversion not supported.");
boost::throw_exception(
std::runtime_error("UTF-8 conversion not supported."));
#endif
} else {
// Already in local encoding, pass unmodified
@@ -68,15 +71,35 @@ namespace boost { namespace program_options {
{
return arg;
}
unsigned
untyped_value::min_tokens() const
{
if (m_zero_tokens)
return 0;
else
return 1;
}
unsigned
untyped_value::max_tokens() const
{
if (m_zero_tokens)
return 0;
else
return 1;
}
void
untyped_value::xparse(boost::any& value_store,
const std::vector<std::string>& new_tokens) const
{
if (!value_store.empty())
throw multiple_occurrences("multiple_occurrences");
boost::throw_exception(
multiple_occurrences("multiple_occurrences"));
if (new_tokens.size() > 1)
throw multiple_values("multiple_values");
boost::throw_exception(multiple_values("multiple_values"));
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
}
@@ -116,7 +139,8 @@ namespace boost { namespace program_options {
else if (s == "off" || s == "no" || s == "0" || s == "false")
v = any(false);
else
throw validation_error("'" + s + "' doesn't look like a bool value.");
boost::throw_exception(validation_error(
"'" + s + "' doesn't look like a bool value."));
}
// This is blatant copy-paste. However, templating this will cause a problem,
@@ -138,7 +162,7 @@ namespace boost { namespace program_options {
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
v = any(false);
else
throw validation_error("invalid bool value");
boost::throw_exception(validation_error("invalid bool value"));
}
#endif
BOOST_PROGRAM_OPTIONS_DECL
@@ -173,7 +197,8 @@ namespace boost { namespace program_options {
void check_first_occurrence(const boost::any& value)
{
if (!value.empty())
throw multiple_occurrences("multiple_occurrences");
boost::throw_exception(
multiple_occurrences("multiple_occurrences"));
}
}

View File

@@ -23,6 +23,9 @@ namespace boost { namespace program_options {
void store(const parsed_options& options, variables_map& xm,
bool utf8)
{
// TODO: what if we have different definition
// for the same option name during different calls
// 'store'.
assert(options.description);
const options_description& desc = *options.description;
@@ -30,23 +33,7 @@ namespace boost { namespace program_options {
// variables_map. Ehmm.. messy.
std::map<std::string, variable_value>& m = xm;
// The set of existing values that should not be changed.
std::set<std::string> final;
for (map<string, variable_value>::iterator k = m.begin();
k != m.end();
++k)
{
if (!k->second.defaulted()) {
// TODO: what if we have different definition
// for the same option name during different calls
// 'store'.
bool composing = desc.count(k->first)
&& desc.find(k->first).semantic()->is_composing();
if (!composing)
final.insert(k->first);
}
}
std::set<std::string> new_final;
// First, convert/store all given options
for (size_t i = 0; i < options.options.size(); ++i) {
@@ -57,20 +44,22 @@ namespace boost { namespace program_options {
continue;
// If option has final value, skip this assignment
if (final.count(name))
if (xm.m_final.count(name))
continue;
// Ignore options which are not described
if (desc.count(name) == 0)
continue;
//TODO: consider this.
//if (desc.count(name) == 0)
// continue;
const option_description& d = desc.find(name);
const option_description& d = desc.find(name, false);
variable_value& v = m[name];
if (v.defaulted()) {
// Explicit assignment here erases defaulted value
v = variable_value();
}
try {
d.semantic()->parse(v.value(), options.options[i].value, utf8);
}
@@ -80,20 +69,43 @@ namespace boost { namespace program_options {
throw;
}
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
// provided. Ignore values of this option for subsequent
// calls to 'store'. We store this to a temporary set,
// so that several assignment inside *this* 'store' call
// are allowed.
if (!d.semantic()->is_composing())
new_final.insert(name);
}
xm.m_final.insert(new_final.begin(), new_final.end());
// Second, apply default values.
set<string> keys = desc.primary_keys();
for (set<string>::const_iterator j = keys.begin(); j != keys.end(); ++j)
if (m.count(*j) == 0) {
const option_description& d = desc.find(*j);
const vector<shared_ptr<option_description> >& all = desc.options();
for(unsigned i = 0; i < all.size(); ++i)
{
const option_description& d = *all[i];
string key = d.key("");
// FIXME: this logic relies on knowledge of option_description
// internals.
// The 'key' is empty if options description contains '*'.
// In that
// case, default value makes no sense at all.
if (key.empty())
{
continue;
}
if (m.count(key) == 0) {
boost::any def;
if (d.semantic()->apply_default(def)) {
m[*j] = variable_value(def, true);
m[*j].m_value_semantic = d.semantic();
m[key] = variable_value(def, true);
m[key].m_value_semantic = d.semantic();
}
}
}
}
BOOST_PROGRAM_OPTIONS_DECL

View File

@@ -1,24 +1,32 @@
project
: requirements
<library>../build//program_options
<library>../build//boost_program_options
<library>/boost/test//boost_test_exec_monitor
<hardcode-dll-paths>true
<link>static
# <define>_GLIBCXX_CONCEPT_CHECKS
# <define>_GLIBCXX_DEBUG
;
rule po-test ( source )
{
return
[ run $(source) ]
[ run $(source) : : : <link>shared : $(source:B)_dll ]
;
}
test-suite program_options :
[ run options_description_test.cpp ]
[ run parsers_test.cpp ]
[ run variable_map_test.cpp ]
[ run cmdline_test.cpp ]
[ run positional_options_test.cpp ]
[ run unicode_test.cpp ]
[ run winmain.cpp ]
[ po-test options_description_test.cpp ]
[ po-test parsers_test.cpp ]
[ 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 ]
;
exe test_convert : test_convert.cpp ../build//program_options ;
exe test_convert : test_convert.cpp ;

View File

@@ -3,8 +3,8 @@
// (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/detail/cmdline.hpp>
using namespace boost::program_options;
using boost::program_options::detail::cmdline;
@@ -50,19 +50,19 @@ int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
return std::distance(b, i) + 3;
}
struct test_case {
const char* input;
int expected_status;
const char* expected_result;
};
/* Parses the syntax description in 'syntax' and initialized
'cmd' accordingly'
The "boost::program_options" in parameter type is needed because CW9
has std::detail and it causes an ambiguity.
*/
void apply_syntax(cmdline& cmd,
void apply_syntax(options_description& desc,
const char* syntax)
{
@@ -70,32 +70,29 @@ void apply_syntax(cmdline& cmd,
stringstream ss;
ss << syntax;
while(ss >> s) {
string long_name;
char short_name = '\0';
char properties = '|';
value_semantic* v = 0;
if (*(s.end()-1) == '=') {
properties = ':';
v = value<string>();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '?') {
properties = '?';
//v = value<string>()->implicit();
v = value<string>();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '*') {
properties = '*';
v = value<vector<string> >()->multitoken();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '+') {
properties = '+';
v = value<vector<string> >()->multitoken();
s.resize(s.size()-1);
}
string::size_type n = s.find(',');
if (n == string::npos) {
long_name = s;
if (v) {
desc.add_options()
(s.c_str(), v, "");
} else {
assert(n == s.size()-2);
long_name = s.substr(0, s.size()-2);
short_name = *s.rbegin();
desc.add_options()
(s.c_str(), "");
}
cmd.add_option(long_name, short_name, properties, 1);
}
}
@@ -114,30 +111,36 @@ void test_cmdline(const char* syntax,
xinput.push_back(s);
}
}
cmdline cmd(xinput, style);
options_description desc;
apply_syntax(desc, syntax);
cmdline cmd(xinput);
cmd.style(style);
cmd.set_options_description(desc);
apply_syntax(cmd, syntax);
string result;
int status = 0;
try {
while(++cmd) {
if (cmd.at_argument()) {
vector<option> options = cmd.run();
for(unsigned i = 0; i < options.size(); ++i)
{
option opt = options[i];
if (opt.position_key != -1) {
if (!result.empty())
result += " ";
result += cmd.argument();
result += opt.value[0];
} else {
if (!result.empty())
result += " ";
if (*cmd.option_name().rbegin() != '*')
result += cmd.option_name() + ":";
else
result += cmd.raw_option_name() + ":";
for (size_t i = 0; i < cmd.option_values().size(); ++i) {
if (i != 0)
result += opt.string_key + ":";
for (size_t j = 0; j < opt.value.size(); ++j) {
if (j != 0)
result += "-";
result += cmd.option_values()[i];
result += opt.value[j];
}
}
}
@@ -177,34 +180,28 @@ void test_long_options()
{"--foo=13", s_extra_parameter, ""},
// Test option with required parameter
{"--bar=", s_empty_adjacent_parameter, ""},
{"--bar", s_missing_parameter, ""},
{"--bar=123", s_success, "bar:123"},
// Test option with optional parameter
{"--baz", s_success, "baz:"},
{"--baz=7", s_success, "baz:7"},
{"--bar=123", s_success, "bar:123"},
{0}
};
test_cmdline("foo bar= baz?", style, test_cases1);
test_cmdline("foo bar=", style, test_cases1);
style = cmdline::style_t(
allow_long | long_allow_next);
test_case test_cases2[] = {
{"--foo", s_success, "foo:"},
{"--bar=10", s_long_adjacent_not_allowed, ""},
{"--bar 10", s_success, "bar:10"},
{"--bar", s_missing_parameter, ""},
{"--bar --foo", s_missing_parameter, ""},
{"--baz", s_success, "baz:"},
{"--baz 10", s_success, "baz:10"},
{"--baz --foo", s_success, "baz: foo:"},
// Since --bar accepts a parameter, --foo is
// considered a value, even though it looks like
// an option.
{"--bar --foo", s_success, "bar:--foo"},
{0}
};
test_cmdline("foo bar= baz?", style, test_cases2);
test_cmdline("foo bar=", style, test_cases2);
style = cmdline::style_t(
allow_long | long_allow_adjacent
| long_allow_next);
@@ -212,17 +209,16 @@ void test_long_options()
test_case test_cases3[] = {
{"--bar=10", s_success, "bar:10"},
{"--bar 11", s_success, "bar:11"},
{"--baz=12", s_success, "baz:12"},
{"--baz 13", s_success, "baz:13"},
{"--baz --foo", s_success, "baz: foo:"},
{0}
};
test_cmdline("foo bar= baz?", style, test_cases3);
test_cmdline("foo bar=", style, test_cases3);
style = cmdline::style_t(
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[] = {
@@ -234,6 +230,7 @@ void test_long_options()
{0}
};
test_cmdline("foo bar= baz? Giz", style, test_cases4);
#endif
}
void test_short_options()
@@ -248,14 +245,14 @@ void test_short_options()
test_case test_cases1[] = {
{"-d d /bar", s_success, "-d: d /bar"},
// This is treated as error when long options are disabled
{"--foo", s_long_not_allowed, ""},
{"--foo", s_success, "--foo"},
{"-d13", s_extra_parameter, ""},
{"-f14", s_success, "-f:14"},
{"-g -f1", s_success, "-g: -f:1"},
{"-f", s_missing_parameter, ""},
{0}
};
test_cmdline(",d ,f= ,g?", style, test_cases1);
test_cmdline(",d ,f= ,g", style, test_cases1);
style = cmdline::style_t(
allow_short | allow_dash_for_short
@@ -265,18 +262,11 @@ void test_short_options()
{"-f 13", s_success, "-f:13"},
{"-f -13", s_success, "-f:-13"},
{"-f", s_missing_parameter, ""},
{"-f --foo", s_missing_parameter, ""},
{"-f /foo", s_success, "-f:/foo"},
{"-f -d", s_success, "-f:-d"},
{"-g 13", s_success, "-g:13"},
{"-g", s_success, "-g:"},
{"-g --foo", s_long_not_allowed, "-g:"},
{"-g /foo", s_success, "-g:/foo"},
{"-g -d", s_success, "-g: -d:"},
{"-f12", s_short_adjacent_not_allowed, ""},
{0}
};
test_cmdline(",d ,f= ,g?", style, test_cases2);
test_cmdline(",d ,f=", style, test_cases2);
style = cmdline::style_t(
allow_short | short_allow_next
@@ -286,12 +276,9 @@ void test_short_options()
{"-f10", s_success, "-f:10"},
{"-f 10", s_success, "-f:10"},
{"-f -d", s_success, "-f:-d"},
{"-g10", s_success, "-g:10"},
{"-g 10", s_success, "-g:10"},
{"-g -d", s_success, "-g: -d:"},
{0}
};
test_cmdline(",d ,f= ,g?", style, test_cases3);
test_cmdline(",d ,f=", style, test_cases3);
style = cmdline::style_t(
allow_short | short_allow_next
@@ -300,14 +287,14 @@ void test_short_options()
test_case test_cases4[] = {
{"-de", s_success, "-d: -e:"},
{"-dg", s_success, "-d: -g:"},
{"-dg10", s_success, "-d: -g:10"},
{"-d12", s_extra_parameter, ""},
{"-gd", s_success, "-g:d"},
{"-df10", s_success, "-d: -f:10"},
// FIXME: review
//{"-d12", s_extra_parameter, ""},
{"-f12", s_success, "-f:12"},
{"-fe", s_success, "-f:e"},
{0}
};
test_cmdline(",d ,f= ,g? ,e", style, test_cases4);
test_cmdline(",d ,f= ,e", style, test_cases4);
}
@@ -323,15 +310,13 @@ void test_dos_options()
test_case test_cases1[] = {
{"/d d -bar", s_success, "-d: d -bar"},
// This is treated as disallowed long option
{"--foo", s_long_not_allowed, ""},
{"--foo", s_success, "--foo"},
{"/d13", s_extra_parameter, ""},
{"/f14", s_success, "-f:14"},
{"/g /f1", s_success, "-g: -f:1"},
{"/f", s_missing_parameter, ""},
{0}
};
test_cmdline(",d ,f= ,g?", style, test_cases1);
test_cmdline(",d ,f=", style, test_cases1);
style = cmdline::style_t(
allow_short
@@ -340,14 +325,14 @@ void test_dos_options()
test_case test_cases2[] = {
{"/de", s_extra_parameter, ""},
{"/gd", s_success, "-g:d"},
{"/fe", s_success, "-f:e"},
{0}
};
test_cmdline(",d ,f= ,g? ,e", style, test_cases2);
test_cmdline(",d ,f= ,e", style, test_cases2);
}
void test_disguised_long()
{
using namespace command_line_style;
@@ -447,43 +432,176 @@ void test_prefix()
test_cmdline("foo*=", style, test_cases1);
}
void test_multiple()
pair<string, string> at_option_parser(string const&s)
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
unix_style | long_allow_next);
test_case test_cases1[] = {
{"--value 1 2 3 4 --help", s_success, "value:1-2-3-4 help:"},
{"--value 1 2 3 4 --", s_success, "value:1-2-3-4"},
{0}
};
test_cmdline("value+ help", style, test_cases1);
if ('@' == s[0])
return std::make_pair(string("response-file"), s.substr(1));
else
return pair<string, string>();
}
void test_style_errors()
pair<string, string> at_option_parser_broken(string const&s)
{
using namespace command_line_style;
char* argv[] = {"program"};
if ('@' == s[0])
return std::make_pair(string("some garbage"), s.substr(1));
else
return pair<string, string>();
}
BOOST_CHECK_THROW(cmdline cmd(1, (const char*const *)argv, allow_long),
invalid_command_line_style);
BOOST_CHECK_THROW(cmdline cmd(1, (const char*const *)argv, allow_short),
invalid_command_line_style);
BOOST_CHECK_THROW(cmdline cmd(1, (const char*const *)argv, allow_short |
short_allow_next),
invalid_command_line_style);
void test_additional_parser()
{
options_description desc;
desc.add_options()
("response-file", value<string>(), "response file")
("foo", value<int>(), "foo")
;
vector<string> input;
input.push_back("@config");
input.push_back("--foo=1");
cmdline cmd(input);
cmd.set_options_description(desc);
cmd.set_additional_parser(at_option_parser);
vector<option> result = cmd.run();
BOOST_REQUIRE(result.size() == 2);
BOOST_CHECK_EQUAL(result[0].string_key, "response-file");
BOOST_CHECK_EQUAL(result[0].value[0], "config");
BOOST_CHECK_EQUAL(result[1].string_key, "foo");
BOOST_CHECK_EQUAL(result[1].value[0], "1");
// Test that invalid options returned by additional style
// parser are detected.
cmdline cmd2(input);
cmd2.set_options_description(desc);
cmd2.set_additional_parser(at_option_parser_broken);
BOOST_CHECK_THROW(cmd2.run(), unknown_option);
}
vector<option> at_option_parser2(vector<string>& args)
{
vector<option> result;
if ('@' == args[0][0]) {
// Simulate reading the response file.
result.push_back(option("foo", vector<string>(1, "1")));
result.push_back(option("bar", vector<string>(1, "1")));
args.erase(args.begin());
}
return result;
}
void test_style_parser()
{
options_description desc;
desc.add_options()
("foo", value<int>(), "foo")
("bar", value<int>(), "bar")
;
vector<string> input;
input.push_back("@config");
cmdline cmd(input);
cmd.set_options_description(desc);
cmd.extra_style_parser(at_option_parser2);
vector<option> result = cmd.run();
BOOST_REQUIRE(result.size() == 2);
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
BOOST_CHECK_EQUAL(result[0].value[0], "1");
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
BOOST_CHECK_EQUAL(result[1].value[0], "1");
}
void test_unregistered()
{
// Check unregisted option when no options are registed at all.
options_description desc;
vector<string> input;
input.push_back("--foo=1");
input.push_back("--bar");
input.push_back("1");
input.push_back("-b");
input.push_back("-biz");
cmdline cmd(input);
cmd.set_options_description(desc);
cmd.allow_unregistered();
vector<option> result = cmd.run();
BOOST_REQUIRE(result.size() == 5);
// --foo=1
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
BOOST_CHECK_EQUAL(result[0].unregistered, true);
BOOST_CHECK_EQUAL(result[0].value[0], "1");
// --bar
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
BOOST_CHECK_EQUAL(result[1].unregistered, true);
BOOST_CHECK(result[1].value.empty());
// '1' is considered a positional option, not a value to
// --bar
BOOST_CHECK(result[2].string_key.empty());
BOOST_CHECK(result[2].position_key == 0);
BOOST_CHECK_EQUAL(result[2].unregistered, false);
BOOST_CHECK_EQUAL(result[2].value[0], "1");
// -b
BOOST_CHECK_EQUAL(result[3].string_key, "-b");
BOOST_CHECK_EQUAL(result[3].unregistered, true);
BOOST_CHECK(result[3].value.empty());
// -biz
BOOST_CHECK_EQUAL(result[4].string_key, "-b");
BOOST_CHECK_EQUAL(result[4].unregistered, true);
BOOST_CHECK_EQUAL(result[4].value[0], "iz");
// Check sticky short options together with unregisted options.
desc.add_options()
("help,h", "")
("magic,m", value<string>(), "")
;
input.clear();
input.push_back("-hc");
input.push_back("-mc");
cmdline cmd2(input);
cmd2.set_options_description(desc);
cmd2.allow_unregistered();
result = cmd2.run();
BOOST_REQUIRE(result.size() == 3);
BOOST_CHECK_EQUAL(result[0].string_key, "help");
BOOST_CHECK_EQUAL(result[0].unregistered, false);
BOOST_CHECK(result[0].value.empty());
BOOST_CHECK_EQUAL(result[1].string_key, "-c");
BOOST_CHECK_EQUAL(result[1].unregistered, true);
BOOST_CHECK(result[1].value.empty());
BOOST_CHECK_EQUAL(result[2].string_key, "magic");
BOOST_CHECK_EQUAL(result[2].unregistered, false);
BOOST_CHECK_EQUAL(result[2].value[0], "c");
// CONSIDER:
// There's a corner case:
// -foo
// when 'allow_long_disguise' is set. Should this be considered
// disguised long option 'foo' or short option '-f' with value 'oo'?
// It's not clear yet, so I'm leaving the decision till later.
}
int test_main(int ac, char* av[])
{
// ### detail::test_cmdline_detail();
test_long_options();
test_short_options();
test_dos_options();
@@ -491,31 +609,9 @@ int test_main(int ac, char* av[])
test_guessing();
test_arguments();
test_prefix();
test_multiple();
test_style_errors();
cmdline cmd((int)ac, (const char*const *)av,
int(command_line_style::unix_style));
cmd.add_option("version", 'v');
cmd.add_option("help", 'h');
cmd.add_option("verbose", 'V');
cmd.add_option("magic", 'm');
cmd.add_option("output", 'o', ':');
try {
while(++cmd) {
if (cmd.at_argument()) {
cout << "Argument : " << cmd.argument() << "\n";
} else {
cout << "Option : " << cmd.option_name()
<< "(" << cmd.option_value() << ")\n";
}
}
}
catch(exception& e) {
cout << e.what() << "\n";
}
test_additional_parser();
test_style_parser();
test_unregistered();
return 0;
}

View File

@@ -16,63 +16,6 @@ using namespace boost;
#include <utility>
using namespace std;
/* This is very wierd test case -- it tests trivial things. After writing it,
I think that XP folks must be somehow wrong.
*/
void test_option_description_construction()
{
option_description d1("a", new untyped_value(), "desc1");
BOOST_TEST(d1.long_name() == "a");
BOOST_TEST(d1.description() == "desc1");
BOOST_TEST(d1.semantic()->name() == "arg");
// It is not possible to compare boost::function
#if 0
function<string, string> f1;
BOOST_TEST(&option_description("x", "y", "z").
validator(f1).validator() ==
&f1);
function<void, string> f2;
BOOST_TEST(&option_description("x", "y", "z").
notify(f2).notify() ==
&f2);
#endif
option_description d4("foo,f", new untyped_value(), "desc1");
BOOST_CHECK(d4.long_name() == "foo");
BOOST_CHECK(d4.short_name() == "f");
}
void test_options_description()
{
options_description desc;
shared_ptr<option_description> d1(
new option_description("first,f", new untyped_value(), ""));
desc.add(d1);
BOOST_TEST(desc.count("first") == 1);
BOOST_TEST(desc.count("-f") == 1);
BOOST_TEST(desc.keys().size() == 2);
BOOST_TEST(desc.keys().count("first") == 1);
BOOST_TEST(desc.keys().count("-f") == 1);
desc.add_options()
("second,s", new untyped_value())
("third,t", new untyped_value())
;
BOOST_TEST(desc.count("second") == 1);
BOOST_TEST(desc.count("-s") == 1);
desc.add_options()
(",x", new untyped_value)
;
BOOST_TEST(desc.primary_keys().size() == 4);
BOOST_TEST(desc.primary_keys().count("first") == 1);
BOOST_TEST(desc.primary_keys().count("second") == 1);
BOOST_TEST(desc.primary_keys().count("third") == 1);
BOOST_TEST(desc.primary_keys().count("-x") == 1);
}
void test_approximation()
{
options_description desc;
@@ -81,18 +24,16 @@ void test_approximation()
("fee", new untyped_value())
("baz", new untyped_value());
BOOST_TEST(desc.count_approx("f") == 2);
BOOST_TEST(desc.count_approx("foo") == 1);
set<string> a = desc.approximations("f");
BOOST_TEST(a.size() == 2);
BOOST_TEST(*a.begin() == "fee");
BOOST_TEST(*(++a.begin()) == "foo");
BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "foo");
// BOOST_CHECK(desc.count_approx("foo") == 1);
// set<string> a = desc.approximations("f");
// BOOST_CHECK(a.size() == 2);
// BOOST_CHECK(*a.begin() == "fee");
// BOOST_CHECK(*(++a.begin()) == "foo");
}
int test_main(int, char* [])
{
test_option_description_construction();
test_options_description();
test_approximation();
return 0;
}

View File

@@ -60,6 +60,11 @@ vector<string> sv(char* array[], unsigned size)
return r;
}
pair<string, string> additional_parser(const std::string&)
{
return pair<string, string>();
}
void test_command_line()
{
// The following commented out blocks used to test parsing
@@ -74,14 +79,14 @@ void test_command_line()
parse_command_line(cmdline1,
cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
BOOST_CRITICAL_TEST(a1.options().size() == 4);
BOOST_TEST(a1.options()[0] == msp("a", ""));
BOOST_TEST(a1.options()[1] == msp("b", "12"));
BOOST_TEST(a1.options()[2] == msp("-f", ""));
BOOST_TEST(a1.options()[3] == msp("-g", "4"));
BOOST_CRITICAL_TEST(a1.arguments().size() == 2);
BOOST_TEST(a1.arguments()[0] == "-");
BOOST_TEST(a1.arguments()[1] == "file");
BOOST_REQUIRE(a1.options().size() == 4);
BOOST_CHECK(a1.options()[0] == msp("a", ""));
BOOST_CHECK(a1.options()[1] == msp("b", "12"));
BOOST_CHECK(a1.options()[2] == msp("-f", ""));
BOOST_CHECK(a1.options()[3] == msp("-g", "4"));
BOOST_REQUIRE(a1.arguments().size() == 2);
BOOST_CHECK(a1.arguments()[0] == "-");
BOOST_CHECK(a1.arguments()[1] == "file");
char* cmdline2[] = { "--a", "--", "file" };
@@ -89,42 +94,45 @@ void test_command_line()
parse_command_line(cmdline2,
cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
BOOST_CRITICAL_TEST(a2.options().size() == 1);
BOOST_TEST(a2.options()[0] == msp("a", ""));
BOOST_TEST(a2.arguments().size() == 1);
BOOST_TEST(a2.arguments()[0] == "file");
BOOST_REQUIRE(a2.options().size() == 1);
BOOST_CHECK(a2.options()[0] == msp("a", ""));
BOOST_CHECK(a2.arguments().size() == 1);
BOOST_CHECK(a2.arguments()[0] == "file");
#endif
options_description desc;
desc.add_options()
("foo,f", new untyped_value(), "")
// Explicit qualification is a workaround for vc6
("bar,b", po::value<std::string>()->implicit(), "")
("bar,b", po::value<std::string>(), "")
("baz", new untyped_value())
("plug*", new untyped_value())
;
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "--bar", "-b4", "-b",
"--plug3=10"};
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
"--plug3=10"};
vector<string> cmdline3 = sv(cmdline3_,
sizeof(cmdline3_)/sizeof(cmdline3_[0]));
vector<option> a3 =
command_line_parser(cmdline3).options(desc).run().options;
BOOST_CRITICAL_TEST(a3.size() == 7);
BOOST_CHECK_EQUAL(a3.size(), 5u);
check_value(a3[0], "foo", "12");
check_value(a3[1], "foo", "4");
check_value(a3[2], "bar", "11");
check_value(a3[3], "bar", "4");
check_value(a3[4], "plug3", "10");
BOOST_TEST(a3[3].string_key == "bar");
BOOST_CRITICAL_TEST(a3[3].value.size() == 0);
// 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;
check_value(a3[4], "bar", "4");
BOOST_CHECK_EQUAL(a4.size(), 4u);
check_value(a4[0], "foo", "4");
check_value(a4[1], "bar", "11");
BOOST_TEST(a3[5].string_key == "bar");
BOOST_CRITICAL_TEST(a3[5].value.size() == 0);
check_value(a3[6], "plug3", "10");
}
void test_config_file()
@@ -151,7 +159,7 @@ void test_config_file()
stringstream ss(content1);
vector<option> a1 = parse_config_file(ss, desc).options;
BOOST_CRITICAL_TEST(a1.size() == 5);
BOOST_REQUIRE(a1.size() == 5);
check_value(a1[0], "gv1", "0");
check_value(a1[1], "plug3", "7");
check_value(a1[2], "b", "true");

View File

@@ -34,7 +34,7 @@ void test_positional_options()
p.add("third", -1);
BOOST_CHECK_EQUAL(p.max_total_count(), std::numeric_limits<unsigned>::max());
BOOST_CHECK_EQUAL(p.max_total_count(), (std::numeric_limits<unsigned>::max)());
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");

View File

@@ -46,7 +46,7 @@ void test_unicode_to_unicode()
void test_unicode_to_native()
{
std::codecvt<wchar_t, char, mbstate_t>* facet =
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
new boost::program_options::detail::utf8_codecvt_facet;
locale::global(locale(locale(), facet));
options_description desc;
@@ -61,13 +61,13 @@ void test_unicode_to_native()
variables_map vm;
store(wcommand_line_parser(args).options(desc).run(), vm);
BOOST_TEST(vm["foo"].as<string>() == "\xD1\x8F");
BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F");
}
void test_native_to_unicode()
{
std::codecvt<wchar_t, char, mbstate_t>* facet =
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
new boost::program_options::detail::utf8_codecvt_facet;
locale::global(locale(locale(), facet));
options_description desc;
@@ -82,7 +82,7 @@ void test_native_to_unicode()
variables_map vm;
store(command_line_parser(args).options(desc).run(), vm);
BOOST_TEST(vm["foo"].as<wstring>() == L"\x044F");
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
}
@@ -107,19 +107,19 @@ void test_command_line()
desc.add_options()
("foo,f", new untyped_value(), "")
// Explicit qualification is a workaround for vc6
("bar,b", po::value<std::string>()->implicit(), "")
("bar,b", po::value<std::string>(), "")
("baz", new untyped_value())
("plug*", new untyped_value())
;
wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11", L"--bar",
L"-b4", L"-b", L"--plug3=10"};
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]));
vector<woption> a4 =
wcommand_line_parser(cmdline4).options(desc).run().options;
BOOST_CRITICAL_TEST(a4.size() == 7);
BOOST_REQUIRE(a4.size() == 5);
check_value(a4[0], "foo", L"1\u0FF52");
check_value(a4[1], "foo", L"4");
@@ -132,7 +132,7 @@ void test_command_line()
void test_config_file()
{
std::codecvt<wchar_t, char, mbstate_t>* facet =
new boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>;
new boost::program_options::detail::utf8_codecvt_facet;
locale::global(locale(locale(), facet));
options_description desc;
@@ -146,7 +146,7 @@ void test_config_file()
variables_map vm;
store(parse_config_file(stream, desc), vm);
BOOST_TEST(vm["foo"].as<string>() == "\xD1\x8F");
BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F");
}
int test_main(int, char* [])

View File

@@ -45,8 +45,8 @@ void test_variable_map()
options_description desc;
desc.add_options()
("foo,f", new untyped_value)
("bar,b", po::value<string>()->implicit())
("biz,z", po::value<string>()->implicit())
("bar,b", po::value<string>())
("biz,z", po::value<string>())
("baz", new untyped_value())
("output,o", new untyped_value(), "")
;
@@ -57,12 +57,12 @@ void test_variable_map()
variables_map vm;
store(a3, vm);
notify(vm);
BOOST_CRITICAL_TEST(vm.size() == 4);
BOOST_TEST(vm["foo"].as<string>() == "'12'");
BOOST_TEST(vm["bar"].as<string>() == "11");
BOOST_TEST(vm.count("biz") == 1);
BOOST_TEST(vm["biz"].as<string>() == "3");
BOOST_TEST(vm["output"].as<string>() == "foo");
BOOST_REQUIRE(vm.size() == 4);
BOOST_CHECK(vm["foo"].as<string>() == "'12'");
BOOST_CHECK(vm["bar"].as<string>() == "11");
BOOST_CHECK(vm.count("biz") == 1);
BOOST_CHECK(vm["biz"].as<string>() == "3");
BOOST_CHECK(vm["output"].as<string>() == "foo");
int i;
desc.add_options()
@@ -78,11 +78,11 @@ void test_variable_map()
variables_map vm2;
store(a4, vm2);
notify(vm2);
BOOST_CRITICAL_TEST(vm2.size() == 3);
BOOST_TEST(vm2["zee"].as<bool>() == true);
BOOST_TEST(vm2["zak"].as<int>() == 13);
BOOST_TEST(vm2["opt"].as<bool>() == false);
BOOST_TEST(i == 13);
BOOST_REQUIRE(vm2.size() == 3);
BOOST_CHECK(vm2["zee"].as<bool>() == true);
BOOST_CHECK(vm2["zak"].as<int>() == 13);
BOOST_CHECK(vm2["opt"].as<bool>() == false);
BOOST_CHECK(i == 13);
options_description desc2;
desc2.add_options()
@@ -98,10 +98,10 @@ void test_variable_map()
variables_map vm3;
store(a5, vm3);
notify(vm3);
BOOST_CRITICAL_TEST(vm3.size() == 3);
BOOST_TEST(vm3["vee"].as<string>() == "42");
BOOST_TEST(vm3["voo"].as<string>() == "1");
BOOST_TEST(vm3["iii"].as<int>() == 123);
BOOST_REQUIRE(vm3.size() == 3);
BOOST_CHECK(vm3["vee"].as<string>() == "42");
BOOST_CHECK(vm3["voo"].as<string>() == "1");
BOOST_CHECK(vm3["iii"].as<int>() == 123);
}
int stored_value;
@@ -227,13 +227,54 @@ void test_priority()
BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7);
}
void test_multiple_assignments_with_different_option_description()
{
// Test that if we store option twice into the same variable_map,
// and some of the options stored the first time are not present
// in the options descrription provided the second time, we don't crash.
options_description desc1("");
desc1.add_options()
("help,h", "")
("includes", po::value< vector<string> >()->composing(), "");
;
options_description desc2("");
desc2.add_options()
("output,o", "");
vector<string> input1;
input1.push_back("--help");
input1.push_back("--includes=a");
parsed_options p1 = command_line_parser(input1).options(desc1).run();
vector<string> input2;
input1.push_back("--output");
parsed_options p2 = command_line_parser(input2).options(desc2).run();
vector<string> input3;
input3.push_back("--includes=b");
parsed_options p3 = command_line_parser(input3).options(desc1).run();
variables_map vm;
store(p1, vm);
store(p2, vm);
store(p3, vm);
BOOST_REQUIRE(vm.count("help") == 1);
BOOST_REQUIRE(vm.count("includes") == 1);
BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[0], "a");
BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[1], "b");
}
int test_main(int, char* [])
{
test_variable_map();
test_semantic_values();
test_priority();
test_multiple_assignments_with_different_option_description();
return 0;
}

View File

@@ -3,7 +3,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifdef _WIN32
#if defined(_WIN32)
#include <string>
#include <vector>
#include <cctype>
@@ -22,11 +22,11 @@ void test_winmain()
#define TEST(input, expected) \
char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\
BOOST_REQUIRE(BOOST_PP_CAT(v, __LINE__).size() == \
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));\
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__),\
BOOST_PP_CAT(e, __LINE__) + \
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));
// The following expectations were obtained in Win2000 shell:
TEST("1 ", {"1"});