1a8c51b3fSopenharmony_ci// Copyright 2015 Google Inc. All rights reserved.
2a8c51b3fSopenharmony_ci//
3a8c51b3fSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4a8c51b3fSopenharmony_ci// you may not use this file except in compliance with the License.
5a8c51b3fSopenharmony_ci// You may obtain a copy of the License at
6a8c51b3fSopenharmony_ci//
7a8c51b3fSopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8a8c51b3fSopenharmony_ci//
9a8c51b3fSopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10a8c51b3fSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11a8c51b3fSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12a8c51b3fSopenharmony_ci// See the License for the specific language governing permissions and
13a8c51b3fSopenharmony_ci// limitations under the License.
14a8c51b3fSopenharmony_ci
15a8c51b3fSopenharmony_ci#include "colorprint.h"
16a8c51b3fSopenharmony_ci
17a8c51b3fSopenharmony_ci#include <cstdarg>
18a8c51b3fSopenharmony_ci#include <cstdio>
19a8c51b3fSopenharmony_ci#include <cstdlib>
20a8c51b3fSopenharmony_ci#include <cstring>
21a8c51b3fSopenharmony_ci#include <memory>
22a8c51b3fSopenharmony_ci#include <string>
23a8c51b3fSopenharmony_ci
24a8c51b3fSopenharmony_ci#include "check.h"
25a8c51b3fSopenharmony_ci#include "internal_macros.h"
26a8c51b3fSopenharmony_ci
27a8c51b3fSopenharmony_ci#ifdef BENCHMARK_OS_WINDOWS
28a8c51b3fSopenharmony_ci#include <io.h>
29a8c51b3fSopenharmony_ci#include <windows.h>
30a8c51b3fSopenharmony_ci#else
31a8c51b3fSopenharmony_ci#include <unistd.h>
32a8c51b3fSopenharmony_ci#endif  // BENCHMARK_OS_WINDOWS
33a8c51b3fSopenharmony_ci
34a8c51b3fSopenharmony_cinamespace benchmark {
35a8c51b3fSopenharmony_cinamespace {
36a8c51b3fSopenharmony_ci#ifdef BENCHMARK_OS_WINDOWS
37a8c51b3fSopenharmony_citypedef WORD PlatformColorCode;
38a8c51b3fSopenharmony_ci#else
39a8c51b3fSopenharmony_citypedef const char* PlatformColorCode;
40a8c51b3fSopenharmony_ci#endif
41a8c51b3fSopenharmony_ci
42a8c51b3fSopenharmony_ciPlatformColorCode GetPlatformColorCode(LogColor color) {
43a8c51b3fSopenharmony_ci#ifdef BENCHMARK_OS_WINDOWS
44a8c51b3fSopenharmony_ci  switch (color) {
45a8c51b3fSopenharmony_ci    case COLOR_RED:
46a8c51b3fSopenharmony_ci      return FOREGROUND_RED;
47a8c51b3fSopenharmony_ci    case COLOR_GREEN:
48a8c51b3fSopenharmony_ci      return FOREGROUND_GREEN;
49a8c51b3fSopenharmony_ci    case COLOR_YELLOW:
50a8c51b3fSopenharmony_ci      return FOREGROUND_RED | FOREGROUND_GREEN;
51a8c51b3fSopenharmony_ci    case COLOR_BLUE:
52a8c51b3fSopenharmony_ci      return FOREGROUND_BLUE;
53a8c51b3fSopenharmony_ci    case COLOR_MAGENTA:
54a8c51b3fSopenharmony_ci      return FOREGROUND_BLUE | FOREGROUND_RED;
55a8c51b3fSopenharmony_ci    case COLOR_CYAN:
56a8c51b3fSopenharmony_ci      return FOREGROUND_BLUE | FOREGROUND_GREEN;
57a8c51b3fSopenharmony_ci    case COLOR_WHITE:  // fall through to default
58a8c51b3fSopenharmony_ci    default:
59a8c51b3fSopenharmony_ci      return 0;
60a8c51b3fSopenharmony_ci  }
61a8c51b3fSopenharmony_ci#else
62a8c51b3fSopenharmony_ci  switch (color) {
63a8c51b3fSopenharmony_ci    case COLOR_RED:
64a8c51b3fSopenharmony_ci      return "1";
65a8c51b3fSopenharmony_ci    case COLOR_GREEN:
66a8c51b3fSopenharmony_ci      return "2";
67a8c51b3fSopenharmony_ci    case COLOR_YELLOW:
68a8c51b3fSopenharmony_ci      return "3";
69a8c51b3fSopenharmony_ci    case COLOR_BLUE:
70a8c51b3fSopenharmony_ci      return "4";
71a8c51b3fSopenharmony_ci    case COLOR_MAGENTA:
72a8c51b3fSopenharmony_ci      return "5";
73a8c51b3fSopenharmony_ci    case COLOR_CYAN:
74a8c51b3fSopenharmony_ci      return "6";
75a8c51b3fSopenharmony_ci    case COLOR_WHITE:
76a8c51b3fSopenharmony_ci      return "7";
77a8c51b3fSopenharmony_ci    default:
78a8c51b3fSopenharmony_ci      return nullptr;
79a8c51b3fSopenharmony_ci  };
80a8c51b3fSopenharmony_ci#endif
81a8c51b3fSopenharmony_ci}
82a8c51b3fSopenharmony_ci
83a8c51b3fSopenharmony_ci}  // end namespace
84a8c51b3fSopenharmony_ci
85a8c51b3fSopenharmony_cistd::string FormatString(const char* msg, va_list args) {
86a8c51b3fSopenharmony_ci  // we might need a second shot at this, so pre-emptivly make a copy
87a8c51b3fSopenharmony_ci  va_list args_cp;
88a8c51b3fSopenharmony_ci  va_copy(args_cp, args);
89a8c51b3fSopenharmony_ci
90a8c51b3fSopenharmony_ci  std::size_t size = 256;
91a8c51b3fSopenharmony_ci  char local_buff[256];
92a8c51b3fSopenharmony_ci  auto ret = vsnprintf(local_buff, size, msg, args_cp);
93a8c51b3fSopenharmony_ci
94a8c51b3fSopenharmony_ci  va_end(args_cp);
95a8c51b3fSopenharmony_ci
96a8c51b3fSopenharmony_ci  // currently there is no error handling for failure, so this is hack.
97a8c51b3fSopenharmony_ci  BM_CHECK(ret >= 0);
98a8c51b3fSopenharmony_ci
99a8c51b3fSopenharmony_ci  if (ret == 0) {  // handle empty expansion
100a8c51b3fSopenharmony_ci    return {};
101a8c51b3fSopenharmony_ci  }
102a8c51b3fSopenharmony_ci  if (static_cast<size_t>(ret) < size) {
103a8c51b3fSopenharmony_ci    return local_buff;
104a8c51b3fSopenharmony_ci  }
105a8c51b3fSopenharmony_ci  // we did not provide a long enough buffer on our first attempt.
106a8c51b3fSopenharmony_ci  size = static_cast<size_t>(ret) + 1;  // + 1 for the null byte
107a8c51b3fSopenharmony_ci  std::unique_ptr<char[]> buff(new char[size]);
108a8c51b3fSopenharmony_ci  ret = vsnprintf(buff.get(), size, msg, args);
109a8c51b3fSopenharmony_ci  BM_CHECK(ret > 0 && (static_cast<size_t>(ret)) < size);
110a8c51b3fSopenharmony_ci  return buff.get();
111a8c51b3fSopenharmony_ci}
112a8c51b3fSopenharmony_ci
113a8c51b3fSopenharmony_cistd::string FormatString(const char* msg, ...) {
114a8c51b3fSopenharmony_ci  va_list args;
115a8c51b3fSopenharmony_ci  va_start(args, msg);
116a8c51b3fSopenharmony_ci  auto tmp = FormatString(msg, args);
117a8c51b3fSopenharmony_ci  va_end(args);
118a8c51b3fSopenharmony_ci  return tmp;
119a8c51b3fSopenharmony_ci}
120a8c51b3fSopenharmony_ci
121a8c51b3fSopenharmony_civoid ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
122a8c51b3fSopenharmony_ci  va_list args;
123a8c51b3fSopenharmony_ci  va_start(args, fmt);
124a8c51b3fSopenharmony_ci  ColorPrintf(out, color, fmt, args);
125a8c51b3fSopenharmony_ci  va_end(args);
126a8c51b3fSopenharmony_ci}
127a8c51b3fSopenharmony_ci
128a8c51b3fSopenharmony_civoid ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
129a8c51b3fSopenharmony_ci                 va_list args) {
130a8c51b3fSopenharmony_ci#ifdef BENCHMARK_OS_WINDOWS
131a8c51b3fSopenharmony_ci  ((void)out);  // suppress unused warning
132a8c51b3fSopenharmony_ci
133a8c51b3fSopenharmony_ci  const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
134a8c51b3fSopenharmony_ci
135a8c51b3fSopenharmony_ci  // Gets the current text color.
136a8c51b3fSopenharmony_ci  CONSOLE_SCREEN_BUFFER_INFO buffer_info;
137a8c51b3fSopenharmony_ci  GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
138a8c51b3fSopenharmony_ci  const WORD old_color_attrs = buffer_info.wAttributes;
139a8c51b3fSopenharmony_ci
140a8c51b3fSopenharmony_ci  // We need to flush the stream buffers into the console before each
141a8c51b3fSopenharmony_ci  // SetConsoleTextAttribute call lest it affect the text that is already
142a8c51b3fSopenharmony_ci  // printed but has not yet reached the console.
143a8c51b3fSopenharmony_ci  fflush(stdout);
144a8c51b3fSopenharmony_ci  SetConsoleTextAttribute(stdout_handle,
145a8c51b3fSopenharmony_ci                          GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
146a8c51b3fSopenharmony_ci  vprintf(fmt, args);
147a8c51b3fSopenharmony_ci
148a8c51b3fSopenharmony_ci  fflush(stdout);
149a8c51b3fSopenharmony_ci  // Restores the text color.
150a8c51b3fSopenharmony_ci  SetConsoleTextAttribute(stdout_handle, old_color_attrs);
151a8c51b3fSopenharmony_ci#else
152a8c51b3fSopenharmony_ci  const char* color_code = GetPlatformColorCode(color);
153a8c51b3fSopenharmony_ci  if (color_code) out << FormatString("\033[0;3%sm", color_code);
154a8c51b3fSopenharmony_ci  out << FormatString(fmt, args) << "\033[m";
155a8c51b3fSopenharmony_ci#endif
156a8c51b3fSopenharmony_ci}
157a8c51b3fSopenharmony_ci
158a8c51b3fSopenharmony_cibool IsColorTerminal() {
159a8c51b3fSopenharmony_ci#if BENCHMARK_OS_WINDOWS
160a8c51b3fSopenharmony_ci  // On Windows the TERM variable is usually not set, but the
161a8c51b3fSopenharmony_ci  // console there does support colors.
162a8c51b3fSopenharmony_ci  return 0 != _isatty(_fileno(stdout));
163a8c51b3fSopenharmony_ci#else
164a8c51b3fSopenharmony_ci  // On non-Windows platforms, we rely on the TERM variable. This list of
165a8c51b3fSopenharmony_ci  // supported TERM values is copied from Google Test:
166a8c51b3fSopenharmony_ci  // <https://github.com/google/googletest/blob/v1.13.0/googletest/src/gtest.cc#L3225-L3259>.
167a8c51b3fSopenharmony_ci  const char* const SUPPORTED_TERM_VALUES[] = {
168a8c51b3fSopenharmony_ci      "xterm",
169a8c51b3fSopenharmony_ci      "xterm-color",
170a8c51b3fSopenharmony_ci      "xterm-256color",
171a8c51b3fSopenharmony_ci      "screen",
172a8c51b3fSopenharmony_ci      "screen-256color",
173a8c51b3fSopenharmony_ci      "tmux",
174a8c51b3fSopenharmony_ci      "tmux-256color",
175a8c51b3fSopenharmony_ci      "rxvt-unicode",
176a8c51b3fSopenharmony_ci      "rxvt-unicode-256color",
177a8c51b3fSopenharmony_ci      "linux",
178a8c51b3fSopenharmony_ci      "cygwin",
179a8c51b3fSopenharmony_ci      "xterm-kitty",
180a8c51b3fSopenharmony_ci      "alacritty",
181a8c51b3fSopenharmony_ci      "foot",
182a8c51b3fSopenharmony_ci      "foot-extra",
183a8c51b3fSopenharmony_ci      "wezterm",
184a8c51b3fSopenharmony_ci  };
185a8c51b3fSopenharmony_ci
186a8c51b3fSopenharmony_ci  const char* const term = getenv("TERM");
187a8c51b3fSopenharmony_ci
188a8c51b3fSopenharmony_ci  bool term_supports_color = false;
189a8c51b3fSopenharmony_ci  for (const char* candidate : SUPPORTED_TERM_VALUES) {
190a8c51b3fSopenharmony_ci    if (term && 0 == strcmp(term, candidate)) {
191a8c51b3fSopenharmony_ci      term_supports_color = true;
192a8c51b3fSopenharmony_ci      break;
193a8c51b3fSopenharmony_ci    }
194a8c51b3fSopenharmony_ci  }
195a8c51b3fSopenharmony_ci
196a8c51b3fSopenharmony_ci  return 0 != isatty(fileno(stdout)) && term_supports_color;
197a8c51b3fSopenharmony_ci#endif  // BENCHMARK_OS_WINDOWS
198a8c51b3fSopenharmony_ci}
199a8c51b3fSopenharmony_ci
200a8c51b3fSopenharmony_ci}  // end namespace benchmark
201