| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmStdIoTerminal.h" | |
| #include <array> | |
| #include <functional> | |
| #include <iosfwd> | |
| #include <ostream> | |
| #include <string> | |
| #include <type_traits> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| #ifdef _WIN32 | |
| # include <windows.h> | |
| #endif | |
| #include <cm/optional> | |
| #include "cmStdIoStream.h" | |
| #include "cmSystemTools.h" | |
| namespace cm { | |
| namespace StdIo { | |
| namespace { | |
| #ifdef _WIN32 | |
| WORD const kConsoleAttrMask = FOREGROUND_RED | FOREGROUND_GREEN | | |
| FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | | |
| BACKGROUND_BLUE | BACKGROUND_INTENSITY; | |
| std::array<WORD, kTermAttrCount> const kConsoleAttrs{ { | |
| 0, // Normal | |
| FOREGROUND_INTENSITY, // ForegroundBold | |
| 0, // ForegroundBlack | |
| FOREGROUND_BLUE, // ForegroundBlue | |
| FOREGROUND_GREEN | FOREGROUND_BLUE, // ForegroundCyan | |
| FOREGROUND_GREEN, // ForegroundGreen | |
| FOREGROUND_RED | FOREGROUND_BLUE, // ForegroundMagenta | |
| FOREGROUND_RED, // ForegroundRed | |
| FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // ForegroundWhite | |
| FOREGROUND_RED | FOREGROUND_GREEN, // ForegroundYellow | |
| BACKGROUND_INTENSITY, // BackgroundBold | |
| 0, // BackgroundBlack | |
| BACKGROUND_BLUE, // BackgroundBlue | |
| BACKGROUND_GREEN | BACKGROUND_BLUE, // BackgroundCyan | |
| BACKGROUND_GREEN, // BackgroundGreen | |
| BACKGROUND_RED | BACKGROUND_BLUE, // BackgroundMagenta | |
| BACKGROUND_RED, // BackgroundRed | |
| BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, // BackgroundWhite | |
| BACKGROUND_RED | BACKGROUND_GREEN, // BackgroundYellow | |
| } }; | |
| WORD ConsoleAttrs(WORD consoleAttrs, TermAttrSet const& attrs) | |
| { | |
| consoleAttrs = | |
| attrs.contains(TermAttr::Normal) ? consoleAttrs & kConsoleAttrMask : 0; | |
| for (TermAttr attr : attrs) { | |
| auto index = static_cast<std::underlying_type<TermAttr>::type>(attr); | |
| consoleAttrs |= kConsoleAttrs[index]; | |
| } | |
| return consoleAttrs; | |
| } | |
| #endif | |
| // VT100 escape sequence strings. | |
| #if defined(__MVS__) // z/OS: assume EBCDIC | |
| # define ESC "\47" | |
| #else | |
| # define ESC "\33" | |
| #endif | |
| std::array<cm::string_view, kTermAttrCount> const kVT100Codes{ { | |
| ESC "[0m"_s, // Normal | |
| ESC "[1m"_s, // ForegroundBold | |
| ESC "[30m"_s, // ForegroundBlack | |
| ESC "[34m"_s, // ForegroundBlue | |
| ESC "[36m"_s, // ForegroundCyan | |
| ESC "[32m"_s, // ForegroundGreen | |
| ESC "[35m"_s, // ForegroundMagenta | |
| ESC "[31m"_s, // ForegroundRed | |
| ESC "[37m"_s, // ForegroundWhite | |
| ESC "[33m"_s, // ForegroundYellow | |
| ""_s, // BackgroundBold | |
| ESC "[40m"_s, // BackgroundBlack | |
| ESC "[44m"_s, // BackgroundBlue | |
| ESC "[46m"_s, // BackgroundCyan | |
| ESC "[42m"_s, // BackgroundGreen | |
| ESC "[45m"_s, // BackgroundMagenta | |
| ESC "[41m"_s, // BackgroundRed | |
| ESC "[47m"_s, // BackgroundWhite | |
| ESC "[43m"_s, // BackgroundYellow | |
| } }; | |
| void SetVT100Attrs(std::ostream& os, TermAttrSet const& attrs) | |
| { | |
| for (TermAttr attr : attrs) { | |
| auto index = static_cast<std::underlying_type<TermAttr>::type>(attr); | |
| os << kVT100Codes[index]; | |
| } | |
| } | |
| auto const TermEnv = []() -> cm::optional<TermKind> { | |
| /* Disable color according to https://bixense.com/clicolors/ convention. */ | |
| if (cm::optional<std::string> noColor = | |
| cmSystemTools::GetEnvVar("NO_COLOR")) { | |
| if (!noColor->empty() && *noColor != "0"_s) { | |
| return TermKind::None; | |
| } | |
| } | |
| /* Force color according to https://bixense.com/clicolors/ convention. */ | |
| if (cm::optional<std::string> cliColorForce = | |
| cmSystemTools::GetEnvVar("CLICOLOR_FORCE")) { | |
| if (!cliColorForce->empty() && *cliColorForce != "0"_s) { | |
| return TermKind::VT100; | |
| } | |
| } | |
| /* Disable color according to https://bixense.com/clicolors/ convention. */ | |
| if (cm::optional<std::string> cliColor = | |
| cmSystemTools::GetEnvVar("CLICOLOR")) { | |
| if (*cliColor == "0"_s) { | |
| return TermKind::None; | |
| } | |
| } | |
| /* GNU make 4.1+ may tell us that its output is destined for a TTY. */ | |
| if (cm::optional<std::string> makeTermOut = | |
| cmSystemTools::GetEnvVar("MAKE_TERMOUT")) { | |
| if (!makeTermOut->empty()) { | |
| return TermKind::VT100; | |
| } | |
| } | |
| return cm::nullopt; | |
| }(); | |
| void Print(OStream& os, TermAttrSet const& attrs, | |
| std::function<void(std::ostream&)> const& f) | |
| { | |
| TermKind kind = TermEnv ? *TermEnv : os.Kind(); | |
| switch (kind) { | |
| case TermKind::None: | |
| f(os.IOS()); | |
| break; | |
| case TermKind::VT100: | |
| if (!attrs.empty()) { | |
| SetVT100Attrs(os.IOS(), attrs); | |
| f(os.IOS()); | |
| SetVT100Attrs(os.IOS(), TermAttr::Normal); | |
| } else { | |
| f(os.IOS()); | |
| } | |
| break; | |
| #ifdef _WIN32 | |
| case TermKind::Console: { | |
| HANDLE console = os.Console(); | |
| CONSOLE_SCREEN_BUFFER_INFO sbi; | |
| if (!attrs.empty() && GetConsoleScreenBufferInfo(console, &sbi)) { | |
| Out().IOS().flush(); | |
| Err().IOS().flush(); | |
| SetConsoleTextAttribute(console, ConsoleAttrs(sbi.wAttributes, attrs)); | |
| f(os.IOS()); | |
| Out().IOS().flush(); | |
| Err().IOS().flush(); | |
| SetConsoleTextAttribute( | |
| console, ConsoleAttrs(sbi.wAttributes, TermAttr::Normal)); | |
| } else { | |
| f(os.IOS()); | |
| } | |
| } break; | |
| #endif | |
| }; | |
| } | |
| } // anonymous namespace | |
| void Print(OStream& os, TermAttrSet const& attrs, cm::string_view s) | |
| { | |
| Print(os, attrs, [s](std::ostream& o) { o << s; }); | |
| } | |
| } | |
| } |