C++ Utilities 5.20.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
commandlineutils.cpp
Go to the documentation of this file.
3
4#include "../io/ansiescapecodes.h"
5
6#include <iostream>
7#include <string>
8
9#ifndef PLATFORM_WINDOWS
10#include <sys/ioctl.h>
11#include <unistd.h>
12#else
13#include <cstring>
14#include <fcntl.h>
15#include <windows.h>
16#endif
17
18using namespace std;
19
20namespace CppUtilities {
21
25bool confirmPrompt(const char *message, Response defaultResponse)
26{
27 cout << message;
28 cout << ' ' << '[';
29 cout << (defaultResponse == Response::Yes ? 'Y' : 'y');
30 cout << '/' << (defaultResponse == Response::No ? 'N' : 'n');
31 cout << ']' << ' ';
32 cout.flush();
33 for (string line;;) {
34 getline(cin, line);
35 if (line == "y" || line == "Y" || (defaultResponse == Response::Yes && line.empty())) {
36 return true;
37 } else if (line == "n" || line == "N" || (defaultResponse == Response::No && line.empty())) {
38 return false;
39 } else {
40 cout << "Please enter [y] or [n]: ";
41 cout.flush();
42 }
43 }
44}
45
49std::optional<bool> isEnvVariableSet(const char *variableName)
50{
51 const char *envValue = std::getenv(variableName);
52 if (!envValue) {
53 return std::nullopt;
54 }
55 for (; *envValue; ++envValue) {
56 switch (*envValue) {
57 case '0':
58 case ' ':
59 break;
60 default:
61 return true;
62 }
63 }
64 return false;
65}
66
72{
73 TerminalSize size;
74#ifndef PLATFORM_WINDOWS
75 ioctl(STDOUT_FILENO, TIOCGWINSZ, reinterpret_cast<winsize *>(&size));
76#else
77 CONSOLE_SCREEN_BUFFER_INFO consoleBufferInfo;
78 if (const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE)) {
79 GetConsoleScreenBufferInfo(stdHandle, &consoleBufferInfo);
80 if (consoleBufferInfo.dwSize.X > 0) {
81 size.columns = static_cast<unsigned short>(consoleBufferInfo.dwSize.X);
82 }
83 if (consoleBufferInfo.dwSize.Y > 0) {
84 size.rows = static_cast<unsigned short>(consoleBufferInfo.dwSize.Y);
85 }
86 }
87#endif
88 return size;
89}
90
91#ifdef PLATFORM_WINDOWS
97static bool enableVirtualTerminalProcessing(DWORD nStdHandle)
98{
99 auto stdHandle = GetStdHandle(nStdHandle);
100 if (stdHandle == INVALID_HANDLE_VALUE) {
101 return false;
102 }
103 auto dwMode = DWORD();
104 if (!GetConsoleMode(stdHandle, &dwMode)) {
105 return false;
106 }
107 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
108 return SetConsoleMode(stdHandle, dwMode);
109}
110
115bool handleVirtualTerminalProcessing()
116{
117 // try to enable virtual terminal processing
118 if (enableVirtualTerminalProcessing(STD_OUTPUT_HANDLE) && enableVirtualTerminalProcessing(STD_ERROR_HANDLE)) {
119 return true;
120 }
121 // disable use on ANSI escape codes otherwise if it makes sense
122 const char *const msyscon = std::getenv("MSYSCON");
123 if (msyscon && std::strstr(msyscon, "mintty")) {
124 return false; // no need to disable escape codes if it is just mintty
125 }
126 const char *const term = std::getenv("TERM");
127 if (term && std::strstr(term, "xterm")) {
128 return false; // no need to disable escape codes if it is some xterm-like terminal
129 }
130 return EscapeCodes::enabled = false;
131}
132
137void stopConsole()
138{
139 fclose(stdout);
140 fclose(stdin);
141 fclose(stderr);
142 if (auto *const consoleWindow = GetConsoleWindow()) {
143 PostMessage(consoleWindow, WM_KEYUP, VK_RETURN, 0);
144 FreeConsole();
145 }
146}
147
163void startConsole()
164{
165 // attach to the parent process' console or allocate a new console if that's not possible
166 const auto consoleEnabled = isEnvVariableSet("ENABLE_CONSOLE");
167 if ((!consoleEnabled.has_value() || consoleEnabled.value()) && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
168 // redirect stdout
169 auto stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE));
170 auto conHandle = _open_osfhandle(stdHandle, _O_TEXT);
171 auto fp = _fdopen(conHandle, "w");
172 *stdout = *fp;
173 setvbuf(stdout, nullptr, _IONBF, 0);
174 // redirect stdin
175 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE));
176 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
177 fp = _fdopen(conHandle, "r");
178 *stdin = *fp;
179 setvbuf(stdin, nullptr, _IONBF, 0);
180 // redirect stderr
181 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE));
182 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
183 fp = _fdopen(conHandle, "w");
184 *stderr = *fp;
185 setvbuf(stderr, nullptr, _IONBF, 0);
186 // sync
187 ios::sync_with_stdio(true);
188 // ensure the console prompt is shown again when app terminates
189 atexit(stopConsole);
190 }
191
192 // set console character set to UTF-8
193 const auto utf8Enabled = isEnvVariableSet("ENABLE_CP_UTF8");
194 if (!utf8Enabled.has_value() || utf8Enabled.value()) {
195 SetConsoleCP(CP_UTF8);
196 SetConsoleOutputCP(CP_UTF8);
197 }
198
199 // enable virtual terminal processing or disable ANSI-escape if that's not possible
200 handleVirtualTerminalProcessing();
201}
202
207pair<vector<unique_ptr<char[]>>, vector<char *>> convertArgsToUtf8()
208{
209 pair<vector<unique_ptr<char[]>>, vector<char *>> res;
210 int argc;
211
212 LPWSTR *argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
213 if (!argv_w || argc <= 0) {
214 return res;
215 }
216
217 res.first.reserve(static_cast<size_t>(argc));
218 res.second.reserve(static_cast<size_t>(argc));
219 for (LPWSTR *i = argv_w, *end = argv_w + argc; i != end; ++i) {
220 int requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, nullptr, 0, 0, 0);
221 if (requiredSize <= 0) {
222 break; // just stop on error
223 }
224
225 auto argv = make_unique<char[]>(static_cast<size_t>(requiredSize));
226 requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, argv.get(), requiredSize, 0, 0);
227 if (requiredSize <= 0) {
228 break;
229 }
230
231 res.second.emplace_back(argv.get());
232 res.first.emplace_back(move(argv));
233 }
234
235 LocalFree(argv_w);
236 return res;
237}
238#endif
239
240} // 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