mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-09 07:39:47 -05:00
StdIo: Provide metadata about stdin, stdout, stderr streams
Detect the kind of terminal to which they are attached, if any. Issue: #26924
This commit is contained in:
@@ -470,6 +470,8 @@ add_library(
|
|||||||
cmStateTypes.h
|
cmStateTypes.h
|
||||||
cmStdIoInit.h
|
cmStdIoInit.h
|
||||||
cmStdIoInit.cxx
|
cmStdIoInit.cxx
|
||||||
|
cmStdIoStream.h
|
||||||
|
cmStdIoStream.cxx
|
||||||
cmStringAlgorithms.cxx
|
cmStringAlgorithms.cxx
|
||||||
cmStringAlgorithms.h
|
cmStringAlgorithms.h
|
||||||
cmSyntheticTargetCache.h
|
cmSyntheticTargetCache.h
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@
|
|||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "cmStdIoStream.h"
|
||||||
|
|
||||||
namespace cm {
|
namespace cm {
|
||||||
namespace StdIo {
|
namespace StdIo {
|
||||||
|
|
||||||
@@ -82,7 +85,11 @@ struct InitStdPipes
|
|||||||
class Globals
|
class Globals
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
std::ios::Init InitIos;
|
||||||
InitStdPipes InitPipes;
|
InitStdPipes InitPipes;
|
||||||
|
IStream StdIn{ std::cin, stdin };
|
||||||
|
OStream StdOut{ std::cout, stdout };
|
||||||
|
OStream StdErr{ std::cerr, stderr };
|
||||||
|
|
||||||
static Globals& Get();
|
static Globals& Get();
|
||||||
};
|
};
|
||||||
@@ -98,5 +105,20 @@ Init::Init()
|
|||||||
Globals::Get();
|
Globals::Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IStream& In()
|
||||||
|
{
|
||||||
|
return Globals::Get().StdIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
OStream& Out()
|
||||||
|
{
|
||||||
|
return Globals::Get().StdOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
OStream& Err()
|
||||||
|
{
|
||||||
|
return Globals::Get().StdErr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/* 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 {
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// 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
|
||||||
|
DWORD mode;
|
||||||
|
auto h = reinterpret_cast<HANDLE>(_get_osfhandle(this->FD_));
|
||||||
|
if (GetConsoleMode(h, &mode)) {
|
||||||
|
this->Console_ = h;
|
||||||
|
DWORD vtMode = mode |
|
||||||
|
(direction == Direction::In ? ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||||
|
: ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||||
|
if (SetConsoleMode(this->Console_, vtMode)) {
|
||||||
|
this->Kind_ = TermKind::VT100;
|
||||||
|
} else {
|
||||||
|
SetConsoleMode(this->Console_, mode);
|
||||||
|
this->Kind_ = TermKind::Console;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static_cast<void>(direction);
|
||||||
|
if (isatty(this->FD_) && TermIsVT100()) {
|
||||||
|
this->Kind_ = TermKind::VT100;
|
||||||
|
}
|
||||||
|
#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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cmConfigure.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
namespace cm {
|
||||||
|
namespace StdIo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the kind of terminal to which a stream is attached, if any.
|
||||||
|
*/
|
||||||
|
enum class TermKind
|
||||||
|
{
|
||||||
|
/** Not an interactive terminal. */
|
||||||
|
None,
|
||||||
|
/** A VT100 terminal. */
|
||||||
|
VT100,
|
||||||
|
#ifdef _WIN32
|
||||||
|
/** A Windows Console that does not support VT100 sequences. */
|
||||||
|
Console,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent stdin, stdout, or stderr stream metadata.
|
||||||
|
*/
|
||||||
|
class Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** The kind of terminal to which the stream is attached, if any. */
|
||||||
|
TermKind Kind() const { return this->Kind_; }
|
||||||
|
|
||||||
|
/** The underlying C++ stream. */
|
||||||
|
std::ios& IOS() const { return this->IOS_; }
|
||||||
|
|
||||||
|
/** The underlying file descriptor. */
|
||||||
|
int FD() const { return this->FD_; }
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/** The underlying HANDLE of an attached Windows Console, if any. */
|
||||||
|
void* Console() const { return this->Console_; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
In,
|
||||||
|
Out,
|
||||||
|
};
|
||||||
|
|
||||||
|
Stream(std::ios& s, FILE* file, Direction direction);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ios& IOS_;
|
||||||
|
int FD_ = -1;
|
||||||
|
TermKind Kind_ = TermKind::None;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
void* Console_ = nullptr;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent stdin metadata.
|
||||||
|
*/
|
||||||
|
class IStream : public Stream
|
||||||
|
{
|
||||||
|
friend class Globals;
|
||||||
|
IStream(std::istream& is, FILE* file);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** The underlying C++ stream. */
|
||||||
|
std::istream& IOS() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent stdout or stderr metadata.
|
||||||
|
*/
|
||||||
|
class OStream : public Stream
|
||||||
|
{
|
||||||
|
friend class Globals;
|
||||||
|
OStream(std::ostream& os, FILE* file);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** The underlying C++ stream. */
|
||||||
|
std::ostream& IOS() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Metadata for stdin. */
|
||||||
|
IStream& In();
|
||||||
|
|
||||||
|
/** Metadata for stdout. */
|
||||||
|
OStream& Out();
|
||||||
|
|
||||||
|
/** Metadata for stderr. */
|
||||||
|
OStream& Err();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,48 @@
|
|||||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||||
|
|
||||||
|
#include <cm/string_view>
|
||||||
|
#include <cmext/string_view>
|
||||||
|
|
||||||
#include "cmStdIoInit.h"
|
#include "cmStdIoInit.h"
|
||||||
|
#include "cmStdIoStream.h"
|
||||||
|
|
||||||
#include "testCommon.h"
|
#include "testCommon.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
void printTermKind(cm::string_view t, cm::StdIo::Stream& s)
|
||||||
|
{
|
||||||
|
switch (s.Kind()) {
|
||||||
|
case cm::StdIo::TermKind::None:
|
||||||
|
std::cout << " " << t << " is not a terminal.\n";
|
||||||
|
break;
|
||||||
|
case cm::StdIo::TermKind::VT100:
|
||||||
|
std::cout << " " << t << " is a VT100 terminal.\n";
|
||||||
|
break;
|
||||||
|
#ifdef _WIN32
|
||||||
|
case cm::StdIo::TermKind::Console:
|
||||||
|
std::cout << " " << t << " is a Windows Console.\n";
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testStream()
|
||||||
|
{
|
||||||
|
std::cout << "testStream()\n";
|
||||||
|
printTermKind("stdin"_s, cm::StdIo::In());
|
||||||
|
printTermKind("stdout"_s, cm::StdIo::Out());
|
||||||
|
printTermKind("stderr"_s, cm::StdIo::Err());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int testStdIo(int /*unused*/, char* /*unused*/[])
|
int testStdIo(int /*unused*/, char* /*unused*/[])
|
||||||
{
|
{
|
||||||
cm::StdIo::Init();
|
cm::StdIo::Init();
|
||||||
return runTests({});
|
return runTests({
|
||||||
|
testStream,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user