diff options
| author | Stefan Boberg <[email protected]> | 2026-04-20 11:02:38 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-04-20 11:02:38 +0200 |
| commit | 4d828df648b9e5093f3509cbeeb5887de8920ede (patch) | |
| tree | ee8f8cf94d7a598bb773d6aa5d3f9d24b0635a47 /src | |
| parent | zencore: CreateProc stdin pipes + BuildArgV quote stripping (#983) (diff) | |
| download | archived-zen-4d828df648b9e5093f3509cbeeb5887de8920ede.tar.xz archived-zen-4d828df648b9e5093f3509cbeeb5887de8920ede.zip | |
zencore: implement SearchPathForExecutable on POSIX (#981)
- The Linux/macOS branch of `SearchPathForExecutable` was previously a no-op that returned the input unchanged. Callers passing a bare executable name (e.g. `llvm-symbolizer`) got the same bare name back even when the binary lived elsewhere on `PATH`.
- Now walks `$PATH` like `execvp` does: skip the search if the input contains a `/`, try each colon-separated entry (empty entry == cwd), and return the first candidate that is both a regular file and executable by the current user. Falls back to returning the input unchanged if nothing matches, preserving the previous behavior for the no-match case.
- Windows branch is unchanged (still uses `SearchPathW`).
Diffstat (limited to 'src')
| -rw-r--r-- | src/zencore/filesystem.cpp | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/src/zencore/filesystem.cpp b/src/zencore/filesystem.cpp index 08e921293..03e04c77c 100644 --- a/src/zencore/filesystem.cpp +++ b/src/zencore/filesystem.cpp @@ -3183,7 +3183,38 @@ SearchPathForExecutable(std::string_view ExecutableName) return PathBuffer.get(); #else - return ExecutableName; + // If the name already contains a path separator, don't search PATH + // (matches the shell / execvp semantics). + if (ExecutableName.find('/') != std::string_view::npos) + { + return std::filesystem::path(ExecutableName); + } + + const char* PathEnv = ::getenv("PATH"); + if (PathEnv == nullptr || *PathEnv == '\0') + { + return std::filesystem::path(ExecutableName); + } + + std::string_view PathView(PathEnv); + while (!PathView.empty()) + { + size_t Sep = PathView.find(':'); + std::string_view Dir = (Sep == std::string_view::npos) ? PathView : PathView.substr(0, Sep); + PathView = (Sep == std::string_view::npos) ? std::string_view{} : PathView.substr(Sep + 1); + + // An empty entry in PATH is interpreted as the current directory. + std::filesystem::path Candidate = Dir.empty() ? std::filesystem::path(".") : std::filesystem::path(Dir); + Candidate /= ExecutableName; + + std::error_code Ec; + if (std::filesystem::is_regular_file(Candidate, Ec) && ::access(Candidate.c_str(), X_OK) == 0) + { + return Candidate; + } + } + + return std::filesystem::path(ExecutableName); #endif } |