mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08: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
|
||||
cmStdIoInit.h
|
||||
cmStdIoInit.cxx
|
||||
cmStdIoStream.h
|
||||
cmStdIoStream.cxx
|
||||
cmStringAlgorithms.cxx
|
||||
cmStringAlgorithms.h
|
||||
cmSyntheticTargetCache.h
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
@@ -18,6 +19,8 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "cmStdIoStream.h"
|
||||
|
||||
namespace cm {
|
||||
namespace StdIo {
|
||||
|
||||
@@ -82,7 +85,11 @@ struct InitStdPipes
|
||||
class Globals
|
||||
{
|
||||
public:
|
||||
std::ios::Init InitIos;
|
||||
InitStdPipes InitPipes;
|
||||
IStream StdIn{ std::cin, stdin };
|
||||
OStream StdOut{ std::cout, stdout };
|
||||
OStream StdErr{ std::cerr, stderr };
|
||||
|
||||
static Globals& Get();
|
||||
};
|
||||
@@ -98,5 +105,20 @@ Init::Init()
|
||||
Globals::Get();
|
||||
}
|
||||
|
||||
IStream& In()
|
||||
{
|
||||
return Globals::Get().StdIn;
|
||||
}
|
||||
|
||||
OStream& Out()
|
||||
{
|
||||
return Globals::Get().StdOut;
|
||||
}
|
||||
|
||||
OStream& Err()
|
||||
{
|
||||
return Globals::Get().StdErr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
161
Source/cmStdIoStream.cxx
Normal file
161
Source/cmStdIoStream.cxx
Normal file
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
103
Source/cmStdIoStream.h
Normal file
103
Source/cmStdIoStream.h
Normal file
@@ -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
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
#include <cm/string_view>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmStdIoInit.h"
|
||||
#include "cmStdIoStream.h"
|
||||
|
||||
#include "testCommon.h"
|
||||
|
||||
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*/[])
|
||||
{
|
||||
cm::StdIo::Init();
|
||||
return runTests({});
|
||||
return runTests({
|
||||
testStream,
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user