// This source code is released into the public domain. export module nihil.posix:open_in_path; import nihil.std; import nihil.error; import :fd; import :getenv; import :open; import :paths; namespace nihil { // Find an executable in the given path, which should be a colon-separated list of directories, and // open it with O_EXEC. If the file can't be found or can't be opened, returns an appropriate error. export [[nodiscard]] auto open_in_path(std::filesystem::path const &file, std::string_view path) -> std::expected { // Absolute pathname skips the search. if (file.is_absolute()) return open(file, open_exec); // Default to ENOENT as the error. auto err = error(std::errc::no_such_file_or_directory); for (auto &&dir : path | std::views::split(':')) { // An empty $PATH element means cwd. auto sdir = dir.empty() ? std::filesystem::path(".") : std::filesystem::path(std::string_view(dir)); if (auto ret = open(sdir / file, open_exec); ret) return ret; // If we get an error other than ENOENT, cache it to return to the caller. // This means we can propagate open() errors. else if (ret.error().root_cause() != std::errc::no_such_file_or_directory) err = std::move(ret.error()); } return std::unexpected(std::move(err)); } // Find an executable in $PATH and open it. If $PATH is not set, uses _PATH_DEFPATH. // If the file can't be found or can't be opened, returns an appropriate error. export [[nodiscard]] auto open_in_path(std::filesystem::path const &file) -> std::expected { auto const path = getenv("PATH").value_or(std::string(paths::defpath)); // NOLINT return open_in_path(file, path); } } // namespace nihil