Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}