Opened 12 years ago

Closed 12 years ago

#4512 closed Bugs (fixed)

[program_options]: wrong option_description returned by options_descriptions::find_nothrow

Reported by: Soren Soe <soren.soe@…> Owned by: Vladimir Prus
Milestone: Boost 1.44.0 Component: program_options
Version: Boost 1.44.0 Severity: Problem
Keywords: Cc:

Description

In 1.43 my code started failing when an option_description has two options where option1 is "all" and option2 is "all-chroots".

I am using command_line_style::allow_guessing and at some point the command_line_parser calls options_description::find_nothrow("all",true,...) in options_description.cpp

It turns out find_nothrow has changed since the earlier version (1.38) which I am currently using. The old version immediately returned on a full_match:

...
// If we have a full patch, 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.
//
// For now, we don't check the situation when there are 
// two full matches. 

if (r == option_description::full_match)
{
    return m_options[i].get();
}
...

But in 1.43, the loop that iterates m_options sets a variable found to which ever match is found last, be that a full_match or an approximate_match:

...
if (r == option_description::full_match)
{                
    full_matches.push_back(m_options[i]->key(name));
} 
else 
{                        
    // FIXME: the use of 'key' here might not
    // be the best approach.
    approximate_matches.push_back(m_options[i]->key(name));
}

found = m_options[i];
...

Ultimately, the variable found is returned and in my case with the partial match even though there really was a full match.

I propose the following implementation of find_nothrow(...). The error checking is unchanged from 1.43 version, but a full_match, if any, will be returned as expected.

const option_description*
options_description::find_nothrow(const std::string& name, 
                                  bool approx,
                                  bool long_ignore_case,
                                  bool short_ignore_case) const
{
    int found = -1;
    vector<string> approximate_matches;
    vector<string> full_matches;
    
    // We use linear search because matching specified option
    // name with the declaraed 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));

            // Use this full match regardless of other matches found.
            found = i;
        } 
        else 
        {                        
            // FIXME: the use of 'key' here might not
            // be the best approach.
            approximate_matches.push_back(m_options[i]->key(name));

            // If this is the first match found the use it.  If         
            // a later full match is found that will be used instead.
            if (found == -1)
              found = i;
        }
    }
    if (full_matches.size() > 1) 
        boost::throw_exception(
            ambiguous_option(name, full_matches));
    
    if (full_matches.empty() && approximate_matches.size() > 1)
        boost::throw_exception(
            ambiguous_option(name, approximate_matches));

    return found>=0 
      ? m_options[found].get() 
      : 0;
}

I have attached my modified version of options_description.cpp

Attachments (1)

options_description.cpp (21.2 KB ) - added by Soren Soe <soren.soe@…> 12 years ago.

Download all attachments as: .zip

Change History (4)

by Soren Soe <soren.soe@…>, 12 years ago

Attachment: options_description.cpp added

comment:1 by Vladimir Prus, 12 years ago

Could you please try trunk or just published 1.44 beta? This issue should be fixed there.

comment:2 by Soren Soe <soren.soe@…>, 12 years ago

Yep, that fixes it. Thank you.

Soren

comment:3 by Vladimir Prus, 12 years ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.