Skip to content

Commit 4770aa4

Browse files
authored
Merge pull request #162 from jmestwa-coder/fix/reject-whitespace-only-numeric-input
Reject whitespace-only input in numeric value parsing
2 parents ecb4de1 + 0fb830a commit 4770aa4

2 files changed

Lines changed: 73 additions & 2 deletions

File tree

args.hxx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3676,11 +3676,15 @@ namespace args
36763676
if (std::is_unsigned<T>::value)
36773677
{
36783678
const unsigned long long parsed = std::strtoull(begin, &end, 0);
3679+
if (end == begin)
3680+
{
3681+
return false;
3682+
}
36793683
while (end != nullptr && *end != '\0' && std::isspace(static_cast<unsigned char>(*end)))
36803684
{
36813685
++end;
36823686
}
3683-
if (end == begin || *end != '\0' || errno == ERANGE || parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3687+
if (*end != '\0' || errno == ERANGE || parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
36843688
{
36853689
errno = saved_errno; // Restore errno on error
36863690
return false;
@@ -3691,11 +3695,15 @@ namespace args
36913695
else
36923696
{
36933697
const long long parsed = std::strtoll(begin, &end, 0);
3698+
if (end == begin)
3699+
{
3700+
return false;
3701+
}
36943702
while (end != nullptr && *end != '\0' && std::isspace(static_cast<unsigned char>(*end)))
36953703
{
36963704
++end;
36973705
}
3698-
if (end == begin || *end != '\0' || errno == ERANGE ||
3706+
if (*end != '\0' || errno == ERANGE ||
36993707
parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
37003708
parsed > static_cast<long long>(std::numeric_limits<T>::max()))
37013709
{

test/whitespace_numeric.cxx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "test_common.hxx"
2+
#include <args.hxx>
3+
#include "test_helpers.hxx"
4+
5+
int main()
6+
{
7+
args::ArgumentParser parser("This is a test program.", "This goes after the options.");
8+
args::ValueFlag<int> foo(parser, "FOO", "test flag", {'f', "foo"});
9+
args::ValueFlag<unsigned int> uid(parser, "UID", "numeric id", {'u', "uid"});
10+
11+
// Valid inputs
12+
{
13+
std::vector<std::string> args{"--foo", "123"};
14+
parser.ParseArgs(args);
15+
test::require(args::get(foo) == 123);
16+
}
17+
18+
{
19+
std::vector<std::string> args{"--foo", " 123"};
20+
parser.ParseArgs(args);
21+
test::require(args::get(foo) == 123);
22+
}
23+
24+
{
25+
std::vector<std::string> args{"--foo", "123 "};
26+
parser.ParseArgs(args);
27+
test::require(args::get(foo) == 123);
28+
}
29+
30+
{
31+
std::vector<std::string> args{"--foo", "+123"};
32+
parser.ParseArgs(args);
33+
test::require(args::get(foo) == 123);
34+
}
35+
36+
{
37+
std::vector<std::string> args{"--uid", "0x10"};
38+
parser.ParseArgs(args);
39+
test::require(args::get(uid) == 16);
40+
}
41+
42+
{
43+
std::vector<std::string> args{"--uid", "010"};
44+
parser.ParseArgs(args);
45+
test::require(args::get(uid) == 8);
46+
}
47+
48+
// Failing inputs
49+
test::require_throws_as<args::ParseError>([&] {
50+
std::vector<std::string> args{"--foo", " "};
51+
parser.ParseArgs(args);
52+
});
53+
test::require_throws_as<args::ParseError>([&] {
54+
std::vector<std::string> args{"--foo", ""};
55+
parser.ParseArgs(args);
56+
});
57+
test::require_throws_as<args::ParseError>([&] {
58+
std::vector<std::string> args{"--uid", " "};
59+
parser.ParseArgs(args);
60+
});
61+
62+
return 0;
63+
}

0 commit comments

Comments
 (0)