#include "pipe.h"
#include <util/generic/yexception.h>
#include <cstdio>
#include <cerrno>
class TPipeBase::TImpl {
public:
inline TImpl(const TString& command, const char* mode)
: Pipe_(nullptr)
{
#ifndef _freebsd_
if (strcmp(mode, "r+") == 0) {
ythrow TSystemError(EINVAL) << "pipe \"r+\" mode is implemented only on FreeBSD";
}
#endif
Pipe_ = ::popen(command.data(), mode);
if (Pipe_ == nullptr) {
ythrow TSystemError() << "failed to open pipe: " << command.Quote();
}
}
inline ~TImpl() {
if (Pipe_ != nullptr) {
::pclose(Pipe_);
}
}
public:
FILE* Pipe_;
};
TPipeBase::TPipeBase(const TString& command, const char* mode)
: Impl_(new TImpl(command, mode))
{
}
TPipeBase::~TPipeBase() = default;
TPipeInput::TPipeInput(const TString& command)
: TPipeBase(command, "r")
{
}
size_t TPipeInput::DoRead(void* buf, size_t len) {
if (Impl_->Pipe_ == nullptr) {
return 0;
}
size_t bytesRead = ::fread(buf, 1, len, Impl_->Pipe_);
if (bytesRead == 0) {
int exitStatus = ::pclose(Impl_->Pipe_);
Impl_->Pipe_ = nullptr;
if (exitStatus == -1) {
ythrow TSystemError() << "pclose() failed";
} else if (exitStatus != 0) {
ythrow yexception() << "subprocess exited with non-zero status(" << exitStatus << ")";
}
}
return bytesRead;
}
TPipeOutput::TPipeOutput(const TString& command)
: TPipeBase(command, "w")
{
}
void TPipeOutput::DoWrite(const void* buf, size_t len) {
if (Impl_->Pipe_ == nullptr || len != ::fwrite(buf, 1, len, Impl_->Pipe_)) {
ythrow TSystemError() << "fwrite failed";
}
}
void TPipeOutput::Close() {
int exitStatus = ::pclose(Impl_->Pipe_);
Impl_->Pipe_ = nullptr;
if (exitStatus == -1) {
ythrow TSystemError() << "pclose() failed";
} else if (exitStatus != 0) {
ythrow yexception() << "subprocess exited with non-zero status(" << exitStatus << ")";
}
}
TPipedBase::TPipedBase(PIPEHANDLE fd)
: Handle_(fd)
{
}
TPipedBase::~TPipedBase() {
if (Handle_.IsOpen()) {
Handle_.Close();
}
}
TPipedInput::TPipedInput(PIPEHANDLE fd)
: TPipedBase(fd)
{
}
TPipedInput::~TPipedInput() = default;
size_t TPipedInput::DoRead(void* buf, size_t len) {
if (!Handle_.IsOpen()) {
return 0;
}
return Handle_.Read(buf, len);
}
TPipedOutput::TPipedOutput(PIPEHANDLE fd)
: TPipedBase(fd)
{
}
TPipedOutput::~TPipedOutput() = default;
void TPipedOutput::DoWrite(const void* buf, size_t len) {
if (!Handle_.IsOpen() || static_cast<ssize_t>(len) != Handle_.Write(buf, len)) {
ythrow TSystemError() << "pipe writing failed";
}
}