mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2026-06-16 20:58:40 -04:00
Squashed 'boost/' content from commit b4feb19f2
git-subtree-dir: boost git-subtree-split: b4feb19f287ee92d87a9624b5d36b7cf46aeadeb
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
|
||||
project boost/program_options
|
||||
: source-location ../src
|
||||
;
|
||||
|
||||
SOURCES =
|
||||
cmdline config_file options_description parsers variables_map
|
||||
value_semantic positional_options utf8_codecvt_facet
|
||||
convert winmain split
|
||||
;
|
||||
|
||||
lib boost_program_options
|
||||
: $(SOURCES).cpp
|
||||
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
|
||||
# See https://svn.boost.org/trac/boost/ticket/5049
|
||||
<target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901
|
||||
:
|
||||
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
|
||||
;
|
||||
|
||||
boost-install boost_program_options ;
|
||||
@@ -0,0 +1,719 @@
|
||||
// Copyright Vladimir Prus 2002-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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::program_options::command_line_style;
|
||||
|
||||
|
||||
string
|
||||
invalid_syntax::get_template(kind_t kind)
|
||||
{
|
||||
// Initially, store the message in 'const char*' variable,
|
||||
// to avoid conversion to string in all cases.
|
||||
const char* msg;
|
||||
switch(kind)
|
||||
{
|
||||
case empty_adjacent_parameter:
|
||||
msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign";
|
||||
break;
|
||||
case missing_parameter:
|
||||
msg = "the required argument for option '%canonical_option%' is missing";
|
||||
break;
|
||||
case unrecognized_line:
|
||||
msg = "the options configuration file contains an invalid line '%invalid_line%'";
|
||||
break;
|
||||
// none of the following are currently used:
|
||||
case long_not_allowed:
|
||||
msg = "the unabbreviated option '%canonical_option%' is not valid";
|
||||
break;
|
||||
case long_adjacent_not_allowed:
|
||||
msg = "the unabbreviated option '%canonical_option%' does not take any arguments";
|
||||
break;
|
||||
case short_adjacent_not_allowed:
|
||||
msg = "the abbreviated option '%canonical_option%' does not take any arguments";
|
||||
break;
|
||||
case extra_parameter:
|
||||
msg = "option '%canonical_option%' does not take any arguments";
|
||||
break;
|
||||
default:
|
||||
msg = "unknown command line syntax error for '%s'";
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
// vc6 needs this, but borland chokes when this is added.
|
||||
#if BOOST_WORKAROUND(_MSC_VER, < 1300)
|
||||
using namespace std;
|
||||
using namespace program_options;
|
||||
#endif
|
||||
|
||||
|
||||
cmdline::cmdline(const vector<string>& args)
|
||||
{
|
||||
init(args);
|
||||
}
|
||||
|
||||
cmdline::cmdline(int argc, const char*const * argv)
|
||||
{
|
||||
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
||||
vector<string> args;
|
||||
copy(argv+1, argv+argc+!argc, inserter(args, args.end()));
|
||||
init(args);
|
||||
#else
|
||||
init(vector<string>(argv+1, argv+argc+!argc));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::init(const vector<string>& args)
|
||||
{
|
||||
this->args = args;
|
||||
m_style = command_line_style::default_style;
|
||||
m_desc = 0;
|
||||
m_positional = 0;
|
||||
m_allow_unregistered = false;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::style(int style)
|
||||
{
|
||||
if (style == 0)
|
||||
style = default_style;
|
||||
|
||||
check_style(style);
|
||||
this->m_style = style_t(style);
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::allow_unregistered()
|
||||
{
|
||||
this->m_allow_unregistered = true;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::check_style(int style) const
|
||||
{
|
||||
bool allow_some_long =
|
||||
(style & allow_long) || (style & allow_long_disguise);
|
||||
|
||||
const char* error = 0;
|
||||
if (allow_some_long &&
|
||||
!(style & long_allow_adjacent) && !(style & long_allow_next))
|
||||
error = "boost::program_options misconfiguration: "
|
||||
"choose one or other of 'command_line_style::long_allow_next' "
|
||||
"(whitespace separated arguments) or "
|
||||
"'command_line_style::long_allow_adjacent' ('=' separated arguments) for "
|
||||
"long options.";
|
||||
|
||||
if (!error && (style & allow_short) &&
|
||||
!(style & short_allow_adjacent) && !(style & short_allow_next))
|
||||
error = "boost::program_options misconfiguration: "
|
||||
"choose one or other of 'command_line_style::short_allow_next' "
|
||||
"(whitespace separated arguments) or "
|
||||
"'command_line_style::short_allow_adjacent' ('=' separated arguments) for "
|
||||
"short options.";
|
||||
|
||||
if (!error && (style & allow_short) &&
|
||||
!(style & allow_dash_for_short) && !(style & allow_slash_for_short))
|
||||
error = "boost::program_options misconfiguration: "
|
||||
"choose one or other of 'command_line_style::allow_slash_for_short' "
|
||||
"(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for "
|
||||
"short options.";
|
||||
|
||||
if (error)
|
||||
boost::throw_exception(invalid_command_line_style(error));
|
||||
|
||||
// Need to check that if guessing and long disguise are enabled
|
||||
// -f will mean the same as -foo
|
||||
}
|
||||
|
||||
bool
|
||||
cmdline::is_style_active(style_t style) const
|
||||
{
|
||||
return ((m_style & style) ? true : false);
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::set_options_description(const options_description& desc)
|
||||
{
|
||||
m_desc = &desc;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::set_positional_options(
|
||||
const positional_options_description& positional)
|
||||
{
|
||||
m_positional = &positional;
|
||||
}
|
||||
|
||||
int
|
||||
cmdline::get_canonical_option_prefix()
|
||||
{
|
||||
if (m_style & allow_long)
|
||||
return allow_long;
|
||||
|
||||
if (m_style & allow_long_disguise)
|
||||
return allow_long_disguise;
|
||||
|
||||
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
|
||||
return allow_dash_for_short;
|
||||
|
||||
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
|
||||
return allow_slash_for_short;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::run()
|
||||
{
|
||||
// The parsing is done by having a set of 'style parsers'
|
||||
// and trying then in order. Each parser is passed a vector
|
||||
// of unparsed tokens and can consume some of them (by
|
||||
// removing elements on front) and return a vector of options.
|
||||
//
|
||||
// We try each style parser in turn, untill some input
|
||||
// is consumed. The returned vector of option may contain the
|
||||
// result of just syntactic parsing of token, say --foo will
|
||||
// be parsed as option with name 'foo', and the style parser
|
||||
// is not required to care if that option is defined, and how
|
||||
// many tokens the value may take.
|
||||
// So, after vector is returned, we validate them.
|
||||
assert(m_desc);
|
||||
|
||||
vector<style_parser> style_parsers;
|
||||
|
||||
if (m_style_parser)
|
||||
style_parsers.push_back(m_style_parser);
|
||||
|
||||
if (m_additional_parser)
|
||||
style_parsers.push_back(
|
||||
boost::bind(&cmdline::handle_additional_parser, this, _1));
|
||||
|
||||
if (m_style & allow_long)
|
||||
style_parsers.push_back(
|
||||
boost::bind(&cmdline::parse_long_option, this, _1));
|
||||
|
||||
if ((m_style & allow_long_disguise))
|
||||
style_parsers.push_back(
|
||||
boost::bind(&cmdline::parse_disguised_long_option, this, _1));
|
||||
|
||||
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
|
||||
style_parsers.push_back(
|
||||
boost::bind(&cmdline::parse_short_option, this, _1));
|
||||
|
||||
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
|
||||
style_parsers.push_back(boost::bind(&cmdline::parse_dos_option, this, _1));
|
||||
|
||||
style_parsers.push_back(boost::bind(&cmdline::parse_terminator, this, _1));
|
||||
|
||||
vector<option> result;
|
||||
while(!args.empty())
|
||||
{
|
||||
bool ok = false;
|
||||
for(unsigned i = 0; i < style_parsers.size(); ++i)
|
||||
{
|
||||
unsigned current_size = static_cast<unsigned>(args.size());
|
||||
vector<option> next = style_parsers[i](args);
|
||||
|
||||
// Check that option names
|
||||
// are valid, and that all values are in place.
|
||||
if (!next.empty())
|
||||
{
|
||||
vector<string> e;
|
||||
for(unsigned k = 0; k < next.size()-1; ++k) {
|
||||
finish_option(next[k], e, style_parsers);
|
||||
}
|
||||
// For the last option, pass the unparsed tokens
|
||||
// so that they can be added to next.back()'s values
|
||||
// if appropriate.
|
||||
finish_option(next.back(), args, style_parsers);
|
||||
for (unsigned j = 0; j < next.size(); ++j)
|
||||
result.push_back(next[j]);
|
||||
}
|
||||
|
||||
if (args.size() != current_size) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
option opt;
|
||||
opt.value.push_back(args[0]);
|
||||
opt.original_tokens.push_back(args[0]);
|
||||
result.push_back(opt);
|
||||
args.erase(args.begin());
|
||||
}
|
||||
}
|
||||
|
||||
/* If an key option is followed by a positional option,
|
||||
can can consume more tokens (e.g. it's multitoken option),
|
||||
give those tokens to it. */
|
||||
vector<option> result2;
|
||||
for (unsigned i = 0; i < result.size(); ++i)
|
||||
{
|
||||
result2.push_back(result[i]);
|
||||
option& opt = result2.back();
|
||||
|
||||
if (opt.string_key.empty())
|
||||
continue;
|
||||
|
||||
const option_description* xd;
|
||||
try
|
||||
{
|
||||
xd = m_desc->find_nothrow(opt.string_key,
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
}
|
||||
catch(error_with_option_name& e)
|
||||
{
|
||||
// add context and rethrow
|
||||
e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix());
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!xd)
|
||||
continue;
|
||||
|
||||
if (xd->semantic()->adjacent_tokens_only())
|
||||
continue;
|
||||
|
||||
unsigned min_tokens = xd->semantic()->min_tokens();
|
||||
unsigned max_tokens = xd->semantic()->max_tokens();
|
||||
if (min_tokens < max_tokens && opt.value.size() < max_tokens)
|
||||
{
|
||||
// This option may grab some more tokens.
|
||||
// We only allow to grab tokens that are not already
|
||||
// recognized as key options.
|
||||
|
||||
int can_take_more = max_tokens - static_cast<int>(opt.value.size());
|
||||
unsigned j = i+1;
|
||||
for (; can_take_more && j < result.size(); --can_take_more, ++j)
|
||||
{
|
||||
option& opt2 = result[j];
|
||||
if (!opt2.string_key.empty())
|
||||
break;
|
||||
|
||||
if (opt2.position_key == INT_MAX)
|
||||
{
|
||||
// We use INT_MAX to mark positional options that
|
||||
// were found after the '--' terminator and therefore
|
||||
// should stay positional forever.
|
||||
break;
|
||||
}
|
||||
|
||||
assert(opt2.value.size() == 1);
|
||||
|
||||
opt.value.push_back(opt2.value[0]);
|
||||
|
||||
assert(opt2.original_tokens.size() == 1);
|
||||
|
||||
opt.original_tokens.push_back(opt2.original_tokens[0]);
|
||||
}
|
||||
i = j-1;
|
||||
}
|
||||
}
|
||||
result.swap(result2);
|
||||
|
||||
|
||||
// Assign position keys to positional options.
|
||||
int position_key = 0;
|
||||
for(unsigned i = 0; i < result.size(); ++i) {
|
||||
if (result[i].string_key.empty())
|
||||
result[i].position_key = position_key++;
|
||||
}
|
||||
|
||||
if (m_positional)
|
||||
{
|
||||
unsigned position = 0;
|
||||
for (unsigned i = 0; i < result.size(); ++i) {
|
||||
option& opt = result[i];
|
||||
if (opt.position_key != -1) {
|
||||
if (position >= m_positional->max_total_count())
|
||||
{
|
||||
boost::throw_exception(too_many_positional_options_error());
|
||||
}
|
||||
opt.string_key = m_positional->name_for_position(position);
|
||||
++position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set case sensitive flag
|
||||
for (unsigned i = 0; i < result.size(); ++i) {
|
||||
if (result[i].string_key.size() > 2 ||
|
||||
(result[i].string_key.size() > 1 && result[i].string_key[0] != '-'))
|
||||
{
|
||||
// it is a long option
|
||||
result[i].case_insensitive = is_style_active(long_case_insensitive);
|
||||
}
|
||||
else
|
||||
{
|
||||
// it is a short option
|
||||
result[i].case_insensitive = is_style_active(short_case_insensitive);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::finish_option(option& opt,
|
||||
vector<string>& other_tokens,
|
||||
const vector<style_parser>& style_parsers)
|
||||
{
|
||||
if (opt.string_key.empty())
|
||||
return;
|
||||
|
||||
//
|
||||
// Be defensive:
|
||||
// will have no original token if option created by handle_additional_parser()
|
||||
std::string original_token_for_exceptions = opt.string_key;
|
||||
if (opt.original_tokens.size())
|
||||
original_token_for_exceptions = opt.original_tokens[0];
|
||||
|
||||
try
|
||||
{
|
||||
// First check that the option is valid, and get its description.
|
||||
const option_description* xd = m_desc->find_nothrow(opt.string_key,
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
|
||||
if (!xd)
|
||||
{
|
||||
if (m_allow_unregistered) {
|
||||
opt.unregistered = true;
|
||||
return;
|
||||
} else {
|
||||
boost::throw_exception(unknown_option());
|
||||
}
|
||||
}
|
||||
const option_description& d = *xd;
|
||||
|
||||
// Canonize the name
|
||||
opt.string_key = d.key(opt.string_key);
|
||||
|
||||
// We check that the min/max number of tokens for the option
|
||||
// agrees with the number of tokens we have. The 'adjacent_value'
|
||||
// (the value in --foo=1) counts as a separate token, and if present
|
||||
// must be consumed. The following tokens on the command line may be
|
||||
// left unconsumed.
|
||||
|
||||
// We don't check if those tokens look like option, or not!
|
||||
|
||||
unsigned min_tokens = d.semantic()->min_tokens();
|
||||
unsigned max_tokens = d.semantic()->max_tokens();
|
||||
|
||||
unsigned present_tokens = static_cast<unsigned>(opt.value.size() + other_tokens.size());
|
||||
|
||||
if (present_tokens >= min_tokens)
|
||||
{
|
||||
if (!opt.value.empty() && max_tokens == 0)
|
||||
{
|
||||
boost::throw_exception(
|
||||
invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
|
||||
}
|
||||
|
||||
// If an option wants, at minimum, N tokens, we grab them there,
|
||||
// when adding these tokens as values to current option we check
|
||||
// if they look like options
|
||||
if (opt.value.size() <= min_tokens)
|
||||
{
|
||||
min_tokens -= static_cast<unsigned>(opt.value.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
min_tokens = 0;
|
||||
}
|
||||
|
||||
// Everything's OK, move the values to the result.
|
||||
for(;!other_tokens.empty() && min_tokens--; )
|
||||
{
|
||||
// check if extra parameter looks like a known option
|
||||
// we use style parsers to check if it is syntactically an option,
|
||||
// additionally we check if an option_description exists
|
||||
vector<option> followed_option;
|
||||
vector<string> next_token(1, other_tokens[0]);
|
||||
for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
|
||||
{
|
||||
followed_option = style_parsers[i](next_token);
|
||||
}
|
||||
if (!followed_option.empty())
|
||||
{
|
||||
original_token_for_exceptions = other_tokens[0];
|
||||
const option_description* od = m_desc->find_nothrow(other_tokens[0],
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
if (od)
|
||||
boost::throw_exception(
|
||||
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
|
||||
}
|
||||
opt.value.push_back(other_tokens[0]);
|
||||
opt.original_tokens.push_back(other_tokens[0]);
|
||||
other_tokens.erase(other_tokens.begin());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::throw_exception(
|
||||
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
|
||||
|
||||
}
|
||||
}
|
||||
// use only original token for unknown_option / ambiguous_option since by definition
|
||||
// they are unrecognised / unparsable
|
||||
catch(error_with_option_name& e)
|
||||
{
|
||||
// add context and rethrow
|
||||
e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix());
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::parse_long_option(vector<string>& args)
|
||||
{
|
||||
vector<option> result;
|
||||
const string& tok = args[0];
|
||||
if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
|
||||
{
|
||||
string name, adjacent;
|
||||
|
||||
string::size_type p = tok.find('=');
|
||||
if (p != tok.npos)
|
||||
{
|
||||
name = tok.substr(2, p-2);
|
||||
adjacent = tok.substr(p+1);
|
||||
if (adjacent.empty())
|
||||
boost::throw_exception( invalid_command_line_syntax(
|
||||
invalid_command_line_syntax::empty_adjacent_parameter,
|
||||
name,
|
||||
name,
|
||||
get_canonical_option_prefix()) );
|
||||
}
|
||||
else
|
||||
{
|
||||
name = tok.substr(2);
|
||||
}
|
||||
option opt;
|
||||
opt.string_key = name;
|
||||
if (!adjacent.empty())
|
||||
opt.value.push_back(adjacent);
|
||||
opt.original_tokens.push_back(tok);
|
||||
result.push_back(opt);
|
||||
args.erase(args.begin());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<option>
|
||||
cmdline::parse_short_option(vector<string>& args)
|
||||
{
|
||||
const string& tok = args[0];
|
||||
if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-')
|
||||
{
|
||||
vector<option> result;
|
||||
|
||||
string name = tok.substr(0,2);
|
||||
string adjacent = tok.substr(2);
|
||||
|
||||
// Short options can be 'grouped', so that
|
||||
// "-d -a" becomes "-da". Loop, processing one
|
||||
// option at a time. We exit the loop when either
|
||||
// we've processed all the token, or when the remainder
|
||||
// of token is considered to be value, not further grouped
|
||||
// option.
|
||||
for(;;) {
|
||||
const option_description* d;
|
||||
try
|
||||
{
|
||||
|
||||
d = m_desc->find_nothrow(name, false, false,
|
||||
is_style_active(short_case_insensitive));
|
||||
}
|
||||
catch(error_with_option_name& e)
|
||||
{
|
||||
// add context and rethrow
|
||||
e.add_context(name, name, get_canonical_option_prefix());
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: check for 'allow_sticky'.
|
||||
if (d && (m_style & allow_sticky) &&
|
||||
d->semantic()->max_tokens() == 0 && !adjacent.empty()) {
|
||||
// 'adjacent' is in fact further option.
|
||||
option opt;
|
||||
opt.string_key = name;
|
||||
result.push_back(opt);
|
||||
|
||||
if (adjacent.empty())
|
||||
{
|
||||
args.erase(args.begin());
|
||||
break;
|
||||
}
|
||||
|
||||
name = string("-") + adjacent[0];
|
||||
adjacent.erase(adjacent.begin());
|
||||
} else {
|
||||
|
||||
option opt;
|
||||
opt.string_key = name;
|
||||
opt.original_tokens.push_back(tok);
|
||||
if (!adjacent.empty())
|
||||
opt.value.push_back(adjacent);
|
||||
result.push_back(opt);
|
||||
args.erase(args.begin());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return vector<option>();
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::parse_dos_option(vector<string>& args)
|
||||
{
|
||||
vector<option> result;
|
||||
const string& tok = args[0];
|
||||
if (tok.size() >= 2 && tok[0] == '/')
|
||||
{
|
||||
string name = "-" + tok.substr(1,1);
|
||||
string adjacent = tok.substr(2);
|
||||
|
||||
option opt;
|
||||
opt.string_key = name;
|
||||
if (!adjacent.empty())
|
||||
opt.value.push_back(adjacent);
|
||||
opt.original_tokens.push_back(tok);
|
||||
result.push_back(opt);
|
||||
args.erase(args.begin());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::parse_disguised_long_option(vector<string>& args)
|
||||
{
|
||||
const string& tok = args[0];
|
||||
if (tok.size() >= 2 &&
|
||||
((tok[0] == '-' && tok[1] != '-') ||
|
||||
((m_style & allow_slash_for_short) && tok[0] == '/')))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive)))
|
||||
{
|
||||
args[0].insert(0, "-");
|
||||
if (args[0][1] == '/')
|
||||
args[0][1] = '-';
|
||||
return parse_long_option(args);
|
||||
}
|
||||
}
|
||||
catch(error_with_option_name& e)
|
||||
{
|
||||
// add context and rethrow
|
||||
e.add_context(tok, tok, get_canonical_option_prefix());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return vector<option>();
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::parse_terminator(vector<string>& args)
|
||||
{
|
||||
vector<option> result;
|
||||
const string& tok = args[0];
|
||||
if (tok == "--")
|
||||
{
|
||||
for(unsigned i = 1; i < args.size(); ++i)
|
||||
{
|
||||
option opt;
|
||||
opt.value.push_back(args[i]);
|
||||
opt.original_tokens.push_back(args[i]);
|
||||
opt.position_key = INT_MAX;
|
||||
result.push_back(opt);
|
||||
}
|
||||
args.clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<option>
|
||||
cmdline::handle_additional_parser(vector<string>& args)
|
||||
{
|
||||
vector<option> result;
|
||||
pair<string, string> r = m_additional_parser(args[0]);
|
||||
if (!r.first.empty()) {
|
||||
option next;
|
||||
next.string_key = r.first;
|
||||
if (!r.second.empty())
|
||||
next.value.push_back(r.second);
|
||||
result.push_back(next);
|
||||
args.erase(args.begin());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::set_additional_parser(additional_parser p)
|
||||
{
|
||||
m_additional_parser = p;
|
||||
}
|
||||
|
||||
void
|
||||
cmdline::extra_style_parser(style_parser s)
|
||||
{
|
||||
m_style_parser = s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}}
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright Vladimir Prus 2002-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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
|
||||
#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>
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
using namespace std;
|
||||
|
||||
common_config_file_iterator::common_config_file_iterator(
|
||||
const std::set<std::string>& allowed_options,
|
||||
bool allow_unregistered)
|
||||
: allowed_options(allowed_options),
|
||||
m_allow_unregistered(allow_unregistered)
|
||||
{
|
||||
for(std::set<std::string>::const_iterator i = allowed_options.begin();
|
||||
i != allowed_options.end();
|
||||
++i)
|
||||
{
|
||||
add_option(i->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
common_config_file_iterator::add_option(const char* name)
|
||||
{
|
||||
string s(name);
|
||||
assert(!s.empty());
|
||||
if (*s.rbegin() == '*') {
|
||||
s.resize(s.size()-1);
|
||||
bool bad_prefixes(false);
|
||||
// If 's' is a prefix of one of allowed suffix, then
|
||||
// lower_bound will return that element.
|
||||
// If some element is prefix of 's', then lower_bound will
|
||||
// return the next element.
|
||||
set<string>::iterator i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.end()) {
|
||||
if (i->find(s) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (i != allowed_prefixes.begin()) {
|
||||
--i;
|
||||
if (s.find(*i) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (bad_prefixes)
|
||||
boost::throw_exception(error("options '" + string(name) + "' and '" +
|
||||
*i + "*' will both match the same "
|
||||
"arguments from the configuration file"));
|
||||
allowed_prefixes.insert(s);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
string trim_ws(const string& s)
|
||||
{
|
||||
string::size_type n, n2;
|
||||
n = s.find_first_not_of(" \t\r\n");
|
||||
if (n == string::npos)
|
||||
return string();
|
||||
else {
|
||||
n2 = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(n, n2-n+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void common_config_file_iterator::get()
|
||||
{
|
||||
string s;
|
||||
string::size_type n;
|
||||
bool found = false;
|
||||
|
||||
while(this->getline(s)) {
|
||||
|
||||
// strip '#' comments and whitespace
|
||||
if ((n = s.find('#')) != string::npos)
|
||||
s = s.substr(0, n);
|
||||
s = trim_ws(s);
|
||||
|
||||
if (!s.empty()) {
|
||||
// Handle section name
|
||||
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
||||
m_prefix = s.substr(1, s.size()-2);
|
||||
if (*m_prefix.rbegin() != '.')
|
||||
m_prefix += '.';
|
||||
}
|
||||
else if ((n = s.find('=')) != string::npos) {
|
||||
|
||||
string name = m_prefix + trim_ws(s.substr(0, n));
|
||||
string value = trim_ws(s.substr(n+1));
|
||||
|
||||
bool registered = allowed_option(name);
|
||||
if (!registered && !m_allow_unregistered)
|
||||
boost::throw_exception(unknown_option(name));
|
||||
|
||||
found = true;
|
||||
this->value().string_key = name;
|
||||
this->value().value.clear();
|
||||
this->value().value.push_back(value);
|
||||
this->value().unregistered = !registered;
|
||||
this->value().original_tokens.clear();
|
||||
this->value().original_tokens.push_back(name);
|
||||
this->value().original_tokens.push_back(value);
|
||||
break;
|
||||
|
||||
} else {
|
||||
boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
found_eof();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
common_config_file_iterator::allowed_option(const std::string& s) const
|
||||
{
|
||||
set<string>::const_iterator i = allowed_options.find(s);
|
||||
if (i != allowed_options.end())
|
||||
return true;
|
||||
// If s is "pa" where "p" is allowed prefix then
|
||||
// lower_bound should find the element after "p".
|
||||
// This depends on 'allowed_prefixes' invariant.
|
||||
i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
std::wstring ws;
|
||||
// On Comeau, using two-argument version causes
|
||||
// call to some internal function with std::wstring, and '\n'
|
||||
// (not L'\n') and compile can't resolve that call.
|
||||
|
||||
if (std::getline(*is, ws, L'\n')) {
|
||||
s = to_utf8(ws);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}}}
|
||||
|
||||
#if 0
|
||||
using boost::program_options::config_file;
|
||||
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
stringstream s(
|
||||
"a = 1\n"
|
||||
"b = 2\n");
|
||||
|
||||
config_file cf(s);
|
||||
cf.add_option("a");
|
||||
cf.add_option("b");
|
||||
|
||||
assert(++cf);
|
||||
assert(cf.name() == "a");
|
||||
assert(cf.value() == "1");
|
||||
assert(++cf);
|
||||
assert(cf.name() == "b");
|
||||
assert(cf.value() == "2");
|
||||
assert(!++cf);
|
||||
}
|
||||
catch(exception& e)
|
||||
{
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
// 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)
|
||||
|
||||
#include <fstream>
|
||||
#include <locale.h>
|
||||
#include <locale>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#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>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace detail {
|
||||
|
||||
/* Internal function to actually perform conversion.
|
||||
The logic in from_8_bit and to_8_bit function is exactly
|
||||
the same, except that one calls 'in' method of codecvt and another
|
||||
calls the 'out' method, and that syntax difference makes straightforward
|
||||
template implementation impossible.
|
||||
|
||||
This functions takes a 'fun' argument, which should have the same
|
||||
parameters and return type and the in/out methods. The actual converting
|
||||
function will pass functional objects created with boost::bind.
|
||||
Experiments show that the performance loss is less than 10%.
|
||||
*/
|
||||
template<class ToChar, class FromChar, class Fun>
|
||||
std::basic_string<ToChar>
|
||||
convert(const std::basic_string<FromChar>& s, Fun fun)
|
||||
|
||||
{
|
||||
std::basic_string<ToChar> result;
|
||||
|
||||
std::mbstate_t state = std::mbstate_t();
|
||||
|
||||
const FromChar* from = s.data();
|
||||
const FromChar* from_end = s.data() + s.size();
|
||||
// The interface of cvt is not really iterator-like, and it's
|
||||
// not possible the tell the required output size without the conversion.
|
||||
// All we can is convert data by pieces.
|
||||
while(from != from_end) {
|
||||
|
||||
// std::basic_string does not provide non-const pointers to the data,
|
||||
// so converting directly into string is not possible.
|
||||
ToChar buffer[32];
|
||||
|
||||
ToChar* to_next = buffer;
|
||||
// Need variable because boost::bind doesn't work with rvalues.
|
||||
ToChar* to_end = buffer + 32;
|
||||
std::codecvt_base::result r =
|
||||
fun(state, from, from_end, from, buffer, to_end, to_next);
|
||||
|
||||
if (r == std::codecvt_base::error)
|
||||
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)
|
||||
boost::throw_exception(
|
||||
std::logic_error("character conversion failed"));
|
||||
|
||||
// Add converted characters
|
||||
result.append(buffer, to_next);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}}
|
||||
|
||||
namespace boost {
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
||||
from_8_bit(const std::string& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
||||
{
|
||||
return detail::convert<wchar_t>(
|
||||
s,
|
||||
boost::bind(&std::codecvt<wchar_t, char, mbstate_t>::in,
|
||||
&cvt,
|
||||
_1, _2, _3, _4, _5, _6, _7));
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
||||
to_8_bit(const std::wstring& s,
|
||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
||||
{
|
||||
return detail::convert<char>(
|
||||
s,
|
||||
boost::bind(&codecvt<wchar_t, char, mbstate_t>::out,
|
||||
&cvt,
|
||||
_1, _2, _3, _4, _5, _6, _7));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
boost::program_options::detail::utf8_codecvt_facet
|
||||
utf8_facet;
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
||||
from_utf8(const std::string& s)
|
||||
{
|
||||
return from_8_bit(s, utf8_facet);
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
||||
to_utf8(const std::wstring& s)
|
||||
{
|
||||
return to_8_bit(s, utf8_facet);
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
||||
from_local_8_bit(const std::string& s)
|
||||
{
|
||||
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
|
||||
return from_8_bit(s,
|
||||
BOOST_USE_FACET(facet_type, locale()));
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
||||
to_local_8_bit(const std::wstring& s)
|
||||
{
|
||||
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
|
||||
return to_8_bit(s,
|
||||
BOOST_USE_FACET(facet_type, locale()));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace program_options
|
||||
{
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::string& s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::wstring& s)
|
||||
{
|
||||
return to_utf8(s);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
// 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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
// FIXME: this is only to get multiple_occurrences class
|
||||
// should move that to a separate headers.
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
namespace {
|
||||
|
||||
template< class charT >
|
||||
std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
|
||||
{
|
||||
std::basic_string< charT > result;
|
||||
for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
|
||||
{
|
||||
result.append(1, static_cast< charT >(std::tolower(str[i])));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
option_description::option_description()
|
||||
{
|
||||
}
|
||||
|
||||
option_description::
|
||||
option_description(const char* name,
|
||||
const value_semantic* s)
|
||||
: m_value_semantic(s)
|
||||
{
|
||||
this->set_name(name);
|
||||
}
|
||||
|
||||
|
||||
option_description::
|
||||
option_description(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description)
|
||||
: m_description(description), m_value_semantic(s)
|
||||
{
|
||||
this->set_name(name);
|
||||
}
|
||||
|
||||
option_description::~option_description()
|
||||
{
|
||||
}
|
||||
|
||||
option_description::match_result
|
||||
option_description::match(const std::string& option,
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
match_result result = no_match;
|
||||
|
||||
std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
|
||||
|
||||
if (!local_long_name.empty()) {
|
||||
|
||||
std::string local_option = (long_ignore_case ? tolower_(option) : option);
|
||||
|
||||
if (*local_long_name.rbegin() == '*')
|
||||
{
|
||||
// The name ends with '*'. Any specified name with the given
|
||||
// prefix is OK.
|
||||
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
|
||||
== 0)
|
||||
result = approximate_match;
|
||||
}
|
||||
|
||||
if (local_long_name == local_option)
|
||||
{
|
||||
result = full_match;
|
||||
}
|
||||
else if (approx)
|
||||
{
|
||||
if (local_long_name.find(local_option) == 0)
|
||||
{
|
||||
result = approximate_match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != full_match)
|
||||
{
|
||||
std::string local_option(short_ignore_case ? tolower_(option) : option);
|
||||
std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
|
||||
|
||||
if (local_short_name == local_option)
|
||||
{
|
||||
result = full_match;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string
|
||||
option_description::canonical_display_name(int prefix_style) const
|
||||
{
|
||||
if (!m_long_name.empty())
|
||||
{
|
||||
if (prefix_style == command_line_style::allow_long)
|
||||
return "--" + m_long_name;
|
||||
if (prefix_style == command_line_style::allow_long_disguise)
|
||||
return "-" + m_long_name;
|
||||
}
|
||||
// sanity check: m_short_name[0] should be '-' or '/'
|
||||
if (m_short_name.length() == 2)
|
||||
{
|
||||
if (prefix_style == command_line_style::allow_slash_for_short)
|
||||
return string("/") + m_short_name[1];
|
||||
if (prefix_style == command_line_style::allow_dash_for_short)
|
||||
return string("-") + m_short_name[1];
|
||||
}
|
||||
if (!m_long_name.empty())
|
||||
return m_long_name;
|
||||
else
|
||||
return m_short_name;
|
||||
}
|
||||
|
||||
|
||||
const std::string&
|
||||
option_description::long_name() const
|
||||
{
|
||||
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
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
shared_ptr<const value_semantic>
|
||||
option_description::semantic() const
|
||||
{
|
||||
return m_value_semantic;
|
||||
}
|
||||
|
||||
std::string
|
||||
option_description::format_name() const
|
||||
{
|
||||
if (!m_short_name.empty())
|
||||
{
|
||||
return m_long_name.empty()
|
||||
? m_short_name
|
||||
: string(m_short_name).append(" [ --").
|
||||
append(m_long_name).append(" ]");
|
||||
}
|
||||
return string("--").append(m_long_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
option_description::format_parameter() const
|
||||
{
|
||||
if (m_value_semantic->max_tokens() != 0)
|
||||
return m_value_semantic->name();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
options_description_easy_init::
|
||||
options_description_easy_init(options_description* owner)
|
||||
: owner(owner)
|
||||
{}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const char* description)
|
||||
{
|
||||
// Create untypes semantic which accepts zero tokens: i.e.
|
||||
// no value can be specified on command line.
|
||||
// FIXME: does not look exception-safe
|
||||
shared_ptr<option_description> d(
|
||||
new option_description(name, new untyped_value(true), description));
|
||||
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const value_semantic* s)
|
||||
{
|
||||
shared_ptr<option_description> d(new option_description(name, s));
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init&
|
||||
options_description_easy_init::
|
||||
operator()(const char* name,
|
||||
const value_semantic* s,
|
||||
const char* description)
|
||||
{
|
||||
shared_ptr<option_description> d(new option_description(name, s, description));
|
||||
|
||||
owner->add(d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const unsigned options_description::m_default_line_length = 80;
|
||||
|
||||
options_description::options_description(unsigned line_length,
|
||||
unsigned min_description_length)
|
||||
: m_line_length(line_length)
|
||||
, m_min_description_length(min_description_length)
|
||||
{
|
||||
// we require a space between the option and description parts, so add 1.
|
||||
assert(m_min_description_length < m_line_length - 1);
|
||||
}
|
||||
|
||||
options_description::options_description(const std::string& caption,
|
||||
unsigned line_length,
|
||||
unsigned min_description_length)
|
||||
: m_caption(caption)
|
||||
, m_line_length(line_length)
|
||||
, m_min_description_length(min_description_length)
|
||||
{
|
||||
// we require a space between the option and description parts, so add 1.
|
||||
assert(m_min_description_length < m_line_length - 1);
|
||||
}
|
||||
|
||||
void
|
||||
options_description::add(shared_ptr<option_description> desc)
|
||||
{
|
||||
m_options.push_back(desc);
|
||||
belong_to_group.push_back(false);
|
||||
}
|
||||
|
||||
options_description&
|
||||
options_description::add(const options_description& desc)
|
||||
{
|
||||
shared_ptr<options_description> d(new options_description(desc));
|
||||
groups.push_back(d);
|
||||
|
||||
for (size_t i = 0; i < desc.m_options.size(); ++i) {
|
||||
add(desc.m_options[i]);
|
||||
belong_to_group.back() = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
options_description_easy_init
|
||||
options_description::add_options()
|
||||
{
|
||||
return options_description_easy_init(this);
|
||||
}
|
||||
|
||||
const option_description&
|
||||
options_description::find(const std::string& name,
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
const option_description* d = find_nothrow(name, approx,
|
||||
long_ignore_case, short_ignore_case);
|
||||
if (!d)
|
||||
boost::throw_exception(unknown_option());
|
||||
return *d;
|
||||
}
|
||||
|
||||
const std::vector< shared_ptr<option_description> >&
|
||||
options_description::options() const
|
||||
{
|
||||
return m_options;
|
||||
}
|
||||
|
||||
const option_description*
|
||||
options_description::find_nothrow(const std::string& name,
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
shared_ptr<option_description> found;
|
||||
bool had_full_match = false;
|
||||
vector<string> approximate_matches;
|
||||
vector<string> full_matches;
|
||||
|
||||
// We use linear search because matching specified option
|
||||
// name with the declared option name need to take care about
|
||||
// case sensitivity and trailing '*' and so we can't use simple map.
|
||||
for(unsigned i = 0; i < m_options.size(); ++i)
|
||||
{
|
||||
option_description::match_result r =
|
||||
m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
|
||||
|
||||
if (r == option_description::no_match)
|
||||
continue;
|
||||
|
||||
if (r == option_description::full_match)
|
||||
{
|
||||
full_matches.push_back(m_options[i]->key(name));
|
||||
found = m_options[i];
|
||||
had_full_match = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: the use of 'key' here might not
|
||||
// be the best approach.
|
||||
approximate_matches.push_back(m_options[i]->key(name));
|
||||
if (!had_full_match)
|
||||
found = m_options[i];
|
||||
}
|
||||
}
|
||||
if (full_matches.size() > 1)
|
||||
boost::throw_exception(ambiguous_option(full_matches));
|
||||
|
||||
// If we have a full match, and an approximate match,
|
||||
// ignore approximate match instead of reporting error.
|
||||
// Say, if we have options "all" and "all-chroots", then
|
||||
// "--all" on the command line should select the first one,
|
||||
// without ambiguity.
|
||||
if (full_matches.empty() && approximate_matches.size() > 1)
|
||||
boost::throw_exception(ambiguous_option(approximate_matches));
|
||||
|
||||
return found.get();
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
std::ostream& operator<<(std::ostream& os, const options_description& desc)
|
||||
{
|
||||
desc.print(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/* Given a string 'par', that contains no newline characters
|
||||
outputs it to 'os' with wordwrapping, that is, as several
|
||||
line.
|
||||
|
||||
Each output line starts with 'indent' space characters,
|
||||
following by characters from 'par'. The total length of
|
||||
line is no longer than 'line_length'.
|
||||
|
||||
*/
|
||||
void format_paragraph(std::ostream& os,
|
||||
std::string par,
|
||||
unsigned indent,
|
||||
unsigned line_length)
|
||||
{
|
||||
// Through reminder of this function, 'line_length' will
|
||||
// be the length available for characters, not including
|
||||
// indent.
|
||||
assert(indent < line_length);
|
||||
line_length -= indent;
|
||||
|
||||
// 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 in the options description"));
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// ignore tab if not on first line
|
||||
if (par_indent >= line_length)
|
||||
{
|
||||
par_indent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (par.size() < line_length)
|
||||
{
|
||||
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!
|
||||
|
||||
while (line_begin < par_end) // paragraph lines
|
||||
{
|
||||
if (!first_line)
|
||||
{
|
||||
// If line starts with space, but second character
|
||||
// is not space, remove the leading space.
|
||||
// We don't remove double spaces because those
|
||||
// might be intentianal.
|
||||
if ((*line_begin == ' ') &&
|
||||
((line_begin + 1 < par_end) &&
|
||||
(*(line_begin + 1) != ' ')))
|
||||
{
|
||||
line_begin += 1; // line_begin != line_end
|
||||
}
|
||||
}
|
||||
|
||||
// Take care to never increment the iterator past
|
||||
// the end, since MSVC 8.0 (brokenly), assumes that
|
||||
// doing that, even if no access happens, is a bug.
|
||||
unsigned remaining = static_cast<unsigned>(std::distance(line_begin, par_end));
|
||||
string::const_iterator line_end = line_begin +
|
||||
((remaining < line_length) ? remaining : line_length);
|
||||
|
||||
// prevent chopped words
|
||||
// Is line_end between two non-space characters?
|
||||
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),
|
||||
reverse_iterator<string::const_iterator>(line_begin),
|
||||
' ')
|
||||
.base();
|
||||
|
||||
if (last_space != line_begin)
|
||||
{
|
||||
// is last_space within the second half ot the
|
||||
// current line
|
||||
if (static_cast<unsigned>(std::distance(last_space, line_end)) <
|
||||
(line_length / 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 += static_cast<unsigned>(par_indent);
|
||||
line_length -= static_cast<unsigned>(par_indent); // there's less to work with now
|
||||
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 uses of 'tokenizer' below 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 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())
|
||||
{
|
||||
if (ss.str().size() >= first_column_width)
|
||||
{
|
||||
os.put('\n'); // first column is too long, lets put description in new line
|
||||
for (unsigned pad = first_column_width; pad > 0; --pad)
|
||||
{
|
||||
os.put(' ');
|
||||
}
|
||||
} else {
|
||||
for(unsigned pad = first_column_width - static_cast<unsigned>(ss.str().size()); pad > 0; --pad)
|
||||
{
|
||||
os.put(' ');
|
||||
}
|
||||
}
|
||||
|
||||
format_description(os, opt.description(),
|
||||
first_column_width, line_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
options_description::get_option_column_width() const
|
||||
{
|
||||
/* Find the maximum width of the option column */
|
||||
unsigned width(23);
|
||||
unsigned i; // vc6 has broken for loop scoping
|
||||
for (i = 0; i < m_options.size(); ++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()));
|
||||
}
|
||||
|
||||
/* Get width of groups as well*/
|
||||
for (unsigned j = 0; j < groups.size(); ++j)
|
||||
width = max(width, groups[j]->get_option_column_width());
|
||||
|
||||
/* this is the column were description should start, if first
|
||||
column is longer, we go to a new line */
|
||||
const unsigned start_of_description_column = m_line_length - m_min_description_length;
|
||||
|
||||
width = (min)(width, start_of_description_column-1);
|
||||
|
||||
/* add an additional space to improve readability */
|
||||
++width;
|
||||
return width;
|
||||
}
|
||||
|
||||
void
|
||||
options_description::print(std::ostream& os, unsigned width) const
|
||||
{
|
||||
if (!m_caption.empty())
|
||||
os << m_caption << ":\n";
|
||||
|
||||
if (!width)
|
||||
width = get_option_column_width();
|
||||
|
||||
/* The options formatting style is stolen from Subversion. */
|
||||
for (unsigned i = 0; i < m_options.size(); ++i)
|
||||
{
|
||||
if (belong_to_group[i])
|
||||
continue;
|
||||
|
||||
const option_description& opt = *m_options[i];
|
||||
|
||||
format_one(os, opt, width, m_line_length);
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < groups.size(); ++j) {
|
||||
os << "\n";
|
||||
groups[j]->print(os, width);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,249 @@
|
||||
// Copyright Vladimir Prus 2002-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)
|
||||
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
#include <boost/program_options/detail/config_file.hpp>
|
||||
#include <boost/program_options/environment_iterator.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__ < 3
|
||||
#include <iostream>
|
||||
#else
|
||||
#include <istream>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// The 'environ' should be declared in some cases. E.g. Linux man page says:
|
||||
// (This variable must be declared in the user program, but is declared in
|
||||
// the header file unistd.h in case the header files came from libc4 or libc5,
|
||||
// and in case they came from glibc and _GNU_SOURCE was defined.)
|
||||
// To be safe, declare it here.
|
||||
|
||||
// It appears that on Mac OS X the 'environ' variable is not
|
||||
// available to dynamically linked libraries.
|
||||
// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843
|
||||
// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html
|
||||
#if defined(__APPLE__) && defined(__DYNAMIC__)
|
||||
// The proper include for this is crt_externs.h, however it's not
|
||||
// available on iOS. The right replacement is not known. See
|
||||
// https://svn.boost.org/trac/boost/ticket/5053
|
||||
extern "C" { extern char ***_NSGetEnviron(void); }
|
||||
#define environ (*_NSGetEnviron())
|
||||
#else
|
||||
#if defined(__MWERKS__)
|
||||
#include <crtl.h>
|
||||
#else
|
||||
#if !defined(_WIN32) || defined(__COMO_VERSION__)
|
||||
extern char** environ;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
namespace {
|
||||
woption woption_from_option(const option& opt)
|
||||
{
|
||||
woption result;
|
||||
result.string_key = opt.string_key;
|
||||
result.position_key = opt.position_key;
|
||||
result.unregistered = opt.unregistered;
|
||||
|
||||
std::transform(opt.value.begin(), opt.value.end(),
|
||||
back_inserter(result.value),
|
||||
boost::bind(from_utf8, _1));
|
||||
|
||||
std::transform(opt.original_tokens.begin(),
|
||||
opt.original_tokens.end(),
|
||||
back_inserter(result.original_tokens),
|
||||
boost::bind(from_utf8, _1));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
basic_parsed_options<wchar_t>
|
||||
::basic_parsed_options(const parsed_options& po)
|
||||
: description(po.description),
|
||||
utf8_encoded_options(po),
|
||||
m_options_prefix(po.m_options_prefix)
|
||||
{
|
||||
for (unsigned i = 0; i < po.options.size(); ++i)
|
||||
options.push_back(woption_from_option(po.options[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class charT>
|
||||
basic_parsed_options<charT>
|
||||
parse_config_file(std::basic_istream<charT>& is,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
set<string> allowed_options;
|
||||
|
||||
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())
|
||||
boost::throw_exception(
|
||||
error("abbreviated option names are not permitted in options configuration files"));
|
||||
|
||||
allowed_options.insert(d.long_name());
|
||||
}
|
||||
|
||||
// Parser return char strings
|
||||
parsed_options result(&desc);
|
||||
copy(detail::basic_config_file_iterator<charT>(
|
||||
is, allowed_options, allow_unregistered),
|
||||
detail::basic_config_file_iterator<charT>(),
|
||||
back_inserter(result.options));
|
||||
// Convert char strings into desired type.
|
||||
return basic_parsed_options<charT>(result);
|
||||
}
|
||||
|
||||
template
|
||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
|
||||
parse_config_file(std::basic_istream<char>& is,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered);
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
template
|
||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
|
||||
parse_config_file(std::basic_istream<wchar_t>& is,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered);
|
||||
#endif
|
||||
|
||||
template<class charT>
|
||||
basic_parsed_options<charT>
|
||||
parse_config_file(const char* filename,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
// Parser return char strings
|
||||
std::basic_ifstream< charT > strm(filename);
|
||||
if (!strm)
|
||||
{
|
||||
boost::throw_exception(reading_file(filename));
|
||||
}
|
||||
return parse_config_file(strm, desc, allow_unregistered);
|
||||
}
|
||||
|
||||
template
|
||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
|
||||
parse_config_file(const char* filename,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered);
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
template
|
||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
|
||||
parse_config_file(const char* filename,
|
||||
const options_description& desc,
|
||||
bool allow_unregistered);
|
||||
#endif
|
||||
|
||||
|
||||
// This versio, which accepts any options without validation, is disabled,
|
||||
// in the hope that nobody will need it and we cant drop it altogether.
|
||||
// Besides, probably the right way to handle all options is the '*' name.
|
||||
#if 0
|
||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
||||
parse_config_file(std::istream& is)
|
||||
{
|
||||
detail::config_file_iterator cf(is, false);
|
||||
parsed_options result(0);
|
||||
copy(cf, detail::config_file_iterator(),
|
||||
back_inserter(result.options));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
||||
parse_environment(const options_description& desc,
|
||||
const function1<std::string, std::string>& name_mapper)
|
||||
{
|
||||
parsed_options result(&desc);
|
||||
|
||||
for(environment_iterator i(environ), e; i != e; ++i) {
|
||||
string option_name = name_mapper(i->first);
|
||||
|
||||
if (!option_name.empty()) {
|
||||
option n;
|
||||
n.string_key = option_name;
|
||||
n.value.push_back(i->second);
|
||||
result.options.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
class prefix_name_mapper {
|
||||
public:
|
||||
prefix_name_mapper(const std::string& prefix)
|
||||
: prefix(prefix)
|
||||
{}
|
||||
|
||||
std::string operator()(const std::string& s)
|
||||
{
|
||||
string result;
|
||||
if (s.find(prefix) == 0) {
|
||||
for(string::size_type n = prefix.size(); n < s.size(); ++n)
|
||||
{
|
||||
// Intel-Win-7.1 does not understand
|
||||
// push_back on string.
|
||||
result += static_cast<char>(tolower(s[n]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
std::string prefix;
|
||||
};
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
||||
parse_environment(const options_description& desc,
|
||||
const std::string& prefix)
|
||||
{
|
||||
return parse_environment(desc, detail::prefix_name_mapper(prefix));
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
||||
parse_environment(const options_description& desc, const char* prefix)
|
||||
{
|
||||
return parse_environment(desc, string(prefix));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
|
||||
#include <boost/program_options/positional_options.hpp>
|
||||
|
||||
#include <boost/limits.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
positional_options_description::positional_options_description()
|
||||
{}
|
||||
|
||||
positional_options_description&
|
||||
positional_options_description::add(const char* name, int max_count)
|
||||
{
|
||||
assert(max_count != -1 || m_trailing.empty());
|
||||
|
||||
if (max_count == -1)
|
||||
m_trailing = name;
|
||||
else {
|
||||
m_names.resize(m_names.size() + max_count, name);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
unsigned
|
||||
positional_options_description::max_total_count() const
|
||||
{
|
||||
return m_trailing.empty() ?
|
||||
static_cast<unsigned>(m_names.size()) : (std::numeric_limits<unsigned>::max)();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
positional_options_description::name_for_position(unsigned position) const
|
||||
{
|
||||
assert(position < max_total_count());
|
||||
|
||||
if (position < m_names.size())
|
||||
return m_names[position];
|
||||
else
|
||||
return m_trailing;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright Sascha Ochsenknecht 2009.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
template< class charT >
|
||||
std::vector<std::basic_string<charT> >
|
||||
split_unix(
|
||||
const std::basic_string<charT>& cmdline,
|
||||
const std::basic_string<charT>& seperator,
|
||||
const std::basic_string<charT>& quote,
|
||||
const std::basic_string<charT>& escape)
|
||||
{
|
||||
typedef boost::tokenizer< boost::escaped_list_separator<charT>,
|
||||
typename std::basic_string<charT>::const_iterator,
|
||||
std::basic_string<charT> > tokenizerT;
|
||||
|
||||
tokenizerT tok(cmdline.begin(), cmdline.end(),
|
||||
boost::escaped_list_separator< charT >(escape, seperator, quote));
|
||||
|
||||
std::vector< std::basic_string<charT> > result;
|
||||
for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) {
|
||||
if (!cur_token->empty())
|
||||
result.push_back(*cur_token);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
// Take a command line string and splits in into tokens, according
|
||||
// to the given collection of seperators chars.
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::string>
|
||||
split_unix(const std::string& cmdline, const std::string& seperator,
|
||||
const std::string& quote, const std::string& escape)
|
||||
{
|
||||
return detail::split_unix< char >(cmdline, seperator, quote, escape);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
|
||||
split_unix(const std::wstring& cmdline, const std::wstring& seperator,
|
||||
const std::wstring& quote, const std::wstring& escape)
|
||||
{
|
||||
return detail::split_unix< wchar_t >(cmdline, seperator, quote, escape);
|
||||
}
|
||||
#endif
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
|
||||
#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.ipp>
|
||||
|
||||
|
||||
#undef BOOST_UTF8_BEGIN_NAMESPACE
|
||||
#undef BOOST_UTF8_END_NAMESPACE
|
||||
#undef BOOST_UTF8_DECL
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
// 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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
#include <boost/program_options/detail/cmdline.hpp>
|
||||
#include <set>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
namespace
|
||||
{
|
||||
std::string convert_value(const std::wstring& s)
|
||||
{
|
||||
try {
|
||||
return to_local_8_bit(s);
|
||||
}
|
||||
catch(const std::exception&) {
|
||||
return "<unrepresentable unicode string>";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
value_semantic_codecvt_helper<char>::
|
||||
parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const
|
||||
{
|
||||
if (utf8) {
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
// Need to convert to local encoding.
|
||||
std::vector<string> local_tokens;
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
std::wstring w = from_utf8(new_tokens[i]);
|
||||
local_tokens.push_back(to_local_8_bit(w));
|
||||
}
|
||||
xparse(value_store, local_tokens);
|
||||
#else
|
||||
boost::throw_exception(
|
||||
std::runtime_error("UTF-8 conversion not supported."));
|
||||
#endif
|
||||
} else {
|
||||
// Already in local encoding, pass unmodified
|
||||
xparse(value_store, new_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
void
|
||||
value_semantic_codecvt_helper<wchar_t>::
|
||||
parse(boost::any& value_store,
|
||||
const std::vector<std::string>& new_tokens,
|
||||
bool utf8) const
|
||||
{
|
||||
std::vector<wstring> tokens;
|
||||
if (utf8) {
|
||||
// Convert from utf8
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
tokens.push_back(from_utf8(new_tokens[i]));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Convert from local encoding
|
||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
||||
tokens.push_back(from_local_8_bit(new_tokens[i]));
|
||||
}
|
||||
}
|
||||
|
||||
xparse(value_store, tokens);
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg");
|
||||
|
||||
std::string
|
||||
untyped_value::name() const
|
||||
{
|
||||
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())
|
||||
boost::throw_exception(
|
||||
multiple_occurrences());
|
||||
if (new_tokens.size() > 1)
|
||||
boost::throw_exception(multiple_values());
|
||||
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
|
||||
bool_switch()
|
||||
{
|
||||
return bool_switch(0);
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
|
||||
bool_switch(bool* v)
|
||||
{
|
||||
typed_value<bool>* r = new typed_value<bool>(v);
|
||||
r->default_value(0);
|
||||
r->zero_tokens();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Validates bool value.
|
||||
Any of "1", "true", "yes", "on" will be converted to "1".<br>
|
||||
Any of "0", "false", "no", "off" will be converted to "0".<br>
|
||||
Case is ignored. The 'xs' vector can either be empty, in which
|
||||
case the value is 'true', or can contain explicit value.
|
||||
*/
|
||||
BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs,
|
||||
bool*, int)
|
||||
{
|
||||
check_first_occurrence(v);
|
||||
string s(get_single_string(xs, true));
|
||||
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
s[i] = char(tolower(s[i]));
|
||||
|
||||
if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
|
||||
v = any(true);
|
||||
else if (s == "off" || s == "no" || s == "0" || s == "false")
|
||||
v = any(false);
|
||||
else
|
||||
boost::throw_exception(invalid_bool_value(s));
|
||||
}
|
||||
|
||||
// This is blatant copy-paste. However, templating this will cause a problem,
|
||||
// since wstring can't be constructed/compared with char*. We'd need to
|
||||
// create auxiliary 'widen' routine to convert from char* into
|
||||
// needed string type, and that's more work.
|
||||
#if !defined(BOOST_NO_STD_WSTRING)
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void validate(any& v, const vector<wstring>& xs, bool*, int)
|
||||
{
|
||||
check_first_occurrence(v);
|
||||
wstring s(get_single_string(xs, true));
|
||||
|
||||
for (size_t i = 0; i < s.size(); ++i)
|
||||
s[i] = wchar_t(tolower(s[i]));
|
||||
|
||||
if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
|
||||
v = any(true);
|
||||
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
|
||||
v = any(false);
|
||||
else
|
||||
boost::throw_exception(invalid_bool_value(convert_value(s)));
|
||||
}
|
||||
#endif
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void validate(any& v, const vector<string>& xs, std::string*, int)
|
||||
{
|
||||
check_first_occurrence(v);
|
||||
v = any(get_single_string(xs));
|
||||
}
|
||||
|
||||
#if !defined(BOOST_NO_STD_WSTRING)
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void validate(any& v, const vector<wstring>& xs, std::string*, int)
|
||||
{
|
||||
check_first_occurrence(v);
|
||||
v = any(get_single_string(xs));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace validators {
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void check_first_occurrence(const boost::any& value)
|
||||
{
|
||||
if (!value.empty())
|
||||
boost::throw_exception(
|
||||
multiple_occurrences());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
invalid_option_value::
|
||||
invalid_option_value(const std::string& bad_value)
|
||||
: validation_error(validation_error::invalid_option_value)
|
||||
{
|
||||
set_substitute("value", bad_value);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
invalid_option_value::
|
||||
invalid_option_value(const std::wstring& bad_value)
|
||||
: validation_error(validation_error::invalid_option_value)
|
||||
{
|
||||
set_substitute("value", convert_value(bad_value));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
invalid_bool_value::
|
||||
invalid_bool_value(const std::string& bad_value)
|
||||
: validation_error(validation_error::invalid_bool_value)
|
||||
{
|
||||
set_substitute("value", bad_value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
error_with_option_name::error_with_option_name( const std::string& template_,
|
||||
const std::string& option_name,
|
||||
const std::string& original_token,
|
||||
int option_style) :
|
||||
error(template_),
|
||||
m_option_style(option_style),
|
||||
m_error_template(template_)
|
||||
{
|
||||
// parameter | placeholder | value
|
||||
// --------- | ----------- | -----
|
||||
set_substitute_default("canonical_option", "option '%canonical_option%'", "option");
|
||||
set_substitute_default("value", "argument ('%value%')", "argument");
|
||||
set_substitute_default("prefix", "%prefix%", "");
|
||||
m_substitutions["option"] = option_name;
|
||||
m_substitutions["original_token"] = original_token;
|
||||
}
|
||||
|
||||
|
||||
const char* error_with_option_name::what() const throw()
|
||||
{
|
||||
// will substitute tokens each time what is run()
|
||||
substitute_placeholders(m_error_template);
|
||||
|
||||
return m_message.c_str();
|
||||
}
|
||||
|
||||
void error_with_option_name::replace_token(const string& from, const string& to) const
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
std::size_t pos = m_message.find(from.c_str(), 0, from.length());
|
||||
// not found: all replaced
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
m_message.replace(pos, from.length(), to);
|
||||
}
|
||||
}
|
||||
|
||||
string error_with_option_name::get_canonical_option_prefix() const
|
||||
{
|
||||
switch (m_option_style)
|
||||
{
|
||||
case command_line_style::allow_dash_for_short:
|
||||
return "-";
|
||||
case command_line_style::allow_slash_for_short:
|
||||
return "/";
|
||||
case command_line_style::allow_long_disguise:
|
||||
return "-";
|
||||
case command_line_style::allow_long:
|
||||
return "--";
|
||||
case 0:
|
||||
return "";
|
||||
}
|
||||
throw std::logic_error("error_with_option_name::m_option_style can only be "
|
||||
"one of [0, allow_dash_for_short, allow_slash_for_short, "
|
||||
"allow_long_disguise or allow_long]");
|
||||
}
|
||||
|
||||
|
||||
string error_with_option_name::get_canonical_option_name() const
|
||||
{
|
||||
if (!m_substitutions.find("option")->second.length())
|
||||
return m_substitutions.find("original_token")->second;
|
||||
|
||||
string original_token = strip_prefixes(m_substitutions.find("original_token")->second);
|
||||
string option_name = strip_prefixes(m_substitutions.find("option")->second);
|
||||
|
||||
// For long options, use option name
|
||||
if (m_option_style == command_line_style::allow_long ||
|
||||
m_option_style == command_line_style::allow_long_disguise)
|
||||
return get_canonical_option_prefix() + option_name;
|
||||
|
||||
// For short options use first letter of original_token
|
||||
if (m_option_style && original_token.length())
|
||||
return get_canonical_option_prefix() + original_token[0];
|
||||
|
||||
// no prefix
|
||||
return option_name;
|
||||
}
|
||||
|
||||
|
||||
void error_with_option_name::substitute_placeholders(const string& error_template) const
|
||||
{
|
||||
m_message = error_template;
|
||||
std::map<std::string, std::string> substitutions(m_substitutions);
|
||||
substitutions["canonical_option"] = get_canonical_option_name();
|
||||
substitutions["prefix"] = get_canonical_option_prefix();
|
||||
|
||||
|
||||
//
|
||||
// replace placeholder with defaults if values are missing
|
||||
//
|
||||
for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
|
||||
iter != m_substitution_defaults.end(); ++iter)
|
||||
{
|
||||
// missing parameter: use default
|
||||
if (substitutions.count(iter->first) == 0 ||
|
||||
substitutions[iter->first].length() == 0)
|
||||
replace_token(iter->second.first, iter->second.second);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// replace placeholder with values
|
||||
// placeholder are denoted by surrounding '%'
|
||||
//
|
||||
for (map<string, string>::iterator iter = substitutions.begin();
|
||||
iter != substitutions.end(); ++iter)
|
||||
replace_token('%' + iter->first + '%', iter->second);
|
||||
}
|
||||
|
||||
|
||||
void ambiguous_option::substitute_placeholders(const string& original_error_template) const
|
||||
{
|
||||
// For short forms, all alternatives must be identical, by
|
||||
// definition, to the specified option, so we don't need to
|
||||
// display alternatives
|
||||
if (m_option_style == command_line_style::allow_dash_for_short ||
|
||||
m_option_style == command_line_style::allow_slash_for_short)
|
||||
{
|
||||
error_with_option_name::substitute_placeholders(original_error_template);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string error_template = original_error_template;
|
||||
// remove duplicates using std::set
|
||||
std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end());
|
||||
std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end());
|
||||
|
||||
error_template += " and matches ";
|
||||
// Being very cautious: should be > 1 alternative!
|
||||
if (alternatives_vec.size() > 1)
|
||||
{
|
||||
for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i)
|
||||
error_template += "'%prefix%" + alternatives_vec[i] + "', ";
|
||||
error_template += "and ";
|
||||
}
|
||||
|
||||
// there is a programming error if multiple options have the same name...
|
||||
if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
|
||||
error_template += "different versions of ";
|
||||
|
||||
error_template += "'%prefix%" + alternatives_vec.back() + "'";
|
||||
|
||||
|
||||
// use inherited logic
|
||||
error_with_option_name::substitute_placeholders(error_template);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
string
|
||||
validation_error::get_template(kind_t kind)
|
||||
{
|
||||
// Initially, store the message in 'const char*' variable,
|
||||
// to avoid conversion to std::string in all cases.
|
||||
const char* msg;
|
||||
switch(kind)
|
||||
{
|
||||
case invalid_bool_value:
|
||||
msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'";
|
||||
break;
|
||||
case invalid_option_value:
|
||||
msg = "the argument ('%value%') for option '%canonical_option%' is invalid";
|
||||
break;
|
||||
case multiple_values_not_allowed:
|
||||
msg = "option '%canonical_option%' only takes a single argument";
|
||||
break;
|
||||
case at_least_one_value_required:
|
||||
msg = "option '%canonical_option%' requires at least one argument";
|
||||
break;
|
||||
// currently unused
|
||||
case invalid_option:
|
||||
msg = "option '%canonical_option%' is not valid";
|
||||
break;
|
||||
default:
|
||||
msg = "unknown error";
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,249 @@
|
||||
// Copyright Vladimir Prus 2002-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)
|
||||
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/config.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/value_semantic.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
using namespace std;
|
||||
|
||||
// First, performs semantic actions for 'oa'.
|
||||
// Then, stores in 'm' all options that are defined in 'desc'.
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
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;
|
||||
|
||||
// We need to access map's operator[], not the overriden version
|
||||
// variables_map. Ehmm.. messy.
|
||||
std::map<std::string, variable_value>& m = xm;
|
||||
|
||||
std::set<std::string> new_final;
|
||||
|
||||
// Declared once, to please Intel in VC++ mode;
|
||||
unsigned i;
|
||||
|
||||
// Declared here so can be used to provide context for exceptions
|
||||
string option_name;
|
||||
string original_token;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// First, convert/store all given options
|
||||
for (i = 0; i < options.options.size(); ++i) {
|
||||
|
||||
option_name = options.options[i].string_key;
|
||||
original_token = options.options[i].original_tokens.size() ?
|
||||
options.options[i].original_tokens[0] :
|
||||
option_name;
|
||||
// Skip positional options without name
|
||||
if (option_name.empty())
|
||||
continue;
|
||||
|
||||
// Ignore unregistered option. The 'unregistered'
|
||||
// field can be true only if user has explicitly asked
|
||||
// to allow unregistered options. We can't store them
|
||||
// to variables map (lacking any information about paring),
|
||||
// so just ignore them.
|
||||
if (options.options[i].unregistered)
|
||||
continue;
|
||||
|
||||
// If option has final value, skip this assignment
|
||||
if (xm.m_final.count(option_name))
|
||||
continue;
|
||||
|
||||
string original_token = options.options[i].original_tokens.size() ?
|
||||
options.options[i].original_tokens[0] : "";
|
||||
const option_description& d = desc.find(option_name, false,
|
||||
false, false);
|
||||
|
||||
variable_value& v = m[option_name];
|
||||
if (v.defaulted()) {
|
||||
// Explicit assignment here erases defaulted value
|
||||
v = variable_value();
|
||||
}
|
||||
|
||||
d.semantic()->parse(v.value(), options.options[i].value, utf8);
|
||||
|
||||
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(option_name);
|
||||
}
|
||||
}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
catch(error_with_option_name& e)
|
||||
{
|
||||
// add context and rethrow
|
||||
e.add_context(option_name, original_token, options.m_options_prefix);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
xm.m_final.insert(new_final.begin(), new_final.end());
|
||||
|
||||
|
||||
|
||||
// Second, apply default values and store required options.
|
||||
const vector<shared_ptr<option_description> >& all = desc.options();
|
||||
for(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[key] = variable_value(def, true);
|
||||
m[key].m_value_semantic = d.semantic();
|
||||
}
|
||||
}
|
||||
|
||||
// add empty value if this is an required option
|
||||
if (d.semantic()->is_required()) {
|
||||
|
||||
// For option names specified in multiple ways, e.g. on the command line,
|
||||
// config file etc, the following precedence rules apply:
|
||||
// "--" > ("-" or "/") > ""
|
||||
// Precedence is set conveniently by a single call to length()
|
||||
string canonical_name = d.canonical_display_name(options.m_options_prefix);
|
||||
if (canonical_name.length() > xm.m_required[key].length())
|
||||
xm.m_required[key] = canonical_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void store(const wparsed_options& options, variables_map& m)
|
||||
{
|
||||
store(options.utf8_encoded_options, m, true);
|
||||
}
|
||||
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
void notify(variables_map& vm)
|
||||
{
|
||||
vm.notify();
|
||||
}
|
||||
|
||||
abstract_variables_map::abstract_variables_map()
|
||||
: m_next(0)
|
||||
{}
|
||||
|
||||
abstract_variables_map::
|
||||
abstract_variables_map(const abstract_variables_map* next)
|
||||
: m_next(next)
|
||||
{}
|
||||
|
||||
const variable_value&
|
||||
abstract_variables_map::operator[](const std::string& name) const
|
||||
{
|
||||
const variable_value& v = get(name);
|
||||
if (v.empty() && m_next)
|
||||
return (*m_next)[name];
|
||||
else if (v.defaulted() && m_next) {
|
||||
const variable_value& v2 = (*m_next)[name];
|
||||
if (!v2.empty() && !v2.defaulted())
|
||||
return v2;
|
||||
else return v;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
abstract_variables_map::next(abstract_variables_map* next)
|
||||
{
|
||||
m_next = next;
|
||||
}
|
||||
|
||||
variables_map::variables_map()
|
||||
{}
|
||||
|
||||
variables_map::variables_map(const abstract_variables_map* next)
|
||||
: abstract_variables_map(next)
|
||||
{}
|
||||
|
||||
void variables_map::clear()
|
||||
{
|
||||
std::map<std::string, variable_value>::clear();
|
||||
m_final.clear();
|
||||
m_required.clear();
|
||||
}
|
||||
|
||||
const variable_value&
|
||||
variables_map::get(const std::string& name) const
|
||||
{
|
||||
static variable_value empty;
|
||||
const_iterator i = this->find(name);
|
||||
if (i == this->end())
|
||||
return empty;
|
||||
else
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void
|
||||
variables_map::notify()
|
||||
{
|
||||
// This checks if all required options occur
|
||||
for (map<string, string>::const_iterator r = m_required.begin();
|
||||
r != m_required.end();
|
||||
++r)
|
||||
{
|
||||
const string& opt = r->first;
|
||||
const string& display_opt = r->second;
|
||||
map<string, variable_value>::const_iterator iter = find(opt);
|
||||
if (iter == end() || iter->second.empty())
|
||||
{
|
||||
boost::throw_exception(required_option(display_opt));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, run notify actions.
|
||||
for (map<string, variable_value>::iterator k = begin();
|
||||
k != end();
|
||||
++k)
|
||||
{
|
||||
/* Users might wish to use variables_map to store their own values
|
||||
that are not parsed, and therefore will not have value_semantics
|
||||
defined. Do not crash on such values. In multi-module programs,
|
||||
one module might add custom values, and the 'notify' function
|
||||
will be called after that, so we check that value_sematics is
|
||||
not NULL. See:
|
||||
https://svn.boost.org/trac/boost/ticket/2782
|
||||
*/
|
||||
if (k->second.m_value_semantic)
|
||||
k->second.m_value_semantic->notify(k->second.value());
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright Vladimir Prus 2002-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)
|
||||
|
||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <cctype>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace boost { namespace program_options {
|
||||
|
||||
// Take a command line string and splits in into tokens, according
|
||||
// to the rules windows command line processor uses.
|
||||
//
|
||||
// The rules are pretty funny, see
|
||||
// http://article.gmane.org/gmane.comp.lib.boost.user/3005
|
||||
// http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
|
||||
BOOST_PROGRAM_OPTIONS_DECL
|
||||
std::vector<std::string> split_winmain(const std::string& input)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::string::const_iterator i = input.begin(), e = input.end();
|
||||
for(;i != e; ++i)
|
||||
if (!isspace((unsigned char)*i))
|
||||
break;
|
||||
|
||||
if (i != e) {
|
||||
|
||||
std::string current;
|
||||
bool inside_quoted = false;
|
||||
bool empty_quote = false;
|
||||
int backslash_count = 0;
|
||||
|
||||
for(; i != e; ++i) {
|
||||
if (*i == '"') {
|
||||
// '"' preceded by even number (n) of backslashes generates
|
||||
// n/2 backslashes and is a quoted block delimiter
|
||||
if (backslash_count % 2 == 0) {
|
||||
current.append(backslash_count / 2, '\\');
|
||||
empty_quote = inside_quoted && current.empty();
|
||||
inside_quoted = !inside_quoted;
|
||||
// '"' preceded by odd number (n) of backslashes generates
|
||||
// (n-1)/2 backslashes and is literal quote.
|
||||
} else {
|
||||
current.append(backslash_count / 2, '\\');
|
||||
current += '"';
|
||||
}
|
||||
backslash_count = 0;
|
||||
} else if (*i == '\\') {
|
||||
++backslash_count;
|
||||
} else {
|
||||
// Not quote or backslash. All accumulated backslashes should be
|
||||
// added
|
||||
if (backslash_count) {
|
||||
current.append(backslash_count, '\\');
|
||||
backslash_count = 0;
|
||||
}
|
||||
if (isspace((unsigned char)*i) && !inside_quoted) {
|
||||
// Space outside quoted section terminate the current argument
|
||||
result.push_back(current);
|
||||
current.resize(0);
|
||||
empty_quote = false;
|
||||
for(;i != e && isspace((unsigned char)*i); ++i)
|
||||
;
|
||||
--i;
|
||||
} else {
|
||||
current += *i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have trailing backslashes, add them
|
||||
if (backslash_count)
|
||||
current.append(backslash_count, '\\');
|
||||
|
||||
// If we have non-empty 'current' or we're still in quoted
|
||||
// section (even if 'current' is empty), add the last token.
|
||||
if (!current.empty() || inside_quoted || empty_quote)
|
||||
result.push_back(current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
|
||||
split_winmain(const std::wstring& cmdline)
|
||||
{
|
||||
std::vector<std::wstring> result;
|
||||
std::vector<std::string> aux = split_winmain(to_internal(cmdline));
|
||||
for (size_t i = 0, e = aux.size(); i < e; ++i)
|
||||
result.push_back(from_utf8(aux[i]));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
}}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user