Skip to content

Commit

Permalink
Implements a new Glob matching function. (Kenix3#518)
Browse files Browse the repository at this point in the history
* Implements a new Glob matching function.

* clang-format

* Correctly cite the glob.c file

* Correctly cite the glob.c file
  • Loading branch information
Kenix3 authored Apr 26, 2024
1 parent dedff4f commit 54a387a
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 20 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ LUS makes use of the following third party libraries and resources:
- [sdl2](https://github.com/libsdl-org/SDL) (zlib) window manager, controllers, and audio player
- [glew](https://github.com/nigels-com/glew) (modified BSD-3-Clause and MIT) OpenGL extension loading library.
- [libzip](https://github.com/nih-at/) (BSD-3-Clause) read `.zip` compatible archives
- [glob_match] (https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c) (Dual MIT/GPL) Glob pattern matching.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ target_sources(libultraship PRIVATE ${Source_Files__Window__Gui})

#=================== Utils ===================

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

if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(APPEND Source_Files__Utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/OSXFolderManager.mm)
Expand Down
6 changes: 1 addition & 5 deletions src/resource/ResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@
#include <algorithm>
#include <thread>
#include <Utils/StringHelper.h>
#include "utils/glob.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"

// TODO: Delete me and find an implementation
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
// NOLINTNEXTLINE
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);

namespace LUS {

ResourceManager::ResourceManager() {
Expand Down
8 changes: 2 additions & 6 deletions src/resource/archive/Archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@
#include "resource/File.h"
#include "resource/ResourceLoader.h"
#include "utils/binarytools/MemoryStream.h"
#include "utils/glob.h"
#include <StrHash64.h>
#include <nlohmann/json.hpp>

// TODO: Delete me and find an implementation
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
// NOLINTNEXTLINE
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);

namespace LUS {
Archive::Archive(const std::string& path)
: mHasGameVersion(false), mGameVersion(UNKNOWN_GAME_VERSION), mPath(path), mIsLoaded(false) {
Expand Down Expand Up @@ -66,7 +62,7 @@ std::shared_ptr<std::unordered_map<uint64_t, std::string>> Archive::ListFiles(co

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

return result;
Expand Down
11 changes: 3 additions & 8 deletions src/resource/archive/ArchiveManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
#include "resource/archive/OtrArchive.h"
#include "resource/archive/O2rArchive.h"
#include "Utils/StringHelper.h"
#include "utils/glob.h"
#include <StrHash64.h>

// TODO: Delete me and find an implementation
// Comes from stormlib. May not be the most efficient, but it's also important to be consistent.
// NOLINTNEXTLINE
extern bool SFileCheckWildCard(const char* szString, const char* szWildCard);

namespace LUS {
ArchiveManager::ArchiveManager() {
}
Expand Down Expand Up @@ -72,9 +68,8 @@ std::shared_ptr<std::vector<std::string>> ArchiveManager::ListFiles(const std::s
auto list = ListFiles();
auto result = std::make_shared<std::vector<std::string>>();

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

return result;
}
Expand Down
123 changes: 123 additions & 0 deletions src/utils/glob.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "glob.h"
#include <stddef.h>

// This has been borrowed from the dual MIT/GPL licensed glob module from the Linux kernel under the MIT license.
// See https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c

/**
* glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
* @str: String to match. The pattern must match the entire string.
*
* Perform shell-style glob matching, returning true (1) if the match
* succeeds, or false (0) if it fails. Equivalent to !fnmatch(@pat, @str, 0).
*
* Pattern metacharacters are ?, *, [ and \.
* (And, inside character classes, !, - and ].)
*
* This is small and simple implementation intended for device blacklists
* where a string is matched against a number of patterns. Thus, it
* does not preprocess the patterns. It is non-recursive, and run-time
* is at most quadratic: strlen(@str)*strlen(@pat).
*
* An example of the worst case is glob_match("*aaaaa", "aaaaaaaaaa");
* it takes 6 passes over the pattern before matching the string.
*
* Like !fnmatch(@pat, @str, 0) and unlike the shell, this does NOT
* treat / or leading . specially; it isn't actually used for pathnames.
*
* Note that according to glob(7) (and unlike bash), character classes
* are complemented by a leading !; this does not support the regex-style
* [^a-z] syntax.
*
* An opening bracket without a matching close is matched literally.
*/
#ifdef __cplusplus
extern "C" {
#endif
bool glob_match(char const* pat, char const* str) {
/*
* Backtrack to previous * on mismatch and retry starting one
* character later in the string. Because * matches all characters
* (no exception for /), it can be easily proved that there's
* never a need to backtrack multiple levels.
*/
char const *back_pat = NULL, *back_str = NULL;

/*
* Loop over each token (character or class) in pat, matching
* it against the remaining unmatched tail of str. Return false
* on mismatch, or true after matching the trailing nul bytes.
*/
for (;;) {
unsigned char c = *str++;
unsigned char d = *pat++;

switch (d) {
case '?': /* Wildcard: anything but nul */
if (c == '\0')
return false;
break;
case '*': /* Any-length wildcard */
if (*pat == '\0') /* Optimize trailing * case */
return true;
back_pat = pat;
back_str = --str; /* Allow zero-length match */
break;
case '[': { /* Character class */
bool match = false, inverted = (*pat == '!');
char const* class = pat + inverted;
unsigned char a = *class ++;

/*
* Iterate over each span in the character class.
* A span is either a single character a, or a
* range a-b. The first span may begin with ']'.
*/
do {
unsigned char b = a;

if (a == '\0') /* Malformed */
goto literal;

if (class[0] == '-' && class[1] != ']') {
b = class[1];

if (b == '\0')
goto literal;

class += 2;
/* Any special action if a > b? */
}
match |= (a <= c && c <= b);
} while ((a = *class ++) != ']');

if (match == inverted)
goto backtrack;
pat = class;
} break;
case '\\':
d = *pat++;
//fallthrough;
default: /* Literal character */
literal:
if (c == d) {
if (d == '\0')
return true;
break;
}
backtrack:
if (c == '\0' || !back_pat)
return false; /* No point continuing */
/* Try again from last *, one character later in str. */
pat = back_pat;
str = ++back_str;
break;
}
}
}

#ifdef __cplusplus
}
;
#endif
10 changes: 10 additions & 0 deletions src/utils/glob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif
bool glob_match(char const* pat, char const* str);
#ifdef __cplusplus
};
#endif

0 comments on commit 54a387a

Please sign in to comment.