1a187aa080377f4707bb28690d51f206c0d49f436Eric Fiselier// Copyright 2015 Google Inc. All rights reserved.
25a71bd6e6656bb1610da794489e65ac7d0b13bc6Dominic Hamon//
33fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// Licensed under the Apache License, Version 2.0 (the "License");
43fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// you may not use this file except in compliance with the License.
53fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// You may obtain a copy of the License at
65a71bd6e6656bb1610da794489e65ac7d0b13bc6Dominic Hamon//
73fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon//     http://www.apache.org/licenses/LICENSE-2.0
85a71bd6e6656bb1610da794489e65ac7d0b13bc6Dominic Hamon//
93fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// Unless required by applicable law or agreed to in writing, software
103fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// distributed under the License is distributed on an "AS IS" BASIS,
113fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// See the License for the specific language governing permissions and
133fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon// limitations under the License.
143fb8268eab1a0f116f5e690e13d1234265998f2cDominic Hamon
15403f35442375f2ee858981b79421ca321645df0Dominic Hamon#include "colorprint.h"
16403f35442375f2ee858981b79421ca321645df0Dominic Hamon
17a187aa080377f4707bb28690d51f206c0d49f436Eric Fiselier#include <cstdarg>
18a863aead1330bf52d4230667a5aae4b7fa5ef69eryobg#include <cstdio>
19917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#include <cstdlib>
20917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#include <cstring>
215686bf1b38f8aa713267097d7c1944140f71b5d3Eric#include <memory>
22917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#include <string>
23403f35442375f2ee858981b79421ca321645df0Dominic Hamon
245686bf1b38f8aa713267097d7c1944140f71b5d3Eric#include "check.h"
25a187aa080377f4707bb28690d51f206c0d49f436Eric Fiselier#include "internal_macros.h"
26403f35442375f2ee858981b79421ca321645df0Dominic Hamon
27ba141ac0d9f2042c73f57fcf870f2a69030fa8daAnton Danielsson#ifdef BENCHMARK_OS_WINDOWS
28dd61391c5d92353c69f2789d0dabbd3311fb2564Matt Clarkson#include <Windows.h>
29332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon#include <io.h>
30917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#else
31917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#include <unistd.h>
32332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon#endif  // BENCHMARK_OS_WINDOWS
33dd61391c5d92353c69f2789d0dabbd3311fb2564Matt Clarkson
34e390e4ebc3c728e69d335a33c0cb00547dcd0ddeDominic Hamonnamespace benchmark {
35403f35442375f2ee858981b79421ca321645df0Dominic Hamonnamespace {
36ba141ac0d9f2042c73f57fcf870f2a69030fa8daAnton Danielsson#ifdef BENCHMARK_OS_WINDOWS
37403f35442375f2ee858981b79421ca321645df0Dominic Hamontypedef WORD PlatformColorCode;
38403f35442375f2ee858981b79421ca321645df0Dominic Hamon#else
39403f35442375f2ee858981b79421ca321645df0Dominic Hamontypedef const char* PlatformColorCode;
40403f35442375f2ee858981b79421ca321645df0Dominic Hamon#endif
41403f35442375f2ee858981b79421ca321645df0Dominic Hamon
42403f35442375f2ee858981b79421ca321645df0Dominic HamonPlatformColorCode GetPlatformColorCode(LogColor color) {
43ba141ac0d9f2042c73f57fcf870f2a69030fa8daAnton Danielsson#ifdef BENCHMARK_OS_WINDOWS
44403f35442375f2ee858981b79421ca321645df0Dominic Hamon  switch (color) {
454ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_RED:
464ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_RED;
474ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_GREEN:
484ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_GREEN;
494ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_YELLOW:
504ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_RED | FOREGROUND_GREEN;
514ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_BLUE:
524ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_BLUE;
534ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_MAGENTA:
544ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_BLUE | FOREGROUND_RED;
554ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_CYAN:
564ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return FOREGROUND_BLUE | FOREGROUND_GREEN;
574ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_WHITE:  // fall through to default
584ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    default:
594ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return 0;
60403f35442375f2ee858981b79421ca321645df0Dominic Hamon  }
61403f35442375f2ee858981b79421ca321645df0Dominic Hamon#else
62403f35442375f2ee858981b79421ca321645df0Dominic Hamon  switch (color) {
634ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_RED:
644ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "1";
654ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_GREEN:
664ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "2";
674ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_YELLOW:
684ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "3";
694ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_BLUE:
704ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "4";
714ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_MAGENTA:
724ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "5";
734ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_CYAN:
744ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "6";
754ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    case COLOR_WHITE:
764ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon      return "7";
774ce184d86ddefe6170914a4813940dbcbe645207Dominic Hamon    default:
78180981716ba7b568820aa21ecf15247b990a8621Eric Fiselier      return nullptr;
79403f35442375f2ee858981b79421ca321645df0Dominic Hamon  };
80403f35442375f2ee858981b79421ca321645df0Dominic Hamon#endif
81403f35442375f2ee858981b79421ca321645df0Dominic Hamon}
825686bf1b38f8aa713267097d7c1944140f71b5d3Eric
83403f35442375f2ee858981b79421ca321645df0Dominic Hamon}  // end namespace
84403f35442375f2ee858981b79421ca321645df0Dominic Hamon
85332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamonstd::string FormatString(const char* msg, va_list args) {
865686bf1b38f8aa713267097d7c1944140f71b5d3Eric  // we might need a second shot at this, so pre-emptivly make a copy
875686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_list args_cp;
885686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_copy(args_cp, args);
895686bf1b38f8aa713267097d7c1944140f71b5d3Eric
905686bf1b38f8aa713267097d7c1944140f71b5d3Eric  std::size_t size = 256;
915686bf1b38f8aa713267097d7c1944140f71b5d3Eric  char local_buff[256];
9209b93ccc6a9aed84c269b6f5b8130c878e518ebbDmitry Trifonov  auto ret = vsnprintf(local_buff, size, msg, args_cp);
935686bf1b38f8aa713267097d7c1944140f71b5d3Eric
945686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_end(args_cp);
955686bf1b38f8aa713267097d7c1944140f71b5d3Eric
965686bf1b38f8aa713267097d7c1944140f71b5d3Eric  // currently there is no error handling for failure, so this is hack.
975686bf1b38f8aa713267097d7c1944140f71b5d3Eric  CHECK(ret >= 0);
985686bf1b38f8aa713267097d7c1944140f71b5d3Eric
99332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon  if (ret == 0)  // handle empty expansion
1005686bf1b38f8aa713267097d7c1944140f71b5d3Eric    return {};
1015686bf1b38f8aa713267097d7c1944140f71b5d3Eric  else if (static_cast<size_t>(ret) < size)
1025686bf1b38f8aa713267097d7c1944140f71b5d3Eric    return local_buff;
1035686bf1b38f8aa713267097d7c1944140f71b5d3Eric  else {
1045686bf1b38f8aa713267097d7c1944140f71b5d3Eric    // we did not provide a long enough buffer on our first attempt.
105332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon    size = (size_t)ret + 1;  // + 1 for the null byte
1065686bf1b38f8aa713267097d7c1944140f71b5d3Eric    std::unique_ptr<char[]> buff(new char[size]);
10709b93ccc6a9aed84c269b6f5b8130c878e518ebbDmitry Trifonov    ret = vsnprintf(buff.get(), size, msg, args);
1085686bf1b38f8aa713267097d7c1944140f71b5d3Eric    CHECK(ret > 0 && ((size_t)ret) < size);
1095686bf1b38f8aa713267097d7c1944140f71b5d3Eric    return buff.get();
1105686bf1b38f8aa713267097d7c1944140f71b5d3Eric  }
1115686bf1b38f8aa713267097d7c1944140f71b5d3Eric}
1125686bf1b38f8aa713267097d7c1944140f71b5d3Eric
113332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamonstd::string FormatString(const char* msg, ...) {
1145686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_list args;
1155686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_start(args, msg);
1165686bf1b38f8aa713267097d7c1944140f71b5d3Eric  auto tmp = FormatString(msg, args);
1175686bf1b38f8aa713267097d7c1944140f71b5d3Eric  va_end(args);
1185686bf1b38f8aa713267097d7c1944140f71b5d3Eric  return tmp;
1195686bf1b38f8aa713267097d7c1944140f71b5d3Eric}
1205686bf1b38f8aa713267097d7c1944140f71b5d3Eric
1215686bf1b38f8aa713267097d7c1944140f71b5d3Ericvoid ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) {
122403f35442375f2ee858981b79421ca321645df0Dominic Hamon  va_list args;
123403f35442375f2ee858981b79421ca321645df0Dominic Hamon  va_start(args, fmt);
12444128d87d2846fc20d540ef41eda6da9ffbb6abaEric Fiselier  ColorPrintf(out, color, fmt, args);
12544128d87d2846fc20d540ef41eda6da9ffbb6abaEric Fiselier  va_end(args);
12644128d87d2846fc20d540ef41eda6da9ffbb6abaEric Fiselier}
127403f35442375f2ee858981b79421ca321645df0Dominic Hamon
128332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamonvoid ColorPrintf(std::ostream& out, LogColor color, const char* fmt,
129332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon                 va_list args) {
130ba141ac0d9f2042c73f57fcf870f2a69030fa8daAnton Danielsson#ifdef BENCHMARK_OS_WINDOWS
131332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon  ((void)out);  // suppress unused warning
13244128d87d2846fc20d540ef41eda6da9ffbb6abaEric Fiselier
133403f35442375f2ee858981b79421ca321645df0Dominic Hamon  const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
13480093519382ae748d682e3914ef4ccf07da6ce35Dominic Hamon
135403f35442375f2ee858981b79421ca321645df0Dominic Hamon  // Gets the current text color.
136403f35442375f2ee858981b79421ca321645df0Dominic Hamon  CONSOLE_SCREEN_BUFFER_INFO buffer_info;
137403f35442375f2ee858981b79421ca321645df0Dominic Hamon  GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
138403f35442375f2ee858981b79421ca321645df0Dominic Hamon  const WORD old_color_attrs = buffer_info.wAttributes;
139403f35442375f2ee858981b79421ca321645df0Dominic Hamon
140403f35442375f2ee858981b79421ca321645df0Dominic Hamon  // We need to flush the stream buffers into the console before each
141403f35442375f2ee858981b79421ca321645df0Dominic Hamon  // SetConsoleTextAttribute call lest it affect the text that is already
142403f35442375f2ee858981b79421ca321645df0Dominic Hamon  // printed but has not yet reached the console.
143403f35442375f2ee858981b79421ca321645df0Dominic Hamon  fflush(stdout);
144403f35442375f2ee858981b79421ca321645df0Dominic Hamon  SetConsoleTextAttribute(stdout_handle,
145403f35442375f2ee858981b79421ca321645df0Dominic Hamon                          GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
146403f35442375f2ee858981b79421ca321645df0Dominic Hamon  vprintf(fmt, args);
147403f35442375f2ee858981b79421ca321645df0Dominic Hamon
148403f35442375f2ee858981b79421ca321645df0Dominic Hamon  fflush(stdout);
149403f35442375f2ee858981b79421ca321645df0Dominic Hamon  // Restores the text color.
150403f35442375f2ee858981b79421ca321645df0Dominic Hamon  SetConsoleTextAttribute(stdout_handle, old_color_attrs);
151403f35442375f2ee858981b79421ca321645df0Dominic Hamon#else
152403f35442375f2ee858981b79421ca321645df0Dominic Hamon  const char* color_code = GetPlatformColorCode(color);
1535686bf1b38f8aa713267097d7c1944140f71b5d3Eric  if (color_code) out << FormatString("\033[0;3%sm", color_code);
1545686bf1b38f8aa713267097d7c1944140f71b5d3Eric  out << FormatString(fmt, args) << "\033[m";
155403f35442375f2ee858981b79421ca321645df0Dominic Hamon#endif
156403f35442375f2ee858981b79421ca321645df0Dominic Hamon}
1575686bf1b38f8aa713267097d7c1944140f71b5d3Eric
158917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinsonbool IsColorTerminal() {
159917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#if BENCHMARK_OS_WINDOWS
160917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  // On Windows the TERM variable is usually not set, but the
161917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  // console there does support colors.
162917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  return 0 != _isatty(_fileno(stdout));
163917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson#else
164917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  // On non-Windows platforms, we rely on the TERM variable. This list of
165917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  // supported TERM values is copied from Google Test:
166917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  // <https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>.
167917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  const char* const SUPPORTED_TERM_VALUES[] = {
168917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      "xterm",         "xterm-color",     "xterm-256color",
169917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      "screen",        "screen-256color", "tmux",
170917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      "tmux-256color", "rxvt-unicode",    "rxvt-unicode-256color",
171917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      "linux",         "cygwin",
172917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  };
173917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson
174917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  const char* const term = getenv("TERM");
175917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson
176917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  bool term_supports_color = false;
177917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  for (const char* candidate : SUPPORTED_TERM_VALUES) {
178917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson    if (term && 0 == strcmp(term, candidate)) {
179917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      term_supports_color = true;
180917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson      break;
181917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson    }
182917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  }
183917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson
184917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson  return 0 != isatty(fileno(stdout)) && term_supports_color;
185332f677b8bec401641a2743ab5d741c13cc6811dDominic Hamon#endif  // BENCHMARK_OS_WINDOWS
186917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson}
187917b86e615f659d9fb9819d1fa765cd459fc6861Nicholas Hutchinson
188e390e4ebc3c728e69d335a33c0cb00547dcd0ddeDominic Hamon}  // end namespace benchmark
189