UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
color_out.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2018-2024 CESNET
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef _WIN32
39#include <windows.h>
40#endif
41
42#include <cstdarg>
43#include <cstdio> // for vsnprintf, fileno, stdout
44#include <cstdlib> // for getenv
45#include <cstring> // for strcmp, strlen
46#include <iterator> // for back_insert_iterator, back_inserter
47#include <unistd.h> // for isatty
48
49#include "debug.h"
50#include "host.h"
51#include "utils/color_out.h"
52
53#define MOD_NAME "[color_out] "
54
55using std::string;
56
57static bool color_stdout;
58
59#ifdef _WIN32
61static bool setWinTermAnsiColors(DWORD stream) {
62 HANDLE h = GetStdHandle(stream);
63 if (h == INVALID_HANDLE_VALUE) {
64 return false;
65 }
66 DWORD dwMode = 0;
67 if (!GetConsoleMode(h, &dwMode)) {
68 return false;
69 }
70 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
71 if (!SetConsoleMode(h, dwMode)) {
72 return false;
73 }
74 return true;
75}
76
78static bool isMsysPty(int fd) {
79 // Dynamic load for binary compability with old Windows
80 const auto ptrGetFileInformationByHandleEx
81 = reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(reinterpret_cast<void *>(
82 GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
83 "GetFileInformationByHandleEx")));
84 if (!ptrGetFileInformationByHandleEx) {
85 return false;
86 }
87
88 HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
89 if (h == INVALID_HANDLE_VALUE) {
90 return false;
91 }
92
93 // Check that it's a pipe:
94 if (GetFileType(h) != FILE_TYPE_PIPE) {
95 return false;
96 }
97
98 // POD type is binary compatible with FILE_NAME_INFO from WinBase.h
99 // It have the same alignment and used to avoid UB in caller code
100 struct MY_FILE_NAME_INFO {
101 DWORD FileNameLength;
102 WCHAR FileName[MAX_PATH];
103 };
104
105 auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(
106 new (std::nothrow) MY_FILE_NAME_INFO());
107 if (!pNameInfo) {
108 return false;
109 }
110
111 // Check pipe name is template of
112 // {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
113 if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
114 sizeof(MY_FILE_NAME_INFO))) {
115 return false;
116 }
117 std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR));
118 if ((name.find(L"msys-") == std::wstring::npos
119 && name.find(L"cygwin-") == std::wstring::npos)
120 || name.find(L"-pty") == std::wstring::npos) {
121 return false;
122 }
123
124 return true;
125}
126#endif // defined _WIN32
127
128// conditional include here (and below) is to allow compilation outside UG
129// without the need to link with host.o, which seems to be tricky
130#ifdef HAVE_CONFIG_H
131ADD_TO_PARAM("log-color", "* log-color[=no]\n"
132 " Force enable/disable ANSI text formatting.\n");
133ADD_TO_PARAM("log-nocolor", "* log-nocolor\n"
134 " Force disable ANSI text formatting.\n");
135#endif
139static bool
141{
142#ifdef HAVE_CONFIG_H
143 const char *const param_val = get_commandline_param("log-color");
144 if (param_val != nullptr) {
145 return strcmp(param_val, "no") != 0;
146 }
147 if (get_commandline_param("log-nocolor") != nullptr) {
148 fprintf(stderr,
149 MOD_NAME "The param log-nocolor is deprecated, use "
150 "'log-color=no' instead\n");
151 return false;
152 }
153#endif
154 const char *env_val = getenv("ULTRAGRID_COLOR_OUT");
155 if (env_val != nullptr && strlen(env_val) > 0) {
156 return strcmp(env_val, "0") != 0;
157 }
158#ifdef _WIN32
159 return isMsysPty(fileno(stdout)) ||
160 (_isatty(fileno(stdout)) &&
161 setWinTermAnsiColors(STD_OUTPUT_HANDLE));
162#else
163 return isatty(fileno(stdout));
164#endif
165}
167bool
173
174template<class OutputIt>
175static int prune_ansi_sequences(const char *in, OutputIt out) {
176 char c = *in;
177 bool in_control = false;
178 int written = 0;
179 while (c != '\0') {
180 switch (c) {
181 case '\033':
182 in_control = true;
183 break;
184 case 'm':
185 if (in_control) {
186 in_control = false;
187 break;
188 }
189 // fall through
190 default:
191 if (!in_control) {
192 *out++ = c;
193 written++;
194 }
195 }
196 c = *++in;
197 }
198 *out = '\0';
199 return written;
200}
201
202string prune_ansi_sequences_str(const char *in) {
203 string out;
204 prune_ansi_sequences(in, std::back_inserter(out));
205 return out;
206}
207
208void prune_ansi_sequences_inplace(std::string& str){
209 auto c_ptr = str.data();
210 auto len = prune_ansi_sequences(c_ptr, c_ptr);
211 str.resize(len);
212}
213
216 prune_ansi_sequences(cstr, cstr);
217 return cstr;
218}
219
220int color_printf(const char *format, ...) {
221 va_list ap;
222 va_start(ap, format);
223 int size = vsnprintf(NULL, 0, format, ap);
224 va_end(ap);
225
226 // format the string
227 auto buf = get_log_output().get_buffer();
228 buf.append(size + 1, '\0');
229 va_start(ap, format);
230 vsnprintf(buf.data(), size + 1, format, ap);
231 va_end(ap);
232
233 if (!color_stdout) {
234 int len = prune_ansi_sequences(buf.data(), buf.data());
235 buf.get().resize(len);
236 }
237
238 buf.submit_raw();
239 return size;
240}
241
int void size_t * len
void append(std::string_view sv)
Definition debug.h:130
Buffer get_buffer()
Definition debug.h:153
char * prune_ansi_sequences_inplace_cstr(char *cstr)
void prune_ansi_sequences_inplace(std::string &str)
static bool color_stdout
Definition color_out.cpp:57
static int prune_ansi_sequences(const char *in, OutputIt out)
bool color_output_init()
sets internal variable color_stdout and retunrs its contents
string prune_ansi_sequences_str(const char *in)
#define MOD_NAME
Definition color_out.cpp:53
static bool is_output_color()
int color_printf(const char *format,...)
Log_output & get_log_output()
Definition debug.h:266
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
const char * name
Definition settings.cpp:193
int vsnprintf(char *s, size_t buf_size, const char *format, va_list ap)
Definition vsnprintf.c:46