Function to check if a C++ character stream is connected to a terminal/console/tty.
Ideally, we would use the file descriptor under-laying the stream buffer of the C++ stdio stream (cin, cout, cerr or clog).
However, there is no way to retrieve the under-laying file descriptor.
So, we use the fact that at program start-up the stdio stream buffers are connected to the program's standard input and output.
This function only works under the following conditions:
The stream buffers of the start-up C++ stdio streams must not change.
Because the addresses of the stream buffers of the start-up C++ stdio streams are used as identifiers.
For instance by deleting them and then allocating a new stream buffer that has the same address as one of these stream buffers of the start-up C++ stdio streams.
The program's stdio must not change after program start-up.
Because the TTY statuses of the stdio stream buffers are stored at program start-up.
For instance if at start-up the std. out is connected to a terminal and later it is redirected to a pipe or file by something external to the program.
[Instead of storing the TTY statuses at start-up you could retrieve them at run-time, but then you must make sure that your program (and all libraries it uses) does not change the stdio file descriptors (0, 1 and 2). Rember that the stdio stream buffers most likely use other (duplicate) file descriptors.]
Code:
#include <iostream>
extern "C" {
#ifdef _WIN32
# include <io.h> // for: _isatty()
#else
# include <unistd.h> // for: isatty()
#endif
}
// Stdio file descriptors.
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
#endif
// Store start-up addresses of C++ stdio stream buffers as identifiers.
// These addresses differ per process and must be statically linked in.
// Assume that the stream buffers at these stored addresses
// are always connected to their underlaying stdio files.
static const streambuf* const StdioBufs[] = {
std::cin.rdbuf(), std::cout.rdbuf(), std::cerr.rdbuf(), std::clog.rdbuf()
};
static const wstreambuf* const StdioWBufs[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
std::wcin.rdbuf(), std::wcout.rdbuf(), std::wcerr.rdbuf(), std::wclog.rdbuf()
};
// Store start-up terminal/TTY statuses of C++ stdio stream buffers.
// These statuses differ per process and must be statically linked in.
// Assume that the statuses don't change during the process life-time.
static const bool StdioTtys[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
#ifdef _WIN32
_isatty(STDIN_FILENO), _isatty(STDOUT_FILENO), _isatty(STDERR_FILENO), _isatty(STDERR_FILENO)
#else
isatty(STDIN_FILENO), isatty(STDOUT_FILENO), isatty(STDERR_FILENO), isatty(STDERR_FILENO)
#endif
};
// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio chararacter streams: cin, cout, cerr and clog.
bool isTTY(const ios& strm)
{
for(unsigned int i = 0; i < sizeof(StdioBufs)/sizeof(StdioBufs[0]); ++i) {
if(strm.rdbuf() == StdioBufs[i])
return StdioTtys[i];
}
return false;
}
// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio wide-chararacter streams: wcin, wcout, wcerr and wclog.
bool isTTY(const wios& strm)
{
for(unsigned int i = 0; i < sizeof(StdioWBufs)/sizeof(StdioWBufs[0]); ++i) {
if(strm.rdbuf() == StdioWBufs[i])
return StdioTtys[i];
}
return false;
}
Note: I've only tested it on Linux.
isatty(2)
(where2
is the fd corresponding tostderr
) to detect ifstderr
points to a terminal. I have no idea what the Windows equivalent would be. – Santiagosantillanstderr
points to a terminal, it doesn't mean that the stream object inoperator<<
points to console. It could be file as well: you can open ANY stream in a terminal program also! – Marleahfd
associated with theofstream
. If you restate the problem as "How do I distinguishcout
/cerr
from otherofstream
s?", then the problem is much simpler and less OS dependent, and maybe sufficient for the purpose. – Santiagosantillanostream
points to eithercout
orcerr
AND whetherstdout
orstderr
is a TTY or a file. And of course, for adding fun, if it is a TTY you might want to check its properties to know if it actually supports colors... – Goldacout
/cerr
, or they may really want to know if they're directed to a TTY. – Santiagosantillan