#include <internal/errredir.h>
#ifdef _TV_UNIX
#include <initializer_list>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
namespace
tvision
{
StderrRedirector::StderrRedirector()
noexcept
{
int
flags;
if
( fileno(stderr) == STDERR_FILENO
&& isatty(STDERR_FILENO)
&& (ttyFd = dup(STDERR_FILENO)) != -1
&& pipe(bufFd) != -1
&& dup2(bufFd[1], STDERR_FILENO) != -1
&& (flags = fcntl(STDERR_FILENO, F_GETFL)) != -1
&& fcntl(STDERR_FILENO, F_SETFL, flags | O_NONBLOCK) != -1
&& fcntl(ttyFd, F_SETFD, FD_CLOEXEC) != -1
&& fcntl(bufFd[0], F_SETFD, FD_CLOEXEC) != -1
&& fcntl(bufFd[1], F_SETFD, FD_CLOEXEC) != -1 )
{
}
else
{
for
(
int
fd : {ttyFd, bufFd[0], bufFd[1]})
if
(fd != -1)
close(fd);
ttyFd = bufFd[0] = bufFd[1] = -1;
}
}
static
bool
isSameFile(
int
fd1,
int
fd2)
{
struct
stat stat1, stat2;
return
fstat(fd1, &stat1) != -1
&& fstat(fd2, &stat2) != -1
&& stat1.st_dev == stat2.st_dev
&& stat1.st_ino == stat2.st_ino;
}
static
void
copyFile(
int
src,
int
dst,
size_t
size)
{
static
thread_local
char
buf
alignas
(4096) [4096];
ssize_t r, w;
size_t
bytesLeft = size;
lseek(src, 0, SEEK_SET);
while
(bytesLeft > 0 && (r = read(src, buf, min(bytesLeft,
sizeof
(buf)))) > 0)
{
bytesLeft -= (
size_t
) r;
while
(r > 0 && (w = write(dst, buf, r)) > 0)
r -= w;
}
}
StderrRedirector::~StderrRedirector()
{
if
(isSameFile(bufFd[1], STDERR_FILENO))
{
dup2(ttyFd, STDERR_FILENO);
int
size;
if
(ioctl(bufFd[0], FIONREAD, &size) != -1 && size > 0)
copyFile(bufFd[0], ttyFd, (
size_t
) size);
}
for
(
int
fd : {ttyFd, bufFd[0], bufFd[1]})
if
(fd != -1)
close(fd);
}
}
#endif // _TV_UNIX