From 95bd231444d793356bdc32f5e9e6b06d2272b35c Mon Sep 17 00:00:00 2001
From: disarray2077 <86157825+disarray2077@users.noreply.github.com>
Date: Mon, 4 Jul 2022 23:39:52 -0300
Subject: [PATCH 1/3] Return Utc in GetLocalTimeZoneFromTzFile
---
BeefLibs/corlib/src/TimeZoneInfo.bf | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/BeefLibs/corlib/src/TimeZoneInfo.bf b/BeefLibs/corlib/src/TimeZoneInfo.bf
index a14a4127..cda8c21d 100644
--- a/BeefLibs/corlib/src/TimeZoneInfo.bf
+++ b/BeefLibs/corlib/src/TimeZoneInfo.bf
@@ -1942,7 +1942,8 @@ namespace System {
}
// the data returned from the PAL is completely bogus; return a dummy entry
return CreateCustomTimeZone(c_localId, TimeSpan.Zero, c_localId, c_localId);*/
- Runtime.NotImplemented();
+ // TODO: Not implemented.
+ return Utc;
}
#endif // !FEATURE_WIN32_REGISTRY
From debcf7eb6c544f96630c19b7bac1ec8123c3d676 Mon Sep 17 00:00:00 2001
From: disarray2077 <86157825+disarray2077@users.noreply.github.com>
Date: Tue, 5 Jul 2022 03:41:14 -0300
Subject: [PATCH 2/3] Add willdcard check in BfpFindFileData_CheckFilter
---
BeefRT/BeefDbg/BeefDbg.vcxproj | 2 +
BeefRT/BeefDbg/BeefDbg.vcxproj.filters | 9 ++++
BeefRT/BeefRT.vcxproj | 2 +
BeefRT/BeefRT.vcxproj.filters | 9 ++++
BeefRT/CMakeLists.txt | 5 ++-
BeefySysLib/BeefySysLib.vcxproj | 2 +
BeefySysLib/BeefySysLib.vcxproj.filters | 9 ++++
BeefySysLib/BeefySysLib_static.vcxproj | 2 +
.../BeefySysLib_static.vcxproj.filters | 9 ++++
BeefySysLib/CMakeLists.txt | 1 +
BeefySysLib/platform/posix/PosixCommon.cpp | 6 ++-
BeefySysLib/platform/win/Platform.cpp | 41 +++++++++++++------
12 files changed, 81 insertions(+), 16 deletions(-)
diff --git a/BeefRT/BeefDbg/BeefDbg.vcxproj b/BeefRT/BeefDbg/BeefDbg.vcxproj
index f8702896..e358105e 100644
--- a/BeefRT/BeefDbg/BeefDbg.vcxproj
+++ b/BeefRT/BeefDbg/BeefDbg.vcxproj
@@ -140,6 +140,7 @@
+
@@ -356,6 +357,7 @@
+
diff --git a/BeefRT/BeefDbg/BeefDbg.vcxproj.filters b/BeefRT/BeefDbg/BeefDbg.vcxproj.filters
index 05028bff..a3a371a5 100644
--- a/BeefRT/BeefDbg/BeefDbg.vcxproj.filters
+++ b/BeefRT/BeefDbg/BeefDbg.vcxproj.filters
@@ -25,6 +25,9 @@
{0027c869-120a-44d3-80e6-e2ab12ce83bc}
+
+ {59aee039-211d-47df-abb4-ca95d75d2c56}
+
{8e5503bc-1b01-46f1-be03-bb504d93f05d}
@@ -105,6 +108,9 @@
BeefySysLib\third_party\utf8proc
+
+ BeefySysLib\third_party\putty
+
BeefySysLib\util
@@ -326,6 +332,9 @@
BeefySysLib\third_party\utf8proc
+
+ BeefySysLib\third_party\putty
+
BeefySysLib\util
diff --git a/BeefRT/BeefRT.vcxproj b/BeefRT/BeefRT.vcxproj
index f4e02f8c..4f781e79 100644
--- a/BeefRT/BeefRT.vcxproj
+++ b/BeefRT/BeefRT.vcxproj
@@ -252,6 +252,7 @@
TurnOffAllWarnings
+
@@ -271,6 +272,7 @@
+
diff --git a/BeefRT/BeefRT.vcxproj.filters b/BeefRT/BeefRT.vcxproj.filters
index 2c8d57e8..b911d04f 100644
--- a/BeefRT/BeefRT.vcxproj.filters
+++ b/BeefRT/BeefRT.vcxproj.filters
@@ -28,6 +28,9 @@
{f94ea9c5-428b-4925-a59e-b7688752d7d7}
+
+ {e790c845-8b10-4edd-b2c0-71e35b0d80b2}
+
@@ -87,6 +90,9 @@
BeefySysLib\third_party\libffi
+
+ BeefySysLib\third_party\putty
+
rt
@@ -148,6 +154,9 @@
BeefySysLib\third_party\libffi\x86
+
+ BeefySysLib\third_party\putty
+
diff --git a/BeefRT/CMakeLists.txt b/BeefRT/CMakeLists.txt
index ed65705b..95ed5308 100644
--- a/BeefRT/CMakeLists.txt
+++ b/BeefRT/CMakeLists.txt
@@ -219,8 +219,9 @@ file(GLOB SRC_FILES
../BeefySysLib/util/BeefPerf.cpp
../BeefySysLib/util/String.cpp
../BeefySysLib/util/UTF8.cpp
- ../BeefySysLib/util/Hash.cpp
- ../BeefySysLib/third_party/utf8proc/utf8proc.c
+ ../BeefySysLib/util/Hash.cpp
+ ../BeefySysLib/third_party/utf8proc/utf8proc.c
+ ../BeefySysLib/third_party/putty/wildcard.c
)
if (${IOS})
diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj
index d9ee8d82..36f61bf6 100644
--- a/BeefySysLib/BeefySysLib.vcxproj
+++ b/BeefySysLib/BeefySysLib.vcxproj
@@ -1926,6 +1926,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"TurnOffAllWarnings
TurnOffAllWarnings
+
@@ -2152,6 +2153,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"
+
diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters
index 3b2dc190..f766dc93 100644
--- a/BeefySysLib/BeefySysLib.vcxproj.filters
+++ b/BeefySysLib/BeefySysLib.vcxproj.filters
@@ -78,6 +78,9 @@
{a159b9ee-1a71-44f5-a412-1e01e20b70c7}
+
+ {0007a912-9292-4e32-8884-0f0cd276e42d}
+
@@ -731,6 +734,9 @@
src\util
+
+ src\third_party\putty
+
@@ -1123,6 +1129,9 @@
src\util
+
+ src\third_party\putty
+
diff --git a/BeefySysLib/BeefySysLib_static.vcxproj b/BeefySysLib/BeefySysLib_static.vcxproj
index e81e9e3a..66f06622 100644
--- a/BeefySysLib/BeefySysLib_static.vcxproj
+++ b/BeefySysLib/BeefySysLib_static.vcxproj
@@ -859,6 +859,7 @@
Level1
Level1
+
@@ -1025,6 +1026,7 @@
+
diff --git a/BeefySysLib/BeefySysLib_static.vcxproj.filters b/BeefySysLib/BeefySysLib_static.vcxproj.filters
index 34f64889..62458bbe 100644
--- a/BeefySysLib/BeefySysLib_static.vcxproj.filters
+++ b/BeefySysLib/BeefySysLib_static.vcxproj.filters
@@ -69,6 +69,9 @@
{d32cb9b0-e79b-49fe-82e2-33302be0baf6}
+
+ {bec18ceb-a7ca-4150-99ee-60a16944b93c}
+
@@ -584,6 +587,9 @@
src\third_party\miniz
+
+ src\third_party\putty
+
@@ -898,6 +904,9 @@
src\third_party\miniz
+
+ src\third_party\putty
+
diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt
index 9a1f1e25..13f954dd 100644
--- a/BeefySysLib/CMakeLists.txt
+++ b/BeefySysLib/CMakeLists.txt
@@ -274,6 +274,7 @@ file(GLOB SRC_FILES
third_party/zlib/uncompr.c
third_party/zlib/zutil.c
third_party/miniz/miniz.c
+ third_party/putty/wildcard.c
util/AllocDebug.cpp
util/BeefPerf.cpp
util/BSpline.cpp
diff --git a/BeefySysLib/platform/posix/PosixCommon.cpp b/BeefySysLib/platform/posix/PosixCommon.cpp
index 15b9c611..03f70b4b 100644
--- a/BeefySysLib/platform/posix/PosixCommon.cpp
+++ b/BeefySysLib/platform/posix/PosixCommon.cpp
@@ -22,6 +22,7 @@
#include "../../util/CritSect.h"
#include "../../util/Dictionary.h"
#include "../../util/Hash.h"
+#include "../../third_party/putty/wildcard.h"
#ifdef BFP_HAS_EXECINFO
#include
#endif
@@ -2378,9 +2379,10 @@ static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
{
if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
return false;
- }
+ }
- //TODO: Check actual wildcards.
+ if (!wc_match(findData->mWildcard.c_str(), findData->mDirEnt->d_name))
+ return false;
return true;
}
diff --git a/BeefySysLib/platform/win/Platform.cpp b/BeefySysLib/platform/win/Platform.cpp
index abb61588..9accafd6 100644
--- a/BeefySysLib/platform/win/Platform.cpp
+++ b/BeefySysLib/platform/win/Platform.cpp
@@ -25,6 +25,7 @@
#include "../util/CritSect.h"
#include "../util/Dictionary.h"
#include "../util/HashSet.h"
+#include "../../third_party/putty/wildcard.h"
#include "util/AllocDebug.h"
@@ -3485,6 +3486,7 @@ struct BfpFindFileData
{
BfpFindFileFlags mFlags;
WIN32_FIND_DATA mFindData;
+ Beefy::String mWildcard;
HANDLE mHandle;
};
@@ -3496,29 +3498,43 @@ static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
bool isDir = (findData->mFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (isDir)
{
- if ((findData->mFlags & BfpFindFileFlag_Directories) != 0)
- {
- if ((wcscmp(findData->mFindData.cFileName, L".") == 0) || (wcscmp(findData->mFindData.cFileName, L"..") == 0))
- {
- return false;
- }
- return true;
- }
+ if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)
+ return false;
+
+ if ((wcscmp(findData->mFindData.cFileName, L".") == 0) || (wcscmp(findData->mFindData.cFileName, L"..") == 0))
+ return false;
}
else
{
- if ((findData->mFlags & BfpFindFileFlag_Files) != 0)
- return true;
+ if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
+ return false;
}
- return false;
+
+ Beefy::String fileName = UTF8Encode(findData->mFindData.cFileName);
+ if (!wc_match(findData->mWildcard.c_str(), fileName.c_str()))
+ return false;
+
+ return true;
}
BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
{
- UTF16String wPath = UTF8Decode(path);
+ Beefy::String findStr = path;
+ Beefy::String wildcard;
+
+ int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
+ if (lastSlashPos != -1)
+ {
+ wildcard = findStr.Substring(lastSlashPos + 1);
+ findStr = findStr.Substring(0, lastSlashPos + 1);
+ findStr.Append("*");
+ }
+ if (wildcard == "*.*")
+ wildcard = "*";
BfpFindFileData* findData = new BfpFindFileData();
findData->mFlags = flags;
+ findData->mWildcard = wildcard;
FINDEX_SEARCH_OPS searchOps;
if ((flags & BfpFindFileFlag_Files) == 0)
@@ -3526,6 +3542,7 @@ BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const cha
else
searchOps = FindExSearchNameMatch;
+ UTF16String wPath = UTF8Decode(findStr);
findData->mHandle = ::FindFirstFileExW(wPath.c_str(), FindExInfoBasic, &findData->mFindData, searchOps, NULL, 0);
if (findData->mHandle == INVALID_HANDLE_VALUE)
{
From 9482c0c1d35d3cbe4471489893e4161284abdf82 Mon Sep 17 00:00:00 2001
From: disarray2077 <86157825+disarray2077@users.noreply.github.com>
Date: Wed, 6 Jul 2022 20:12:38 -0300
Subject: [PATCH 3/3] Add missing files
---
BeefySysLib/third_party/putty/LICENSE | 28 ++
BeefySysLib/third_party/putty/wildcard.c | 340 +++++++++++++++++++++++
BeefySysLib/third_party/putty/wildcard.h | 18 ++
3 files changed, 386 insertions(+)
create mode 100644 BeefySysLib/third_party/putty/LICENSE
create mode 100644 BeefySysLib/third_party/putty/wildcard.c
create mode 100644 BeefySysLib/third_party/putty/wildcard.h
diff --git a/BeefySysLib/third_party/putty/LICENSE b/BeefySysLib/third_party/putty/LICENSE
new file mode 100644
index 00000000..478b68d8
--- /dev/null
+++ b/BeefySysLib/third_party/putty/LICENSE
@@ -0,0 +1,28 @@
+PuTTY is copyright 1997-2020 Simon Tatham.
+
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
+Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian
+Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav
+Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff,
+and CORE SDI S.A.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/BeefySysLib/third_party/putty/wildcard.c b/BeefySysLib/third_party/putty/wildcard.c
new file mode 100644
index 00000000..2b1f858f
--- /dev/null
+++ b/BeefySysLib/third_party/putty/wildcard.c
@@ -0,0 +1,340 @@
+/*
+ * Wildcard matching engine for use with SFTP-based file transfer
+ * programs (PSFTP, new-look PSCP): since SFTP has no notion of
+ * getting the remote side to do globbing (and rightly so) we have
+ * to do it locally, by retrieving all the filenames in a directory
+ * and checking each against the wildcard pattern.
+ */
+
+#include
+#include
+#include
+
+#include "wildcard.h"
+
+/*
+ * Definition of wildcard syntax:
+ *
+ * - * matches any sequence of characters, including zero.
+ * - ? matches exactly one character which can be anything.
+ * - [abc] matches exactly one character which is a, b or c.
+ * - [a-f] matches anything from a through f.
+ * - [^a-f] matches anything _except_ a through f.
+ * - [-_] matches - or _; [^-_] matches anything else. (The - is
+ * non-special if it occurs immediately after the opening
+ * bracket or ^.)
+ * - [a^] matches an a or a ^. (The ^ is non-special if it does
+ * _not_ occur immediately after the opening bracket.)
+ * - \*, \?, \[, \], \\ match the single characters *, ?, [, ], \.
+ * - All other characters are non-special and match themselves.
+ */
+
+/*
+ * Some notes on differences from POSIX globs (IEEE Std 1003.1, 2003 ed.):
+ * - backslashes act as escapes even within [] bracket expressions
+ * - does not support [!...] for non-matching list (POSIX are weird);
+ * NB POSIX allows [^...] as well via "A bracket expression starting
+ * with an unquoted circumflex character produces unspecified
+ * results". If we wanted to allow [!...] we might want to define
+ * [^!] as having its literal meaning (match '^' or '!').
+ * - none of the scary [[:class:]] stuff, etc
+ */
+
+/*
+ * The wildcard matching technique we use is very simple and
+ * potentially O(N^2) in running time, but I don't anticipate it
+ * being that bad in reality (particularly since N will be the size
+ * of a filename, which isn't all that much). Perhaps one day, once
+ * PuTTY has grown a regexp matcher for some other reason, I might
+ * come back and reimplement wildcards by translating them into
+ * regexps or directly into NFAs; but for the moment, in the
+ * absence of any other need for the NFA->DFA translation engine,
+ * anything more than the simplest possible wildcard matcher is
+ * vast code-size overkill.
+ *
+ * Essentially, these wildcards are much simpler than regexps in
+ * that they consist of a sequence of rigid fragments (? and [...]
+ * can never match more or less than one character) separated by
+ * asterisks. It is therefore extremely simple to look at a rigid
+ * fragment and determine whether or not it begins at a particular
+ * point in the test string; so we can search along the string
+ * until we find each fragment, then search for the next. As long
+ * as we find each fragment in the _first_ place it occurs, there
+ * will never be a danger of having to backpedal and try to find it
+ * again somewhere else.
+ */
+
+enum {
+ WC_TRAILINGBACKSLASH = 1,
+ WC_UNCLOSEDCLASS,
+ WC_INVALIDRANGE
+};
+
+/*
+ * Error reporting is done by returning various negative values
+ * from the wildcard routines. Passing any such value to wc_error
+ * will give a human-readable message.
+ */
+const char *wc_error(int value)
+{
+ value = abs(value);
+ switch (value) {
+ case WC_TRAILINGBACKSLASH:
+ return "'\' occurred at end of string (expected another character)";
+ case WC_UNCLOSEDCLASS:
+ return "expected ']' to close character class";
+ case WC_INVALIDRANGE:
+ return "character range was not terminated (']' just after '-')";
+ }
+ return "INTERNAL ERROR: unrecognised wildcard error number";
+}
+
+/*
+ * This is the routine that tests a target string to see if an
+ * initial substring of it matches a fragment. If successful, it
+ * returns 1, and advances both `fragment' and `target' past the
+ * fragment and matching substring respectively. If unsuccessful it
+ * returns zero. If the wildcard fragment suffers a syntax error,
+ * it returns <0 and the precise value indexes into wc_error.
+ */
+static int wc_match_fragment(const char **fragment, const char **target,
+ const char *target_end)
+{
+ const char *f, *t;
+
+ f = *fragment;
+ t = *target;
+ /*
+ * The fragment terminates at either the end of the string, or
+ * the first (unescaped) *.
+ */
+ while (*f && *f != '*' && t < target_end) {
+ /*
+ * Extract one character from t, and one character's worth
+ * of pattern from f, and step along both. Return 0 if they
+ * fail to match.
+ */
+ if (*f == '\\') {
+ /*
+ * Backslash, which means f[1] is to be treated as a
+ * literal character no matter what it is. It may not
+ * be the end of the string.
+ */
+ if (!f[1])
+ return -WC_TRAILINGBACKSLASH; /* error */
+ if (f[1] != *t)
+ return 0; /* failed to match */
+ f += 2;
+ } else if (*f == '?') {
+ /*
+ * Question mark matches anything.
+ */
+ f++;
+ } else if (*f == '[') {
+ bool invert = false;
+ bool matched = false;
+ /*
+ * Open bracket introduces a character class.
+ */
+ f++;
+ if (*f == '^') {
+ invert = true;
+ f++;
+ }
+ while (*f != ']') {
+ if (*f == '\\')
+ f++; /* backslashes still work */
+ if (!*f)
+ return -WC_UNCLOSEDCLASS; /* error again */
+ if (f[1] == '-') {
+ int lower, upper, ourchr;
+ lower = (unsigned char) *f++;
+ f++; /* eat the minus */
+ if (*f == ']')
+ return -WC_INVALIDRANGE; /* different error! */
+ if (*f == '\\')
+ f++; /* backslashes _still_ work */
+ if (!*f)
+ return -WC_UNCLOSEDCLASS; /* error again */
+ upper = (unsigned char) *f++;
+ ourchr = (unsigned char) *t;
+ if (lower > upper) {
+ int t = lower; lower = upper; upper = t;
+ }
+ if (ourchr >= lower && ourchr <= upper)
+ matched = true;
+ } else {
+ matched |= (*t == *f++);
+ }
+ }
+ if (invert == matched)
+ return 0; /* failed to match character class */
+ f++; /* eat the ] */
+ } else {
+ /*
+ * Non-special character matches itself.
+ */
+ if (*f != *t)
+ return 0;
+ f++;
+ }
+ /*
+ * Now we've done that, increment t past the character we
+ * matched.
+ */
+ t++;
+ }
+ if (!*f || *f == '*') {
+ /*
+ * We have reached the end of f without finding a mismatch;
+ * so we're done. Update the caller pointers and return 1.
+ */
+ *fragment = f;
+ *target = t;
+ return 1;
+ }
+ /*
+ * Otherwise, we must have reached the end of t before we
+ * reached the end of f; so we've failed. Return 0.
+ */
+ return 0;
+}
+
+/*
+ * This is the real wildcard matching routine. It returns 1 for a
+ * successful match, 0 for an unsuccessful match, and <0 for a
+ * syntax error in the wildcard.
+ */
+static int wc_match_inner(
+ const char *wildcard, const char *target, size_t target_len)
+{
+ const char *target_end = target + target_len;
+ int ret;
+
+ /*
+ * Every time we see a '*' _followed_ by a fragment, we just
+ * search along the string for a location at which the fragment
+ * matches. The only special case is when we see a fragment
+ * right at the start, in which case we just call the matching
+ * routine once and give up if it fails.
+ */
+ if (*wildcard != '*') {
+ ret = wc_match_fragment(&wildcard, &target, target_end);
+ if (ret <= 0)
+ return ret; /* pass back failure or error alike */
+ }
+
+ while (*wildcard) {
+ assert(*wildcard == '*');
+ while (*wildcard == '*')
+ wildcard++;
+
+ /*
+ * It's possible we've just hit the end of the wildcard
+ * after seeing a *, in which case there's no need to
+ * bother searching any more because we've won.
+ */
+ if (!*wildcard)
+ return 1;
+
+ /*
+ * Now `wildcard' points at the next fragment. So we
+ * attempt to match it against `target', and if that fails
+ * we increment `target' and try again, and so on. When we
+ * find we're about to try matching against the empty
+ * string, we give up and return 0.
+ */
+ ret = 0;
+ while (*target) {
+ const char *save_w = wildcard, *save_t = target;
+
+ ret = wc_match_fragment(&wildcard, &target, target_end);
+
+ if (ret < 0)
+ return ret; /* syntax error */
+
+ if (ret > 0 && !*wildcard && target != target_end) {
+ /*
+ * Final special case - literally.
+ *
+ * This situation arises when we are matching a
+ * _terminal_ fragment of the wildcard (that is,
+ * there is nothing after it, e.g. "*a"), and it
+ * has matched _too early_. For example, matching
+ * "*a" against "parka" will match the "a" fragment
+ * against the _first_ a, and then (if it weren't
+ * for this special case) matching would fail
+ * because we're at the end of the wildcard but not
+ * at the end of the target string.
+ *
+ * In this case what we must do is measure the
+ * length of the fragment in the target (which is
+ * why we saved `target'), jump straight to that
+ * distance from the end of the string using
+ * strlen, and match the same fragment again there
+ * (which is why we saved `wildcard'). Then we
+ * return whatever that operation returns.
+ */
+ target = target_end - (target - save_t);
+ wildcard = save_w;
+ return wc_match_fragment(&wildcard, &target, target_end);
+ }
+
+ if (ret > 0)
+ break;
+ target++;
+ }
+ if (ret > 0)
+ continue;
+ return 0;
+ }
+
+ /*
+ * If we reach here, it must be because we successfully matched
+ * a fragment and then found ourselves right at the end of the
+ * wildcard. Hence, we return 1 if and only if we are also
+ * right at the end of the target.
+ */
+ return target == target_end;
+}
+
+int wc_match(const char *wildcard, const char *target)
+{
+ return wc_match_inner(wildcard, target, strlen(target));
+}
+
+/*
+ * Another utility routine that translates a non-wildcard string
+ * into its raw equivalent by removing any escaping backslashes.
+ * Expects a target string buffer of anything up to the length of
+ * the original wildcard. You can also pass NULL as the output
+ * buffer if you're only interested in the return value.
+ *
+ * Returns true on success, or false if a wildcard character was
+ * encountered. In the latter case the output string MAY not be
+ * zero-terminated and you should not use it for anything!
+ */
+bool wc_unescape(char *output, const char *wildcard)
+{
+ while (*wildcard) {
+ if (*wildcard == '\\') {
+ wildcard++;
+ /* We are lenient about trailing backslashes in non-wildcards. */
+ if (*wildcard) {
+ if (output)
+ *output++ = *wildcard;
+ wildcard++;
+ }
+ } else if (*wildcard == '*' || *wildcard == '?' ||
+ *wildcard == '[' || *wildcard == ']') {
+ return false; /* it's a wildcard! */
+ } else {
+ if (output)
+ *output++ = *wildcard;
+ wildcard++;
+ }
+ }
+ if (output)
+ *output = '\0';
+ return true; /* it's clean */
+}
\ No newline at end of file
diff --git a/BeefySysLib/third_party/putty/wildcard.h b/BeefySysLib/third_party/putty/wildcard.h
new file mode 100644
index 00000000..3c4a75ee
--- /dev/null
+++ b/BeefySysLib/third_party/putty/wildcard.h
@@ -0,0 +1,18 @@
+#ifndef PUTTY_WILDCARD_H_INCLUDE
+#define PUTTY_WILDCARD_H_INCLUDE
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *wc_error(int value);
+int wc_match(const char *wildcard, const char *target);
+bool wc_unescape(char *output, const char *wildcard);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PUTTY_WILDCARD_H_INCLUDE
\ No newline at end of file