Codechange: replace strncasecmp with case ignoring variant of StrStarts/EndsWith

This commit is contained in:
Rubidium 2023-04-27 13:21:08 +02:00 committed by rubidium42
parent 86786a7af6
commit 4dd5f994be
10 changed files with 410 additions and 41 deletions

View File

@ -2022,7 +2022,7 @@ DEF_CONSOLE_CMD(ConFont)
byte arg_index = 2;
/* We may encounter "aa" or "noaa" but it must be the last argument. */
if (StrEqualsIgnoreCase(argv[arg_index], "aa") || StrEqualsIgnoreCase(argv[arg_index], "noaa")) {
aa = strncasecmp(argv[arg_index++], "no", 2) != 0;
aa = !StrStartsWithIgnoreCase(argv[arg_index++], "no");
if (argc > arg_index) return false;
} else {
/* For <name> we want a string. */
@ -2044,7 +2044,7 @@ DEF_CONSOLE_CMD(ConFont)
if (argc > arg_index) {
/* Last argument must be "aa" or "noaa". */
if (!StrEqualsIgnoreCase(argv[arg_index], "aa") && !StrEqualsIgnoreCase(argv[arg_index], "noaa")) return false;
aa = strncasecmp(argv[arg_index++], "no", 2) != 0;
aa = !StrStartsWithIgnoreCase(argv[arg_index++], "no");
if (argc > arg_index) return false;
}
@ -2220,7 +2220,7 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
const std::vector<GRFFile *> &files = GetAllGRFFiles();
/* "list" sub-command */
if (argc == 1 || strncasecmp(argv[1], "lis", 3) == 0) {
if (argc == 1 || StrStartsWithIgnoreCase(argv[1], "lis")) {
IConsolePrint(CC_INFO, "Loaded GRF files:");
int i = 1;
for (GRFFile *grf : files) {
@ -2236,7 +2236,7 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
}
/* "select" sub-command */
if (strncasecmp(argv[1], "sel", 3) == 0 && argc >= 3) {
if (StrStartsWithIgnoreCase(argv[1], "sel") && argc >= 3) {
for (size_t argnum = 2; argnum < argc; ++argnum) {
int grfnum = atoi(argv[argnum]);
if (grfnum < 1 || grfnum > (int)files.size()) { // safe cast, files.size() should not be larger than a few hundred in the most extreme cases
@ -2254,7 +2254,7 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
}
/* "unselect" sub-command */
if (strncasecmp(argv[1], "uns", 3) == 0 && argc >= 3) {
if (StrStartsWithIgnoreCase(argv[1], "uns") && argc >= 3) {
for (size_t argnum = 2; argnum < argc; ++argnum) {
if (StrEqualsIgnoreCase(argv[argnum], "all")) {
_newgrf_profilers.clear();
@ -2273,7 +2273,7 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
}
/* "start" sub-command */
if (strncasecmp(argv[1], "sta", 3) == 0) {
if (StrStartsWithIgnoreCase(argv[1], "sta")) {
std::string grfids;
size_t started = 0;
for (NewGRFProfiler &pr : _newgrf_profilers) {
@ -2309,13 +2309,13 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
}
/* "stop" sub-command */
if (strncasecmp(argv[1], "sto", 3) == 0) {
if (StrStartsWithIgnoreCase(argv[1], "sto")) {
NewGRFProfiler::FinishAll();
return true;
}
/* "abort" sub-command */
if (strncasecmp(argv[1], "abo", 3) == 0) {
if (StrStartsWithIgnoreCase(argv[1], "abo")) {
for (NewGRFProfiler &pr : _newgrf_profilers) {
pr.Abort();
}

View File

@ -769,7 +769,7 @@ static bool ChangeWorkingDirectoryToExecutable(const char *exe)
bool success = false;
#ifdef WITH_COCOA
char *app_bundle = strchr(tmp, '.');
while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
while (app_bundle != nullptr && !StrStartsWithIgnoreCase(app_bundle, ".app")) app_bundle = strchr(&app_bundle[1], '.');
if (app_bundle != nullptr) *app_bundle = '\0';
#endif /* WITH_COCOA */

View File

@ -386,7 +386,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c
/* found file must be directory, but not '.' or '..' */
if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) &&
(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
(!FiosIsHiddenFile(dirent) || StrStartsWithIgnoreCase(PERSONAL_DIR, d_name)) &&
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
fios = &file_list.emplace_back();
fios->type = FIOS_TYPE_DIR;

View File

@ -99,8 +99,9 @@ static uint16 ParseCode(const char *start, const char *end)
assert(start <= end);
while (start < end && *start == ' ') start++;
while (end > start && *end == ' ') end--;
std::string_view str{start, (size_t)(start - end)};
for (uint i = 0; i < lengthof(_keycode_to_name); i++) {
if (strlen(_keycode_to_name[i].name) == (size_t)(end - start) && strncasecmp(start, _keycode_to_name[i].name, end - start) == 0) {
if (StrEqualsIgnoreCase(str, _keycode_to_name[i].name)) {
return _keycode_to_name[i].keycode;
}
}

View File

@ -421,7 +421,7 @@ struct NetworkChatWindow : public Window {
}
len = strlen(cur_name);
if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
if (tb_len < len && StrStartsWith(cur_name, tb_buf)) {
/* Save the data it was before completion */
if (!second_scan) seprintf(_chat_tab_completion_buf, lastof(_chat_tab_completion_buf), "%s", tb->buf);
_chat_tab_completion_active = true;

View File

@ -134,9 +134,9 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
err = FT_New_Face(_library, font_path, index, face);
if (err != FT_Err_Ok) break;
if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
if (StrStartsWithIgnoreCase(font_name, (*face)->family_name)) break;
/* Try english name if font name failed */
if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
if (StrStartsWithIgnoreCase(font_name + strlen(font_name) + 1, (*face)->family_name)) break;
err = FT_Err_Cannot_Open_Resource;
} while ((FT_Long)++index != (*face)->num_faces);

View File

@ -37,10 +37,6 @@
# define _GNU_SOURCE
#endif
#if defined(__HAIKU__) || defined(__CYGWIN__)
# include <strings.h> /* strncasecmp */
#endif
/* It seems that we need to include stdint.h before anything else
* We need INT64_MAX, which for most systems comes from stdint.h. However, MSVC
* does not have stdint.h.
@ -71,11 +67,6 @@
# include <sys/types.h>
#endif
#if defined(__OS2__)
# include <types.h>
# define strcasecmp stricmp
#endif
/* Stuff for GCC */
#if defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))
# define NORETURN __attribute__ ((noreturn))
@ -207,9 +198,6 @@
# endif
# endif
# define strcasecmp stricmp
# define strncasecmp strnicmp
/* MSVC doesn't have these :( */
# define S_ISDIR(mode) (mode & S_IFDIR)
# define S_ISREG(mode) (mode & S_IFREG)
@ -411,11 +399,6 @@ void NORETURN AssertFailedError(int line, const char *file, const char *expressi
# define assert(expression) if (unlikely(!(expression))) AssertFailedError(__LINE__, __FILE__, #expression);
#endif
#if defined(OPENBSD)
/* OpenBSD uses strcasecmp(3) */
# define _stricmp strcasecmp
#endif
#if defined(MAX_PATH)
/* It's already defined, no need to override */
#elif defined(PATH_MAX) && PATH_MAX > 0

View File

@ -23,26 +23,27 @@
#include <iomanip>
#ifdef _MSC_VER
#include <errno.h> // required by vsnprintf implementation for MSVC
# include <errno.h> // required by vsnprintf implementation for MSVC
# define strncasecmp strnicmp
#endif
#ifdef _WIN32
#include "os/windows/win32.h"
# include "os/windows/win32.h"
#endif
#ifdef WITH_UNISCRIBE
#include "os/windows/string_uniscribe.h"
# include "os/windows/string_uniscribe.h"
#endif
#ifdef WITH_ICU_I18N
/* Required by strnatcmp. */
#include <unicode/ustring.h>
#include "language.h"
#include "gfx_func.h"
# include <unicode/ustring.h>
# include "language.h"
# include "gfx_func.h"
#endif /* WITH_ICU_I18N */
#if defined(WITH_COCOA)
#include "os/macosx/string_osx.h"
# include "os/macosx/string_osx.h"
#endif
/* The function vsnprintf is used internally to perform the required formatting
@ -355,6 +356,18 @@ bool StrStartsWith(const std::string_view str, const std::string_view prefix)
return str.compare(0, prefix_len, prefix, 0, prefix_len) == 0;
}
/**
* Check whether the given string starts with the given prefix, ignoring case.
* @param str The string to look at.
* @param prefix The prefix to look for.
* @return True iff the begin of the string is the same as the prefix, ignoring case.
*/
bool StrStartsWithIgnoreCase(std::string_view str, const std::string_view prefix)
{
if (str.size() < prefix.size()) return false;
return StrEqualsIgnoreCase(str.substr(0, prefix.size()), prefix);
}
/**
* Check whether the given string ends with the given suffix.
* @param str The string to look at.
@ -396,6 +409,18 @@ struct CaseInsensitiveCharTraits : public std::char_traits<char> {
/** Case insensitive string view. */
typedef std::basic_string_view<char, CaseInsensitiveCharTraits> CaseInsensitiveStringView;
/**
* Check whether the given string ends with the given suffix, ignoring case.
* @param str The string to look at.
* @param suffix The suffix to look for.
* @return True iff the end of the string is the same as the suffix, ignoring case.
*/
bool StrEndsWithIgnoreCase(std::string_view str, const std::string_view suffix)
{
if (str.size() < suffix.size()) return false;
return StrEqualsIgnoreCase(str.substr(str.size() - suffix.size()), suffix);
}
/**
* Compares two string( view)s, while ignoring the case of the characters.
* @param str1 The first string.

View File

@ -48,11 +48,13 @@ void str_strip_colours(char *str);
bool strtolower(char *str);
bool strtolower(std::string &str, std::string::size_type offs = 0);
bool StrValid(const char *str, const char *last) NOACCESS(2);
[[nodiscard]] bool StrValid(const char *str, const char *last) NOACCESS(2);
void StrTrimInPlace(std::string &str);
bool StrStartsWith(const std::string_view str, const std::string_view prefix);
bool StrEndsWith(const std::string_view str, const std::string_view suffix);
[[nodiscard]] bool StrStartsWith(const std::string_view str, const std::string_view prefix);
[[nodiscard]] bool StrStartsWithIgnoreCase(std::string_view str, const std::string_view prefix);
[[nodiscard]] bool StrEndsWith(const std::string_view str, const std::string_view suffix);
[[nodiscard]] bool StrEndsWithIgnoreCase(std::string_view str, const std::string_view suffix);
[[nodiscard]] int StrCompareIgnoreCase(const std::string_view str1, const std::string_view str2);
[[nodiscard]] bool StrEqualsIgnoreCase(const std::string_view str1, const std::string_view str2);

View File

@ -13,6 +13,8 @@
#include "../string_func.h"
/**** String compare/equals *****/
TEST_CASE("StrCompareIgnoreCase - std::string")
{
/* Same string, with different cases. */
@ -158,3 +160,359 @@ TEST_CASE("StrEqualsIgnoreCase - std::string_view")
CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(0, 2))); // Same position, different lengths
CHECK(!StrEqualsIgnoreCase(base.substr(0, 2), base.substr(0, 1))); // Same position, different lengths
}
/**** String starts with *****/
TEST_CASE("StrStartsWith - std::string")
{
/* Everything starts with an empty prefix. */
CHECK(StrStartsWith(std::string{""}, std::string{""}));
CHECK(StrStartsWith(std::string{"a"}, std::string{""}));
/* Equal strings. */
CHECK(StrStartsWith(std::string{"a"}, std::string{"a"}));
CHECK(StrStartsWith(std::string{"A"}, std::string{"A"}));
/* Starts with same. */
CHECK(StrStartsWith(std::string{"ab"}, std::string{"a"}));
CHECK(StrStartsWith(std::string{"Ab"}, std::string{"A"}));
/* Different cases. */
CHECK(!StrStartsWith(std::string{"a"}, std::string{"A"}));
CHECK(!StrStartsWith(std::string{"A"}, std::string{"a"}));
CHECK(!StrStartsWith(std::string{"ab"}, std::string{"A"}));
CHECK(!StrStartsWith(std::string{"Ab"}, std::string{"a"}));
/* Does not start the same. */
CHECK(!StrStartsWith(std::string{""}, std::string{"b"}));
CHECK(!StrStartsWith(std::string{"a"}, std::string{"b"}));
CHECK(!StrStartsWith(std::string{"b"}, std::string{"a"}));
CHECK(!StrStartsWith(std::string{"a"}, std::string{"aa"}));
}
TEST_CASE("StrStartsWith - char pointer")
{
CHECK(StrStartsWith("", ""));
CHECK(StrStartsWith("a", ""));
/* Equal strings. */
CHECK(StrStartsWith("a", "a"));
CHECK(StrStartsWith("A", "A"));
/* Starts with same. */
CHECK(StrStartsWith("ab", "a"));
CHECK(StrStartsWith("Ab", "A"));
/* Different cases. */
CHECK(!StrStartsWith("a", "A"));
CHECK(!StrStartsWith("A", "a"));
CHECK(!StrStartsWith("ab", "A"));
CHECK(!StrStartsWith("Ab", "a"));
/* Does not start the same. */
CHECK(!StrStartsWith("", "b"));
CHECK(!StrStartsWith("a", "b"));
CHECK(!StrStartsWith("b", "a"));
CHECK(!StrStartsWith("a", "aa"));
}
TEST_CASE("StrStartsWith - std::string_view")
{
/*
* With std::string_view the only way to access the data is via .data(),
* which does not guarantee the termination that would be required by
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
* or strcasecmp would fail if it does not account for the length of the
* view. Thus, contrary to the string/char* tests, this uses the same base
* string but gets different sections to trigger these corner cases.
*/
std::string_view base{"aabAb"};
/* Everything starts with an empty prefix. */
CHECK(StrStartsWith(base.substr(0, 0), base.substr(1, 0))); // Different positions
CHECK(StrStartsWith(base.substr(0, 1), base.substr(0, 0)));
/* Equals string. */
CHECK(StrStartsWith(base.substr(0, 1), base.substr(1, 1))); // Different positions
CHECK(StrStartsWith(base.substr(3, 1), base.substr(3, 1)));
/* Starts with same. */
CHECK(StrStartsWith(base.substr(1, 2), base.substr(0, 1)));
CHECK(StrStartsWith(base.substr(3, 2), base.substr(3, 1)));
/* Different cases. */
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(3, 1)));
CHECK(!StrStartsWith(base.substr(3, 1), base.substr(0, 1)));
CHECK(!StrStartsWith(base.substr(1, 2), base.substr(3, 1)));
CHECK(!StrStartsWith(base.substr(3, 2), base.substr(0, 1)));
/* Does not start the same. */
CHECK(!StrStartsWith(base.substr(2, 0), base.substr(2, 1)));
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(2, 1)));
CHECK(!StrStartsWith(base.substr(2, 1), base.substr(0, 1)));
CHECK(!StrStartsWith(base.substr(0, 1), base.substr(0, 2)));
}
TEST_CASE("StrStartsWithIgnoreCase - std::string")
{
/* Everything starts with an empty prefix. */
CHECK(StrStartsWithIgnoreCase(std::string{""}, std::string{""}));
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{""}));
/* Equals string, ignoring case. */
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
/* Starts with same, ignoring case. */
CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"a"}));
CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"A"}));
CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"a"}));
CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"A"}));
/* Does not start the same. */
CHECK(!StrStartsWithIgnoreCase(std::string{""}, std::string{"b"}));
CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
CHECK(!StrStartsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
}
TEST_CASE("StrStartsWithIgnoreCase - char pointer")
{
/* Everything starts with an empty prefix. */
CHECK(StrStartsWithIgnoreCase("", ""));
CHECK(StrStartsWithIgnoreCase("a", ""));
/* Equals string, ignoring case. */
CHECK(StrStartsWithIgnoreCase("a", "a"));
CHECK(StrStartsWithIgnoreCase("a", "A"));
CHECK(StrStartsWithIgnoreCase("A", "a"));
CHECK(StrStartsWithIgnoreCase("A", "A"));
/* Starts with same, ignoring case. */
CHECK(StrStartsWithIgnoreCase("ab", "a"));
CHECK(StrStartsWithIgnoreCase("ab", "A"));
CHECK(StrStartsWithIgnoreCase("Ab", "a"));
CHECK(StrStartsWithIgnoreCase("Ab", "A"));
/* Does not start the same. */
CHECK(!StrStartsWithIgnoreCase("", "b"));
CHECK(!StrStartsWithIgnoreCase("a", "b"));
CHECK(!StrStartsWithIgnoreCase("b", "a"));
CHECK(!StrStartsWithIgnoreCase("a", "aa"));
}
TEST_CASE("StrStartsWithIgnoreCase - std::string_view")
{
/*
* With std::string_view the only way to access the data is via .data(),
* which does not guarantee the termination that would be required by
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
* or strcasecmp would fail if it does not account for the length of the
* view. Thus, contrary to the string/char* tests, this uses the same base
* string but gets different sections to trigger these corner cases.
*/
std::string_view base{"aabAb"};
/* Everything starts with an empty prefix. */
CHECK(StrStartsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
/* Equals string, ignoring case. */
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
/* Starts with same, ignoring case. */
CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(0, 1)));
CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(3, 1)));
CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(0, 1)));
CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(3, 1)));
/* Does not start the same. */
CHECK(!StrStartsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
CHECK(!StrStartsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
}
/**** String ends with *****/
TEST_CASE("StrEndsWith - std::string")
{
/* Everything ends with an empty prefix. */
CHECK(StrEndsWith(std::string{""}, std::string{""}));
CHECK(StrEndsWith(std::string{"a"}, std::string{""}));
/* Equal strings. */
CHECK(StrEndsWith(std::string{"a"}, std::string{"a"}));
CHECK(StrEndsWith(std::string{"A"}, std::string{"A"}));
/* Ends with same. */
CHECK(StrEndsWith(std::string{"ba"}, std::string{"a"}));
CHECK(StrEndsWith(std::string{"bA"}, std::string{"A"}));
/* Different cases. */
CHECK(!StrEndsWith(std::string{"a"}, std::string{"A"}));
CHECK(!StrEndsWith(std::string{"A"}, std::string{"a"}));
CHECK(!StrEndsWith(std::string{"ba"}, std::string{"A"}));
CHECK(!StrEndsWith(std::string{"bA"}, std::string{"a"}));
/* Does not end the same. */
CHECK(!StrEndsWith(std::string{""}, std::string{"b"}));
CHECK(!StrEndsWith(std::string{"a"}, std::string{"b"}));
CHECK(!StrEndsWith(std::string{"b"}, std::string{"a"}));
CHECK(!StrEndsWith(std::string{"a"}, std::string{"aa"}));
}
TEST_CASE("StrEndsWith - char pointer")
{
CHECK(StrEndsWith("", ""));
CHECK(StrEndsWith("a", ""));
/* Equal strings. */
CHECK(StrEndsWith("a", "a"));
CHECK(StrEndsWith("A", "A"));
/* Ends with same. */
CHECK(StrEndsWith("ba", "a"));
CHECK(StrEndsWith("bA", "A"));
/* Different cases. */
CHECK(!StrEndsWith("a", "A"));
CHECK(!StrEndsWith("A", "a"));
CHECK(!StrEndsWith("ba", "A"));
CHECK(!StrEndsWith("bA", "a"));
/* Does not end the same. */
CHECK(!StrEndsWith("", "b"));
CHECK(!StrEndsWith("a", "b"));
CHECK(!StrEndsWith("b", "a"));
CHECK(!StrEndsWith("a", "aa"));
}
TEST_CASE("StrEndsWith - std::string_view")
{
/*
* With std::string_view the only way to access the data is via .data(),
* which does not guarantee the termination that would be required by
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
* or strcasecmp would fail if it does not account for the length of the
* view. Thus, contrary to the string/char* tests, this uses the same base
* string but gets different sections to trigger these corner cases.
*/
std::string_view base{"aabAba"};
/* Everything ends with an empty prefix. */
CHECK(StrEndsWith(base.substr(0, 0), base.substr(1, 0))); // Different positions
CHECK(StrEndsWith(base.substr(0, 1), base.substr(0, 0)));
/* Equals string. */
CHECK(StrEndsWith(base.substr(0, 1), base.substr(1, 1))); // Different positions
CHECK(StrEndsWith(base.substr(3, 1), base.substr(3, 1)));
/* Ends with same. */
CHECK(StrEndsWith(base.substr(4, 2), base.substr(0, 1)));
CHECK(StrEndsWith(base.substr(2, 2), base.substr(3, 1)));
/* Different cases. */
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(3, 1)));
CHECK(!StrEndsWith(base.substr(3, 1), base.substr(0, 1)));
CHECK(!StrEndsWith(base.substr(4, 2), base.substr(3, 1)));
CHECK(!StrEndsWith(base.substr(2, 2), base.substr(0, 1)));
/* Does not end the same. */
CHECK(!StrEndsWith(base.substr(2, 0), base.substr(2, 1)));
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(2, 1)));
CHECK(!StrEndsWith(base.substr(2, 1), base.substr(0, 1)));
CHECK(!StrEndsWith(base.substr(0, 1), base.substr(0, 2)));
}
TEST_CASE("StrEndsWithIgnoreCase - std::string")
{
/* Everything ends with an empty prefix. */
CHECK(StrEndsWithIgnoreCase(std::string{""}, std::string{""}));
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{""}));
/* Equals string, ignoring case. */
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
/* Ends with same, ignoring case. */
CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"a"}));
CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"A"}));
CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"a"}));
CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"A"}));
/* Does not end the same. */
CHECK(!StrEndsWithIgnoreCase(std::string{""}, std::string{"b"}));
CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
CHECK(!StrEndsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
}
TEST_CASE("StrEndsWithIgnoreCase - char pointer")
{
/* Everything ends with an empty prefix. */
CHECK(StrEndsWithIgnoreCase("", ""));
CHECK(StrEndsWithIgnoreCase("a", ""));
/* Equals string, ignoring case. */
CHECK(StrEndsWithIgnoreCase("a", "a"));
CHECK(StrEndsWithIgnoreCase("a", "A"));
CHECK(StrEndsWithIgnoreCase("A", "a"));
CHECK(StrEndsWithIgnoreCase("A", "A"));
/* Ends with same, ignoring case. */
CHECK(StrEndsWithIgnoreCase("ba", "a"));
CHECK(StrEndsWithIgnoreCase("ba", "A"));
CHECK(StrEndsWithIgnoreCase("bA", "a"));
CHECK(StrEndsWithIgnoreCase("bA", "A"));
/* Does not end the same. */
CHECK(!StrEndsWithIgnoreCase("", "b"));
CHECK(!StrEndsWithIgnoreCase("a", "b"));
CHECK(!StrEndsWithIgnoreCase("b", "a"));
CHECK(!StrEndsWithIgnoreCase("a", "aa"));
}
TEST_CASE("StrEndsWithIgnoreCase - std::string_view")
{
/*
* With std::string_view the only way to access the data is via .data(),
* which does not guarantee the termination that would be required by
* things such as stricmp/strcasecmp. So, just passing .data() into stricmp
* or strcasecmp would fail if it does not account for the length of the
* view. Thus, contrary to the string/char* tests, this uses the same base
* string but gets different sections to trigger these corner cases.
*/
std::string_view base{"aabAba"};
/* Everything ends with an empty prefix. */
CHECK(StrEndsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
/* Equals string, ignoring case. */
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
/* Ends with same, ignoring case. */
CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(0, 1)));
CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(3, 1)));
CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(0, 1)));
CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(3, 1)));
/* Does not end the same. */
CHECK(!StrEndsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
CHECK(!StrEndsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
}