Commit d7b930846cdccfc8bcecc4d7150ddcbadffac360

Authored by DevCodeOne
Committed by jarro2783
1 parent 8ce9a587

Fix some strange issues in integer_parser (#80)

* Prevent malformed numbers from being parsed as correct numbers.

Fixes #78. If you passed a string for example "test" it would get parsed to 1400.
The problem was that the parser did not throw an exception when an incorrect char was encountered.
Also a number without 0x in front with hexadecimal digits in it got parsed.
The number was treated as a hexadecimal number but it was still calculated with base 10.
So now before the current char is used, it is checked if it is valid in the current base.
Furthermore the number 0x0 was not a valid number, it now is a special case in the `integer_pattern`.

* Fixed `integer_pattern` so it works correctly under clang. Added testcase for invalid integers and for 0x0 being a valid number.
include/cxxopts.hpp
@@ -431,7 +431,7 @@ namespace cxxopts @@ -431,7 +431,7 @@ namespace cxxopts
431 namespace 431 namespace
432 { 432 {
433 std::basic_regex<char> integer_pattern 433 std::basic_regex<char> integer_pattern
434 - ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|(0)"); 434 + ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|((0x)?0)");
435 } 435 }
436 436
437 namespace detail 437 namespace detail
@@ -533,14 +533,18 @@ namespace cxxopts @@ -533,14 +533,18 @@ namespace cxxopts
533 { 533 {
534 digit = *iter - '0'; 534 digit = *iter - '0';
535 } 535 }
536 - else if (*iter >= 'a' && *iter <= 'f') 536 + else if (base == 16 && *iter >= 'a' && *iter <= 'f')
537 { 537 {
538 digit = *iter - 'a' + 10; 538 digit = *iter - 'a' + 10;
539 } 539 }
540 - else if (*iter >= 'A' && *iter <= 'F') 540 + else if (base == 16 && *iter >= 'A' && *iter <= 'F')
541 { 541 {
542 digit = *iter - 'A' + 10; 542 digit = *iter - 'A' + 10;
543 } 543 }
  544 + else
  545 + {
  546 + throw argument_incorrect_type(text);
  547 + }
544 548
545 if (umax - digit < result * base) 549 if (umax - digit < result * base)
546 { 550 {
test/options.cpp
@@ -246,7 +246,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;) @@ -246,7 +246,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;)
246 options.add_options() 246 options.add_options()
247 ("positional", "Integers", cxxopts::value<std::vector<int>>()); 247 ("positional", "Integers", cxxopts::value<std::vector<int>>());
248 248
249 - Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf"}); 249 + Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf", "0x0"});
250 250
251 char** argv = av.argv(); 251 char** argv = av.argv();
252 auto argc = av.argc(); 252 auto argc = av.argc();
@@ -254,7 +254,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;) @@ -254,7 +254,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;)
254 options.parse_positional("positional"); 254 options.parse_positional("positional");
255 options.parse(argc, argv); 255 options.parse(argc, argv);
256 256
257 - REQUIRE(options.count("positional") == 6); 257 + REQUIRE(options.count("positional") == 7);
258 258
259 auto& positional = options["positional"].as<std::vector<int>>(); 259 auto& positional = options["positional"].as<std::vector<int>>();
260 CHECK(positional[0] == 5); 260 CHECK(positional[0] == 5);
@@ -263,6 +263,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;) @@ -263,6 +263,7 @@ TEST_CASE(&quot;Integers&quot;, &quot;[options]&quot;)
263 CHECK(positional[3] == 0); 263 CHECK(positional[3] == 0);
264 CHECK(positional[4] == 0xab); 264 CHECK(positional[4] == 0xab);
265 CHECK(positional[5] == 0xaf); 265 CHECK(positional[5] == 0xaf);
  266 + CHECK(positional[6] == 0x0);
266 } 267 }
267 268
268 TEST_CASE("Unsigned integers", "[options]") 269 TEST_CASE("Unsigned integers", "[options]")
@@ -364,3 +365,16 @@ TEST_CASE(&quot;Floats&quot;, &quot;[options]&quot;) @@ -364,3 +365,16 @@ TEST_CASE(&quot;Floats&quot;, &quot;[options]&quot;)
364 CHECK(positional[3] == -1.5e6); 365 CHECK(positional[3] == -1.5e6);
365 } 366 }
366 367
  368 +TEST_CASE("Invalid integers", "[integer]") {
  369 + cxxopts::Options options("invalid_integers", "rejects invalid integers");
  370 + options.add_options()
  371 + ("positional", "Integers", cxxopts::value<std::vector<int>>());
  372 +
  373 + Argv av({"ints", "--", "Ae"});
  374 +
  375 + char **argv = av.argv();
  376 + auto argc = av.argc();
  377 +
  378 + options.parse_positional("positional");
  379 + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type);
  380 +}