C++ Utilities 5.26.1
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
commandlineutils.cpp
Go to the documentation of this file.
3
5
6#include <iostream>
7#include <string>
8
9#include <fcntl.h>
10#ifdef PLATFORM_WINDOWS
11#include <cstring>
12#include <io.h>
13#include <tchar.h>
14#include <windows.h>
15#else
16#include <sys/ioctl.h>
17#include <unistd.h>
18#endif
19
20using namespace std;
21
22namespace CppUtilities {
23
27bool confirmPrompt(const char *message, Response defaultResponse)
28{
29 cout << message;
30 cout << ' ' << '[';
31 cout << (defaultResponse == Response::Yes ? 'Y' : 'y');
32 cout << '/' << (defaultResponse == Response::No ? 'N' : 'n');
33 cout << ']' << ' ';
34 cout.flush();
35 for (string line;;) {
36 getline(cin, line);
37 if (line == "y" || line == "Y" || (defaultResponse == Response::Yes && line.empty())) {
38 return true;
39 } else if (line == "n" || line == "N" || (defaultResponse == Response::No && line.empty())) {
40 return false;
41 } else {
42 cout << "Please enter [y] or [n]: ";
43 cout.flush();
44 }
45 }
46}
47
51std::optional<bool> isEnvVariableSet(const char *variableName)
52{
53 const char *envValue = std::getenv(variableName);
54 if (!envValue) {
55 return std::nullopt;
56 }
57 for (; *envValue; ++envValue) {
58 switch (*envValue) {
59 case '0':
60 case ' ':
61 break;
62 default:
63 return true;
64 }
65 }
66 return false;
67}
68
74{
75 TerminalSize size;
76#ifndef PLATFORM_WINDOWS
77 ioctl(STDOUT_FILENO, TIOCGWINSZ, reinterpret_cast<winsize *>(&size));
78#else
79 CONSOLE_SCREEN_BUFFER_INFO consoleBufferInfo;
80 if (const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE)) {
81 GetConsoleScreenBufferInfo(stdHandle, &consoleBufferInfo);
82 if (consoleBufferInfo.dwSize.X > 0) {
83 size.columns = static_cast<unsigned short>(consoleBufferInfo.dwSize.X);
84 }
85 if (consoleBufferInfo.dwSize.Y > 0) {
86 size.rows = static_cast<unsigned short>(consoleBufferInfo.dwSize.Y);
87 }
88 }
89#endif
90 return size;
91}
92
93#ifdef PLATFORM_WINDOWS
97static bool isMintty()
98{
99 static const auto mintty = [] {
100 const char *const msyscon = std::getenv("MSYSCON");
101 const char *const termprog = std::getenv("TERM_PROGRAM");
102 return (msyscon && std::strstr(msyscon, "mintty")) || (termprog && std::strstr(termprog, "mintty"));
103 }();
104 return mintty;
105}
106
112static bool enableVirtualTerminalProcessing(DWORD nStdHandle)
113{
114 auto stdHandle = GetStdHandle(nStdHandle);
115 if (stdHandle == INVALID_HANDLE_VALUE) {
116 return false;
117 }
118 auto dwMode = DWORD();
119 if (!GetConsoleMode(stdHandle, &dwMode)) {
120 return false;
121 }
122 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
123 return SetConsoleMode(stdHandle, dwMode);
124}
125
130bool handleVirtualTerminalProcessing()
131{
132 // try to enable virtual terminal processing
133 if (enableVirtualTerminalProcessing(STD_OUTPUT_HANDLE) && enableVirtualTerminalProcessing(STD_ERROR_HANDLE)) {
134 return true;
135 }
136 // disable use of ANSI escape codes otherwise if it makes sense
137 if (isMintty()) {
138 return false; // no need to disable escape codes if it is just mintty
139 }
140 if (const char *const term = std::getenv("TERM"); term && std::strstr(term, "xterm")) {
141 return false; // no need to disable escape codes if it is some xterm-like terminal
142 }
143 return EscapeCodes::enabled = false;
144}
145
150void stopConsole()
151{
152 fclose(stdout);
153 fclose(stdin);
154 fclose(stderr);
155 if (auto *const consoleWindow = GetConsoleWindow()) {
156 FreeConsole();
157 }
158}
159
182void startConsole()
183{
184 // skip if ENABLE_CONSOLE is set to 0 or not set at all
185 if (const auto e = isEnvVariableSet("ENABLE_CONSOLE"); !e.has_value() || !e.value()) {
186 return;
187 }
188
189 // check whether there's a redirection; skip messing with any streams then to not break redirections/pipes
190 auto pos = std::fpos_t();
191 std::fgetpos(stdout, &pos);
192 const auto skipstdout = pos >= 0;
193 std::fgetpos(stderr, &pos);
194 const auto skipstderr = pos >= 0;
195 std::fgetpos(stdin, &pos);
196 const auto skipstdin = pos >= 0;
197 const auto skip = skipstdout || skipstderr || skipstdin;
198
199 // attach to the parent process' console or allocate a new console if that's not possible
200 if (!skip && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
201 FILE *fp;
202#ifdef _MSC_VER
203 // take care of normal streams
204 if (!skipstdout) {
205 freopen_s(&fp, "CONOUT$", "w", stdout);
206 std::cout.clear();
207 std::clog.clear();
208 }
209 if (!skipstderr) {
210 freopen_s(&fp, "CONOUT$", "w", stderr);
211 std::cerr.clear();
212 }
213 if (!skipstdin) {
214 freopen_s(&fp, "CONIN$", "r", stdin);
215 std::cin.clear();
216 }
217 // take care of wide streams
218 auto hConOut = CreateFile(
219 _T("CONOUT$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
220 auto hConIn = CreateFile(
221 _T("CONIN$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
222 if (!skipstdout) {
223 SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
224 std::wcout.clear();
225 std::wclog.clear();
226 }
227 if (!skipstderr) {
228 SetStdHandle(STD_ERROR_HANDLE, hConOut);
229 std::wcerr.clear();
230 }
231 if (!skipstdin) {
232 SetStdHandle(STD_INPUT_HANDLE, hConIn);
233 std::wcin.clear();
234 }
235#else
236 // redirect stdout
237 auto stdHandle = std::intptr_t();
238 auto conHandle = int();
239 if (!skipstdout) {
240 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE));
241 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
242 fp = _fdopen(conHandle, "w");
243 *stdout = *fp;
244 setvbuf(stdout, nullptr, _IONBF, 0);
245 }
246 // redirect stdin
247 if (!skipstdin) {
248 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE));
249 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
250 fp = _fdopen(conHandle, "r");
251 *stdin = *fp;
252 setvbuf(stdin, nullptr, _IONBF, 0);
253 }
254 // redirect stderr
255 if (!skipstderr) {
256 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE));
257 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
258 fp = _fdopen(conHandle, "w");
259 *stderr = *fp;
260 setvbuf(stderr, nullptr, _IONBF, 0);
261 }
262 // sync
263 ios::sync_with_stdio(true);
264#endif
265 // ensure the console prompt is shown again when app terminates
266 std::atexit(stopConsole);
267 }
268
269 // set console character set to UTF-8
270 if (const auto e = isEnvVariableSet("ENABLE_CP_UTF8"); !e.has_value() || e.value()) {
271 SetConsoleCP(CP_UTF8);
272 SetConsoleOutputCP(CP_UTF8);
273 }
274
275 // enable virtual terminal processing or disable ANSI-escape if that's not possible
276 if (const auto e = isEnvVariableSet("ENABLE_HANDLING_VIRTUAL_TERMINAL_PROCESSING"); !e.has_value() || e.value()) {
277 handleVirtualTerminalProcessing();
278 }
279}
280
285pair<vector<unique_ptr<char[]>>, vector<char *>> convertArgsToUtf8()
286{
287 pair<vector<unique_ptr<char[]>>, vector<char *>> res;
288 int argc;
289
290 LPWSTR *argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
291 if (!argv_w || argc <= 0) {
292 return res;
293 }
294
295 res.first.reserve(static_cast<size_t>(argc));
296 res.second.reserve(static_cast<size_t>(argc));
297 for (LPWSTR *i = argv_w, *end = argv_w + argc; i != end; ++i) {
298 int requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, nullptr, 0, 0, 0);
299 if (requiredSize <= 0) {
300 break; // just stop on error
301 }
302
303 auto argv = make_unique<char[]>(static_cast<size_t>(requiredSize));
304 requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, argv.get(), requiredSize, 0, 0);
305 if (requiredSize <= 0) {
306 break;
307 }
308
309 res.second.emplace_back(argv.get());
310 res.first.emplace_back(std::move(argv));
311 }
312
313 LocalFree(argv_w);
314 return res;
315}
316#endif
317
318} // namespace CppUtilities
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
Contains all utilities provides by the c++utilities library.
CPP_UTILITIES_EXPORT TerminalSize determineTerminalSize()
Returns the current size of the terminal.
Response
The Response enum is used to specify the default response for the confirmPrompt() method.
CPP_UTILITIES_EXPORT bool confirmPrompt(const char *message, Response defaultResponse=Response::None)
Prompts for confirmation displaying the specified message.
CPP_UTILITIES_EXPORT std::optional< bool > isEnvVariableSet(const char *variableName)
Returns whether the specified env variable is set to a non-zero and non-white-space-only value.
STL namespace.
The TerminalSize struct describes a terminal size.
unsigned short columns
number of columns
unsigned short rows
number of rows
constexpr int i