From ee35d256aa485d35be1bab307ec9833780934577 Mon Sep 17 00:00:00 2001 From: Lukas Werling Date: Sat, 2 Feb 2019 23:52:02 +0100 Subject: [PATCH] Add basic character classes to WildcardMatch() --- src/platform/C4SoundSystem.cpp | 2 +- src/platform/StdFile.cpp | 35 +++++++++++++++++++++++++++---- tests/CMakeLists.txt | 2 +- tests/StdFileTest.cpp | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 tests/StdFileTest.cpp diff --git a/src/platform/C4SoundSystem.cpp b/src/platform/C4SoundSystem.cpp index 4eaf61043..6c64d270c 100644 --- a/src/platform/C4SoundSystem.cpp +++ b/src/platform/C4SoundSystem.cpp @@ -89,7 +89,7 @@ void C4SoundSystem::Execute() C4SoundEffect* C4SoundSystem::GetEffect(const char *szSndName) { // Remember wildcards before adding .* extension - if there are 2 versions with different file extensions, play the last added - bool bRandomSound = SCharCount('?',szSndName) || SCharCount('*',szSndName); + bool bRandomSound = IsWildcardString(szSndName); // Evaluate sound name char szName[C4MaxSoundName+2+1]; SCopy(szSndName,szName,C4MaxSoundName); diff --git a/src/platform/StdFile.cpp b/src/platform/StdFile.cpp index 21038b480..b6db6d677 100644 --- a/src/platform/StdFile.cpp +++ b/src/platform/StdFile.cpp @@ -364,8 +364,33 @@ bool IsWildcardString(const char *szString) { // safety if (!szString) return false; - // known wildcard characters: *? - return (SCharCount('?', szString)>0) || (SCharCount('*', szString)>0); + // known wildcard characters: *?[ + return (SCharCount('?', szString)>0) || (SCharCount('*', szString)>0) || (SCharCount('[', szString)>0); +} + +// Parses a character class, skips over it and returns whether it matches the given character. +static bool WildcardMatchCharacterClass(const char **charclass, char matchchar) +{ + const char *cc = *charclass; + if (*cc++ != '[') return false; + bool match = false; + // ] is allowed as first character, e.g. []] + do + { + // range, e.g. [a-z] + if (cc[1] == '-' && cc[2] != ']') + { + if (matchchar >= cc[0] && matchchar <= cc[2]) + match = true; + cc += 2; + } + // single character + else if (*cc == matchchar) + match = true; + } + while (*++cc && *cc != ']'); + *charclass = cc; + return match; } bool WildcardMatch(const char *szWildcard, const char *szString) @@ -382,8 +407,10 @@ bool WildcardMatch(const char *szWildcard, const char *szString) // nothing left to match? else if (!*pPos) break; - // equal or one-character-wildcard? proceed - else if (*pWild == '?' || tolower(*pWild) == tolower(*pPos)) + // character class, equal or one-character-wildcard? proceed + else if ((*pWild == '[' && WildcardMatchCharacterClass(&pWild, *pPos)) + || *pWild == '?' + || tolower(*pWild) == tolower(*pPos)) { pWild++; pPos++; } // backtrack possible? else if (pLPos) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fa59eea4a..caf304a92 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -113,7 +113,7 @@ if (GTEST_FOUND AND GMOCK_FOUND) AUX_SOURCE_DIRECTORY("${CMAKE_CURRENT_LIST_DIR}" TESTS_SOURCES) add_executable(tests EXCLUDE_FROM_ALL ${TESTS_SOURCES} ${C4SCRIPT_SOURCES}) set_property(TARGET "tests" PROPERTY FOLDER "Testing") - target_link_libraries(tests gtest libc4script libmisc) + target_link_libraries(tests gtest gmock libc4script libmisc) if(UNIX AND NOT APPLE) target_link_libraries(tests rt) endif() diff --git a/tests/StdFileTest.cpp b/tests/StdFileTest.cpp new file mode 100644 index 000000000..66caea813 --- /dev/null +++ b/tests/StdFileTest.cpp @@ -0,0 +1,38 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2019, The OpenClonk Team and contributors + * + * Distributed under the terms of the ISC license; see accompanying file + * "COPYING" for details. + * + * "Clonk" is a registered trademark of Matthes Bender, used with permission. + * See accompanying file "TRADEMARK" for details. + * + * To redistribute this file separately, substitute the full license texts + * for the above references. + */ + +#include +#include "platform/StdFile.h" + +#include + +TEST(StdFileTest, IsWildcardStringTest) +{ + EXPECT_TRUE(IsWildcardString("ab*cde")); + EXPECT_TRUE(IsWildcardString("abcd?e")); + EXPECT_TRUE(IsWildcardString("[abc]de")); + EXPECT_FALSE(IsWildcardString("foobar")); +} + +TEST(StdFileTest, WildcardMatchTest) +{ + EXPECT_TRUE(WildcardMatch("abc*", "abcdefg")); + EXPECT_FALSE(WildcardMatch("abc*", "Xabcdefg")); + EXPECT_TRUE(WildcardMatch("a?c*g", "abcdefg")); + EXPECT_TRUE(WildcardMatch("a[1-9]?", "a5b")); + EXPECT_TRUE(WildcardMatch("a[abc][A-Z]", "acX")); + EXPECT_TRUE(WildcardMatch("[[]", "[")); + EXPECT_TRUE(WildcardMatch("[[-]", "-")); +}