libcopp  2.2.0
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 
26 namespace util {
27 namespace cli {
28 
29 namespace detail {
30 static char tolower(char c) {
31  if (c >= 'A' && c <= 'Z') {
32  return c - 'A' + 'a';
33  }
34 
35  return c;
36 }
37 
38 static 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 
75 shell_font::shell_font(int iFlag) : m_iFlag(iFlag) {}
76 
78 
79 std::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  ;
110  if (iStart < 8) base[1] += static_cast<char>(iStart);
111  ret += std::string((!bFirst) ? ";" : "") + base;
112  bFirst = false;
113  }
114 
115  // 背景色
116  iFlag >>= 8;
117  if (iFlag & 0xff) {
118  std::string base = "40";
119  int iStart = 0;
120  for (; iStart < 8 && !(iFlag & (1 << iStart)); ++iStart)
121  ;
122  if (iStart < 8) base[1] += static_cast<char>(iStart);
123  ret += std::string((!bFirst) ? ";" : "") + base;
124  // bFirst = false; no need to set because not used later
125  }
126 
127  ret += "m";
128 
129  return ret;
130 }
131 
133 
135 
137  std::unordered_set<std::string> color_term;
138  color_term.insert("eterm");
139  color_term.insert("ansi");
140  color_term.insert("color-xterm");
141  color_term.insert("con132x25");
142  color_term.insert("con132x30");
143  color_term.insert("con132x43");
144  color_term.insert("con132x60");
145  color_term.insert("con80x25");
146  color_term.insert("con80x28");
147  color_term.insert("con80x30");
148  color_term.insert("con80x43");
149  color_term.insert("con80x50");
150  color_term.insert("con80x60");
151  color_term.insert("cons25");
152  color_term.insert("console");
153  color_term.insert("cygwin");
154  color_term.insert("dtterm");
155  color_term.insert("eterm-color");
156  color_term.insert("gnome");
157  color_term.insert("jfbterm");
158  color_term.insert("konsole");
159  color_term.insert("kterm");
160  color_term.insert("linux");
161  color_term.insert("linux-c");
162  color_term.insert("mach-color");
163  color_term.insert("mlterm");
164  color_term.insert("putty");
165  std::unordered_set<std::string> color_term_prefix;
166  color_term_prefix.insert("xterm");
167  color_term_prefix.insert("screen");
168  color_term_prefix.insert("vt100");
169  color_term_prefix.insert("vt220");
170  color_term_prefix.insert("rxvt");
171 
172  do {
173  std::string term_name = detail::getenv("TERM");
174  if (term_name.empty()) {
175  break;
176  }
177 
178  std::transform(term_name.begin(), term_name.end(), term_name.begin(), detail::tolower);
179 
180  if (color_term.end() != color_term.find(term_name)) {
181  return 1;
182  }
183  for (auto &prefix : color_term_prefix) {
184  if (0 == UTIL_STRFUNC_STRNCMP(term_name.c_str(), prefix.c_str(), prefix.size())) {
185  return 1;
186  }
187  }
188 
189  // Special rule
190  if (term_name.size() >= 4 && 0 == UTIL_STRFUNC_STRNCMP(term_name.c_str() + term_name.size() - 4, "-256", 4)) {
191  return 1;
192  }
193 
194  if (term_name.size() >= 9 && 0 == UTIL_STRFUNC_STRNCMP(term_name.c_str() + term_name.size() - 9, "-256color", 9)) {
195  return 1;
196  }
197  } while (false);
198 
199  {
200  std::string tf_build = detail::getenv("TF_BUILD");
201  std::string agent_name = detail::getenv("AGENT_NAME");
202  if (!tf_build.empty() && !agent_name.empty()) {
203  return 1;
204  }
205  }
206 
207  if (!detail::getenv("COLORTERM").empty()) {
208  return 1;
209  }
210 
211  do {
212  std::string term_program = detail::getenv("TERM_PROGRAM");
213  if (term_program.empty()) {
214  break;
215  }
216  std::transform(term_program.begin(), term_program.end(), term_program.begin(), detail::tolower);
217  if (term_program.find("apple_terminal") != std::string::npos ||
218  term_program.find("iterm.app") != std::string::npos) {
219  return 1;
220  }
221  } while (false);
222 
223  // Detect CI
224  do {
225  std::string ci = detail::getenv("CI");
226  if (ci.empty()) {
227  break;
228  }
229  const char *known_ci_names[] = {"travis", "circleci", "appveyor", "gitlab_ci",
230  "github_actions", "buildkite", "drone"};
231  for (auto known_ci_name : known_ci_names) {
232  if (0 == UTIL_STRFUNC_STRCASE_CMP(ci.c_str(), known_ci_name)) {
233  return 1;
234  }
235  }
236 
237  std::string ci_name = detail::getenv("CI_NAME");
238  if (ci_name.empty()) {
239  break;
240  }
241  if (0 == UTIL_STRFUNC_STRCASE_CMP(ci_name.c_str(), "codeship")) {
242  return 1;
243  }
244  } while (false);
245 
246  return -1;
247 }
248 
249 std::string shell_font::GenerateString(const std::string &strInput, int iFlag) {
250  static int status_ = 0;
251 
252  if (0 == status_) {
253  status_ = _check_term_color_status();
254  }
255 
256  if (status_ < 0 || iFlag == 0) return strInput;
257  return GetStyleCode(iFlag) + strInput + GetStyleCloseCode();
258 }
259 
260 std::string shell_font::GenerateString(const std::string &strInput) { return GenerateString(strInput, m_iFlag); }
261 
262 #ifdef SHELL_FONT_USING_WIN32_CONSOLE
263 
264 static std::map<int, WORD> &_get_flag_mapping() {
265  static std::map<int, WORD> ret;
266  if (ret.empty()) {
268  ret[shell_font_style::SHELL_FONT_SPEC_BOLD] = COMMON_LVB_LEADING_BYTE;
269  ret[shell_font_style::SHELL_FONT_SPEC_UNDERLINE] = COMMON_LVB_UNDERSCORE;
270  ret[shell_font_style::SHELL_FONT_SPEC_FLASH] = 0; // 不支持
271  ret[shell_font_style::SHELL_FONT_SPEC_DARK] = 0; // 不支持
272 
274  ret[shell_font_style::SHELL_FONT_COLOR_RED] = FOREGROUND_RED | FOREGROUND_INTENSITY;
275  ret[shell_font_style::SHELL_FONT_COLOR_GREEN] = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
276  ret[shell_font_style::SHELL_FONT_COLOR_YELLOW] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
277  ret[shell_font_style::SHELL_FONT_COLOR_BLUE] = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
278  ret[shell_font_style::SHELL_FONT_COLOR_MAGENTA] = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
279  ret[shell_font_style::SHELL_FONT_COLOR_CYAN] = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
281  FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
282 
286  ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_YELLOW] = BACKGROUND_RED | BACKGROUND_GREEN;
288  ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_MAGENTA] = BACKGROUND_RED | BACKGROUND_BLUE;
289  ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_CYAN] = BACKGROUND_BLUE | BACKGROUND_GREEN;
290  ret[shell_font_style::SHELL_FONT_BACKGROUND_COLOR_WHITE] = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
291  }
292 
293  return ret;
294 }
295 
296 static WORD _get_default_color() { return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; }
297 
298 #endif
299 
301 #ifdef SHELL_FONT_USING_WIN32_CONSOLE
302  if (os == &std::cout) {
303  hOsHandle = GetStdHandle(STD_OUTPUT_HANDLE);
304  } else if (os == &std::cerr) {
305  hOsHandle = GetStdHandle(STD_ERROR_HANDLE);
306  } else {
307  hOsHandle = nullptr;
308  }
309 #endif
310 }
311 
313  if (nullptr == pOs) {
314  return;
315  }
316 
317  reset();
318 }
319 
321 
323  pOs = other.pOs;
324 
325 #ifdef SHELL_FONT_USING_WIN32_CONSOLE
326  hOsHandle = other.hOsHandle;
327 #endif
329 
330  return (*this);
331 }
332 
334  close();
335  (*pOs) << "nullptr";
336  return (*this);
337 }
338 
340  shell_font_style::shell_font_spec style) const {
341  open(style);
342  return (*this);
343 }
344 
347  open(style);
348  return (*this);
349 }
350 
353  open(style);
354  return (*this);
355 }
356 
358  close();
359  (*pOs) << fn;
360  return (*this);
361 }
362 
365  reset();
366  return (*this);
367  }
368 
369  flag |= f;
370  return (*this);
371 }
372 
374  if (nullptr == pOs) {
375  return;
376  }
377 
379  return;
380  }
381 
382 #ifdef SHELL_FONT_USING_WIN32_CONSOLE
383  if (nullptr != hOsHandle) {
384  std::map<int, WORD> &color_map = _get_flag_mapping();
385  WORD style = 0;
386  int left_flag = flag;
387 
388  while (left_flag) {
389  int f = left_flag & (left_flag ^ (left_flag - 1));
390  std::map<int, WORD>::iterator iter = color_map.find(f);
391  if (iter != color_map.end()) {
392  style |= iter->second;
393  }
394 
395  left_flag = left_flag & (left_flag - 1);
396  }
397 
398  SetConsoleTextAttribute(hOsHandle, style);
399  }
400 #else
401  (*pOs) << shell_font::GetStyleCode(flag);
402 #endif
403 
405 }
406 
408  if (nullptr == pOs) {
409  return;
410  }
411 
412  close();
413 
414 #ifdef SHELL_FONT_USING_WIN32_CONSOLE
415  if (nullptr != hOsHandle) {
416  SetConsoleTextAttribute(hOsHandle, _get_default_color());
417  }
418 #else
419 
420  (*pOs) << shell_font::GetStyleCloseCode();
421 
422 #endif
423 }
424 
426 
428 
429 } // namespace cli
430 } // namespace util
static std::string GetStyleCloseCode()
Definition: shell_font.cpp:134
shell_font(int iFlag=0)
Definition: shell_font.cpp:75
std::string GenerateString(const std::string &strInput)
Definition: shell_font.cpp:260
std::string GetStyleCode()
Definition: shell_font.cpp:132
shell_stream_opr & operator=(const shell_stream_opr &)
Definition: shell_font.cpp:322
shell_stream_opr(const shell_stream_opr &)
Definition: shell_font.cpp:320
const shell_stream_opr & operator<<(const Ty &v) const
Definition: shell_font.h:164
const shell_stream_opr & open(int flag) const
Definition: shell_font.cpp:363
shell_stream_opr operator()() const
Definition: shell_font.cpp:427
std::ostream stream_t
Definition: shell_font.h:141
shell_stream(stream_t &stream=std::cout)
Definition: shell_font.cpp:425
static char tolower(char c)
static std::string getenv(const char *name)
Definition: shell_font.cpp:38
static int _check_term_color_status()
Definition: shell_font.cpp:136
#define UTIL_STRFUNC_STRNCMP(l, r, s)
Definition: shell_font.cpp:22
#define SHELL_FONT_SET_OPT_END
Definition: shell_font.cpp:10
#define UTIL_STRFUNC_STRCASE_CMP(l, r)
Definition: shell_font.cpp:19