| /* 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 <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; }); |
| } |
| |
| } |
| } |