C++ Utilities 5.17.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
51{
52 TerminalSize size;
53#ifndef PLATFORM_WINDOWS
54 ioctl(STDOUT_FILENO, TIOCGWINSZ, reinterpret_cast<winsize *>(&size));
55#else
56 CONSOLE_SCREEN_BUFFER_INFO consoleBufferInfo;
57 if (const HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE)) {
58 GetConsoleScreenBufferInfo(stdHandle, &consoleBufferInfo);
59 if (consoleBufferInfo.dwSize.X > 0) {
60 size.columns = static_cast<unsigned short>(consoleBufferInfo.dwSize.X);
61 }
62 if (consoleBufferInfo.dwSize.Y > 0) {
63 size.rows = static_cast<unsigned short>(consoleBufferInfo.dwSize.Y);
64 }
65 }
66#endif
67 return size;
68}
69
70#ifdef PLATFORM_WINDOWS
76static bool enableVirtualTerminalProcessing(DWORD nStdHandle)
77{
78 auto stdHandle = GetStdHandle(nStdHandle);
79 if (stdHandle == INVALID_HANDLE_VALUE) {
80 return false;
81 }
82 auto dwMode = DWORD();
83 if (!GetConsoleMode(stdHandle, &dwMode)) {
84 return false;
85 }
86 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
87 return SetConsoleMode(stdHandle, dwMode);
88}
89
94bool handleVirtualTerminalProcessing()
95{
96 // try to enable virtual terminal processing
97 if (enableVirtualTerminalProcessing(STD_OUTPUT_HANDLE) && enableVirtualTerminalProcessing(STD_ERROR_HANDLE)) {
98 return true;
99 }
100 // disable use on ANSI escape codes otherwise if it makes sense
101 const char *const msyscon = std::getenv("MSYSCON");
102 if (msyscon && std::strstr(msyscon, "mintty")) {
103 return false; // no need to disable escape codes if it is just mintty
104 }
105 const char *const term = std::getenv("TERM");
106 if (term && std::strstr(term, "xterm")) {
107 return false; // no need to disable escape codes if it is some xterm-like terminal
108 }
109 return EscapeCodes::enabled = false;
110}
111
116void stopConsole()
117{
118 fclose(stdout);
119 fclose(stdin);
120 fclose(stderr);
121 if (auto *const consoleWindow = GetConsoleWindow()) {
122 PostMessage(consoleWindow, WM_KEYUP, VK_RETURN, 0);
123 FreeConsole();
124 }
125}
126
142void startConsole()
143{
144 // attach to the parent process' console or allocate a new console if that's not possible
145 const auto consoleEnabled = isEnvVariableSet("ENABLE_CONSOLE");
146 if ((!consoleEnabled.has_value() || consoleEnabled.value()) && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
147 // redirect stdout
148 auto stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE));
149 auto conHandle = _open_osfhandle(stdHandle, _O_TEXT);
150 auto fp = _fdopen(conHandle, "w");
151 *stdout = *fp;
152 setvbuf(stdout, nullptr, _IONBF, 0);
153 // redirect stdin
154 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE));
155 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
156 fp = _fdopen(conHandle, "r");
157 *stdin = *fp;
158 setvbuf(stdin, nullptr, _IONBF, 0);
159 // redirect stderr
160 stdHandle = reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE));
161 conHandle = _open_osfhandle(stdHandle, _O_TEXT);
162 fp = _fdopen(conHandle, "w");
163 *stderr = *fp;
164 setvbuf(stderr, nullptr, _IONBF, 0);
165 // sync
166 ios::sync_with_stdio(true);
167 // ensure the console prompt is shown again when app terminates
168 atexit(stopConsole);
169 }
170
171 // set console character set to UTF-8
172 const auto utf8Enabled = isEnvVariableSet("ENABLE_CP_UTF8");
173 if (!utf8Enabled.has_value() || utf8Enabled.value()) {
174 SetConsoleCP(CP_UTF8);
175 SetConsoleOutputCP(CP_UTF8);
176 }
177
178 // enable virtual terminal processing or disable ANSI-escape if that's not possible
179 handleVirtualTerminalProcessing();
180}
181
186pair<vector<unique_ptr<char[]>>, vector<char *>> convertArgsToUtf8()
187{
188 pair<vector<unique_ptr<char[]>>, vector<char *>> res;
189 int argc;
190
191 LPWSTR *argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
192 if (!argv_w || argc <= 0) {
193 return res;
194 }
195
196 res.first.reserve(static_cast<size_t>(argc));
197 res.second.reserve(static_cast<size_t>(argc));
198 for (LPWSTR *i = argv_w, *end = argv_w + argc; i != end; ++i) {
199 int requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, nullptr, 0, 0, 0);
200 if (requiredSize <= 0) {
201 break; // just stop on error
202 }
203
204 auto argv = make_unique<char[]>(static_cast<size_t>(requiredSize));
205 requiredSize = WideCharToMultiByte(CP_UTF8, 0, *i, -1, argv.get(), requiredSize, 0, 0);
206 if (requiredSize <= 0) {
207 break;
208 }
209
210 res.second.emplace_back(argv.get());
211 res.first.emplace_back(move(argv));
212 }
213
214 LocalFree(argv_w);
215 return res;
216}
217#endif
218
219} // 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.
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