Skip to content

Commit a5e45ed

Browse files
author
Sascha Ochsenknecht
committed
reactive case_insensitive style for cmdline
[SVN r58273]
1 parent 970e377 commit a5e45ed

File tree

7 files changed

+125
-49
lines changed

7 files changed

+125
-49
lines changed

include/boost/program_options/cmdline.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,19 @@ namespace boost { namespace program_options { namespace command_line_style {
6262
long option name if guessing is in effect.
6363
*/
6464
allow_guessing = allow_sticky << 1,
65-
/** Ignore the difference in case for options.
66-
@todo Should this apply to long options only?
65+
/** Ignore the difference in case for long options.
6766
*/
68-
case_insensitive = allow_guessing << 1,
67+
long_case_insensitive = allow_guessing << 1,
68+
/** Ignore the difference in case for short options.
69+
*/
70+
short_case_insensitive = long_case_insensitive << 1,
71+
/** Ignore the difference in case for all options.
72+
*/
73+
case_insensitive = (long_case_insensitive | short_case_insensitive),
6974
/** Allow long options with single option starting character,
7075
e.g <tt>-foo=10</tt>
7176
*/
72-
allow_long_disguise = case_insensitive << 1,
77+
allow_long_disguise = short_case_insensitive << 1,
7378
/** The more-or-less traditional unix style. */
7479
unix_style = (allow_short | short_allow_adjacent | short_allow_next
7580
| allow_long | long_allow_adjacent | long_allow_next

include/boost/program_options/option.hpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ namespace boost { namespace program_options {
2323
template<class charT>
2424
class basic_option {
2525
public:
26-
basic_option() : position_key(-1), unregistered(false) {}
26+
basic_option()
27+
: position_key(-1)
28+
, unregistered(false)
29+
, case_insensitive(false)
30+
{}
2731
basic_option(const std::string& string_key,
28-
const std::vector< std::string> &value)
29-
: string_key(string_key), value(value), unregistered(false)
32+
const std::vector< std::string> &value)
33+
: string_key(string_key)
34+
, value(value)
35+
, unregistered(false)
36+
, case_insensitive(false)
3037
{}
3138

3239
/** String key of this option. Intentionally independent of the template
@@ -50,7 +57,10 @@ namespace boost { namespace program_options {
5057
recovered from the "original_tokens" member.
5158
*/
5259
bool unregistered;
53-
60+
/** True if string_key has to be handled
61+
case insensitive.
62+
*/
63+
bool case_insensitive;
5464
};
5565
typedef basic_option<char> option;
5666
typedef basic_option<wchar_t> woption;

include/boost/program_options/options_description.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ namespace program_options {
8383
/** Given 'option', specified in the input source,
8484
return 'true' is 'option' specifies *this.
8585
*/
86-
match_result match(const std::string& option, bool approx) const;
86+
match_result match(const std::string& option, bool approx,
87+
bool long_ignore_case, bool short_ignore_case) const;
8788

8889
/** Return the key that should identify the option, in
8990
particular in the variables_map class.
@@ -191,11 +192,15 @@ namespace program_options {
191192
*/
192193
options_description_easy_init add_options();
193194

194-
const option_description& find(const std::string& name, bool approx)
195-
const;
195+
const option_description& find(const std::string& name,
196+
bool approx,
197+
bool long_ignore_case = false,
198+
bool short_ignore_case = false) const;
196199

197200
const option_description* find_nothrow(const std::string& name,
198-
bool approx) const;
201+
bool approx,
202+
bool long_ignore_case = false,
203+
bool short_ignore_case = false) const;
199204

200205

201206
const std::vector< shared_ptr<option_description> >& options() const;

src/cmdline.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ namespace boost { namespace program_options { namespace detail {
172172
// Need to check that if guessing and long disguise are enabled
173173
// -f will mean the same as -foo
174174
}
175+
176+
bool
177+
cmdline::is_style_active(style_t style) const
178+
{
179+
return ((m_style & style) ? true : false);
180+
}
175181

176182
void
177183
cmdline::set_options_description(const options_description& desc)
@@ -284,7 +290,9 @@ namespace boost { namespace program_options { namespace detail {
284290

285291
const option_description* xd =
286292
m_desc->find_nothrow(opt.string_key,
287-
(m_style & allow_guessing));
293+
is_style_active(allow_guessing),
294+
is_style_active(long_case_insensitive),
295+
is_style_active(short_case_insensitive));
288296
if (!xd)
289297
continue;
290298

@@ -348,6 +356,21 @@ namespace boost { namespace program_options { namespace detail {
348356
}
349357
}
350358
}
359+
360+
// set case sensitive flag
361+
for (unsigned i = 0; i < result.size(); ++i) {
362+
if (result[i].string_key.size() > 2 ||
363+
(result[i].string_key.size() > 1 && result[i].string_key[0] != '-'))
364+
{
365+
// it is a long option
366+
result[i].case_insensitive = is_style_active(long_case_insensitive);
367+
}
368+
else
369+
{
370+
// it is a short option
371+
result[i].case_insensitive = is_style_active(short_case_insensitive);
372+
}
373+
}
351374

352375
return result;
353376
}
@@ -361,9 +384,10 @@ namespace boost { namespace program_options { namespace detail {
361384
return;
362385

363386
// First check that the option is valid, and get its description.
364-
// TODO: case-sensitivity.
365387
const option_description* xd = m_desc->find_nothrow(opt.string_key,
366-
(m_style & allow_guessing) ? true : false);
388+
is_style_active(allow_guessing),
389+
is_style_active(long_case_insensitive),
390+
is_style_active(short_case_insensitive));
367391

368392
if (!xd)
369393
{
@@ -427,7 +451,9 @@ namespace boost { namespace program_options { namespace detail {
427451
if (!followed_option.empty())
428452
{
429453
const option_description* od = m_desc->find_nothrow(other_tokens[0],
430-
(m_style & allow_guessing) ? true : false);
454+
is_style_active(allow_guessing),
455+
is_style_active(long_case_insensitive),
456+
is_style_active(short_case_insensitive));
431457
if (od)
432458
boost::throw_exception(invalid_command_line_syntax(opt.string_key,
433459
invalid_command_line_syntax::missing_parameter));
@@ -461,7 +487,7 @@ namespace boost { namespace program_options { namespace detail {
461487
adjacent = tok.substr(p+1);
462488
if (adjacent.empty())
463489
boost::throw_exception( invalid_command_line_syntax(name,
464-
invalid_command_line_syntax::empty_adjacent_parameter));
490+
invalid_command_line_syntax::empty_adjacent_parameter) );
465491
}
466492
else
467493
{
@@ -498,7 +524,8 @@ namespace boost { namespace program_options { namespace detail {
498524
// option.
499525
for(;;) {
500526
const option_description* d
501-
= m_desc->find_nothrow(name, false);
527+
= m_desc->find_nothrow(name, false, false,
528+
is_style_active(short_case_insensitive));
502529

503530
// FIXME: check for 'allow_sticky'.
504531
if (d && (m_style & allow_sticky) &&
@@ -563,7 +590,9 @@ namespace boost { namespace program_options { namespace detail {
563590
((m_style & allow_slash_for_short) && tok[0] == '/')))
564591
{
565592
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
566-
(m_style & allow_guessing) ? true : false))
593+
is_style_active(allow_guessing),
594+
is_style_active(long_case_insensitive),
595+
is_style_active(short_case_insensitive)))
567596
{
568597
args[0].insert(0, "-");
569598
if (args[0][1] == '/')

src/options_description.cpp

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,22 @@ using namespace std;
2828

2929
namespace boost { namespace program_options {
3030

31+
namespace {
32+
33+
template< class charT >
34+
std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
35+
{
36+
std::basic_string< charT > result;
37+
for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
38+
{
39+
result.append(1, static_cast< charT >(std::tolower(str[i])));
40+
}
41+
return result;
42+
}
43+
44+
} // unnamed namespace
45+
46+
3147
option_description::option_description()
3248
{
3349
}
@@ -55,39 +71,51 @@ namespace boost { namespace program_options {
5571
}
5672

5773
option_description::match_result
58-
option_description::match(const std::string& option, bool approx) const
74+
option_description::match(const std::string& option,
75+
bool approx,
76+
bool long_ignore_case,
77+
bool short_ignore_case) const
5978
{
60-
match_result result = no_match;
61-
if (!m_long_name.empty()) {
79+
match_result result = no_match;
80+
81+
std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
82+
83+
if (!local_long_name.empty()) {
84+
85+
std::string local_option = (long_ignore_case ? tolower_(option) : option);
6286

63-
if (*m_long_name.rbegin() == '*')
87+
if (*local_long_name.rbegin() == '*')
6488
{
6589
// The name ends with '*'. Any specified name with the given
6690
// prefix is OK.
67-
if (option.find(m_long_name.substr(0, m_long_name.length()-1))
91+
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
6892
== 0)
6993
result = approximate_match;
7094
}
7195

72-
if (approx)
96+
if (local_long_name == local_option)
97+
{
98+
result = full_match;
99+
}
100+
else if (approx)
73101
{
74-
if (m_long_name.find(option) == 0)
102+
if (local_long_name.find(local_option) == 0)
75103
{
76-
if (m_long_name == option)
77-
result = full_match;
78-
else
79-
result = approximate_match;
104+
result = approximate_match;
80105
}
81106
}
82-
else
107+
}
108+
109+
if (result != full_match)
110+
{
111+
std::string local_option(short_ignore_case ? tolower_(option) : option);
112+
std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
113+
114+
if (local_short_name == local_option)
83115
{
84-
if (m_long_name == option)
85-
result = full_match;
116+
result = full_match;
86117
}
87118
}
88-
89-
if (m_short_name == option)
90-
result = full_match;
91119

92120
return result;
93121
}
@@ -253,9 +281,13 @@ namespace boost { namespace program_options {
253281
}
254282

255283
const option_description&
256-
options_description::find(const std::string& name, bool approx) const
284+
options_description::find(const std::string& name,
285+
bool approx,
286+
bool long_ignore_case,
287+
bool short_ignore_case) const
257288
{
258-
const option_description* d = find_nothrow(name, approx);
289+
const option_description* d = find_nothrow(name, approx,
290+
long_ignore_case, short_ignore_case);
259291
if (!d)
260292
boost::throw_exception(unknown_option(name));
261293
return *d;
@@ -269,7 +301,9 @@ namespace boost { namespace program_options {
269301

270302
const option_description*
271303
options_description::find_nothrow(const std::string& name,
272-
bool approx) const
304+
bool approx,
305+
bool long_ignore_case,
306+
bool short_ignore_case) const
273307
{
274308
shared_ptr<option_description> found;
275309
vector<string> approximate_matches;
@@ -281,7 +315,7 @@ namespace boost { namespace program_options {
281315
for(unsigned i = 0; i < m_options.size(); ++i)
282316
{
283317
option_description::match_result r =
284-
m_options[i]->match(name, approx);
318+
m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
285319

286320
if (r == option_description::no_match)
287321
continue;

src/variables_map.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ namespace boost { namespace program_options {
5858
if (xm.m_final.count(name))
5959
continue;
6060

61-
// Ignore options which are not described
62-
//TODO: consider this.
63-
//if (desc.count(name) == 0)
64-
// continue;
65-
66-
const option_description& d = desc.find(name, false);
61+
const option_description& d = desc.find(name, false,
62+
false, false);
6763

6864
variable_value& v = m[name];
6965
if (v.defaulted()) {

test/cmdline_test.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,6 @@ void test_long_options()
218218
allow_long | long_allow_adjacent
219219
| long_allow_next | case_insensitive);
220220

221-
// FIXME: restore
222-
#if 0
223221
// Test case insensitive style.
224222
// Note that option names are normalized to lower case.
225223
test_case test_cases4[] = {
@@ -231,7 +229,6 @@ void test_long_options()
231229
{0, 0, 0}
232230
};
233231
test_cmdline("foo bar= baz? Giz", style, test_cases4);
234-
#endif
235232
}
236233

237234
void test_short_options()

0 commit comments

Comments
 (0)