Skip to content

Commit

Permalink
cmdlib: fix broken Q_strcasecmp and Q_strncasecmp
Browse files Browse the repository at this point in the history
string_view isn't null terminated, can't use C string functions
  • Loading branch information
ericwa committed Nov 29, 2024
1 parent 638076f commit d7eca5f
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 21 deletions.
55 changes: 34 additions & 21 deletions common/cmdlib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,34 +41,47 @@
#include <algorithm>
#include <string>

#if defined(__has_include) && __has_include(<strings.h>)
#include <strings.h>
#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(<strings.h>)
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(<strings.h>)
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
Expand Down
1 change: 1 addition & 0 deletions include/common/cmdlib.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <ostream>
#include <tuple> // 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
Expand Down
30 changes: 30 additions & 0 deletions tests/test_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit d7eca5f

Please sign in to comment.