Skip to content

Commit a2747ad

Browse files
committed
fix: expand_absolute_path() function and utils::patternMatch()
1 parent d90b3c3 commit a2747ad

File tree

3 files changed

+70
-67
lines changed

3 files changed

+70
-67
lines changed

src/common/file_helper/filesystem/src/filesystem_wrapper.cpp

+18-11
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,28 @@ namespace filesystem_wrapper
121121

122122
if (exists(baseDir))
123123
{
124-
for (const auto& entry : list_directory(baseDir))
124+
if(is_directory(baseDir))
125125
{
126-
const auto entryName {entry.filename().string()};
127-
128-
if (Utils::patternMatch(entryName, pattern))
126+
for (const auto& entry : list_directory(baseDir))
129127
{
130-
std::string nextPath;
131-
nextPath += baseDir;
132-
nextPath += std::filesystem::path::preferred_separator;
133-
nextPath += entryName;
134-
nextPath += nextDirectoryPos == std::string::npos ? "" : path.substr(nextDirectoryPos);
135-
136-
expand_absolute_path(nextPath, output);
128+
const auto entryName {entry.filename().string()};
129+
130+
if (Utils::patternMatch(entryName, pattern))
131+
{
132+
std::string nextPath;
133+
nextPath += baseDir;
134+
nextPath += std::filesystem::path::preferred_separator;
135+
nextPath += entryName;
136+
nextPath += nextDirectoryPos == std::string::npos ? "" : path.substr(nextDirectoryPos);
137+
138+
expand_absolute_path(nextPath, output);
139+
}
137140
}
138141
}
142+
else if (Utils::patternMatch(baseDir, pattern))
143+
{
144+
output.push_back(baseDir);
145+
}
139146
}
140147
}
141148
else

src/common/file_helper/filesystem/tests/filesystem_test.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ constexpr auto PATH_TO_EXPAND_3 {"/tmp/wazuh_test/prefix_*_data/prefix*"};
2222
constexpr auto PATH_TO_EXPAND_4 {"/tmp/wazuh_test/prefix_*_data/*_1"};
2323
constexpr auto PATH_TO_EXPAND_5 {"/tmp/wazuh_test/prefix_?_data/*_1"};
2424
constexpr auto PATH_TO_EXPAND_6 {"/tmp/wazuh_test/prefix_*_data/prefix?*1"};
25+
constexpr auto PATH_TO_EXPAND_7 {"/tmp/wazuh_test/*/*1"};
2526
constexpr auto TMP_PATH {"/tmp"};
2627
constexpr auto ROOT_PATH {"/tmp/wazuh_test"};
2728
constexpr auto ROOT_PATH_1 {"/tmp/wazuh_test/prefix_1_data"};
@@ -41,6 +42,7 @@ constexpr auto PATH_TO_EXPAND_3 {"C:\\tmp\\wazuh_test\\prefix_*_data\\prefix*"};
4142
constexpr auto PATH_TO_EXPAND_4 {"C:\\tmp\\wazuh_test\\prefix_*_data\\*_1"};
4243
constexpr auto PATH_TO_EXPAND_5 {"C:\\tmp\\wazuh_test\\prefix_?_data\\*_1"};
4344
constexpr auto PATH_TO_EXPAND_6 {"C:\\tmp\\wazuh_test\\prefix_*_data\\prefix?*1"};
45+
constexpr auto PATH_TO_EXPAND_7 {"C:\\tmp\\wazuh_test\\*\\*1"};
4446
constexpr auto TMP_PATH {"C:\\tmp"};
4547
constexpr auto ROOT_PATH {"C:\\tmp\\wazuh_test"};
4648
constexpr auto ROOT_PATH_1 {"C:\\tmp\\wazuh_test\\prefix_1_data"};
@@ -218,6 +220,25 @@ TEST_F(FileSystemTest, FilesystemExpandWildcardWithPrefix)
218220
EXPECT_EQ(output.size(), PATH_MATCH_SIZE);
219221
}
220222

223+
TEST_F(FileSystemTest, FilesystemExpandWildcardWithPrefixFull)
224+
{
225+
constexpr auto PATH_MATCH_SIZE {2u};
226+
std::deque<std::string> output;
227+
std::unordered_map<std::string, uint32_t> outputMap;
228+
229+
const auto fsWrapper = std::make_unique<filesystem_wrapper::FileSystemWrapper>();
230+
fsWrapper->expand_absolute_path(PATH_TO_EXPAND_7, output);
231+
232+
for (const auto& item : output)
233+
{
234+
outputMap[item]++;
235+
}
236+
237+
EXPECT_TRUE(outputMap.at(EXPAND_PATH_1) == 1);
238+
EXPECT_TRUE(outputMap.at(EXPAND_PATH_3) == 1);
239+
EXPECT_EQ(output.size(), PATH_MATCH_SIZE);
240+
}
241+
221242
TEST_F(FileSystemTest, FilesystemExpandWildcardWithSuffix)
222243
{
223244
constexpr auto PATH_MATCH_SIZE {2u};

src/common/globHelper/include/globHelper.h

+31-56
Original file line numberDiff line numberDiff line change
@@ -26,74 +26,49 @@ namespace Utils
2626
#pragma warning(disable: 4505)
2727
#endif
2828

29-
static bool patternMatch(const std::string& entryName, const std::string& pattern)
30-
{
31-
auto match { true };
32-
// Match the glob pattern without regex
33-
auto patternPos { 0u };
29+
static bool patternMatch(const std::string& entryName, const std::string& pattern)
30+
{
31+
std::string_view entry(entryName);
32+
std::string_view pat(pattern);
33+
34+
// Dynamic programming approach - create a table for memoization
35+
// dp[i][j] is true if the first i characters of entry match the first j characters of pattern
36+
std::vector<std::vector<bool>> dp(entry.size() + 1, std::vector<bool>(pat.size() + 1, false));
37+
38+
// Empty pattern matches empty string
39+
dp[0][0] = true;
3440

35-
for (auto i { 0u }; i < entryName.size(); ++i)
41+
// Handle patterns like "*", "*a", "a*b*" etc. where '*' can match empty strings
42+
for (size_t j = 1; j <= pat.size(); ++j)
43+
{
44+
if (pat[j - 1] == '*')
3645
{
37-
if (patternPos < pattern.size())
38-
{
39-
// 'x' matches 'x'
40-
if (entryName.at(i) == pattern.at(patternPos))
41-
{
42-
++patternPos;
43-
}
44-
// '*' matches any number of characters
45-
else if (pattern.at(patternPos) == '*')
46-
{
47-
// '*' matches zero characters
48-
if (patternPos + 1 < pattern.size() && pattern.at(patternPos + 1) == entryName.at(i))
49-
{
50-
++patternPos;
51-
--i;
52-
}
53-
// '*' matches one or more characters
54-
else if (patternPos + 1 == pattern.size())
55-
{
56-
break;
57-
}
58-
}
59-
// '?' matches any single character
60-
else if (pattern.at(patternPos) == '?')
61-
{
62-
++patternPos;
63-
}
64-
// No match
65-
else
66-
{
67-
match = false;
68-
break;
69-
}
70-
}
71-
else
72-
{
73-
match = false;
74-
break;
75-
}
46+
dp[0][j] = dp[0][j - 1];
7647
}
48+
}
7749

78-
// if the pattern is not fully matched, check if the remaining characters are '*'
79-
// and if so, the match is successful.
80-
while (match && patternPos < pattern.size())
50+
// Fill the dp table
51+
for (size_t i = 1; i <= entry.size(); ++i)
52+
{
53+
for (size_t j = 1; j <= pat.size(); ++j)
8154
{
82-
// '*' matches zero characters
83-
if (pattern.at(patternPos) == '*')
55+
if (pat[j - 1] == '*')
8456
{
85-
++patternPos;
57+
// '*' can match 0 or more characters
58+
// Either ignore '*' (dp[i][j-1]) or use '*' to match current character (dp[i-1][j])
59+
dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
8660
}
87-
// No match
88-
else
61+
else if (pat[j - 1] == '?' || entry[i - 1] == pat[j - 1])
8962
{
90-
match = false;
63+
// '?' matches any single character, or exact character match
64+
dp[i][j] = dp[i - 1][j - 1];
9165
}
9266
}
93-
94-
return match;
9567
}
9668

69+
return dp[entry.size()][pat.size()];
70+
}
71+
9772
#ifdef __GNUC__
9873
#pragma GCC diagnostic pop
9974
#endif

0 commit comments

Comments
 (0)