From 5b175c9704f6ef5db29ac9aa8ab30029aa2e0dc7 Mon Sep 17 00:00:00 2001 From: Eduard Tykhoniuk <50710486+MAKMED1337@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:45:17 +0200 Subject: [PATCH] config: Return an error on invalid hex values. (#54) * config.cpp: fix parsing invalid hex Instead of crashing on an invalid hex, return an error. * config: return an error on an invalid hex value * cleanup * style: add references for catching exceptions * style: ignore `std::out_of_range` --- src/config.cpp | 32 +++++++++++++++++++++---------- tests/config/error2.conf | 5 ----- tests/config/invalid-numbers.conf | 15 +++++++++++++++ tests/parse/main.cpp | 21 +++++++++++++------- 4 files changed, 51 insertions(+), 22 deletions(-) delete mode 100644 tests/config/error2.conf create mode 100644 tests/config/invalid-numbers.conf diff --git a/src/config.cpp b/src/config.cpp index 62efa97..fdb71db 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,6 +1,8 @@ #include "config.hpp" +#include #include #include +#include #include #include #include @@ -160,14 +162,18 @@ void CConfig::commence() { } static std::expected configStringToInt(const std::string& VALUE) { + auto parseHex = [](const std::string& value) -> std::expected { + try { + size_t position; + auto result = stoll(value, &position, 16); + if (position == value.size()) + return result; + } catch (const std::exception&) {} + return std::unexpected("invalid hex " + value); + }; if (VALUE.starts_with("0x")) { // Values with 0x are hex - size_t position; - auto result = stoll(VALUE, &position, 16); - if (position == VALUE.size()) - return result; - - return std::unexpected("invalid hex " + VALUE); + return parseHex(VALUE); } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) { const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6)); @@ -191,10 +197,13 @@ static std::expected configStringToInt(const std::string& return a * (Hyprlang::INT)0x1000000 + r.value() * (Hyprlang::INT)0x10000 + g.value() * (Hyprlang::INT)0x100 + b.value(); } else if (VALUEWITHOUTFUNC.length() == 8) { - const auto RGBA = std::stoll(VALUEWITHOUTFUNC, nullptr, 16); + const auto RGBA = parseHex(VALUEWITHOUTFUNC); + + if (!RGBA.has_value()) + return RGBA; // now we need to RGBA -> ARGB. The config holds ARGB only. - return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF); + return (RGBA.value() >> 8) + 0x1000000 * (RGBA.value() & 0xFF); } return std::unexpected("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values"); @@ -217,9 +226,12 @@ static std::expected configStringToInt(const std::string& return (Hyprlang::INT)0xFF000000 + r.value() * (Hyprlang::INT)0x10000 + g.value() * (Hyprlang::INT)0x100 + b.value(); } else if (VALUEWITHOUTFUNC.length() == 6) { - const auto RGB = std::stoll(VALUEWITHOUTFUNC, nullptr, 16); + const auto RGB = parseHex(VALUEWITHOUTFUNC); - return RGB + 0xFF000000; + if (!RGB.has_value()) + return RGB; + + return RGB.value() + 0xFF000000; } return std::unexpected("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values"); diff --git a/tests/config/error2.conf b/tests/config/error2.conf deleted file mode 100644 index f41910a..0000000 --- a/tests/config/error2.conf +++ /dev/null @@ -1,5 +0,0 @@ -# This config houses two errors - -invalidHex = 0xQQ -emptyHex = 0x - diff --git a/tests/config/invalid-numbers.conf b/tests/config/invalid-numbers.conf new file mode 100644 index 0000000..3c989a0 --- /dev/null +++ b/tests/config/invalid-numbers.conf @@ -0,0 +1,15 @@ +# Every number/color in this file is invalid + +invalidHex = 0x1Q +emptyHex = 0x +hugeHex = 0xFFFFFFFFFFFFFFFF + +invalidInt = 1A +emptyInt = + +invalidColor = rgb(ABCDEQ) +invalidFirstCharColor = rgb(QABCDE) + +invalidColorAlpha = rgba(9ABCDEFQ) +invalidFirstCharColorAlpha = rgba(Q9ABCDEF) + diff --git a/tests/parse/main.cpp b/tests/parse/main.cpp index 0c28d17..a20d558 100755 --- a/tests/parse/main.cpp +++ b/tests/parse/main.cpp @@ -279,17 +279,24 @@ int main(int argc, char** argv, char** envp) { const auto ERRORSTR = std::string{ERRORS.getError()}; EXPECT(std::count(ERRORSTR.begin(), ERRORSTR.end(), '\n'), 1); - std::cout << " → Testing error2.conf\n"; - Hyprlang::CConfig errorConfig2("./config/error2.conf", {.throwAllErrors = true}); - errorConfig2.addConfigValue("invalidHex", (Hyprlang::INT)0); - errorConfig2.addConfigValue("emptyHex", (Hyprlang::INT)0); + std::cout << " → Testing invalid-numbers.conf\n"; + Hyprlang::CConfig invalidNumbersConfig("./config/invalid-numbers.conf", {.throwAllErrors = true}); + invalidNumbersConfig.addConfigValue("invalidHex", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("emptyHex", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("hugeHex", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("invalidInt", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("emptyInt", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("invalidColor", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("invalidFirstCharColor", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("invalidColorAlpha", (Hyprlang::INT)0); + invalidNumbersConfig.addConfigValue("invalidFirstCharColorAlpha", (Hyprlang::INT)0); - errorConfig2.commence(); - const auto ERRORS2 = errorConfig2.parse(); + invalidNumbersConfig.commence(); + const auto ERRORS2 = invalidNumbersConfig.parse(); EXPECT(ERRORS2.error, true); const auto ERRORSTR2 = std::string{ERRORS2.getError()}; - EXPECT(std::count(ERRORSTR2.begin(), ERRORSTR2.end(), '\n'), 1); + EXPECT(std::count(ERRORSTR2.begin(), ERRORSTR2.end(), '\n'), 9 - 1); } catch (const char* e) { std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n"; return 1;