ac2a609d0a
This PR it just a hack to fix #1160 , it doesn't solve the underlying problem. It fixes the problem because by using execvp() directly, it avoids the call to `sh` done with `system()`, which has a bug on Ubuntu 22.04 which makes it i,compatibles with the glibc inside the AppImage. It doesn't fix the underlying problem because the programs we call themselves still link to the AppImage's libraries instead of the system ones.
328 lines
11 KiB
C++
328 lines
11 KiB
C++
#include <hex/helpers/fs.hpp>
|
|
|
|
#include <hex/api/content_registry.hpp>
|
|
#include <hex/api/project_file_manager.hpp>
|
|
#include <hex/helpers/logger.hpp>
|
|
#include <hex/helpers/fmt.hpp>
|
|
#include <hex/helpers/utils_linux.hpp>
|
|
|
|
#include <xdg.hpp>
|
|
|
|
#if defined(OS_WINDOWS)
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
#elif defined(OS_LINUX)
|
|
#include <xdg.hpp>
|
|
#include <linux/limits.h>
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
|
|
#include <wolv/io/file.hpp>
|
|
#include <wolv/io/fs.hpp>
|
|
|
|
namespace hex::fs {
|
|
|
|
static std::function<void(const std::string&)> s_fileBrowserErrorCallback;
|
|
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {
|
|
s_fileBrowserErrorCallback = callback;
|
|
}
|
|
|
|
// With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp
|
|
|
|
void openFileExternal(const std::fs::path &filePath) {
|
|
if (!wolv::io::fs::exists(filePath))
|
|
return;
|
|
|
|
#if defined(OS_WINDOWS)
|
|
hex::unused(
|
|
ShellExecute(nullptr, "open", wolv::util::toUTF8String(filePath).c_str(), nullptr, nullptr, SW_SHOWNORMAL)
|
|
);
|
|
#elif defined(OS_MACOS)
|
|
hex::unused(system(
|
|
hex::format("open {}", wolv::util::toUTF8String(filePath)).c_str()
|
|
));
|
|
#elif defined(OS_LINUX)
|
|
executeCmd({"xdg-open", wolv::util::toUTF8String(filePath)});
|
|
#endif
|
|
}
|
|
|
|
void openFolderExternal(const std::fs::path &dirPath) {
|
|
if (!wolv::io::fs::exists(dirPath))
|
|
return;
|
|
|
|
#if defined(OS_WINDOWS)
|
|
hex::unused(system(
|
|
hex::format("explorer.exe {}", wolv::util::toUTF8String(dirPath)).c_str()
|
|
));
|
|
#elif defined(OS_MACOS)
|
|
hex::unused(system(
|
|
hex::format("open {}", wolv::util::toUTF8String(dirPath)).c_str()
|
|
));
|
|
#elif defined(OS_LINUX)
|
|
executeCmd({"xdg-open", wolv::util::toUTF8String(dirPath)});
|
|
#endif
|
|
}
|
|
|
|
void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) {
|
|
if (!wolv::io::fs::exists(selectedFilePath))
|
|
return;
|
|
|
|
#if defined(OS_WINDOWS)
|
|
hex::unused(system(
|
|
hex::format(R"(explorer.exe /select,"{}")", wolv::util::toUTF8String(selectedFilePath)).c_str()
|
|
));
|
|
#elif defined(OS_MACOS)
|
|
hex::unused(system(
|
|
hex::format(
|
|
R"(osascript -e 'tell application "Finder" to reveal POSIX file "{}"')",
|
|
wolv::util::toUTF8String(selectedFilePath)
|
|
).c_str()
|
|
));
|
|
system(R"(osascript -e 'tell application "Finder" to activate')");
|
|
#elif defined(OS_LINUX)
|
|
// fallback to only opening the folder for now
|
|
// TODO actually select the file
|
|
executeCmd({"xdg-open", wolv::util::toUTF8String(selectedFilePath.parent_path())});
|
|
#endif
|
|
}
|
|
|
|
bool openFileBrowser(DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {
|
|
NFD::ClearError();
|
|
|
|
if (NFD::Init() != NFD_OKAY) {
|
|
log::error("NFD init returned an error: {}", NFD::GetError());
|
|
if (s_fileBrowserErrorCallback != nullptr)
|
|
s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
|
|
return false;
|
|
}
|
|
|
|
NFD::UniquePathU8 outPath;
|
|
NFD::UniquePathSet outPaths;
|
|
nfdresult_t result;
|
|
switch (mode) {
|
|
case DialogMode::Open:
|
|
if (multiple)
|
|
result = NFD::OpenDialogMultiple(outPaths, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
|
|
else
|
|
result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
|
|
break;
|
|
case DialogMode::Save:
|
|
result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str());
|
|
break;
|
|
case DialogMode::Folder:
|
|
result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str());
|
|
break;
|
|
default:
|
|
std::unreachable();
|
|
}
|
|
|
|
if (result == NFD_OKAY){
|
|
if(outPath != nullptr) {
|
|
callback(reinterpret_cast<char8_t*>(outPath.get()));
|
|
}
|
|
if (outPaths != nullptr) {
|
|
nfdpathsetsize_t numPaths = 0;
|
|
if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) {
|
|
for (size_t i = 0; i < numPaths; i++) {
|
|
NFD::UniquePathSetPath path;
|
|
if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY)
|
|
callback(reinterpret_cast<char8_t*>(path.get()));
|
|
}
|
|
}
|
|
}
|
|
} else if (result == NFD_ERROR) {
|
|
log::error("Requested file dialog returned an error: {}", NFD::GetError());
|
|
if (s_fileBrowserErrorCallback != nullptr)
|
|
s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details");
|
|
}
|
|
|
|
NFD::Quit();
|
|
|
|
return result == NFD_OKAY;
|
|
}
|
|
|
|
std::vector<std::fs::path> getDataPaths() {
|
|
std::vector<std::fs::path> paths;
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
if (!ImHexApi::System::isPortableVersion()) {
|
|
PWSTR wAppDataPath = nullptr;
|
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) {
|
|
paths.emplace_back(wAppDataPath);
|
|
CoTaskMemFree(wAppDataPath);
|
|
}
|
|
}
|
|
|
|
#elif defined(OS_MACOS)
|
|
|
|
paths.push_back(wolv::io::fs::getApplicationSupportDirectoryPath());
|
|
|
|
#elif defined(OS_LINUX)
|
|
|
|
paths.push_back(xdg::DataHomeDir());
|
|
|
|
auto dataDirs = xdg::DataDirs();
|
|
std::copy(dataDirs.begin(), dataDirs.end(), std::back_inserter(paths));
|
|
|
|
#endif
|
|
|
|
for (auto &path : paths)
|
|
path = path / "imhex";
|
|
|
|
#if defined(OS_MACOS)
|
|
|
|
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
|
|
paths.push_back(*executablePath);
|
|
|
|
#else
|
|
|
|
if (auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value())
|
|
paths.push_back(executablePath->parent_path());
|
|
|
|
#endif
|
|
|
|
|
|
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
|
|
std::copy(additionalDirs.begin(), additionalDirs.end(), std::back_inserter(paths));
|
|
|
|
if (ProjectFile::hasPath()) {
|
|
paths.push_back(ProjectFile::getPath().parent_path());
|
|
}
|
|
|
|
return paths;
|
|
}
|
|
|
|
static std::vector<std::fs::path> getConfigPaths() {
|
|
#if defined(OS_WINDOWS)
|
|
return getDataPaths();
|
|
#elif defined(OS_MACOS)
|
|
return getDataPaths();
|
|
#elif defined(OS_LINUX)
|
|
return {xdg::ConfigHomeDir() / "imhex"};
|
|
#endif
|
|
}
|
|
|
|
std::vector<std::fs::path> appendPath(std::vector<std::fs::path> paths, const std::fs::path &folder) {
|
|
for (auto &path : paths)
|
|
path = path / folder;
|
|
|
|
return paths;
|
|
}
|
|
|
|
std::vector<std::fs::path> getPluginPaths() {
|
|
std::vector<std::fs::path> paths = getDataPaths();
|
|
#if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION)
|
|
paths.push_back(SYSTEM_PLUGINS_LOCATION);
|
|
#endif
|
|
return paths;
|
|
}
|
|
|
|
|
|
std::vector<std::fs::path> getDefaultPaths(ImHexPath path, bool listNonExisting) {
|
|
std::vector<std::fs::path> result;
|
|
|
|
switch (path) {
|
|
case ImHexPath::END:
|
|
return { };
|
|
case ImHexPath::Constants:
|
|
result = appendPath(getDataPaths(), "constants");
|
|
break;
|
|
case ImHexPath::Config:
|
|
result = appendPath(getConfigPaths(), "config");
|
|
break;
|
|
case ImHexPath::Encodings:
|
|
result = appendPath(getDataPaths(), "encodings");
|
|
break;
|
|
case ImHexPath::Logs:
|
|
result = appendPath(getDataPaths(), "logs");
|
|
break;
|
|
case ImHexPath::Plugins:
|
|
result = appendPath(getPluginPaths(), "plugins");
|
|
break;
|
|
case ImHexPath::Libraries:
|
|
result = appendPath(getPluginPaths(), "lib");
|
|
break;
|
|
case ImHexPath::Resources:
|
|
result = appendPath(getDataPaths(), "resources");
|
|
break;
|
|
case ImHexPath::Magic:
|
|
result = appendPath(getDataPaths(), "magic");
|
|
break;
|
|
case ImHexPath::Patterns:
|
|
result = appendPath(getDataPaths(), "patterns");
|
|
break;
|
|
case ImHexPath::PatternsInclude:
|
|
result = appendPath(getDataPaths(), "includes");
|
|
break;
|
|
case ImHexPath::Yara:
|
|
result = appendPath(getDataPaths(), "yara");
|
|
break;
|
|
case ImHexPath::Recent:
|
|
result = appendPath(getConfigPaths(), "recent");
|
|
break;
|
|
case ImHexPath::Scripts:
|
|
result = appendPath(getDataPaths(), "scripts");
|
|
break;
|
|
case ImHexPath::Inspectors:
|
|
result = appendPath(getDefaultPaths(ImHexPath::Scripts), "inspectors");
|
|
break;
|
|
case ImHexPath::Nodes:
|
|
result = appendPath(getDefaultPaths(ImHexPath::Scripts), "nodes");
|
|
break;
|
|
case ImHexPath::Themes:
|
|
result = appendPath(getDataPaths(), "themes");
|
|
break;
|
|
case ImHexPath::Layouts:
|
|
result = appendPath(getDataPaths(), "layouts");
|
|
break;
|
|
}
|
|
|
|
if (!listNonExisting) {
|
|
result.erase(std::remove_if(result.begin(), result.end(), [](const auto &path) {
|
|
return !wolv::io::fs::isDirectory(path);
|
|
}), result.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool isPathWritable(const std::fs::path &path) {
|
|
constexpr static auto TestFileName = "__imhex__tmp__";
|
|
{
|
|
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Read);
|
|
if (file.isValid()) {
|
|
if (!file.remove())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
|
|
bool result = file.isValid();
|
|
if (!file.remove())
|
|
return false;
|
|
|
|
return result;
|
|
}
|
|
|
|
std::fs::path toShortPath(const std::fs::path &path) {
|
|
#if defined(OS_WINDOWS)
|
|
size_t size = GetShortPathNameW(path.c_str(), nullptr, 0);
|
|
if (size == 0)
|
|
return path;
|
|
|
|
std::wstring newPath(size, 0x00);
|
|
GetShortPathNameW(path.c_str(), newPath.data(), newPath.size());
|
|
newPath.pop_back();
|
|
|
|
return newPath;
|
|
#else
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
|
|
}
|