Skip to content

Commit 4cda95d

Browse files
committed
fixed issue due to not using *longest* prefix match in joined parameter/value sequences
1 parent 380c117 commit 4cda95d

File tree

2 files changed

+109
-10
lines changed

2 files changed

+109
-10
lines changed

include/clipp.h

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
* ___ _ _ ___ ___
33
* | _|| | | | | _ \ _ \ CLIPP - command line interfaces for modern C++
4-
* | |_ | |_ | | | _/ _/ version 1.2.2
4+
* | |_ | |_ | | | _/ _/ version 1.2.3
55
* |___||___||_| |_| |_| https://github.com/muellan/clipp
66
*
77
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -4366,11 +4366,16 @@ struct select_values {
43664366
*****************************************************************************/
43674367
class match_t {
43684368
public:
4369+
using size_type = arg_string::size_type;
4370+
43694371
match_t() = default;
4372+
43704373
match_t(arg_string s, scoped_dfs_traverser p):
43714374
str_{std::move(s)}, pos_{std::move(p)}
43724375
{}
43734376

4377+
size_type length() const noexcept { return str_.size(); }
4378+
43744379
const arg_string& str() const noexcept { return str_; }
43754380
const scoped_dfs_traverser& pos() const noexcept { return pos_; }
43764381

@@ -4420,28 +4425,30 @@ full_match(scoped_dfs_traverser pos, const arg_string& arg,
44204425
*****************************************************************************/
44214426
template<class ParamSelector>
44224427
match_t
4423-
prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4424-
const ParamSelector& select)
4428+
longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4429+
const ParamSelector& select)
44254430
{
4431+
match_t longest;
4432+
44264433
while(pos) {
44274434
if(pos->is_param()) {
44284435
const auto& param = pos->as_param();
44294436
if(select(param)) {
4430-
const auto match = param.match(arg);
4437+
auto match = param.match(arg);
44314438
if(match.prefix()) {
44324439
if(match.length() == arg.size()) {
44334440
return match_t{arg, std::move(pos)};
44344441
}
4435-
else {
4436-
return match_t{arg.substr(match.at(), match.length()),
4437-
std::move(pos)};
4442+
else if(match.length() > longest.length()) {
4443+
longest = match_t{arg.substr(match.at(), match.length()),
4444+
pos};
44384445
}
44394446
}
44404447
}
44414448
}
44424449
++pos;
44434450
}
4444-
return match_t{};
4451+
return longest;
44454452
}
44464453

44474454

@@ -4747,7 +4754,7 @@ class parser
47474754
bool try_match_joined_sequence(arg_string arg,
47484755
const ParamSelector& acceptFirst)
47494756
{
4750-
auto fstMatch = detail::prefix_match(pos_, arg, acceptFirst);
4757+
auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst);
47514758

47524759
if(!fstMatch) return false;
47534760

@@ -4824,7 +4831,7 @@ class parser
48244831
std::vector<match_t> matches;
48254832

48264833
while(!arg.empty()) {
4827-
auto match = detail::prefix_match(parse.pos_, arg, select);
4834+
auto match = detail::longest_prefix_match(parse.pos_, arg, select);
48284835

48294836
if(!match) return false;
48304837

test/prefix_test.cpp

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*****************************************************************************
2+
*
3+
* CLIPP - command line interfaces for modern C++
4+
*
5+
* released under MIT license
6+
*
7+
* (c) 2017-2019 André Müller; [email protected]
8+
*
9+
*****************************************************************************/
10+
11+
#include "testing.h"
12+
13+
14+
//-------------------------------------------------------------------
15+
struct active {
16+
active() = default;
17+
active(bool a_, int i_): a{a_}, i{i_} {}
18+
bool a = false;
19+
int i = 0;
20+
21+
friend bool operator == (const active& x, const active& y) noexcept {
22+
return (x.a == y.a && x.i == y.i);
23+
}
24+
};
25+
26+
27+
//-------------------------------------------------------------------
28+
static void
29+
test(int lineNo,
30+
const std::initializer_list<const char*> args,
31+
const active& matches )
32+
{
33+
using namespace clipp;
34+
active m;
35+
36+
auto cli = (
37+
option("-a").set(m.a),
38+
option("-ab", "-a-b", "-a-b=") & value("i", m.i)
39+
);
40+
41+
run_wrapped_variants({ __FILE__, lineNo }, args, cli,
42+
[&]{ m = active{}; },
43+
[&]{ return m == matches; });
44+
}
45+
46+
47+
//-------------------------------------------------------------------
48+
int main()
49+
{
50+
using std::string;
51+
52+
try {
53+
test(__LINE__, {""}, active{0,0});
54+
test(__LINE__, {"-a"}, active{1,0});
55+
56+
test(__LINE__, {"-ab"}, active{0,0});
57+
test(__LINE__, {"-a-b"}, active{0,0});
58+
test(__LINE__, {"-a-b="}, active{0,0});
59+
60+
test(__LINE__, {"-ab", "2"}, active{0,2});
61+
test(__LINE__, {"-a-b", "3"}, active{0,3});
62+
test(__LINE__, {"-a-b=", "4"}, active{0,4});
63+
64+
test(__LINE__, {"-ab2" }, active{0,2});
65+
test(__LINE__, {"-a-b3" }, active{0,3});
66+
test(__LINE__, {"-a-b=4"}, active{0,4});
67+
68+
test(__LINE__, {"-a", "-ab", "2"}, active{1,2});
69+
test(__LINE__, {"-a", "-a-b", "3"}, active{1,3});
70+
test(__LINE__, {"-a", "-a-b=", "4"}, active{1,4});
71+
72+
test(__LINE__, {"-a", "-ab2" }, active{1,2});
73+
test(__LINE__, {"-a", "-a-b3" }, active{1,3});
74+
test(__LINE__, {"-a", "-a-b=4"}, active{1,4});
75+
76+
test(__LINE__, {"-ab", "2", "-a"}, active{1,2});
77+
test(__LINE__, {"-a-b", "3", "-a"}, active{1,3});
78+
test(__LINE__, {"-a-b=", "4", "-a"}, active{1,4});
79+
80+
test(__LINE__, {"-a", "-ab" }, active{1,0});
81+
test(__LINE__, {"-a", "-a-b" }, active{1,0});
82+
test(__LINE__, {"-a", "-a-b="}, active{1,0});
83+
84+
test(__LINE__, {"-ab", "-a"}, active{1,0});
85+
test(__LINE__, {"-a-b", "-a"}, active{1,0});
86+
test(__LINE__, {"-a-b=", "-a"}, active{1,0});
87+
}
88+
catch(std::exception& e) {
89+
std::cerr << e.what() << std::endl;
90+
return 1;
91+
}
92+
}

0 commit comments

Comments
 (0)