diff --git a/include/boost/program_options/cmdline.hpp b/include/boost/program_options/cmdline.hpp
index 0e8fea2..8705e60 100644
--- a/include/boost/program_options/cmdline.hpp
+++ b/include/boost/program_options/cmdline.hpp
@@ -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 -foo=10
*/
- 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
diff --git a/include/boost/program_options/option.hpp b/include/boost/program_options/option.hpp
index 635f708..557c692 100644
--- a/include/boost/program_options/option.hpp
+++ b/include/boost/program_options/option.hpp
@@ -23,10 +23,17 @@ namespace boost { namespace program_options {
template
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 option;
typedef basic_option woption;
diff --git a/include/boost/program_options/options_description.hpp b/include/boost/program_options/options_description.hpp
index 2770d41..0486f02 100644
--- a/include/boost/program_options/options_description.hpp
+++ b/include/boost/program_options/options_description.hpp
@@ -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 >& options() const;
diff --git a/src/cmdline.cpp b/src/cmdline.cpp
index 8abc7eb..be31385 100644
--- a/src/cmdline.cpp
+++ b/src/cmdline.cpp
@@ -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] == '/')
diff --git a/src/options_description.cpp b/src/options_description.cpp
index a9acacf..bfd113d 100644
--- a/src/options_description.cpp
+++ b/src/options_description.cpp
@@ -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 found;
vector 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;
diff --git a/src/variables_map.cpp b/src/variables_map.cpp
index 449d23c..29b1de9 100644
--- a/src/variables_map.cpp
+++ b/src/variables_map.cpp
@@ -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()) {
diff --git a/test/cmdline_test.cpp b/test/cmdline_test.cpp
index c6ab492..7c26db2 100644
--- a/test/cmdline_test.cpp
+++ b/test/cmdline_test.cpp
@@ -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()