← Back to file list Raw

src/U_Files.cpp

#include "U_Files.h"
#include <string>
#include "U_Log.h"
#if defined(_WIN32)
#include <windows.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#else
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#endif
std::filesystem::path FileUtils::get_executable_path()
{
#if defined(_WIN32)
std::wstring buffer(MAX_PATH, L'\0');
DWORD len = ::GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
if (len == 0)
{
return {};
}
buffer.resize(len);
return std::filesystem::path(buffer).parent_path();
#elif defined(__APPLE__)
uint32_t bufsize = 0;
_NSGetExecutablePath(nullptr, &bufsize);
std::vector<char> buffer(bufsize);
if (_NSGetExecutablePath(buffer.data(), &bufsize) != 0)
{
return {};
}
char resolved[PATH_MAX];
if (realpath(buffer.data(), resolved))
{
return std::filesystem::path(resolved).parent_path();
}
return std::filesystem::path(buffer.data()).parent_path();
#else
std::vector<char> buffer(PATH_MAX);
ssize_t len = ::readlink("/proc/self/exe", buffer.data(), buffer.size() - 1);
if (len == -1)
{
return {};
}
buffer[len] = '\0';
return std::filesystem::path(buffer.data()).parent_path();
#endif
}
bool FileUtils::is_within_root(const std::filesystem::path& root, const std::filesystem::path& user_path)
{
try
{
auto abs_root = std::filesystem::weakly_canonical(root);
auto abs_target = std::filesystem::weakly_canonical(abs_root / user_path);
return std::mismatch(abs_root.begin(), abs_root.end(), abs_target.begin()).first == abs_root.end();
}
catch(...)
{
return false;
}
return false;
}
std::optional<std::filesystem::path> FileUtils::find_first_by_name(const std::filesystem::path& root, const std::string& name, bool case_insensitive, bool follow_symlinks)
{
try
{
auto abs_root = std::filesystem::weakly_canonical(root);
std::filesystem::directory_options options = std::filesystem::directory_options::skip_permission_denied;
if (follow_symlinks) { options |= std::filesystem::directory_options::follow_directory_symlink; }
for (std::filesystem::recursive_directory_iterator it(abs_root, options), end; it != end; ++it)
{
const std::filesystem::directory_entry& de = *it;
std::string fname = de.path().filename().string();
auto eq = [&](const std::string& a, const std::string& b)
{
if (!case_insensitive) { return a == b; }
auto tolow = [](char c){ return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); };
if (a.size() != b.size()) { return false; }
for (size_t i = 0; i < a.size(); ++i)
{
if (tolow(a[i]) != tolow(b[i])) { return false; }
}
return true;
};
if (eq(fname, name))
{
std::filesystem::path candidate = de.path();
if (is_within_root(abs_root, candidate))
{
return std::filesystem::weakly_canonical(candidate);
}
}
if (de.is_symlink() && de.is_directory() && !follow_symlinks)
{
it.disable_recursion_pending();
}
}
}
catch (const std::filesystem::filesystem_error& e)
{
return std::nullopt;
}
catch (const std::exception& e)
{
return std::nullopt;
}
return std::nullopt;
}