cmStdIoTerminal.cxx 5.6 KB

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