/src/CMake/Source/cmStdIoTerminal.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmStdIoTerminal.h" |
4 | | |
5 | | #include <array> |
6 | | #include <functional> |
7 | | #include <iosfwd> |
8 | | #include <ostream> |
9 | | #include <string> |
10 | | #include <type_traits> |
11 | | |
12 | | #include <cm/string_view> |
13 | | #include <cmext/string_view> |
14 | | |
15 | | #ifdef _WIN32 |
16 | | # include <windows.h> |
17 | | #endif |
18 | | |
19 | | #include <cm/optional> |
20 | | |
21 | | #include "cmStdIoStream.h" |
22 | | #include "cmSystemTools.h" |
23 | | |
24 | | namespace cm { |
25 | | namespace StdIo { |
26 | | |
27 | | namespace { |
28 | | |
29 | | #ifdef _WIN32 |
30 | | WORD const kConsoleAttrMask = FOREGROUND_RED | FOREGROUND_GREEN | |
31 | | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | |
32 | | BACKGROUND_BLUE | BACKGROUND_INTENSITY; |
33 | | std::array<WORD, kTermAttrCount> const kConsoleAttrs{ { |
34 | | 0, // Normal |
35 | | FOREGROUND_INTENSITY, // ForegroundBold |
36 | | 0, // ForegroundBlack |
37 | | FOREGROUND_BLUE, // ForegroundBlue |
38 | | FOREGROUND_GREEN | FOREGROUND_BLUE, // ForegroundCyan |
39 | | FOREGROUND_GREEN, // ForegroundGreen |
40 | | FOREGROUND_RED | FOREGROUND_BLUE, // ForegroundMagenta |
41 | | FOREGROUND_RED, // ForegroundRed |
42 | | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // ForegroundWhite |
43 | | FOREGROUND_RED | FOREGROUND_GREEN, // ForegroundYellow |
44 | | BACKGROUND_INTENSITY, // BackgroundBold |
45 | | 0, // BackgroundBlack |
46 | | BACKGROUND_BLUE, // BackgroundBlue |
47 | | BACKGROUND_GREEN | BACKGROUND_BLUE, // BackgroundCyan |
48 | | BACKGROUND_GREEN, // BackgroundGreen |
49 | | BACKGROUND_RED | BACKGROUND_BLUE, // BackgroundMagenta |
50 | | BACKGROUND_RED, // BackgroundRed |
51 | | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, // BackgroundWhite |
52 | | BACKGROUND_RED | BACKGROUND_GREEN, // BackgroundYellow |
53 | | } }; |
54 | | |
55 | | WORD ConsoleAttrs(WORD consoleAttrs, TermAttrSet const& attrs) |
56 | | { |
57 | | consoleAttrs = |
58 | | attrs.contains(TermAttr::Normal) ? consoleAttrs & kConsoleAttrMask : 0; |
59 | | for (TermAttr attr : attrs) { |
60 | | auto index = static_cast<std::underlying_type<TermAttr>::type>(attr); |
61 | | consoleAttrs |= kConsoleAttrs[index]; |
62 | | } |
63 | | return consoleAttrs; |
64 | | } |
65 | | #endif |
66 | | |
67 | | // VT100 escape sequence strings. |
68 | | #if defined(__MVS__) // z/OS: assume EBCDIC |
69 | | # define ESC "\47" |
70 | | #else |
71 | | # define ESC "\33" |
72 | | #endif |
73 | | |
74 | | std::array<cm::string_view, kTermAttrCount> const kVT100Codes{ { |
75 | | ESC "[0m"_s, // Normal |
76 | | ESC "[1m"_s, // ForegroundBold |
77 | | ESC "[30m"_s, // ForegroundBlack |
78 | | ESC "[34m"_s, // ForegroundBlue |
79 | | ESC "[36m"_s, // ForegroundCyan |
80 | | ESC "[32m"_s, // ForegroundGreen |
81 | | ESC "[35m"_s, // ForegroundMagenta |
82 | | ESC "[31m"_s, // ForegroundRed |
83 | | ESC "[37m"_s, // ForegroundWhite |
84 | | ESC "[33m"_s, // ForegroundYellow |
85 | | ""_s, // BackgroundBold |
86 | | ESC "[40m"_s, // BackgroundBlack |
87 | | ESC "[44m"_s, // BackgroundBlue |
88 | | ESC "[46m"_s, // BackgroundCyan |
89 | | ESC "[42m"_s, // BackgroundGreen |
90 | | ESC "[45m"_s, // BackgroundMagenta |
91 | | ESC "[41m"_s, // BackgroundRed |
92 | | ESC "[47m"_s, // BackgroundWhite |
93 | | ESC "[43m"_s, // BackgroundYellow |
94 | | } }; |
95 | | |
96 | | void SetVT100Attrs(std::ostream& os, TermAttrSet const& attrs) |
97 | 0 | { |
98 | 0 | for (TermAttr attr : attrs) { |
99 | 0 | auto index = static_cast<std::underlying_type<TermAttr>::type>(attr); |
100 | 0 | os << kVT100Codes[index]; |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | 4 | auto const TermEnv = []() -> cm::optional<TermKind> { |
105 | | /* Disable color according to https://bixense.com/clicolors/ convention. */ |
106 | 4 | if (cm::optional<std::string> noColor = |
107 | 4 | cmSystemTools::GetEnvVar("NO_COLOR")) { |
108 | 0 | if (!noColor->empty() && *noColor != "0"_s) { |
109 | 0 | return TermKind::None; |
110 | 0 | } |
111 | 0 | } |
112 | | /* Force color according to https://bixense.com/clicolors/ convention. */ |
113 | 4 | if (cm::optional<std::string> cliColorForce = |
114 | 4 | cmSystemTools::GetEnvVar("CLICOLOR_FORCE")) { |
115 | 0 | if (!cliColorForce->empty() && *cliColorForce != "0"_s) { |
116 | 0 | return TermKind::VT100; |
117 | 0 | } |
118 | 0 | } |
119 | | /* Disable color according to https://bixense.com/clicolors/ convention. */ |
120 | 4 | if (cm::optional<std::string> cliColor = |
121 | 4 | cmSystemTools::GetEnvVar("CLICOLOR")) { |
122 | 0 | if (*cliColor == "0"_s) { |
123 | 0 | return TermKind::None; |
124 | 0 | } |
125 | 0 | } |
126 | | /* GNU make 4.1+ may tell us that its output is destined for a TTY. */ |
127 | 4 | if (cm::optional<std::string> makeTermOut = |
128 | 4 | cmSystemTools::GetEnvVar("MAKE_TERMOUT")) { |
129 | 0 | if (!makeTermOut->empty()) { |
130 | 0 | return TermKind::VT100; |
131 | 0 | } |
132 | 0 | } |
133 | 4 | return cm::nullopt; |
134 | 4 | }(); |
135 | | |
136 | | void Print(OStream& os, TermAttrSet const& attrs, |
137 | | std::function<void(std::ostream&)> const& f) |
138 | 0 | { |
139 | 0 | TermKind kind = TermEnv ? *TermEnv : os.Kind(); |
140 | 0 | switch (kind) { |
141 | 0 | case TermKind::None: |
142 | 0 | f(os.IOS()); |
143 | 0 | break; |
144 | 0 | case TermKind::VT100: |
145 | 0 | if (!attrs.empty()) { |
146 | 0 | SetVT100Attrs(os.IOS(), attrs); |
147 | 0 | f(os.IOS()); |
148 | 0 | SetVT100Attrs(os.IOS(), TermAttr::Normal); |
149 | 0 | } else { |
150 | 0 | f(os.IOS()); |
151 | 0 | } |
152 | 0 | break; |
153 | | #ifdef _WIN32 |
154 | | case TermKind::Console: { |
155 | | HANDLE console = os.Console(); |
156 | | CONSOLE_SCREEN_BUFFER_INFO sbi; |
157 | | if (!attrs.empty() && GetConsoleScreenBufferInfo(console, &sbi)) { |
158 | | Out().IOS().flush(); |
159 | | Err().IOS().flush(); |
160 | | SetConsoleTextAttribute(console, ConsoleAttrs(sbi.wAttributes, attrs)); |
161 | | f(os.IOS()); |
162 | | Out().IOS().flush(); |
163 | | Err().IOS().flush(); |
164 | | SetConsoleTextAttribute( |
165 | | console, ConsoleAttrs(sbi.wAttributes, TermAttr::Normal)); |
166 | | } else { |
167 | | f(os.IOS()); |
168 | | } |
169 | | } break; |
170 | | #endif |
171 | 0 | }; |
172 | 0 | } |
173 | | |
174 | | } // anonymous namespace |
175 | | |
176 | | void Print(OStream& os, TermAttrSet const& attrs, cm::string_view s) |
177 | 0 | { |
178 | 0 | Print(os, attrs, [s](std::ostream& o) { o << s; }); |
179 | 0 | } |
180 | | |
181 | | } |
182 | | } |