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
EnsureInitialized()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
WriteToStdOut(const std::string & output)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
OutputString(const std::string & output,TextDecoration dec)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
OutputString(const std::string & output,TextDecoration dec)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
PrintShortHelp(const std::string & line)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
PrintLongHelp(const std::string & text)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