FindFiles.h#
Fully qualified name: carb/filesystem/FindFiles.h
File members: carb/filesystem/FindFiles.h
// SPDX-FileCopyrightText: Copyright (c) 2020-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
#pragma once
#include "../extras/Path.h"
#include "../extras/StringProcessor.h"
#include "../extras/StringUtils.h"
#include "FileSystemUtils.h"
#include "../Format.h"
#include "../cpp/Span.h"
#include "../../omni/str/Wildcard.h"
#include <cstdint>
#include <cstring>
namespace carb
{
namespace filesystem
{
using FindFilesFlag = uint32_t;
constexpr FindFilesFlag kFindFilesFlagNone = 0x0;
constexpr FindFilesFlag kFindFilesFlagRecursive = (1 << 0);
constexpr FindFilesFlag kFindFilesFlagMatchStem = (1 << 1);
constexpr FindFilesFlag kFindFilesFlagReplaceEnvironmentVariables = (1 << 2);
#if CARB_VERSION_ATLEAST(carb_filesystem_IFileSystem, 2, 0)
using FindFilesOnFilterNonCanonicalFn = WalkAction(cpp::zstring_view path, void* userData);
using FindFilesOnMatchedFn = void(cpp::zstring_view canonical, void* userData);
using FindFilesOnExcludedFn = void(cpp::zstring_view canonical, void* userData);
using FindFilesOnSkippedFn = void(cpp::zstring_view canonical, void* userData);
using FindFilesOnSearchPathFn = void(cpp::zstring_view path, void* userData);
namespace detail
{
using StrType = cpp::zstring_view;
inline cpp::zstring_view toCbType(cpp::zstring_view s)
{
return s;
}
} // namespace detail
#else
using FindFilesOnFilterNonCanonicalFn = WalkAction(const char* path, void* userData);
using FindFilesOnMatchedFn = void(const char* canonical, void* userData);
using FindFilesOnExcludedFn = void(const char* canonical, void* userData);
using FindFilesOnSkippedFn = void(const char* canonical, void* userData);
using FindFilesOnSearchPathFn = void(const char* path, void* userData);
namespace detail
{
using StrType = const char*;
inline const char* toCbType(cpp::zstring_view s)
{
return s.c_str();
}
} // namespace detail
#endif
struct FindFilesArgs
{
#if CARB_VERSION_ATLEAST(carb_filesystem_IFileSystem, 2, 0)
# define CARBLOCAL_SPAN_SIZE(first, second) uint32_t((first).size())
cpp::span<const cpp::string_view> searchPaths;
cpp::span<const cpp::string_view> matchWildcards;
cpp::span<const cpp::string_view> excludeWildcards;
cpp::span<const cpp::string_view> ignorePrefixes;
#else
# define CARBLOCAL_SPAN_SIZE(first, second) (second)
const char* const* searchPaths;
uint32_t searchPathsCount;
const char* const* matchWildcards;
uint32_t matchWildcardsCount;
const char* const* excludeWildcards;
uint32_t excludeWildcardsCount;
const char* const* ignorePrefixes;
uint32_t ignorePrefixesCount;
#endif
IFileSystem* fs;
FindFilesOnFilterNonCanonicalFn* onFilterNonCanonical;
void* onFilterNonCanonicalContext;
FindFilesOnMatchedFn* onMatched;
void* onMatchedContext;
FindFilesOnExcludedFn* onExcluded;
void* onExcludedContext;
FindFilesOnSkippedFn* onSkipped;
void* onSkippedContext;
FindFilesOnSearchPathFn* onSearchPath;
void* onSearchPathContext;
FindFilesFlag flags;
};
namespace detail
{
enum Result
{
eExcluded = -1,
eNotMatched,
eMatched
};
#if CARB_VERSION_ATLEAST(carb_filesystem_IFileSystem, 2, 0)
inline Result matched(const FindFilesArgs& args, cpp::zstring_view canonical, cpp::zstring_view target)
{
bool matched = false;
for (size_t s = 0; s <= args.ignorePrefixes.size(); ++s)
{
cpp::string_view prefix = s > 0 ? args.ignorePrefixes[s - 1] : cpp::string_view{};
if (target.starts_with(prefix))
{
target.remove_prefix(prefix.size());
if (omni::str::matchWildcards(target, args.matchWildcards))
{
if (omni::str::matchWildcards(target, args.excludeWildcards))
{
if (args.onExcluded)
args.onExcluded(canonical, args.onExcludedContext);
return eExcluded;
}
matched = true; // keep checking other exclusions
}
}
}
return matched ? eMatched : eNotMatched;
}
#else
inline Result matched(const FindFilesArgs& args, cpp::zstring_view canonical, cpp::zstring_view target)
{
bool matched = false;
for (size_t s = 0; s <= args.ignorePrefixesCount; ++s)
{
cpp::string_view prefix =
s > 0 ? cpp::string_view(cpp::unsafe_length, args.ignorePrefixes[s - 1]) : cpp::string_view{};
if (target.starts_with(prefix))
{
target.remove_prefix(prefix.size());
if (omni::str::matchWildcards(target.c_str(), args.matchWildcards, args.matchWildcardsCount))
{
if (omni::str::matchWildcards(target.c_str(), args.excludeWildcards, args.excludeWildcardsCount))
{
if (args.onExcluded)
args.onExcluded(canonical.c_str(), args.onExcludedContext);
return eExcluded;
}
matched = true; // keep checking other exclusions
}
}
}
return matched ? eMatched : eNotMatched;
}
#endif
} // namespace detail
inline bool findFiles(const FindFilesArgs& args);
#ifndef DOXYGEN_BUILD
namespace detail
{
struct FindFilesContext
{
const FindFilesArgs* args;
IFileSystem* fs;
};
inline WalkAction onFile(const FileInfo& info, cpp::zstring_view inPath, const FindFilesContext& context)
{
if (info.type == DirectoryItemType::eFile)
{
// the canonicalization of a file is expensive. here we give the user a chance to
// tell us if we should canonicalize the file
if (context.args->onFilterNonCanonical)
{
WalkAction action =
context.args->onFilterNonCanonical(toCbType(inPath), context.args->onFilterNonCanonicalContext);
switch (action)
{
case WalkAction::eStop:
return WalkAction::eStop;
case WalkAction::eSkip:
return WalkAction::eContinue;
default: // eContinue
break; // fall-through
}
}
auto canonical = makeCanonicalPath(context.fs, inPath).value_or("");
extras::Path path(canonical);
const std::string target =
((context.args->flags & kFindFilesFlagMatchStem) ? path.getStem() : path.getFilename()).getString();
switch (matched(*context.args, canonical, target))
{
case eExcluded:
return WalkAction::eContinue;
case eMatched:
if (context.args->onMatched)
context.args->onMatched(toCbType(canonical), context.args->onMatchedContext);
break;
case eNotMatched:
if (context.args->onSkipped)
context.args->onSkipped(toCbType(canonical), context.args->onSkippedContext);
break;
}
}
return WalkAction::eContinue;
}
} // namespace detail
#endif
inline bool findFiles(const FindFilesArgs& args)
{
if (!CARBLOCAL_SPAN_SIZE(args.searchPaths, args.searchPathsCount))
{
CARB_LOG_ERROR(CARB_LOG_DEFAULT_CHANNEL, "searchPath must be specified");
return false;
}
if (!CARBLOCAL_SPAN_SIZE(args.matchWildcards, args.matchWildcardsCount))
{
CARB_LOG_ERROR(CARB_LOG_DEFAULT_CHANNEL, "match wildcard must be specified");
return false;
}
auto framework = getFramework();
if (!framework)
{
CARB_LOG_ERROR(CARB_LOG_DEFAULT_CHANNEL, "carb::Framework not active");
return false;
}
IFileSystem* fs;
if (args.fs)
{
fs = framework->verifyInterface<IFileSystem>(args.fs);
if (!fs)
{
CARB_LOG_ERROR(CARB_LOG_DEFAULT_CHANNEL, "incompatible carb::filesystem::IFileSystem");
return false;
}
}
else
{
fs = framework->tryAcquireInterface<IFileSystem>();
if (!fs)
{
CARB_LOG_ERROR(CARB_LOG_DEFAULT_CHANNEL, "unable to acquire carb::filesystem::IFileSystem");
return false;
}
}
detail::FindFilesContext userData = { &args, fs };
for (uint32_t i = 0; i < CARBLOCAL_SPAN_SIZE(args.searchPaths, args.searchPathsCount); ++i)
{
auto dir = args.searchPaths[i];
std::string dirWithEnv;
if (args.flags & kFindFilesFlagReplaceEnvironmentVariables)
{
dirWithEnv = extras::replaceEnvironmentVariables(std::string(dir));
dir = detail::toCbType(dirWithEnv);
}
extras::Path path{ dir };
std::string fullPath =
path.isAbsolute() ? path.getString() : carb::fmt::format("{}/{}", fs->getAppDirectoryPath(), path.c_str());
if (args.onSearchPath)
{
args.onSearchPath(detail::toCbType(fullPath), args.onSearchPathContext);
}
using namespace std::placeholders;
(void)detail::forEachDirectoryItem(fs, fullPath, !!(args.flags & kFindFilesFlagRecursive),
std::bind(detail::onFile, _1, _2, std::ref(userData)));
}
return true;
}
} // namespace filesystem
} // namespace carb
#undef CARBLOCAL_SPAN_SIZE