1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "tools/gn/standard_out.h" 6 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "base/logging.h" 11 #include "base/strings/string_split.h" 12 #include "build/build_config.h" 13 14 #if defined(OS_WIN) 15 #include <windows.h> 16 #else 17 #include <stdio.h> 18 #include <unistd.h> 19 #endif 20 21 namespace { 22 23 bool initialized = false; 24 25 static const char kSwitchColor[] = "color"; 26 static const char kSwitchNoColor[] = "nocolor"; 27 28 #if defined(OS_WIN) 29 HANDLE hstdout; 30 WORD default_attributes; 31 #endif 32 bool is_console = false; 33 34 void EnsureInitialized() { 35 if (initialized) 36 return; 37 initialized = true; 38 39 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); 40 if (cmdline->HasSwitch(kSwitchNoColor)) { 41 // Force color off. 42 is_console = false; 43 return; 44 } 45 46 #if defined(OS_WIN) 47 // On Windows, we can't force the color on. If the output handle isn't a 48 // console, there's nothing we can do about it. 49 hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE); 50 CONSOLE_SCREEN_BUFFER_INFO info; 51 is_console = !!::GetConsoleScreenBufferInfo(hstdout, &info); 52 default_attributes = info.wAttributes; 53 #else 54 if (cmdline->HasSwitch(kSwitchColor)) 55 is_console = true; 56 else 57 is_console = isatty(fileno(stdout)); 58 #endif 59 } 60 61 void WriteToStdOut(const std::string& output) { 62 size_t written_bytes = fwrite(output.data(), 1, output.size(), stdout); 63 DCHECK_EQ(output.size(), written_bytes); 64 } 65 66 } // namespace 67 68 #if defined(OS_WIN) 69 70 void OutputString(const std::string& output, TextDecoration dec) { 71 EnsureInitialized(); 72 if (is_console) { 73 switch (dec) { 74 case DECORATION_NONE: 75 break; 76 case DECORATION_DIM: 77 ::SetConsoleTextAttribute(hstdout, FOREGROUND_INTENSITY); 78 break; 79 case DECORATION_RED: 80 ::SetConsoleTextAttribute(hstdout, 81 FOREGROUND_RED | FOREGROUND_INTENSITY); 82 break; 83 case DECORATION_GREEN: 84 // Keep green non-bold. 85 ::SetConsoleTextAttribute(hstdout, FOREGROUND_GREEN); 86 break; 87 case DECORATION_BLUE: 88 ::SetConsoleTextAttribute(hstdout, 89 FOREGROUND_BLUE | FOREGROUND_INTENSITY); 90 break; 91 case DECORATION_YELLOW: 92 ::SetConsoleTextAttribute(hstdout, 93 FOREGROUND_RED | FOREGROUND_GREEN); 94 break; 95 } 96 } 97 98 DWORD written = 0; 99 ::WriteFile(hstdout, output.c_str(), static_cast<DWORD>(output.size()), 100 &written, NULL); 101 102 if (is_console) 103 ::SetConsoleTextAttribute(hstdout, default_attributes); 104 } 105 106 #else 107 108 void OutputString(const std::string& output, TextDecoration dec) { 109 EnsureInitialized(); 110 if (is_console) { 111 switch (dec) { 112 case DECORATION_NONE: 113 break; 114 case DECORATION_DIM: 115 WriteToStdOut("\e[2m"); 116 break; 117 case DECORATION_RED: 118 WriteToStdOut("\e[31m\e[1m"); 119 break; 120 case DECORATION_GREEN: 121 WriteToStdOut("\e[32m"); 122 break; 123 case DECORATION_BLUE: 124 WriteToStdOut("\e[34m\e[1m"); 125 break; 126 case DECORATION_YELLOW: 127 WriteToStdOut("\e[33m\e[1m"); 128 break; 129 } 130 } 131 132 WriteToStdOut(output.data()); 133 134 if (is_console && dec != DECORATION_NONE) 135 WriteToStdOut("\e[0m"); 136 } 137 138 #endif 139 140 void PrintShortHelp(const std::string& line) { 141 size_t colon_offset = line.find(':'); 142 size_t first_normal = 0; 143 if (colon_offset != std::string::npos) { 144 OutputString(" " + line.substr(0, colon_offset), DECORATION_YELLOW); 145 first_normal = colon_offset; 146 } 147 148 // See if the colon is followed by a " [" and if so, dim the contents of [ ]. 149 if (first_normal > 0 && 150 line.size() > first_normal + 2 && 151 line[first_normal + 1] == ' ' && line[first_normal + 2] == '[') { 152 size_t begin_bracket = first_normal + 2; 153 OutputString(": "); 154 first_normal = line.find(']', begin_bracket); 155 if (first_normal == std::string::npos) 156 first_normal = line.size(); 157 else 158 first_normal++; 159 OutputString(line.substr(begin_bracket, first_normal - begin_bracket), 160 DECORATION_DIM); 161 } 162 163 OutputString(line.substr(first_normal) + "\n"); 164 } 165 166 void PrintLongHelp(const std::string& text) { 167 std::vector<std::string> lines; 168 base::SplitStringDontTrim(text, '\n', &lines); 169 170 for (size_t i = 0; i < lines.size(); i++) { 171 const std::string& line = lines[i]; 172 173 // Check for a heading line. 174 if (!line.empty() && line[0] != ' ') { 175 // Highlight up to the colon (if any). 176 size_t chars_to_highlight = line.find(':'); 177 if (chars_to_highlight == std::string::npos) 178 chars_to_highlight = line.size(); 179 OutputString(line.substr(0, chars_to_highlight), DECORATION_YELLOW); 180 OutputString(line.substr(chars_to_highlight) + "\n"); 181 continue; 182 } 183 184 // Check for a comment. 185 TextDecoration dec = DECORATION_NONE; 186 for (size_t char_i = 0; char_i < line.size(); char_i++) { 187 if (line[char_i] == '#') { 188 // Got a comment, draw dimmed. 189 dec = DECORATION_DIM; 190 break; 191 } else if (line[char_i] != ' ') { 192 break; 193 } 194 } 195 196 OutputString(line + "\n", dec); 197 } 198 } 199 200