diff --git a/Core/GameEngine/Include/Common/UnicodeString.h b/Core/GameEngine/Include/Common/UnicodeString.h index d924b2f9af..78e0b3e180 100644 --- a/Core/GameEngine/Include/Common/UnicodeString.h +++ b/Core/GameEngine/Include/Common/UnicodeString.h @@ -306,6 +306,30 @@ class UnicodeString */ int compareNoCase(const WideChar* s) const; + /** + return true iff self starts with the given string. + */ + Bool startsWith(const WideChar* p) const; + inline Bool startsWith(const UnicodeString& stringSrc) const { return startsWith(stringSrc.str()); } + + /** + return true iff self starts with the given string. (case insensitive) + */ + Bool startsWithNoCase(const WideChar* p) const; + inline Bool startsWithNoCase(const UnicodeString& stringSrc) const { return startsWithNoCase(stringSrc.str()); } + + /** + return true iff self ends with the given string. + */ + Bool endsWith(const WideChar* p) const; + Bool endsWith(const UnicodeString& stringSrc) const { return endsWith(stringSrc.str()); } + + /** + return true iff self ends with the given string. (case insensitive) + */ + Bool endsWithNoCase(const WideChar* p) const; + Bool endsWithNoCase(const UnicodeString& stringSrc) const { return endsWithNoCase(stringSrc.str()); } + /** conceptually similar to strtok(): diff --git a/Core/GameEngine/Source/Common/System/AsciiString.cpp b/Core/GameEngine/Source/Common/System/AsciiString.cpp index 1bf7ff672c..1be7898993 100644 --- a/Core/GameEngine/Source/Common/System/AsciiString.cpp +++ b/Core/GameEngine/Source/Common/System/AsciiString.cpp @@ -477,57 +477,25 @@ void AsciiString::format_va(const char* format, va_list args) // ----------------------------------------------------- Bool AsciiString::startsWith(const char* p) const { - if (*p == 0) - return true; // everything starts with the empty string - - int lenThis = getLength(); - int lenThat = strlen(p); - if (lenThis < lenThat) - return false; // that must be smaller than this - - return strncmp(peek(), p, lenThat) == 0; + return ::startsWith(peek(), p); } // ----------------------------------------------------- Bool AsciiString::startsWithNoCase(const char* p) const { - if (*p == 0) - return true; // everything starts with the empty string - - int lenThis = getLength(); - int lenThat = strlen(p); - if (lenThis < lenThat) - return false; // that must be smaller than this - - return strnicmp(peek(), p, lenThat) == 0; + return ::startsWithNoCase(peek(), p); } // ----------------------------------------------------- Bool AsciiString::endsWith(const char* p) const { - if (*p == 0) - return true; // everything ends with the empty string - - int lenThis = getLength(); - int lenThat = strlen(p); - if (lenThis < lenThat) - return false; // that must be smaller than this - - return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0; + return ::endsWith(peek(), p); } // ----------------------------------------------------- Bool AsciiString::endsWithNoCase(const char* p) const { - if (*p == 0) - return true; // everything ends with the empty string - - int lenThis = getLength(); - int lenThat = strlen(p); - if (lenThis < lenThat) - return false; // that must be smaller than this - - return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0; + return ::endsWithNoCase(peek(), p); } //----------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/Common/System/UnicodeString.cpp b/Core/GameEngine/Source/Common/System/UnicodeString.cpp index b6c6ba2b81..3f0fe47b04 100644 --- a/Core/GameEngine/Source/Common/System/UnicodeString.cpp +++ b/Core/GameEngine/Source/Common/System/UnicodeString.cpp @@ -406,6 +406,30 @@ void UnicodeString::format_va(const WideChar* format, va_list args) } } +// ----------------------------------------------------- +Bool UnicodeString::startsWith(const WideChar* p) const +{ + return ::startsWith(peek(), p); +} + +// ----------------------------------------------------- +Bool UnicodeString::startsWithNoCase(const WideChar* p) const +{ + return ::startsWithNoCase(peek(), p); +} + +// ----------------------------------------------------- +Bool UnicodeString::endsWith(const WideChar* p) const +{ + return ::endsWith(peek(), p); +} + +// ----------------------------------------------------- +Bool UnicodeString::endsWithNoCase(const WideChar* p) const +{ + return ::endsWithNoCase(peek(), p); +} + //----------------------------------------------------------------------------- Bool UnicodeString::nextToken(UnicodeString* tok, UnicodeString delimiters) { diff --git a/Core/Libraries/Source/WWVegas/WWLib/stringex.h b/Core/Libraries/Source/WWVegas/WWLib/stringex.h index fe452ac218..8e23b0227a 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/stringex.h +++ b/Core/Libraries/Source/WWVegas/WWLib/stringex.h @@ -20,6 +20,7 @@ #include "bittype.h" #include +#include // Declaration @@ -55,6 +56,14 @@ template size_t strlmove_t(char (&dst)[Size], const char *src); template size_t strlmcat_t(char (&dst)[Size], const char *src); #endif +template int strncmp_t(const T *str1, const T *str2, size_t maxcount); +template int strnicmp_t(const T* str1, const T* str2, size_t maxcount); + +template bool startsWith(const T *str, const T *prefix); +template bool startsWithNoCase(const T *str, const T *prefix); +template bool endsWithNoCase(const T *str, const T *suffix); +template bool endsWithNoCase(const T *str, const T *suffix); + // Implementation @@ -182,3 +191,112 @@ template size_t strlcat_t(char (&dst)[Size], const char *src) { ret template size_t strlmove_t(char (&dst)[Size], const char *src) { return strlmove_t(dst, src, Size); } template size_t strlmcat_t(char (&dst)[Size], const char *src) { return strlmcat_t(dst, src, Size); } #endif + +// Templated strncmp. +// Compares up to maxcount chars or a null byte is encountered, whichever comes first. +// Returns < 0 if str1 is less than str2, 0 is str1 and str2 are equal and > 0 if str2 is greater than str1. +template int strncmp_t(const T *str1, const T *str2, const size_t maxcount) +{ + for (size_t i = 0; i < maxcount; ++i) + { + const T c1 = str1[i]; + const T c2 = str2[i]; + const int diff = (int)c1 - (int)c2; + if (diff != 0) + { + return diff; + } + if (c1 == T(0)) // both c1 and c2 are null terminators + { + return 0; + } + } + return 0; +} + +// Lower case conversion helpers +inline char tolower_t(char c) +{ + // cast to unsigned char for correct behavior of tolower() + return (char)tolower((unsigned char)c); +} + +inline wchar_t tolower_t(wchar_t c) +{ + return (wchar_t)towlower(c); +} + +// Templated strnicmp. +// Case insensitively compares up to maxcount chars or a null byte is encountered, whichever comes first. +// Returns < 0 if str1 is less than str2, 0 is str1 and str2 are equal and > 0 if str2 is greater than str1. +template int strnicmp_t(const T *str1, const T *str2, const size_t maxcount) +{ + for (size_t i = 0; i < maxcount; ++i) + { + const T c1 = tolower_t(str1[i]); + const T c2 = tolower_t(str2[i]); + const int diff = (int)c1 - (int)c2; + if (diff != 0) + { + return diff; + } + if (c1 == T(0)) // both c1 and c2 are null terminators + { + return 0; + } + } + return 0; +} + +template inline bool startsWith(const T *str, const T *prefix) +{ + if (*prefix == T(0)) + return true; // everything starts with the empty string + + const size_t strlen = strlen_t(str); + const size_t prefixlen = strlen_t(prefix); + if (strlen < prefixlen) + return false; // prefix must be as long or shorter than str + + return strncmp_t(str, prefix, prefixlen) == 0; +} + +template inline bool startsWithNoCase(const T *str, const T *prefix) +{ + if (*prefix == T(0)) + return true; // everything starts with the empty string + + const size_t strlen = strlen_t(str); + const size_t prefixlen = strlen_t(prefix); + if (strlen < prefixlen) + return false; // prefix must be as long or shorter than str + + return strnicmp_t(str, prefix, prefixlen) == 0; +} + +template inline bool endsWith(const T *str, const T *suffix) +{ + if (*suffix == T(0)) + return true; // everything ends with the empty string + + const size_t strlen = strlen_t(str); + const size_t suffixlen = strlen_t(suffix); + if (strlen < suffixlen) + return false; // suffix must be as long or shorter than str + + return strncmp_t(str + strlen - suffixlen, suffix, suffixlen) == 0; +} + +template inline bool endsWithNoCase(const T *str, const T *suffix) +{ + if (*suffix == T(0)) + return true; // everything ends with the empty string + + const size_t strlen = strlen_t(str); + const size_t suffixlen = strlen_t(suffix); + if (strlen < suffixlen) + return false; // suffix must be as long or shorter than str + + return strnicmp_t(str + strlen - suffixlen, suffix, suffixlen) == 0; +} +