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