1
0

cmStdIoTerminal.cxx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. /* Force color according to https://bixense.com/clicolors/ convention. */
  92. if (cm::optional<std::string> cliColorForce =
  93. cmSystemTools::GetEnvVar("CLICOLOR_FORCE")) {
  94. if (!cliColorForce->empty() && *cliColorForce != "0"_s) {
  95. return TermKind::VT100;
  96. }
  97. }
  98. /* Disable color according to https://bixense.com/clicolors/ convention. */
  99. if (cm::optional<std::string> cliColor =
  100. cmSystemTools::GetEnvVar("CLICOLOR")) {
  101. if (*cliColor == "0"_s) {
  102. return TermKind::None;
  103. }
  104. }
  105. /* GNU make 4.1+ may tell us that its output is destined for a TTY. */
  106. if (cm::optional<std::string> makeTermOut =
  107. cmSystemTools::GetEnvVar("MAKE_TERMOUT")) {
  108. if (!makeTermOut->empty()) {
  109. return TermKind::VT100;
  110. }
  111. }
  112. return cm::nullopt;
  113. }();
  114. void Print(OStream& os, TermAttrSet const& attrs,
  115. std::function<void(std::ostream&)> const& f)
  116. {
  117. TermKind kind = TermEnv ? *TermEnv : os.Kind();
  118. switch (kind) {
  119. case TermKind::None:
  120. f(os.IOS());
  121. break;
  122. case TermKind::VT100:
  123. SetVT100Attrs(os.IOS(), attrs);
  124. f(os.IOS());
  125. SetVT100Attrs(os.IOS(), TermAttr::Normal);
  126. break;
  127. #ifdef _WIN32
  128. case TermKind::Console: {
  129. HANDLE console = os.Console();
  130. CONSOLE_SCREEN_BUFFER_INFO sbi;
  131. if (!attrs.empty() && GetConsoleScreenBufferInfo(console, &sbi)) {
  132. Out().IOS().flush();
  133. Err().IOS().flush();
  134. SetConsoleTextAttribute(console, ConsoleAttrs(sbi.wAttributes, attrs));
  135. f(os.IOS());
  136. Out().IOS().flush();
  137. Err().IOS().flush();
  138. SetConsoleTextAttribute(
  139. console, ConsoleAttrs(sbi.wAttributes, TermAttr::Normal));
  140. } else {
  141. f(os.IOS());
  142. }
  143. } break;
  144. #endif
  145. };
  146. }
  147. } // anonymous namespace
  148. void Print(OStream& os, TermAttrSet const& attrs, cm::string_view s)
  149. {
  150. Print(os, attrs, [s](std::ostream& o) { o << s; });
  151. }
  152. }
  153. }