/* * This source code is released into the public domain. */ module; #include #include #include #include #include #include #include module nihil.posix; import nihil.error; import nihil.monad; namespace nihil { fd::fd() noexcept = default; fd::fd(int fileno) noexcept : m_fileno(fileno) { } fd::~fd() { if (*this) std::ignore = this->close(); } fd::fd(fd &&other) noexcept : m_fileno(std::exchange(other.m_fileno, invalid_fileno)) { } auto fd::operator=(this fd &self, fd &&other) noexcept -> fd & { if (&self != &other) self.m_fileno = std::exchange(other.m_fileno, invalid_fileno); return self; } fd::operator bool(this fd const &self) noexcept { return self.m_fileno != invalid_fileno; } auto fd::close(this fd &self) -> std::expected { auto const ret = ::close(self.get()); self.m_fileno = invalid_fileno; if (ret == 0) return {}; return std::unexpected(error(std::errc(errno))); } auto fd::get(this fd const &self) -> int { if (self) return self.m_fileno; throw std::logic_error("Attempt to call get() on invalid fd"); } auto fd::release(this fd &&self) -> int { if (self) return std::exchange(self.m_fileno, invalid_fileno); throw std::logic_error("Attempt to release an invalid fd"); } auto dup(fd const &self) -> std::expected { auto const newfd = ::dup(self.get()); if (newfd != -1) return newfd; return std::unexpected(error(std::errc(errno))); } auto dup(fd const &self, int newfd) -> std::expected { auto const ret = ::dup2(self.get(), newfd); if (ret != -1) return newfd; return std::unexpected(error(std::errc(errno))); } auto raw_dup(fd const &self) -> std::expected { auto const newfd = ::dup(self.get()); if (newfd != -1) return newfd; return std::unexpected(error(std::errc(errno))); } auto raw_dup(fd const &self, int newfd) -> std::expected { auto const ret = ::dup2(self.get(), newfd); if (ret != -1) return newfd; return std::unexpected(error(std::errc(errno))); } auto getflags(fd const &self) -> std::expected { auto const flags = ::fcntl(self.get(), F_GETFL); if (flags != -1) return flags; return std::unexpected(error(std::errc(errno))); } auto replaceflags(fd &self, int newflags) -> std::expected { auto const ret = ::fcntl(self.get(), F_SETFL, newflags); if (ret == 0) return {}; return std::unexpected(error(std::errc(errno))); } auto setflags(fd &self, int newflags) -> std::expected { auto flags = co_await getflags(self); flags |= newflags; co_await replaceflags(self, flags); co_return flags; } auto clearflags(fd &self, int clrflags) -> std::expected { auto flags = co_await getflags(self); flags &= ~clrflags; co_await replaceflags(self, flags); co_return flags; } auto getfdflags(fd const &self) -> std::expected { auto const flags = ::fcntl(self.get(), F_GETFD); if (flags != -1) return flags; return std::unexpected(error(std::errc(errno))); } auto replacefdflags(fd &self, int newflags) -> std::expected { auto const ret = ::fcntl(self.get(), F_SETFD, newflags); if (ret != -1) return {}; return std::unexpected(error(std::errc(errno))); } auto setfdflags(fd &self, int newflags) -> std::expected { auto flags = co_await getfdflags(self); flags |= newflags; co_await replacefdflags(self, flags); co_return flags; } auto clearfdflags(fd &self, int clrflags) -> std::expected { auto flags = co_await getfdflags(self); flags &= ~clrflags; co_await replacefdflags(self, flags); co_return flags; } auto pipe() -> std::expected, error> { auto fds = std::array{}; if (auto const ret = ::pipe(fds.data()); ret != 0) return std::unexpected(error(std::errc(errno))); return {{fd(fds[0]), fd(fds[1])}}; } auto fd::write(this fd &self, std::span buffer) -> std::expected { auto const ret = ::write(self.get(), buffer.data(), buffer.size()); if (ret >= 0) return ret; return std::unexpected(error(std::errc(errno))); } auto fd::read(this fd &self, std::span buffer) -> std::expected, error> { auto const ret = ::read(self.get(), buffer.data(), buffer.size()); if (ret >= 0) return buffer.subspan(0, ret); return std::unexpected(error(std::errc(errno))); } } // namespace nihil