blob: b1cb19c0df69f1d5541ca8db85b5a058729fa018 [file] [log] [blame] [edit]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmStdIoStream.h"
#include <algorithm>
#include <array>
#include <cstdio>
#include <istream> // IWYU pragma: keep
#include <ostream> // IWYU pragma: keep
#ifdef _WIN32
# include <windows.h>
# include <io.h> // for _get_osfhandle
#else
# include <string>
# include <cm/optional>
# include <cm/string_view>
# include <cmext/string_view>
# include <unistd.h>
#endif
#include "cm_fileno.hxx"
#ifndef _WIN32
# include "cmSystemTools.h"
#endif
namespace cm {
namespace StdIo {
namespace {
#ifdef _WIN32
# ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
# define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
# endif
# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
# endif
#else
// List of known `TERM` names that support VT100 escape sequences.
// Order by `LC_COLLATE=C sort` to search using `std::lower_bound`.
std::array<cm::string_view, 56> const kVT100Names{ {
"Eterm"_s,
"alacritty"_s,
"alacritty-direct"_s,
"ansi"_s,
"color-xterm"_s,
"con132x25"_s,
"con132x30"_s,
"con132x43"_s,
"con132x60"_s,
"con80x25"_s,
"con80x28"_s,
"con80x30"_s,
"con80x43"_s,
"con80x50"_s,
"con80x60"_s,
"cons25"_s,
"console"_s,
"cygwin"_s,
"dtterm"_s,
"eterm-color"_s,
"gnome"_s,
"gnome-256color"_s,
"konsole"_s,
"konsole-256color"_s,
"kterm"_s,
"linux"_s,
"linux-c"_s,
"mach-color"_s,
"mlterm"_s,
"msys"_s,
"putty"_s,
"putty-256color"_s,
"rxvt"_s,
"rxvt-256color"_s,
"rxvt-cygwin"_s,
"rxvt-cygwin-native"_s,
"rxvt-unicode"_s,
"rxvt-unicode-256color"_s,
"screen"_s,
"screen-256color"_s,
"screen-256color-bce"_s,
"screen-bce"_s,
"screen-w"_s,
"screen.linux"_s,
"st-256color"_s,
"tmux"_s,
"tmux-256color"_s,
"vt100"_s,
"xterm"_s,
"xterm-16color"_s,
"xterm-256color"_s,
"xterm-88color"_s,
"xterm-color"_s,
"xterm-debian"_s,
"xterm-kitty"_s,
"xterm-termite"_s,
} };
bool TermIsVT100()
{
if (cm::optional<std::string> term = cmSystemTools::GetEnvVar("TERM")) {
// NOLINTNEXTLINE(readability-qualified-auto)
auto i = std::lower_bound(kVT100Names.begin(), kVT100Names.end(), *term);
if (i != kVT100Names.end() && *i == *term) {
return true;
}
}
return false;
}
#endif
} // anonymous namespace
Stream::Stream(std::ios& s, FILE* file, Direction direction)
: IOS_(s)
, FD_(cm_fileno(file))
{
#ifdef _WIN32
auto h = reinterpret_cast<HANDLE>(_get_osfhandle(this->FD_));
if (GetConsoleMode(h, &this->ConsoleOrigMode_)) {
this->Console_ = h;
DWORD vtMode = this->ConsoleOrigMode_ |
(direction == Direction::In ? ENABLE_VIRTUAL_TERMINAL_INPUT
: ENABLE_VIRTUAL_TERMINAL_PROCESSING);
if (SetConsoleMode(this->Console_, vtMode)) {
this->Kind_ = TermKind::VT100;
} else {
SetConsoleMode(this->Console_, this->ConsoleOrigMode_);
this->Kind_ = TermKind::Console;
}
}
#else
static_cast<void>(direction);
if (isatty(this->FD_) && TermIsVT100()) {
this->Kind_ = TermKind::VT100;
}
#endif
}
#ifdef _WIN32
Stream::~Stream()
{
if (this->Console_) {
this->IOS_.rdbuf()->pubsync();
SetConsoleMode(this->Console_, this->ConsoleOrigMode_);
}
}
#else
Stream::~Stream() = default;
#endif
IStream::IStream(std::istream& is, FILE* file)
: Stream(is, file, Direction::In)
{
}
std::istream& IStream::IOS() const
{
return dynamic_cast<std::istream&>(this->Stream::IOS());
}
OStream::OStream(std::ostream& os, FILE* file)
: Stream(os, file, Direction::Out)
{
}
std::ostream& OStream::IOS() const
{
return dynamic_cast<std::ostream&>(this->Stream::IOS());
}
}
}