libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
shell_font.cpp
Go to the documentation of this file.
1// Copyright 2023 owent
2
3#include "cli/shell_font.h"
4
5#include <algorithm>
6#include <cstdlib>
7#include <cstring>
8#include <unordered_set>
9
10#define SHELL_FONT_SET_OPT_END "\033[0m"
11
12#ifndef UTIL_STRFUNC_STRCASE_CMP
13# if defined(_MSC_VER) && _MSC_VER >= 1600
14# define UTIL_STRFUNC_STRCASE_CMP(l, r) _stricmp(l, r)
15# define UTIL_STRFUNC_STRNCASE_CMP(l, r, s) _strnicmp(l, r, s)
16# define UTIL_STRFUNC_STRCMP(l, r) strcmp(l, r)
17# define UTIL_STRFUNC_STRNCMP(l, r, s) strncmp(l, r, s)
18# else
19# define UTIL_STRFUNC_STRCASE_CMP(l, r) strcasecmp(l, r)
20# define UTIL_STRFUNC_STRNCASE_CMP(l, r, s) strncasecmp(l, r, s)
21# define UTIL_STRFUNC_STRCMP(l, r) strcmp(l, r)
22# define UTIL_STRFUNC_STRNCMP(l, r, s) strncmp(l, r, s)
23# endif
24#endif
25
26namespace util {
27namespace cli {
28
29namespace detail {
30static char tolower(char c) {
31 if (c >= 'A' && c <= 'Z') {
32 return static_cast<char>(c - 'A' + 'a');
33 }
34
35 return c;
36}
37
38static std::string getenv(const char *name) {
39 std::string ret;
40#if (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
41 size_t len = 0;
42 if (0 != getenv_s(&len, nullptr, 0, name)) {
43 return ret;
44 }
45 if (len == 0) {
46 return ret;
47 }
48
49 ret.resize(len, 0);
50 if (0 != getenv_s(&len, &ret[0], ret.size(), name)) {
51 ret.clear();
52 return ret;
53 }
54
55 while (!ret.empty() && ret[ret.size() - 1] == '\0') {
56 ret.pop_back();
57 }
58
59 return ret;
60#else
61 char *val = ::getenv(name);
62 if (nullptr != val) {
63 ret = val;
64 }
65
66 while (!ret.empty() && ret[ret.size() - 1] == '\0') {
67 ret.pop_back();
68 }
69 return ret;
70#endif
71}
72
73} // namespace detail
74
75shell_font::shell_font(int iFlag) : m_iFlag(iFlag) {}
76
78
79std::string shell_font::GetStyleCode(int iFlag) {
80 std::string ret;
81 ret.reserve(32);
82 ret = "\033[";
83 bool bFirst = true;
84
85 // 第一部分,特殊样式
87 ret += "1";
88 bFirst = false;
89 }
91 ret += std::string((!bFirst) ? ";" : "") + "4";
92 bFirst = false;
93 }
95 ret += std::string((!bFirst) ? ";" : "") + "5";
96 bFirst = false;
97 }
99 ret += std::string((!bFirst) ? ";" : "") + "2";
100 bFirst = false;
101 }
102
103 // 前景色
104 iFlag >>= 8;
105 if (iFlag & 0xff) {
106 std::string base = "30";
107 int iStart = 0;
108 for (; iStart < 8 && !(iFlag & (1 << iStart)); ++iStart);
109 if (iStart < 8) base[1] = static_cast<char>(base[1] + iStart);
110 ret += std::string((!bFirst) ? ";" : "") + base;
111 bFirst = false;
112 }
113
114 // 背景色
115 iFlag >>= 8;
116 if (iFlag & 0xff) {
117 std::string base = "40";
118 int iStart = 0;
119 for (; iStart < 8 && !(iFlag & (1 << iStart)); ++iStart);
120 if (iStart < 8) base[1] = static_cast<char>(base[1] + iStart);
121 ret += std::string((!bFirst) ? ";" : "") + base;
122 // bFirst = false; no need to set because not used later
123 }
124
125 ret += "m";
126
127 return ret;
128}
129
131
133
135 std::unordered_set<std::string> color_term;
136 color_term.insert("eterm");
137 color_term.insert("ansi");
138 color_term.insert("color-xterm");
139 color_term.insert("con132x25");
140 color_term.insert("con132x30");
141 color_term.insert("con132x43");
142 color_term.insert("con132x60");
143 color_term.insert("con80x25");
144 color_term.insert("con80x28");
145 color_term.insert("con80x30");
146 color_term.insert("con80x43");
147 color_term.insert("con80x50");
148 color_term.insert("con80x60");
149 color_term.insert("cons25");
150 color_term.insert("console");
151 color_term.insert("cygwin");
152 color_term.insert("dtterm");
153 color_term.insert("eterm-color");
154 color_term.insert("gnome");
155 color_term.insert("jfbterm");
156 color_term.insert("konsole");
157 color_term.insert("kterm");
158 color_term.insert("linux");
159 color_term.insert("linux-c");
160 color_term.insert("mach-color");
161 color_term.insert("mlterm");
162 color_term.insert("putty");
163 std::unordered_set<std::string> color_term_prefix;
164 color_term_prefix.insert("xterm");
165 color_term_prefix.insert("screen");
166 color_term_prefix.insert("vt100");
167 color_term_prefix.insert("vt220");
168 color_term_prefix.insert("rxvt");
169
170 do {
171 std::string term_name = detail::getenv("TERM");
172 if (term_name.empty()) {
173 break;
174 }
175
176 std::transform(term_name.begin(), term_name.end(), term_name.begin(), detail::tolower);
177
178 if (color_term.end() != color_term.find(term_name)) {
179 return 1;
180 }
181 for (auto &prefix : color_term_prefix) {
182 if (0 == UTIL_STRFUNC_STRNCMP(term_name.c_str(), prefix.c_str(), prefix.size())) {
183 return 1;
184 }
185 }
186
187 // Special rule
188 if (term_name.size() >= 4 && 0 == UTIL_STRFUNC_STRNCMP(term_name.c_str() + term_name.size() - 4, "-256", 4)) {
189 return 1;
190 }
191
192 if (term_name.size() >= 9 && 0 == UTIL_STRFUNC_STRNCMP(term_name.c_str() + term_name.size() - 9, "-256color", 9)) {
193 return 1;
194 }
195 } while (false);
196
197 {
198 std::string tf_build = detail::getenv("TF_BUILD");
199 std::string agent_name = detail::getenv("AGENT_NAME");
200 if (!tf_build.empty() && !agent_name.empty()) {
201 return 1;
202 }
203 }
204
205 if (!detail::getenv("COLORTERM").empty()) {
206 return 1;
207 }
208
209 do {
210 std::string term_program = detail::getenv("TERM_PROGRAM");
211 if (term_program.empty()) {
212 break;
213 }
214 std::transform(term_program.begin(), term_program.end(), term_program.begin(), detail::tolower);
215 if (term_program.find("apple_terminal") != std::string::npos ||
216 term_program.find("iterm.app") != std::string::npos) {
217 return 1;
218 }
219 } while (false);
220
221 // Detect CI
222 do {
223 std::string ci = detail::getenv("CI");
224 if (ci.empty()) {
225 break;
226 }
227 const char *known_ci_names[] = {"travis", "circleci", "appveyor", "gitlab_ci",
228 "github_actions", "buildkite", "drone"};
229 for (auto known_ci_name : known_ci_names) {
230 if (0 == UTIL_STRFUNC_STRCASE_CMP(ci.c_str(), known_ci_name)) {
231 return 1;
232 }
233 }
234
235 std::string ci_name = detail::getenv("CI_NAME");
236 if (ci_name.empty()) {
237 break;
238 }
239 if (0 == UTIL_STRFUNC_STRCASE_CMP(ci_name.c_str(), "codeship")) {
240 return 1;
241 }
242 } while (false);
243
244 return -1;
245}
246
247std::string shell_font::GenerateString(const std::string &strInput, int iFlag) {
248 static int status_ = 0;
249
250 if (0 == status_) {
251 status_ = _check_term_color_status();
252 }
253
254 if (status_ < 0 || iFlag == 0) return strInput;
255 return GetStyleCode(iFlag) + strInput + GetStyleCloseCode();
256}
257
258std::string shell_font::GenerateString(const std::string &strInput) { return GenerateString(strInput, m_iFlag); }
259
260#ifdef SHELL_FONT_USING_WIN32_CONSOLE
261
262static std::map<int, WORD> &_get_flag_mapping() {
263 static std::map<int, WORD> ret;
264 if (ret.empty()) {
266 ret[shell_font_style::SHELL_FONT_SPEC_BOLD] = COMMON_LVB_LEADING_BYTE;
267 ret[shell_font_style::SHELL_FONT_SPEC_UNDERLINE] = COMMON_LVB_UNDERSCORE;
268 ret[shell_font_style::SHELL_FONT_SPEC_FLASH] = 0; // 不支持
269 ret[shell_font_style::SHELL_FONT_SPEC_DARK] = 0; // 不支持
270
272 ret[shell_font_style::SHELL_FONT_COLOR_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY;
273 ret[shell_font_style::SHELL_FONT_COLOR_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
274 ret[shell_font_style::SHELL_FONT_COLOR_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
275 ret[shell_font_style::SHELL_FONT_COLOR_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
276 ret[shell_font_style::SHELL_FONT_COLOR_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
277 ret[shell_font_style::SHELL_FONT_COLOR_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
279 FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
280
284 ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN;
286 ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_MAGENTA] = BACKGROUND_RED | BACKGROUND_BLUE;
287 ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_CYAN] = BACKGROUND_BLUE | BACKGROUND_GREEN;
288 ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
289 }
290
291 return ret;
292}
293
294static WORD _get_default_color() { return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; }
295
296#endif
297
299#ifdef SHELL_FONT_USING_WIN32_CONSOLE
300 if (os == &std::cout) {
301 hOsHandle = GetStdHandle(STD_OUTPUT_HANDLE);
302 } else if (os == &std::cerr) {
303 hOsHandle = GetStdHandle(STD_ERROR_HANDLE);
304 } else {
305 hOsHandle = nullptr;
306 }
307#endif
308}
309
311 if (nullptr == pOs) {
312 return;
313 }
314
315 reset();
316}
317
319
321 pOs = other.pOs;
322
323#ifdef SHELL_FONT_USING_WIN32_CONSOLE
324 hOsHandle = other.hOsHandle;
325#endif
327
328 return (*this);
329}
330
332 close();
333 (*pOs) << "nullptr";
334 return (*this);
335}
336
342
348
354
356 close();
357 (*pOs) << fn;
358 return (*this);
359}
360
363 reset();
364 return (*this);
365 }
366
367 flag |= f;
368 return (*this);
369}
370
372 if (nullptr == pOs) {
373 return;
374 }
375
377 return;
378 }
379
380#ifdef SHELL_FONT_USING_WIN32_CONSOLE
381 if (nullptr != hOsHandle) {
382 std::map<int, WORD> &color_map = _get_flag_mapping();
383 WORD style = 0;
384 int left_flag = flag;
385
386 while (left_flag) {
387 int f = left_flag & (left_flag ^ (left_flag - 1));
388 std::map<int, WORD>::iterator iter = color_map.find(f);
389 if (iter != color_map.end()) {
390 style |= iter->second;
391 }
392
393 left_flag = left_flag & (left_flag - 1);
394 }
395
396 SetConsoleTextAttribute(hOsHandle, style);
397 }
398#else
399 (*pOs) << shell_font::GetStyleCode(flag);
400#endif
401
403}
404
406 if (nullptr == pOs) {
407 return;
408 }
409
410 close();
411
412#ifdef SHELL_FONT_USING_WIN32_CONSOLE
413 if (nullptr != hOsHandle) {
414 SetConsoleTextAttribute(hOsHandle, _get_default_color());
415 }
416#else
417
419
420#endif
421}
422
424
426
427} // namespace cli
428} // namespace util
static std::string GetStyleCloseCode()
shell_font(int iFlag=0)
std::string GenerateString(const std::string &strInput)
std::string GetStyleCode()
shell_stream_opr & operator=(const shell_stream_opr &)
shell_stream_opr(const shell_stream_opr &)
const shell_stream_opr & open(int flag) const
const shell_stream_opr & operator<<(const Ty &v) const
Definition shell_font.h:178
shell_stream_opr operator()() const
shell_stream(stream_t &stream=std::cout)
static char tolower(char c)
static std::string getenv(const char *name)
static int _check_term_color_status()
#define UTIL_STRFUNC_STRNCMP(l, r, s)
#define SHELL_FONT_SET_OPT_END
#define UTIL_STRFUNC_STRCASE_CMP(l, r)