From d7eca5f317b20515c08c9ee2f259bec2998189c6 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Fri, 29 Nov 2024 00:01:59 -0700 Subject: [PATCH] cmdlib: fix broken Q_strcasecmp and Q_strncasecmp string_view isn't null terminated, can't use C string functions --- common/cmdlib.cc | 55 +++++++++++++++++++++++++--------------- include/common/cmdlib.hh | 1 + tests/test_common.cc | 30 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/common/cmdlib.cc b/common/cmdlib.cc index 468f5f46..2a3407a5 100644 --- a/common/cmdlib.cc +++ b/common/cmdlib.cc @@ -41,34 +41,47 @@ #include #include -#if defined(__has_include) && __has_include() -#include -#endif +char Q_tolower(char x) +{ + if (x >= 'A' && x <= 'Z') { + x += 'a' - 'A'; + } + return x; +} int32_t Q_strncasecmp(std::string_view a, std::string_view b, size_t maxcount) { - return -#ifdef _WIN32 - _strnicmp -#elif defined(__has_include) && __has_include() - strncasecmp -#else - strnicmp -#endif - (a.data(), b.data(), maxcount); + return Q_strcasecmp( + a.substr(0, maxcount), + b.substr(0, maxcount)); } int32_t Q_strcasecmp(std::string_view a, std::string_view b) { - return -#ifdef _WIN32 - _stricmp -#elif defined(__has_include) && __has_include() - strcasecmp -#else - stricmp -#endif - (a.data(), b.data()); + const char *a_ptr = a.data(); + const char *b_ptr = b.data(); + const size_t common_size = std::min(a.size(), b.size()); + + for (size_t i = 0; i < common_size; ++i) { + char achar = Q_tolower(a_ptr[i]); + char bchar = Q_tolower(b_ptr[i]); + + if (achar < bchar) { + return -1; + } + if (achar > bchar) { + return 1; + } + } + + // the first common_size chars are the same + if (a.size() < b.size()) { + return -1; + } + if (a.size() > b.size()) { + return 1; + } + return 0; } void // mxd diff --git a/include/common/cmdlib.hh b/include/common/cmdlib.hh index 2b2fe11b..b6078cb6 100644 --- a/include/common/cmdlib.hh +++ b/include/common/cmdlib.hh @@ -30,6 +30,7 @@ #include #include // for std::apply() +char Q_tolower(char x); int32_t Q_strncasecmp(std::string_view a, std::string_view b, size_t maxcount); int32_t Q_strcasecmp(std::string_view a, std::string_view b); bool string_iequals(std::string_view a, std::string_view b); // mxd diff --git a/tests/test_common.cc b/tests/test_common.cc index 00a737df..9f1c2532 100644 --- a/tests/test_common.cc +++ b/tests/test_common.cc @@ -382,3 +382,33 @@ TEST(qmat, transpose) EXPECT_EQ(in.transpose(), exp); } + +TEST(string, strcasecmp) +{ + EXPECT_EQ('x', Q_tolower('X')); + EXPECT_EQ('"', Q_tolower('"')); + + const char *test = "abcA**"; + + // lhs < rhs + EXPECT_LT(Q_strcasecmp("a", "aa"), 0); + EXPECT_LT(Q_strcasecmp("aaa", "BBB"), 0); + EXPECT_LT(Q_strcasecmp("AAA", "bbb"), 0); + + // lhs == rhs + EXPECT_EQ(Q_strcasecmp(std::string_view(&test[0], 1), std::string_view(&test[3], 1)), 0); + EXPECT_EQ(Q_strcasecmp("test", "TEST"), 0); + EXPECT_EQ(Q_strcasecmp("test", "test"), 0); + + // lhs > rhs + EXPECT_GT(Q_strcasecmp("test", "aaaa"), 0); + EXPECT_GT(Q_strcasecmp("test", "AAAA"), 0); + EXPECT_GT(Q_strcasecmp("test", "tes"), 0); + EXPECT_GT(Q_strcasecmp("TEST", "T"), 0); +} + +TEST(string, strncasecmp) +{ + EXPECT_EQ(Q_strncasecmp("*lava123", "*LAVA", 5), 0); + EXPECT_EQ(Q_strncasecmp("*lava123", "*LAVA", 8), 1); +} \ No newline at end of file