Auto-detect whether Benchmark should produce colorized output (#126)

* Auto-detect whether to produce colorized output

Rename --color_print to --benchmark_color for consistency with the other
flags (and Google Test). Old flag name is kept around for compatibility.

The --benchmark_color/--color_print flag takes a third option, "auto",
which is the new default. In this mode, we attempt to auto-detect
whether to produce colorized output. (The logic for deciding whether to
use colorized output was lifted from GTest:
<https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>.)

* Update CONTRIBUTORS, AUTHORS
diff --git a/AUTHORS b/AUTHORS
index 0e40922..5a545fa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,6 +24,7 @@
 Kaito Udagawa <umireon@gmail.com>
 Lei Xu <eddyxu@gmail.com>
 Matt Clarkson <mattyclarkson@gmail.com>
+Nick Hutchinson <nshutchinson@gmail.com>
 Oleksandr Sochka <sasha.sochka@gmail.com>
 Paul Redmond <paul.redmond@gmail.com>
 Radoslav Yovchev <radoslav.tm@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 4bff126..33cd941 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -40,6 +40,7 @@
 Kai Wolf <kai.wolf@gmail.com>
 Lei Xu <eddyxu@gmail.com>
 Matt Clarkson <mattyclarkson@gmail.com>
+Nick Hutchinson <nshutchinson@gmail.com>
 Oleksandr Sochka <sasha.sochka@gmail.com>
 Pascal Leroy <phl@google.com>
 Paul Redmond <paul.redmond@gmail.com>
diff --git a/src/benchmark.cc b/src/benchmark.cc
index a5073f5..da195f9 100644
--- a/src/benchmark.cc
+++ b/src/benchmark.cc
@@ -34,6 +34,7 @@
 #include <thread>
 
 #include "check.h"
+#include "colorprint.h"
 #include "commandlineflags.h"
 #include "complexity.h"
 #include "log.h"
@@ -82,7 +83,12 @@
 
 DEFINE_string(benchmark_out, "", "The file to write additonal output to");
 
-DEFINE_bool(color_print, true, "Enables colorized logging.");
+DEFINE_string(benchmark_color, "auto",
+              "Whether to use colors in the output.  Valid values: "
+              "'true'/'yes'/1, 'false'/'no'/0, and 'auto'. 'auto' means to use "
+              "colors if the output is being sent to a terminal and the TERM "
+              "environment variable is set to a terminal type that supports "
+              "colors.");
 
 DEFINE_int32(v, 0, "The level of verbose logging to output");
 
@@ -546,8 +552,14 @@
   std::unique_ptr<BenchmarkReporter> default_console_reporter;
   std::unique_ptr<BenchmarkReporter> default_file_reporter;
   if (!console_reporter) {
-    auto output_opts = FLAGS_color_print ? ConsoleReporter::OO_Color
-                                          : ConsoleReporter::OO_None;
+    auto output_opts = ConsoleReporter::OO_None;
+    if (FLAGS_benchmark_color == "auto")
+      output_opts = IsColorTerminal() ? ConsoleReporter::OO_Color
+                                      : ConsoleReporter::OO_None;
+    else
+      output_opts = IsTruthyFlagValue(FLAGS_benchmark_color)
+                        ? ConsoleReporter::OO_Color
+                        : ConsoleReporter::OO_None;
     default_console_reporter = internal::CreateReporter(
           FLAGS_benchmark_format, output_opts);
     console_reporter = default_console_reporter.get();
@@ -602,7 +614,7 @@
           "          [--benchmark_format=<console|json|csv>]\n"
           "          [--benchmark_out=<filename>]\n"
           "          [--benchmark_out_format=<json|console|csv>]\n"
-          "          [--color_print={true|false}]\n"
+          "          [--benchmark_color={auto|true|false}]\n"
           "          [--v=<verbosity>]\n");
   exit(0);
 }
@@ -627,8 +639,12 @@
                         &FLAGS_benchmark_out) ||
         ParseStringFlag(argv[i], "benchmark_out_format",
                         &FLAGS_benchmark_out_format) ||
-        ParseBoolFlag(argv[i], "color_print",
-                       &FLAGS_color_print) ||
+        ParseStringFlag(argv[i], "benchmark_color",
+                        &FLAGS_benchmark_color) ||
+        // "color_print" is the deprecated name for "benchmark_color".
+        // TODO: Remove this.
+        ParseStringFlag(argv[i], "color_print",
+                        &FLAGS_benchmark_color) ||
         ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
       for (int j = i; j != *argc; ++j) argv[j] = argv[j + 1];
 
@@ -643,6 +659,9 @@
   if (*flag != "console" && *flag != "json" && *flag != "csv") {
     PrintUsageAndExit();
   }
+  if (FLAGS_benchmark_color.empty()) {
+    PrintUsageAndExit();
+  }
 }
 
 int InitializeStreams() {
diff --git a/src/colorprint.cc b/src/colorprint.cc
index b7d316f..f24e6f8 100644
--- a/src/colorprint.cc
+++ b/src/colorprint.cc
@@ -16,16 +16,20 @@
 
 #include <cstdarg>
 #include <cstdio>
-#include <cstdarg>
-#include <string>
+#include <cstdlib>
+#include <cstring>
 #include <memory>
+#include <string>
 
 #include "check.h"
 #include "internal_macros.h"
 
 #ifdef BENCHMARK_OS_WINDOWS
+#include <io.h>
 #include <Windows.h>
-#endif
+#else
+#include <unistd.h>
+#endif // BENCHMARK_OS_WINDOWS
 
 namespace benchmark {
 namespace {
@@ -151,4 +155,34 @@
 
 }
 
+bool IsColorTerminal() {
+#if BENCHMARK_OS_WINDOWS
+  // On Windows the TERM variable is usually not set, but the
+  // console there does support colors.
+  return 0 != _isatty(_fileno(stdout));
+#else
+  // On non-Windows platforms, we rely on the TERM variable. This list of
+  // supported TERM values is copied from Google Test:
+  // <https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>.
+  const char* const SUPPORTED_TERM_VALUES[] = {
+      "xterm",         "xterm-color",     "xterm-256color",
+      "screen",        "screen-256color", "tmux",
+      "tmux-256color", "rxvt-unicode",    "rxvt-unicode-256color",
+      "linux",         "cygwin",
+  };
+
+  const char* const term = getenv("TERM");
+
+  bool term_supports_color = false;
+  for (const char* candidate : SUPPORTED_TERM_VALUES) {
+    if (term && 0 == strcmp(term, candidate)) {
+      term_supports_color = true;
+      break;
+    }
+  }
+
+  return 0 != isatty(fileno(stdout)) && term_supports_color;
+#endif // BENCHMARK_OS_WINDOWS
+}
+
 }  // end namespace benchmark
diff --git a/src/colorprint.h b/src/colorprint.h
index d924795..30d4300 100644
--- a/src/colorprint.h
+++ b/src/colorprint.h
@@ -23,6 +23,10 @@
 void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, va_list args);
 void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...);
 
+// Returns true if stdout appears to be a terminal that supports colored
+// output, false otherwise.
+bool IsColorTerminal();
+
 }  // end namespace benchmark
 
 #endif  // BENCHMARK_COLORPRINT_H_
diff --git a/src/commandlineflags.cc b/src/commandlineflags.cc
index 3e9a37a..c6fba55 100644
--- a/src/commandlineflags.cc
+++ b/src/commandlineflags.cc
@@ -14,6 +14,7 @@
 
 #include "commandlineflags.h"
 
+#include <cctype>
 #include <cstdlib>
 #include <cstring>
 #include <iostream>
@@ -74,17 +75,6 @@
   return true;
 }
 
-inline const char* GetEnv(const char* name) {
-#if defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
-  // Environment variables which we programmatically clear will be set to the
-  // empty string rather than unset (nullptr).  Handle that case.
-  const char* const env = getenv(name);
-  return (env != nullptr && env[0] != '\0') ? env : nullptr;
-#else
-  return getenv(name);
-#endif
-}
-
 // Returns the name of the environment variable corresponding to the
 // given flag.  For example, FlagToEnvVar("foo") will return
 // "BENCHMARK_FOO" in the open-source version.
@@ -104,7 +94,7 @@
 // The value is considered true iff it's not "0".
 bool BoolFromEnv(const char* flag, bool default_value) {
   const std::string env_var = FlagToEnvVar(flag);
-  const char* const string_value = GetEnv(env_var.c_str());
+  const char* const string_value = getenv(env_var.c_str());
   return string_value == nullptr ? default_value : strcmp(string_value, "0") != 0;
 }
 
@@ -113,7 +103,7 @@
 // doesn't represent a valid 32-bit integer, returns default_value.
 int32_t Int32FromEnv(const char* flag, int32_t default_value) {
   const std::string env_var = FlagToEnvVar(flag);
-  const char* const string_value = GetEnv(env_var.c_str());
+  const char* const string_value = getenv(env_var.c_str());
   if (string_value == nullptr) {
     // The environment variable is not set.
     return default_value;
@@ -133,7 +123,7 @@
 // the given flag; if it's not set, returns default_value.
 const char* StringFromEnv(const char* flag, const char* default_value) {
   const std::string env_var = FlagToEnvVar(flag);
-  const char* const value = GetEnv(env_var.c_str());
+  const char* const value = getenv(env_var.c_str());
   return value == nullptr ? default_value : value;
 }
 
@@ -175,7 +165,7 @@
   if (value_str == nullptr) return false;
 
   // Converts the string value to a bool.
-  *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+  *value = IsTruthyFlagValue(value_str);
   return true;
 }
 
@@ -217,4 +207,12 @@
 bool IsFlag(const char* str, const char* flag) {
   return (ParseFlagValue(str, flag, true) != nullptr);
 }
+
+bool IsTruthyFlagValue(const std::string& str) {
+  if (str.empty())
+    return true;
+  char ch = str[0];
+  return isalnum(ch) &&
+         !(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N');
+}
 }  // end namespace benchmark
diff --git a/src/commandlineflags.h b/src/commandlineflags.h
index 34b9c6f..945c9a9 100644
--- a/src/commandlineflags.h
+++ b/src/commandlineflags.h
@@ -38,8 +38,7 @@
 // Parses a string for a bool flag, in the form of either
 // "--flag=value" or "--flag".
 //
-// In the former case, the value is taken as true as long as it does
-// not start with '0', 'f', or 'F'.
+// In the former case, the value is taken as true if it passes IsTruthyValue().
 //
 // In the latter case, the value is taken as true.
 //
@@ -71,6 +70,10 @@
 // Returns true if the string matches the flag.
 bool IsFlag(const char* str, const char* flag);
 
+// Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or
+// some non-alphanumeric character. As a special case, also returns true if
+// value is the empty string.
+bool IsTruthyFlagValue(const std::string& value);
 }  // end namespace benchmark
 
 #endif  // BENCHMARK_COMMANDLINEFLAGS_H_