Subject: [Boost-bugs] [Boost C++ Libraries] #4469: Bugs in program_options implementation
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2010-07-26 15:34:10
#4469: Bugs in program_options implementation
----------------------------------+-----------------------------------------
Reporter: lodos@⦠| Owner: vladimir_prus
Type: Bugs | Status: new
Milestone: Boost 1.44.0 | Component: program_options
Version: Boost 1.44.0 | Severity: Problem
Keywords: |
----------------------------------+-----------------------------------------
The function options_description::find_nothrow throws an exception when
there are at least 2 exact matches. Since this function is in the public
interface this could be considered a bug (it must not throw). Please note
that this function is used several times within the implementation without
try clauses. The exception thrown might be being used by callers of the
method find, so you cannot just remove it. It is best to implement find
and find_nothrow as follows:
const option_description&
options_description::find(const std::string& name,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
shared_ptr<option_description> found;
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];
}
else
{
// FIXME: the use of 'key' here might not
// be the best approach.
approximate_matches.push_back(m_options[i]->key(name));
if (!found)
found = m_options[i];
}
}
if (full_matches.size() > 1)
boost::throw_exception(
ambiguous_option(name, 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(name, approximate_matches));
if (!found)
boost::throw_exception(unknown_option(name));
return *found;
}
const option_description*
options_description::find_nothrow(const std::string& name,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
try {
const option_description& d = find(name, approx,
long_ignore_case,
short_ignore_case);
return &d;
}
catch(const std::exception&) {
return NULL;
}
}
This modification uncovers a bug in function cmdline::finish_option
(cmdline.cpp) where find_nothrow is being called assuming that no
exception will be thrown. The first part of this function might be
modified to:
void
cmdline::finish_option(option& opt,
vector<string>& other_tokens,
const vector<style_parser>& style_parsers)
{
if (opt.string_key.empty())
return;
const option_description* xd;
try {
// First check that the option is valid, and get its
description.
xd = &m_desc->find(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
}
catch(const std::exception&) {
if (m_allow_unregistered) {
opt.unregistered = true;
return;
}
throw;
}
const option_description& d = *xd;
// Canonize the name
opt.string_key = d.key(opt.string_key);
........
}
Now it can throw the correct exception thrown by the find method. This is
the only way callers could know the correct exception. It was apparently
working before because find_nothrow was throwing exceptions not captured
within cmdline::finish_option. You may chose to eliminate the xd variable
altogether and initialize and use d within the try clause. What is
important is to call find instead of find_nothrow.
The following test would have detected these problems and the one solved
in https://svn.boost.org/trac/boost/ticket/4159:
// Multiple arguments
test_case test_cases2[] = {
{"--fname file --fname2 file2", s_success, "fname: file fname2:
file2"},
{"--fnam file --fnam file2", s_ambiguous_option, ""},
{"--fnam file --fname2 file2", s_ambiguous_option, ""},
{"--fname2 file2 --fnam file", s_ambiguous_option, ""},
{0, 0, 0}
};
test_cmdline("fname fname2", style, test_cases2);
This test may be added at the end of the test_guessing function in
cmdline_test.cpp.
Thank you.
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/4469> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:03 UTC