mirror of
https://github.com/boostorg/program_options.git
synced 2026-01-19 04:22:15 +00:00
reactive case_insensitive style for cmdline
[SVN r58273]
This commit is contained in:
@@ -62,14 +62,19 @@ namespace boost { namespace program_options { namespace command_line_style {
|
||||
long option name if guessing is in effect.
|
||||
*/
|
||||
allow_guessing = allow_sticky << 1,
|
||||
/** Ignore the difference in case for options.
|
||||
@todo Should this apply to long options only?
|
||||
/** Ignore the difference in case for long options.
|
||||
*/
|
||||
case_insensitive = allow_guessing << 1,
|
||||
long_case_insensitive = allow_guessing << 1,
|
||||
/** Ignore the difference in case for short options.
|
||||
*/
|
||||
short_case_insensitive = long_case_insensitive << 1,
|
||||
/** Ignore the difference in case for all options.
|
||||
*/
|
||||
case_insensitive = (long_case_insensitive | short_case_insensitive),
|
||||
/** Allow long options with single option starting character,
|
||||
e.g <tt>-foo=10</tt>
|
||||
*/
|
||||
allow_long_disguise = case_insensitive << 1,
|
||||
allow_long_disguise = short_case_insensitive << 1,
|
||||
/** The more-or-less traditional unix style. */
|
||||
unix_style = (allow_short | short_allow_adjacent | short_allow_next
|
||||
| allow_long | long_allow_adjacent | long_allow_next
|
||||
|
||||
@@ -23,10 +23,17 @@ namespace boost { namespace program_options {
|
||||
template<class charT>
|
||||
class basic_option {
|
||||
public:
|
||||
basic_option() : position_key(-1), unregistered(false) {}
|
||||
basic_option()
|
||||
: position_key(-1)
|
||||
, unregistered(false)
|
||||
, case_insensitive(false)
|
||||
{}
|
||||
basic_option(const std::string& string_key,
|
||||
const std::vector< std::string> &value)
|
||||
: string_key(string_key), value(value), unregistered(false)
|
||||
const std::vector< std::string> &value)
|
||||
: string_key(string_key)
|
||||
, value(value)
|
||||
, unregistered(false)
|
||||
, case_insensitive(false)
|
||||
{}
|
||||
|
||||
/** String key of this option. Intentionally independent of the template
|
||||
@@ -50,7 +57,10 @@ namespace boost { namespace program_options {
|
||||
recovered from the "original_tokens" member.
|
||||
*/
|
||||
bool unregistered;
|
||||
|
||||
/** True if string_key has to be handled
|
||||
case insensitive.
|
||||
*/
|
||||
bool case_insensitive;
|
||||
};
|
||||
typedef basic_option<char> option;
|
||||
typedef basic_option<wchar_t> woption;
|
||||
|
||||
@@ -83,7 +83,8 @@ namespace program_options {
|
||||
/** Given 'option', specified in the input source,
|
||||
return 'true' is 'option' specifies *this.
|
||||
*/
|
||||
match_result match(const std::string& option, bool approx) const;
|
||||
match_result match(const std::string& option, bool approx,
|
||||
bool long_ignore_case, bool short_ignore_case) const;
|
||||
|
||||
/** Return the key that should identify the option, in
|
||||
particular in the variables_map class.
|
||||
@@ -191,11 +192,15 @@ namespace program_options {
|
||||
*/
|
||||
options_description_easy_init add_options();
|
||||
|
||||
const option_description& find(const std::string& name, bool approx)
|
||||
const;
|
||||
const option_description& find(const std::string& name,
|
||||
bool approx,
|
||||
bool long_ignore_case = false,
|
||||
bool short_ignore_case = false) const;
|
||||
|
||||
const option_description* find_nothrow(const std::string& name,
|
||||
bool approx) const;
|
||||
bool approx,
|
||||
bool long_ignore_case = false,
|
||||
bool short_ignore_case = false) const;
|
||||
|
||||
|
||||
const std::vector< shared_ptr<option_description> >& options() const;
|
||||
|
||||
@@ -172,6 +172,12 @@ namespace boost { namespace program_options { namespace detail {
|
||||
// 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)
|
||||
@@ -284,7 +290,9 @@ namespace boost { namespace program_options { namespace detail {
|
||||
|
||||
const option_description* xd =
|
||||
m_desc->find_nothrow(opt.string_key,
|
||||
(m_style & allow_guessing));
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
if (!xd)
|
||||
continue;
|
||||
|
||||
@@ -348,6 +356,21 @@ namespace boost { namespace program_options { namespace detail {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -361,9 +384,10 @@ namespace boost { namespace program_options { namespace detail {
|
||||
return;
|
||||
|
||||
// First check that the option is valid, and get its description.
|
||||
// TODO: case-sensitivity.
|
||||
const option_description* xd = m_desc->find_nothrow(opt.string_key,
|
||||
(m_style & allow_guessing) ? true : false);
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
|
||||
if (!xd)
|
||||
{
|
||||
@@ -427,7 +451,9 @@ namespace boost { namespace program_options { namespace detail {
|
||||
if (!followed_option.empty())
|
||||
{
|
||||
const option_description* od = m_desc->find_nothrow(other_tokens[0],
|
||||
(m_style & allow_guessing) ? true : false);
|
||||
is_style_active(allow_guessing),
|
||||
is_style_active(long_case_insensitive),
|
||||
is_style_active(short_case_insensitive));
|
||||
if (od)
|
||||
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
|
||||
invalid_command_line_syntax::missing_parameter));
|
||||
@@ -461,7 +487,7 @@ namespace boost { namespace program_options { namespace detail {
|
||||
adjacent = tok.substr(p+1);
|
||||
if (adjacent.empty())
|
||||
boost::throw_exception( invalid_command_line_syntax(name,
|
||||
invalid_command_line_syntax::empty_adjacent_parameter));
|
||||
invalid_command_line_syntax::empty_adjacent_parameter) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -498,7 +524,8 @@ namespace boost { namespace program_options { namespace detail {
|
||||
// option.
|
||||
for(;;) {
|
||||
const option_description* d
|
||||
= m_desc->find_nothrow(name, false);
|
||||
= m_desc->find_nothrow(name, false, false,
|
||||
is_style_active(short_case_insensitive));
|
||||
|
||||
// FIXME: check for 'allow_sticky'.
|
||||
if (d && (m_style & allow_sticky) &&
|
||||
@@ -563,7 +590,9 @@ namespace boost { namespace program_options { namespace detail {
|
||||
((m_style & allow_slash_for_short) && tok[0] == '/')))
|
||||
{
|
||||
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
|
||||
(m_style & allow_guessing) ? true : false))
|
||||
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] == '/')
|
||||
|
||||
@@ -28,6 +28,22 @@ 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()
|
||||
{
|
||||
}
|
||||
@@ -55,39 +71,51 @@ namespace boost { namespace program_options {
|
||||
}
|
||||
|
||||
option_description::match_result
|
||||
option_description::match(const std::string& option, bool approx) const
|
||||
option_description::match(const std::string& option,
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
match_result result = no_match;
|
||||
if (!m_long_name.empty()) {
|
||||
match_result result = no_match;
|
||||
|
||||
std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
|
||||
|
||||
if (!local_long_name.empty()) {
|
||||
|
||||
std::string local_option = (long_ignore_case ? tolower_(option) : option);
|
||||
|
||||
if (*m_long_name.rbegin() == '*')
|
||||
if (*local_long_name.rbegin() == '*')
|
||||
{
|
||||
// The name ends with '*'. Any specified name with the given
|
||||
// prefix is OK.
|
||||
if (option.find(m_long_name.substr(0, m_long_name.length()-1))
|
||||
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
|
||||
== 0)
|
||||
result = approximate_match;
|
||||
}
|
||||
|
||||
if (approx)
|
||||
if (local_long_name == local_option)
|
||||
{
|
||||
if (m_long_name.find(option) == 0)
|
||||
{
|
||||
if (m_long_name == option)
|
||||
result = full_match;
|
||||
else
|
||||
result = approximate_match;
|
||||
}
|
||||
result = full_match;
|
||||
}
|
||||
else
|
||||
else if (approx)
|
||||
{
|
||||
if (m_long_name == option)
|
||||
result = full_match;
|
||||
if (local_long_name.find(local_option) == 0)
|
||||
{
|
||||
result = approximate_match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_short_name == option)
|
||||
result = full_match;
|
||||
if (result != full_match)
|
||||
{
|
||||
std::string local_option(short_ignore_case ? tolower_(option) : option);
|
||||
std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
|
||||
|
||||
if (local_short_name == local_option)
|
||||
{
|
||||
result = full_match;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -253,9 +281,13 @@ namespace boost { namespace program_options {
|
||||
}
|
||||
|
||||
const option_description&
|
||||
options_description::find(const std::string& name, bool approx) const
|
||||
options_description::find(const std::string& name,
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
const option_description* d = find_nothrow(name, approx);
|
||||
const option_description* d = find_nothrow(name, approx,
|
||||
long_ignore_case, short_ignore_case);
|
||||
if (!d)
|
||||
boost::throw_exception(unknown_option(name));
|
||||
return *d;
|
||||
@@ -269,7 +301,9 @@ namespace boost { namespace program_options {
|
||||
|
||||
const option_description*
|
||||
options_description::find_nothrow(const std::string& name,
|
||||
bool approx) const
|
||||
bool approx,
|
||||
bool long_ignore_case,
|
||||
bool short_ignore_case) const
|
||||
{
|
||||
shared_ptr<option_description> found;
|
||||
vector<string> approximate_matches;
|
||||
@@ -281,7 +315,7 @@ namespace boost { namespace program_options {
|
||||
for(unsigned i = 0; i < m_options.size(); ++i)
|
||||
{
|
||||
option_description::match_result r =
|
||||
m_options[i]->match(name, approx);
|
||||
m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
|
||||
|
||||
if (r == option_description::no_match)
|
||||
continue;
|
||||
|
||||
@@ -58,12 +58,8 @@ namespace boost { namespace program_options {
|
||||
if (xm.m_final.count(name))
|
||||
continue;
|
||||
|
||||
// Ignore options which are not described
|
||||
//TODO: consider this.
|
||||
//if (desc.count(name) == 0)
|
||||
// continue;
|
||||
|
||||
const option_description& d = desc.find(name, false);
|
||||
const option_description& d = desc.find(name, false,
|
||||
false, false);
|
||||
|
||||
variable_value& v = m[name];
|
||||
if (v.defaulted()) {
|
||||
|
||||
@@ -218,8 +218,6 @@ void test_long_options()
|
||||
allow_long | long_allow_adjacent
|
||||
| long_allow_next | case_insensitive);
|
||||
|
||||
// FIXME: restore
|
||||
#if 0
|
||||
// Test case insensitive style.
|
||||
// Note that option names are normalized to lower case.
|
||||
test_case test_cases4[] = {
|
||||
@@ -231,7 +229,6 @@ void test_long_options()
|
||||
{0, 0, 0}
|
||||
};
|
||||
test_cmdline("foo bar= baz? Giz", style, test_cases4);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_short_options()
|
||||
|
||||
Reference in New Issue
Block a user