4#include "Stroika/Foundation/StroikaPreComp.h"
7#include "Stroika/Foundation/Characters/SDKString.h"
10#include "Stroika/Foundation/Containers/Set.h"
12#include "CommandLine.h"
18using namespace Stroika::Foundation::Traversal;
25Execution::InvalidCommandLineArgument::InvalidCommandLineArgument ()
29Execution::InvalidCommandLineArgument::InvalidCommandLineArgument (
const String& message)
34Execution::InvalidCommandLineArgument::InvalidCommandLineArgument (
const String& message,
const String& argument)
47DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
48DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
56bool Execution::MatchesCommandLineArgument (
const String& actualArg,
const String& matchesArgPattern)
59 if (actualArg.empty ()) {
62#if qStroika_Foundation_Common_Platform_Windows
63 if (actualArg[0] !=
'-' and actualArg[0] !=
'/') {
67 if (actualArg[0] !=
'-') {
71 return Simplify2Compare_ (actualArg) == Simplify2Compare_ (matchesArgPattern);
74bool Execution::MatchesCommandLineArgument (
const Iterable<String>& argList,
const String& matchesArgPattern)
76 return static_cast<bool> (
77 argList.
Find ([matchesArgPattern] (
String i) ->
bool {
return Execution::MatchesCommandLineArgument (i, matchesArgPattern); }));
80optional<String> Execution::MatchesCommandLineArgumentWithValue ([[maybe_unused]]
const String& actualArg, [[maybe_unused]]
const String& matchesArgPattern)
82 Require (matchesArgPattern.size () > 0 and matchesArgPattern[matchesArgPattern.size () - 1] ==
'=');
88optional<String> Execution::MatchesCommandLineArgumentWithValue (
const Iterable<String>& argList,
const String& matchesArgPattern)
91 argList.
Find ([matchesArgPattern] (
const String& i) ->
bool {
return Execution::MatchesCommandLineArgument (i, matchesArgPattern); });
92 if (i != argList.
end ()) {
94 if (i == argList.
end ()) [[unlikely]] {
98 return optional<String>{*i};
103DISABLE_COMPILER_MSC_WARNING_END (4996);
104DISABLE_COMPILER_GCC_WARNING_END (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
105DISABLE_COMPILER_CLANG_WARNING_END (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
112String CommandLine::Option::GetArgumentDescription (
bool includeArg)
const
118 if (fSingleCharName and fLongName) {
120 return "(-{} {}|--{}={})"_f(*fSingleCharName, argName, *fLongName, argName);
123 return "(-{}|--{})"_f(*fSingleCharName, *fLongName);
126 else if (this->fSingleCharName) {
128 return "-{} {}"_f(*fSingleCharName, argName);
131 return "-{}"_f(*fSingleCharName);
134 else if (fLongName) {
136 return "--"sv + *fLongName +
"="sv + argName;
139 return "--"sv + *fLongName;
152String CommandLine::Option::ToString ()
const
156 if (fSingleCharName) {
157 sb <<
"SingleCharName: "sv << *fSingleCharName <<
","sv;
160 sb <<
"LongName: "sv << *fLongName <<
","sv;
162 sb <<
"CaseSensitive: "sv << fLongNameCaseSensitive <<
","sv;
163 sb <<
"SupportsArgument: "sv << fSupportsArgument <<
","sv;
164 sb <<
"IfSupportsArgumentThenRequired: "sv << fIfSupportsArgumentThenRequired <<
","sv;
165 sb <<
"SupportsArgument: "sv << fSupportsArgument <<
","sv;
166 sb <<
"Repeatable: "sv << fRepeatable <<
","sv;
168 sb <<
"HelpArgName: "sv << *fHelpArgName <<
","sv;
170 if (fHelpOptionText) {
171 sb <<
"HelpOptionText: "sv << *fHelpOptionText <<
","sv;
186 size_t e = cmdLine.
length ();
189 for (
size_t i = 0; i < e; ++i) {
191 if (endQuoteChar !=
'\0' and c == endQuoteChar) {
196 else if (c ==
'\'' or c ==
'\"') {
199 else if (endQuoteChar !=
'\0') {
209 if (curToken.
size () != 0) {
216 if (curToken.
size () != 0) {
223 : fArgs_{ParseArgs_ (cmdLine)}
227CommandLine ::CommandLine (WrapInShell wrapInShell,
const String& cmdLine)
229#if qStroika_Foundation_Common_Platform_Windows
234 if (const
char* env_p =
std::getenv ("COMSPEC")) {
235 return String::FromNarrowSDKString (env_p);
237 DISABLE_COMPILER_MSC_WARNING_END (4996)
238 return "C:\\WINDOWS\\system32\\cmd.exe"sv;
241 switch (wrapInShell) {
242 case WrapInShell::eBash:
246 fShellStyleQuoting_ = StringShellQuoting::eBash;
248#if qStroika_Foundation_Common_Platform_Windows
249 case WrapInShell::eWindowsCMD:
256 fShellStyleQuoting_ = StringShellQuoting::eWindowsCMD;
266 for (
int i = 0; i < argc; ++i) {
273 for (
int i = 0; i < argc; ++i) {
274 fArgs_.push_back (argv[i]);
280 return GenerateUsage (GetAppName (), options);
285 const String kIndent_ =
" "sv;
287 sb <<
"Usage: "sv << exeName;
288 options.
Apply ([&] (Option o) {
289 sb <<
" [" << o.GetArgumentDescription (
true) <<
"]"sv;
298 else if (not o.fRequired) {
303 size_t maxArgDescLen{0};
304 options.
Apply ([&] (
const Option& o) {
305 if (o.fHelpOptionText) {
306 maxArgDescLen = max (maxArgDescLen, o.GetArgumentDescription ().length ());
309 options.
Apply ([&] (
const Option& o) {
310 if (o.fHelpOptionText) {
311 String argDesc = o.GetArgumentDescription ();
312 sb << kIndent_ << argDesc <<
" "_k.Repeat (static_cast<unsigned int> (kIndent_.length () + maxArgDescLen - argDesc.size ()))
313 <<
"/* " << *o.fHelpOptionText <<
" */\n";
323 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
324 if (not all.First ([&] (
Option o) {
325 if (optional<pair<bool, optional<String>>> oRes = ParseOneArg_ (o, &argi)) {
334 if (
auto o = unused.First ([] (Option o) { return o.fRequired; })) {
339String CommandLine::GetAppName (
bool onlyBaseName)
const
341 if (fArgs_.empty ()) {
345 filesystem::path p = fArgs_[0].As<filesystem::path> ();
351tuple<bool, Sequence<String>> CommandLine::Get (
const Option& o)
const
355 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
356 if (optional<pair<
bool, optional<String>>> oRes = ParseOneArg_ (o, &argi)) {
361 arguments += *oRes->second;
363 if (not o.fRepeatable) {
368 if (o.fRequired and not found and arguments.
empty ()) {
371 if (found and o.fSupportsArgument and o.fIfSupportsArgumentThenRequired and arguments.
empty ()) {
374 return make_tuple (found, arguments);
377String CommandLine::ToString ()
const
379 return this->As<String> ();
382optional<pair<bool, optional<String>>> CommandLine::ParseOneArg_ (
const Option& o,
Iterator<String>* argi)
385 Require (not argi->
Done ());
388 if (o.fSingleCharName and ai.
length () == 2 and ai[0] ==
'-' and ai[1] == o.fSingleCharName) {
389 if (o.fSupportsArgument) {
391 if ((*argi).Done ()) {
392 if (o.fIfSupportsArgumentThenRequired) {
394 "Command line argument requires an argument to it, but none provided (= or following argument)"sv, ai});
396 return make_pair (
true, nullopt);
399 return make_pair (
true, **argi);
402 return make_pair (
true, nullopt);
407 if (o.fLongName and ai.
length () >= 2 + o.fLongName->size () and ai[0] ==
'-' and ai[1] ==
'-' and
409 if (o.fSupportsArgument) {
412 if (restOfArgi.
size () >= 1 and restOfArgi[0] ==
'=') {
413 return make_pair (
true, restOfArgi.
SubString (1));
417 if ((*argi).Done ()) {
418 if (o.fIfSupportsArgumentThenRequired) {
420 "Command line argument requires an argument to it, but none provided (= or following argument)"sv, ai});
422 return make_pair (
true, nullopt);
425 return make_pair (
true, **argi);
429 return make_pair (
true, nullopt);
432 if (not o.fSingleCharName and not o.fLongName and o.fSupportsArgument and not(ai.
size () >= 2 and ai.
StartsWith (
"-"sv))) {
434 return make_pair (
false, **argi);
440String CommandLine::As<String> ()
const
442 return As<String> (this->fShellStyleQuoting_);
446String CommandLine::As<String> (optional<CommandLine::StringShellQuoting> shellStyle)
const
451 return fArgs_.Join<
String> (
454 if (shellStyle == nullopt) {
455 if (i.ContainsAny ({
' ',
'\"'})) {
456 return "\"{}\""_f(i);
463 else if (shellStyle == StringShellQuoting::eWindowsCMD) {
465 if (i.ContainsAny ({
' ',
'\"'})) {
466 return "\"{}\""_f(i);
473 else if (shellStyle == StringShellQuoting::eBash) {
475 if (i.ContainsAny ({
' ',
'\"'})) {
476 return "\"{}\""_f(i);
#define AssertNotImplemented()
#define RequireNotReached()
#define RequireNotNull(p)
constexpr bool IsWhitespace() const noexcept
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual size_t size() const noexcept
nonvirtual String str() const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual size_t length() const noexcept
static String FromNarrowSDKString(const char *from)
nonvirtual size_t size() const noexcept
nonvirtual String SubString(SZ from) const
nonvirtual bool StartsWith(const Character &c, CompareOptions co=eWithCase) const
nonvirtual String StripAll(bool(*removeCharIf)(Character)) const
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void Append(ArgByValueType< value_type > item)
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Validate(Iterable< Option > options) const
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual Iterator< T > Find(THAT_FUNCTION &&that, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument bool-returning function (or lambda) on each element of the container,...
nonvirtual bool empty() const
Returns true iff size() == 0.
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
An Iterator<T> is a copyable object which allows traversing the contents of some container....
nonvirtual bool Done() const
Done () means there is nothing left in this iterator (a synonym for (it == container....
DISABLE_COMPILER_MSC_WARNING_START(4996)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
optional< String > fHelpArgName