Skip to content

Commit 6271962

Browse files
committedSep 20, 2018
fixed handling of empty args
1 parent dfda4ac commit 6271962

File tree

3 files changed

+128
-27
lines changed

3 files changed

+128
-27
lines changed
 

‎include/clipp.h

+14-18
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.0
4+
* | |_ | |_ | | | _/ _/ version 1.2.1
55
* |___||___||_| |_| |_| https://github.com/muellan/clipp
66
*
77
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -114,12 +114,12 @@ class subrange {
114114

115115
/** @brief returns true, if query string is a prefix of the subject string */
116116
constexpr bool prefix() const noexcept {
117-
return at_ == 0 && length_ > 0;
117+
return at_ == 0;
118118
}
119119

120120
/** @brief returns true, if query is a substring of the query string */
121121
constexpr explicit operator bool () const noexcept {
122-
return at_ != arg_string::npos && length_ > 0;
122+
return at_ != arg_string::npos;
123123
}
124124

125125
private:
@@ -605,8 +605,7 @@ class map_arg_to
605605
map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
606606

607607
void operator () (const char* s) const {
608-
if(t_ && s && (std::strlen(s) > 0))
609-
*t_ = detail::make<T>::from(s);
608+
if(t_ && s) *t_ = detail::make<T>::from(s);
610609
}
611610

612611
private:
@@ -914,6 +913,7 @@ inline subrange
914913
substring_match(const std::basic_string<C,T,A>& subject,
915914
const std::basic_string<C,T,A>& query)
916915
{
916+
if(subject.empty() && query.empty()) return subrange(0,0);
917917
if(subject.empty() || query.empty()) return subrange{};
918918
auto i = subject.find(query);
919919
if(i == std::basic_string<C,T,A>::npos) return subrange{};
@@ -2014,12 +2014,13 @@ class parameter :
20142014
subrange
20152015
match(const arg_string& arg) const
20162016
{
2017-
if(arg.empty()) return subrange{};
2018-
20192017
if(flags_.empty()) {
20202018
return matcher_(arg);
20212019
}
20222020
else {
2021+
//empty flags are not allowed
2022+
if(arg.empty()) return subrange{};
2023+
20232024
if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
20242025
return subrange{0,arg.size()};
20252026
}
@@ -4337,7 +4338,7 @@ class match_t {
43374338
const arg_string& str() const noexcept { return str_; }
43384339
const scoped_dfs_traverser& pos() const noexcept { return pos_; }
43394340

4340-
explicit operator bool() const noexcept { return !str_.empty(); }
4341+
explicit operator bool() const noexcept { return bool(pos_); }
43414342

43424343
private:
43434344
arg_string str_;
@@ -4357,8 +4358,6 @@ match_t
43574358
full_match(scoped_dfs_traverser pos, const arg_string& arg,
43584359
const ParamSelector& select)
43594360
{
4360-
if(arg.empty()) return match_t{};
4361-
43624361
while(pos) {
43634362
if(pos->is_param()) {
43644363
const auto& param = pos->as_param();
@@ -4388,8 +4387,6 @@ match_t
43884387
prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
43894388
const ParamSelector& select)
43904389
{
4391-
if(arg.empty()) return match_t{};
4392-
43934390
while(pos) {
43944391
if(pos->is_param()) {
43954392
const auto& param = pos->as_param();
@@ -4424,8 +4421,6 @@ match_t
44244421
partial_match(scoped_dfs_traverser pos, const arg_string& arg,
44254422
const ParamSelector& select)
44264423
{
4427-
if(arg.empty()) return match_t{};
4428-
44294424
while(pos) {
44304425
if(pos->is_param()) {
44314426
const auto& param = pos->as_param();
@@ -4581,7 +4576,7 @@ class parser
45814576
++eaten_;
45824577
++index_;
45834578

4584-
if(!valid() || arg.empty()) return false;
4579+
if(!valid()) return false;
45854580

45864581
if(!blocked_ && try_match(arg)) return true;
45874582

@@ -4856,7 +4851,7 @@ class parser
48564851
void add_match(const match_t& match)
48574852
{
48584853
const auto& pos = match.pos();
4859-
if(!pos || !pos->is_param() || match.str().empty()) return;
4854+
if(!pos || !pos->is_param()) return;
48604855

48614856
pos_.next_after_match(pos);
48624857

@@ -5256,7 +5251,7 @@ parse(std::initializer_list<const char*> arglist, const group& cli)
52565251
arg_list args;
52575252
args.reserve(arglist.size());
52585253
for(auto a : arglist) {
5259-
if(std::strlen(a) > 0) args.push_back(a);
5254+
args.push_back(a);
52605255
}
52615256

52625257
return parse(std::move(args), cli);
@@ -6924,7 +6919,8 @@ void print(OStream& os, const parsing_result& result)
69246919
template<class OStream>
69256920
void print(OStream& os, const parameter& p)
69266921
{
6927-
if(p.blocking()) os << '!';
6922+
if(p.greedy()) os << '!';
6923+
if(p.blocking()) os << '~';
69286924
if(!p.required()) os << '[';
69296925
os << doc_label(p);
69306926
if(p.repeatable()) os << "...";

‎test/empty_args.cpp

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*****************************************************************************
2+
*
3+
* CLIPP - command line interfaces for modern C++
4+
*
5+
* released under MIT license
6+
*
7+
* (c) 2017-2018 André Müller; foss@andremueller-online.de
8+
*
9+
*****************************************************************************/
10+
11+
#include "testing.h"
12+
13+
14+
//-------------------------------------------------------------------
15+
struct active {
16+
active() = default;
17+
explicit
18+
active(bool o_, std::string ov_ = "_", std::string pv_ = "_"):
19+
o{o_}, ov{std::move(ov_)}, pv{std::move(pv_)}
20+
{}
21+
22+
bool o = false;
23+
std::string ov = "_";
24+
std::string pv = "_";
25+
26+
friend bool operator == (const active& x, const active& y) noexcept {
27+
return (x.o == y.o && x.ov == y.ov && x.pv == y.pv);
28+
}
29+
};
30+
31+
32+
//-------------------------------------------------------------------
33+
void test_empty(int lineNo,
34+
const std::initializer_list<const char*> args,
35+
const active& matches)
36+
{
37+
using namespace clipp;
38+
39+
active m;
40+
auto cli = (
41+
option("-o", "--opt").set(m.o) & value(match::any, "O", m.ov),
42+
value("P", m.pv)
43+
);
44+
45+
run_wrapped_variants({ __FILE__, lineNo }, args, cli,
46+
[&]{ m = active{}; },
47+
[&]{ return m == matches; });
48+
49+
}
50+
51+
52+
//-------------------------------------------------------------------
53+
void test_nonempty(int lineNo,
54+
const std::initializer_list<const char*> args,
55+
const active& matches)
56+
{
57+
using namespace clipp;
58+
59+
active m;
60+
auto cli = (
61+
option("-o", "--opt").set(m.o) & value(match::nonempty, "O", m.ov),
62+
value("P", m.pv)
63+
);
64+
65+
run_wrapped_variants({ __FILE__, lineNo }, args, cli,
66+
[&]{ m = active{}; },
67+
[&]{ return m == matches; });
68+
69+
}
70+
71+
72+
//-------------------------------------------------------------------
73+
int main()
74+
{
75+
try {
76+
test_empty(__LINE__, {}, active{});
77+
test_empty(__LINE__, {""}, active{});
78+
test_empty(__LINE__, {"-o"}, active{true});
79+
test_empty(__LINE__, {"-o", ""}, active{true, ""});
80+
test_empty(__LINE__, {"-o", "X"}, active{true, "X"});
81+
test_empty(__LINE__, {"X"}, active{false, "_", "X"});
82+
test_empty(__LINE__, {"-o", "", "X"}, active{true, "", "X"});
83+
84+
85+
test_nonempty(__LINE__, {}, active{});
86+
test_nonempty(__LINE__, {""}, active{});
87+
test_nonempty(__LINE__, {"-o"}, active{true});
88+
test_nonempty(__LINE__, {"-o", ""}, active{true});
89+
test_nonempty(__LINE__, {"-o", "X"}, active{true, "X"});
90+
test_nonempty(__LINE__, {"X"}, active{false, "_", "X"});
91+
92+
//ambiguous -> cannot map to value "P", expects value "O" first
93+
test_nonempty(__LINE__, {"-o", "", "X"}, active{true});
94+
95+
}
96+
catch(std::exception& e) {
97+
std::cerr << e.what() << std::endl;
98+
return 1;
99+
}
100+
}

‎test/testing.h

+14-9
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ wrapped_variants(const CLI& cli)
4949
,
5050
/* 3*/ group{group{cli}}
5151
,
52-
/* 4*/ group{option("?????"), cli}
52+
/* 4*/ group{option("?a?"), cli}
5353
,
54-
/* 5*/ group{cli, option("?????")}
54+
/* 5*/ group{cli, option("?a?")}
5555
,
56-
/* 6*/ group{option("!!!!!"), cli, option("?????")}
56+
/* 6*/ group{option("?b?"), cli, option("?a?")}
5757
,
58-
/* 7*/ group{group{option("?????")}, cli}
58+
/* 7*/ group{group{option("?a?")}, cli}
5959
,
60-
/* 8*/ group{cli, group{option("?????")}}
60+
/* 8*/ group{cli, group{option("?a?")}}
6161
,
62-
/* 9*/ group{option("?????"), group{cli}}
62+
/* 9*/ group{option("?a?"), group{cli}}
6363
,
64-
/*10*/ group{group{cli}, option("?????")}
64+
/*10*/ group{group{cli}, option("?a?")}
6565
,
66-
/*11*/ group{option("!!!!!"), group{cli}, option("?????")}
66+
/*11*/ group{option("?b?"), group{cli}, option("?a?")}
6767
};
6868
}
6969

@@ -99,6 +99,9 @@ void run_wrapped_variants(
9999
// << " with variant " << variant << '\n'
100100
// << "==============================================\n";
101101
// clipp::debug::print(cout, wrappedCli);
102+
// cout << "args = { ";
103+
// for(const auto& a : args) cout << '"' << a << "\" ";
104+
// cout << "}\n";
102105
// auto res = clipp::parse(args, wrappedCli);
103106
// cout << "----------------------------------------------\n";
104107
// clipp::debug::print(cout, res);
@@ -133,8 +136,10 @@ void run_test(
133136
// cout << "==============================================\n"
134137
// << " in file " << info.file << " in line " << info.line << '\n'
135138
// << "==============================================\n";
136-
137139
// clipp::debug::print(cout, cli);
140+
// cout << "args = { ";
141+
// for(const auto& a : args) cout << '"' << a << "\" ";
142+
// cout << "}\n";
138143
// auto res = clipp::parse(args, cli);
139144
// cout << "----------------------------------------------\n";
140145
// clipp::debug::print(cout, res);

0 commit comments

Comments
 (0)