From 31f158e4c881bea2b526102b2024b0fb34ade42d Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 20 Sep 2022 11:12:23 -0400 Subject: cmStringAlgorithms: Add functions to parse strings to long long integers --- Source/cmStringAlgorithms.cxx | 32 ++++++++++++++++++++++++++++++ Source/cmStringAlgorithms.h | 10 ++++++++++ Tests/CMakeLib/testStringAlgorithms.cxx | 35 +++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx index 1bb6808..f73c854 100644 --- a/Source/cmStringAlgorithms.cxx +++ b/Source/cmStringAlgorithms.cxx @@ -250,6 +250,38 @@ bool cmStrToULong(std::string const& str, unsigned long* value) return cmStrToULong(str.c_str(), value); } +bool cmStrToLongLong(const char* str, long long* value) +{ + errno = 0; + char* endp; + *value = strtoll(str, &endp, 10); + return (*endp == '\0') && (endp != str) && (errno == 0); +} + +bool cmStrToLongLong(std::string const& str, long long* value) +{ + return cmStrToLongLong(str.c_str(), value); +} + +bool cmStrToULongLong(const char* str, unsigned long long* value) +{ + errno = 0; + char* endp; + while (cmIsSpace(*str)) { + ++str; + } + if (*str == '-') { + return false; + } + *value = strtoull(str, &endp, 10); + return (*endp == '\0') && (endp != str) && (errno == 0); +} + +bool cmStrToULongLong(std::string const& str, unsigned long long* value) +{ + return cmStrToULongLong(str.c_str(), value); +} + template std::size_t getJoinedLength(Range const& rng, cm::string_view separator) { diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h index 492e588..83938bc 100644 --- a/Source/cmStringAlgorithms.h +++ b/Source/cmStringAlgorithms.h @@ -303,3 +303,13 @@ bool cmStrToLong(std::string const& str, long* value); * integer */ bool cmStrToULong(const char* str, unsigned long* value); bool cmStrToULong(std::string const& str, unsigned long* value); + +/** Converts a string to long long. Expects that the whole string + * is an integer */ +bool cmStrToLongLong(const char* str, long long* value); +bool cmStrToLongLong(std::string const& str, long long* value); + +/** Converts a string to unsigned long long. Expects that the whole string + * is an integer */ +bool cmStrToULongLong(const char* str, unsigned long long* value); +bool cmStrToULongLong(std::string const& str, unsigned long long* value); diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx index c2706c1..1e6b611 100644 --- a/Tests/CMakeLib/testStringAlgorithms.cxx +++ b/Tests/CMakeLib/testStringAlgorithms.cxx @@ -227,6 +227,41 @@ int testStringAlgorithms(int /*unused*/, char* /*unused*/ []) } // ---------------------------------------------------------------------- + // Test cmStrToLongLong + { + long long value; + assert_ok(cmStrToLongLong("1", &value) && value == 1, + "cmStrToLongLong parses a positive decimal integer."); + assert_ok(cmStrToLongLong(" 1", &value) && value == 1, + "cmStrToLongLong parses a decimal integer after whitespace."); + + assert_ok(cmStrToLongLong("-1", &value) && value == -1, + "cmStrToLongLong parses a negative decimal integer."); + assert_ok( + cmStrToLongLong(" -1", &value) && value == -1, + "cmStrToLongLong parses a negative decimal integer after whitespace."); + + assert_ok(!cmStrToLongLong("1x", &value), + "cmStrToLongLong rejects trailing content."); + } + + // ---------------------------------------------------------------------- + // Test cmStrToULongLong + { + unsigned long long value; + assert_ok(cmStrToULongLong("1", &value) && value == 1, + "cmStrToULongLong parses a decimal integer."); + assert_ok(cmStrToULongLong(" 1", &value) && value == 1, + "cmStrToULongLong parses a decimal integer after whitespace."); + assert_ok(!cmStrToULongLong("-1", &value), + "cmStrToULongLong rejects a negative number."); + assert_ok(!cmStrToULongLong(" -1", &value), + "cmStrToULongLong rejects a negative number after whitespace."); + assert_ok(!cmStrToULongLong("1x", &value), + "cmStrToULongLong rejects trailing content."); + } + + // ---------------------------------------------------------------------- // Test cmStrLen { constexpr auto len = cmStrLen("Hello world!"); -- cgit v0.12 From 8fc822e13a8bf8695e475655f647d5d69f99c414 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 20 Sep 2022 11:16:29 -0400 Subject: file: Avoid strange istringstream crash in cmake.org binaries on Alpine Linux Somehow using `istringstream` and `operator >>` to parse an integer crashes on Alpine Linux, at least when compiled with the settings we use for the official `cmake.org` Linux binaries. Since commit fd0c285b12 (file: Fix types of the OFFSET and LIMIT arguments, 2022-01-04, v3.23.0-rc1~133^2), this causes the `file(READ)` command to crash when parsing its `LIMIT` or `OFFSET` argument. Parse the input string with our dedicated helper to avoid the crash. Fixes: #23872 --- Source/cmFileCommand.cxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index da2f15f..fb15a1b 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -199,13 +199,19 @@ bool HandleReadCommand(std::vector const& args, // is there a limit? std::string::size_type sizeLimit = std::string::npos; if (!arguments.Limit.empty()) { - std::istringstream(arguments.Limit) >> sizeLimit; + unsigned long long limit; + if (cmStrToULongLong(arguments.Limit, &limit)) { + sizeLimit = static_cast(limit); + } } // is there an offset? cmsys::ifstream::off_type offset = 0; if (!arguments.Offset.empty()) { - std::istringstream(arguments.Offset) >> offset; + long long off; + if (cmStrToLongLong(arguments.Offset, &off)) { + offset = static_cast(off); + } } file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6 -- cgit v0.12