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