reactive case_insensitive style for cmdline

[SVN r58273]
This commit is contained in:
Sascha Ochsenknecht
2009-12-10 20:25:53 +00:00
parent 970e377710
commit a5e45eda5f
7 changed files with 126 additions and 50 deletions

View File

@@ -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

View File

@@ -23,10 +23,17 @@ namespace boost { namespace program_options {
template<class charT>
class basic_option {
public:
basic_option() : position_key(-1), unregistered(false) {}
basic_option()
: position_key(-1)
, unregistered(false)
, case_insensitive(false)
{}
basic_option(const std::string& string_key,
const std::vector< std::string> &value)
: string_key(string_key), value(value), unregistered(false)
const std::vector< std::string> &value)
: string_key(string_key)
, value(value)
, unregistered(false)
, case_insensitive(false)
{}
/** String key of this option. Intentionally independent of the template
@@ -50,7 +57,10 @@ namespace boost { namespace program_options {
recovered from the "original_tokens" member.
*/
bool unregistered;
/** True if string_key has to be handled
case insensitive.
*/
bool case_insensitive;
};
typedef basic_option<char> option;
typedef basic_option<wchar_t> woption;

View File

@@ -83,7 +83,8 @@ namespace program_options {
/** Given 'option', specified in the input source,
return 'true' is 'option' specifies *this.
*/
match_result match(const std::string& option, bool approx) const;
match_result match(const std::string& option, bool approx,
bool long_ignore_case, bool short_ignore_case) const;
/** Return the key that should identify the option, in
particular in the variables_map class.
@@ -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;

View File

@@ -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] == '/')

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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()