Skip to content

Commit 54a387a

Browse files
authored
Implements a new Glob matching function. (Kenix3#518)
* Implements a new Glob matching function. * clang-format * Correctly cite the glob.c file * Correctly cite the glob.c file
1 parent dedff4f commit 54a387a

File tree

7 files changed

+141
-20
lines changed

7 files changed

+141
-20
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ LUS makes use of the following third party libraries and resources:
5353
- [sdl2](https://github.com/libsdl-org/SDL) (zlib) window manager, controllers, and audio player
5454
- [glew](https://github.com/nigels-com/glew) (modified BSD-3-Clause and MIT) OpenGL extension loading library.
5555
- [libzip](https://github.com/nih-at/) (BSD-3-Clause) read `.zip` compatible archives
56+
- [glob_match] (https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c) (Dual MIT/GPL) Glob pattern matching.

src/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ target_sources(libultraship PRIVATE ${Source_Files__Window__Gui})
9999

100100
#=================== Utils ===================
101101

102-
file(GLOB Source_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "utils/*.h" "utils/*.cpp")
102+
file(GLOB Source_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "utils/*.h" "utils/*.cpp" "utils/*.c")
103103

104104
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
105105
list(APPEND Source_Files__Utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/OSXFolderManager.mm)

src/resource/ResourceManager.cpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@
55
#include <algorithm>
66
#include <thread>
77
#include <Utils/StringHelper.h>
8+
#include "utils/glob.h"
89
#include "public/bridge/consolevariablebridge.h"
910
#include "Context.h"
1011

11-
// TODO: Delete me and find an implementation
12-
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
13-
// NOLINTNEXTLINE
14-
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);
15-
1612
namespace LUS {
1713

1814
ResourceManager::ResourceManager() {

src/resource/archive/Archive.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@
77
#include "resource/File.h"
88
#include "resource/ResourceLoader.h"
99
#include "utils/binarytools/MemoryStream.h"
10+
#include "utils/glob.h"
1011
#include <StrHash64.h>
1112
#include <nlohmann/json.hpp>
1213

13-
// TODO: Delete me and find an implementation
14-
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
15-
// NOLINTNEXTLINE
16-
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);
17-
1814
namespace LUS {
1915
Archive::Archive(const std::string& path)
2016
: mHasGameVersion(false), mGameVersion(UNKNOWN_GAME_VERSION), mPath(path), mIsLoaded(false) {
@@ -66,7 +62,7 @@ std::shared_ptr<std::unordered_map<uint64_t, std::string>> Archive::ListFiles(co
6662

6763
std::copy_if(mHashes->begin(), mHashes->end(), std::inserter(*result, result->begin()),
6864
[filter](const std::pair<const int64_t, const std::string&> entry) {
69-
return SFileCheckWildCard(entry.second.c_str(), filter.c_str());
65+
return glob_match(filter.c_str(), entry.second.c_str());
7066
});
7167

7268
return result;

src/resource/archive/ArchiveManager.cpp

+3-8
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,9 @@
77
#include "resource/archive/OtrArchive.h"
88
#include "resource/archive/O2rArchive.h"
99
#include "Utils/StringHelper.h"
10+
#include "utils/glob.h"
1011
#include <StrHash64.h>
1112

12-
// TODO: Delete me and find an implementation
13-
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
14-
// NOLINTNEXTLINE
15-
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);
16-
1713
namespace LUS {
1814
ArchiveManager::ArchiveManager() {
1915
}
@@ -72,9 +68,8 @@ std::shared_ptr<std::vector<std::string>> ArchiveManager::ListFiles(const std::s
7268
auto list = ListFiles();
7369
auto result = std::make_shared<std::vector<std::string>>();
7470

75-
std::copy_if(list->begin(), list->end(), std::back_inserter(*result), [filter](const std::string& filePath) {
76-
return SFileCheckWildCard(filePath.c_str(), filter.c_str());
77-
});
71+
std::copy_if(list->begin(), list->end(), std::back_inserter(*result),
72+
[filter](const std::string& filePath) { return glob_match(filter.c_str(), filePath.c_str()); });
7873

7974
return result;
8075
}

src/utils/glob.c

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#include "glob.h"
2+
#include <stddef.h>
3+
4+
// This has been borrowed from the dual MIT/GPL licensed glob module from the Linux kernel under the MIT license.
5+
// See https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c
6+
7+
/**
8+
* glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
9+
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
10+
* @str: String to match. The pattern must match the entire string.
11+
*
12+
* Perform shell-style glob matching, returning true (1) if the match
13+
* succeeds, or false (0) if it fails. Equivalent to !fnmatch(@pat, @str, 0).
14+
*
15+
* Pattern metacharacters are ?, *, [ and \.
16+
* (And, inside character classes, !, - and ].)
17+
*
18+
* This is small and simple implementation intended for device blacklists
19+
* where a string is matched against a number of patterns. Thus, it
20+
* does not preprocess the patterns. It is non-recursive, and run-time
21+
* is at most quadratic: strlen(@str)*strlen(@pat).
22+
*
23+
* An example of the worst case is glob_match("*aaaaa", "aaaaaaaaaa");
24+
* it takes 6 passes over the pattern before matching the string.
25+
*
26+
* Like !fnmatch(@pat, @str, 0) and unlike the shell, this does NOT
27+
* treat / or leading . specially; it isn't actually used for pathnames.
28+
*
29+
* Note that according to glob(7) (and unlike bash), character classes
30+
* are complemented by a leading !; this does not support the regex-style
31+
* [^a-z] syntax.
32+
*
33+
* An opening bracket without a matching close is matched literally.
34+
*/
35+
#ifdef __cplusplus
36+
extern "C" {
37+
#endif
38+
bool glob_match(char const* pat, char const* str) {
39+
/*
40+
* Backtrack to previous * on mismatch and retry starting one
41+
* character later in the string. Because * matches all characters
42+
* (no exception for /), it can be easily proved that there's
43+
* never a need to backtrack multiple levels.
44+
*/
45+
char const *back_pat = NULL, *back_str = NULL;
46+
47+
/*
48+
* Loop over each token (character or class) in pat, matching
49+
* it against the remaining unmatched tail of str. Return false
50+
* on mismatch, or true after matching the trailing nul bytes.
51+
*/
52+
for (;;) {
53+
unsigned char c = *str++;
54+
unsigned char d = *pat++;
55+
56+
switch (d) {
57+
case '?': /* Wildcard: anything but nul */
58+
if (c == '\0')
59+
return false;
60+
break;
61+
case '*': /* Any-length wildcard */
62+
if (*pat == '\0') /* Optimize trailing * case */
63+
return true;
64+
back_pat = pat;
65+
back_str = --str; /* Allow zero-length match */
66+
break;
67+
case '[': { /* Character class */
68+
bool match = false, inverted = (*pat == '!');
69+
char const* class = pat + inverted;
70+
unsigned char a = *class ++;
71+
72+
/*
73+
* Iterate over each span in the character class.
74+
* A span is either a single character a, or a
75+
* range a-b. The first span may begin with ']'.
76+
*/
77+
do {
78+
unsigned char b = a;
79+
80+
if (a == '\0') /* Malformed */
81+
goto literal;
82+
83+
if (class[0] == '-' && class[1] != ']') {
84+
b = class[1];
85+
86+
if (b == '\0')
87+
goto literal;
88+
89+
class += 2;
90+
/* Any special action if a > b? */
91+
}
92+
match |= (a <= c && c <= b);
93+
} while ((a = *class ++) != ']');
94+
95+
if (match == inverted)
96+
goto backtrack;
97+
pat = class;
98+
} break;
99+
case '\\':
100+
d = *pat++;
101+
//fallthrough;
102+
default: /* Literal character */
103+
literal:
104+
if (c == d) {
105+
if (d == '\0')
106+
return true;
107+
break;
108+
}
109+
backtrack:
110+
if (c == '\0' || !back_pat)
111+
return false; /* No point continuing */
112+
/* Try again from last *, one character later in str. */
113+
pat = back_pat;
114+
str = ++back_str;
115+
break;
116+
}
117+
}
118+
}
119+
120+
#ifdef __cplusplus
121+
}
122+
;
123+
#endif

src/utils/glob.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
#include <stdbool.h>
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
bool glob_match(char const* pat, char const* str);
8+
#ifdef __cplusplus
9+
};
10+
#endif

0 commit comments

Comments
 (0)