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