commits
tags
#include "torlinkc/util/open_folder.hpp"
#include <chrono>
#include <filesystem>
#include <thread>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
namespace torlinkc {
namespace {
// Forks, execs the first candidate that exists is left to the shell's PATH
// lookup (execvp), and waits up to ~4s for a clean exit. Never throws.
bool spawnAndWait(const std::vector<std::vector<std::string>>& candidates) {
for (const auto& argvStrings : candidates) {
pid_t pid = fork();
if (pid < 0) continue;
if (pid == 0) {
std::vector<char*> argv;
for (const auto& s : argvStrings) argv.push_back(const_cast<char*>(s.c_str()));
argv.push_back(nullptr);
execvp(argv[0], argv.data());
_exit(127); // execvp only returns on failure
}
bool exited = false;
int status = 0;
for (int i = 0; i < 40; ++i) { // ~4s in 100ms slices
if (waitpid(pid, &status, WNOHANG) == pid) {
exited = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (!exited) {
kill(pid, SIGKILL);
waitpid(pid, nullptr, 0);
continue;
}
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return true;
}
return false;
}
} // namespace
bool openFolder(const std::string& dir) {
// Check the path ourselves first, matching the original's rationale (some
// launchers silently open a fallback location for a nonexistent path,
// which would look like success).
std::error_code ec;
if (dir.empty() || !std::filesystem::exists(dir, ec)) return false;
#ifdef __APPLE__
return spawnAndWait({{"open", dir}});
#else
return spawnAndWait({{"xdg-open", dir}, {"gio", "open", dir}});
#endif
}
} // namespace torlinkc