4#include "Stroika/Foundation/StroikaPreComp.h"
7#include "Stroika/Foundation/Characters/SDKString.h"
10#include "Stroika/Foundation/Containers/Set.h"
11#include "Stroika/Foundation/Execution/Module.h"
14#include "CommandLine.h"
21using namespace Stroika::Foundation::Traversal;
28InvalidCommandLineArgument::InvalidCommandLineArgument ()
32InvalidCommandLineArgument::InvalidCommandLineArgument (
const String& message)
36InvalidCommandLineArgument::InvalidCommandLineArgument (
const String& message,
const String& argument)
48DISABLE_COMPILER_GCC_WARNING_START (
"GCC diagnostic ignored \"-Wdeprecated-declarations\"");
49DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wdeprecated-declarations\"");
57bool Execution::MatchesCommandLineArgument (
const String& actualArg,
const String& matchesArgPattern)
60 if (actualArg.empty ()) {
63#if qStroika_Foundation_Common_Platform_Windows
64 if (actualArg[0] !=
'-' and actualArg[0] !=
'/') {
68 if (actualArg[0] !=
'-') {
72 return Simplify2Compare_ (actualArg) == Simplify2Compare_ (matchesArgPattern);
75bool Execution::MatchesCommandLineArgument (
const Iterable<String>& argList,
const String& matchesArgPattern)
77 return static_cast<bool> (
78 argList.
Find ([matchesArgPattern] (
String i) ->
bool {
return Execution::MatchesCommandLineArgument (i, matchesArgPattern); }));
81optional<String> Execution::MatchesCommandLineArgumentWithValue ([[maybe_unused]]
const String& actualArg, [[maybe_unused]]
const String& matchesArgPattern)
83 Require (matchesArgPattern.size () > 0 and matchesArgPattern[matchesArgPattern.size () - 1] ==
'=');
89optional<String> Execution::MatchesCommandLineArgumentWithValue (
const Iterable<String>& argList,
const String& matchesArgPattern)
91 auto i = argList.
Find ([matchesArgPattern] (
const String& i) ->
bool {
return 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 includeArgName)
const
115 includeArgName =
false;
118 if (fSingleCharName and fLongName) {
119 if (includeArgName) {
120 return "(-{} {}|--{}={})"_f(*fSingleCharName, argName, *fLongName, argName);
123 return "(-{}|--{})"_f(*fSingleCharName, *fLongName);
126 else if (this->fSingleCharName) {
127 if (includeArgName) {
128 return "-{} {}"_f(*fSingleCharName, argName);
131 return "-{}"_f(*fSingleCharName);
134 else if (fLongName) {
135 if (includeArgName) {
136 return "--"sv + *fLongName +
"="sv + argName;
139 return "--"sv + *fLongName;
143 if (includeArgName) {
152String CommandLine::Option::ToString ()
const
156 if (fSingleCharName) {
157 sb <<
"SingleCharName: "sv << *fSingleCharName <<
","sv;
160 sb <<
"LongName: "sv << *fLongName <<
","sv;
162 if (this->IsPositionArgument ()) {
163 sb <<
"POSITIONAL ARGUMENT,"sv;
165 sb <<
"CaseSensitive: "sv << fLongNameCaseSensitive <<
","sv;
166 if (not this->IsPositionArgument ()) {
167 sb <<
"SupportsArgument: "sv << fSupportsArgument <<
","sv;
168 sb <<
"IfSupportsArgumentThenRequired: "sv << fIfSupportsArgumentThenRequired <<
","sv;
170 sb <<
"Repeatable: "sv << fRepeatable <<
","sv;
171 if (fSkipFirstNArguments) {
172 sb <<
"SkipFirstNArguments: "sv << fSkipFirstNArguments <<
","sv;
175 sb <<
"HelpArgName: "sv << *fHelpArgName <<
","sv;
177 if (fHelpOptionText) {
178 sb <<
"HelpOptionText: "sv << *fHelpOptionText <<
","sv;
193 size_t e = cmdLine.
length ();
196 for (
size_t i = 0; i < e; ++i) {
198 if (endQuoteChar !=
'\0' and c == endQuoteChar) {
203 else if (c ==
'\'' or c ==
'\"') {
206 else if (endQuoteChar !=
'\0') {
216 if (curToken.
size () != 0) {
223 if (curToken.
size () != 0) {
230 : fArgs_{ParseArgs_ (cmdLine)}
236 switch (wrapInShell) {
237 case WrapInShell::eBash:
238#if qStroika_Foundation_Common_Platform_Windows
254 fShellStyleQuoting_ = StringShellQuoting::eBash;
256#if qStroika_Foundation_Common_Platform_Windows
257 case WrapInShell::eWindowsCMD: {
261 DISABLE_COMPILER_MSC_WARNING_START (4996)
262 if (const
char* env_p =
std::getenv ("COMSPEC")) {
265 DISABLE_COMPILER_MSC_WARNING_END (4996)
266 return "C:\\WINDOWS\\system32\\cmd.exe"sv;
284 for (
int i = 0; i < argc; ++i) {
291 for (
int i = 0; i < argc; ++i) {
292 fArgs_.push_back (argv[i]);
298 return GenerateUsage (GetAppName (), options);
303 static const String kIndent_ =
" "sv;
305 sb <<
"Usage: "sv << exeName;
306 options.
Apply ([&] (
const Option& o) {
307 sb <<
" [" << o.GetArgumentDescription (
true) <<
"]"sv;
316 else if (not o.fRequired) {
321 size_t maxArgDescLen{0};
322 options.
Apply ([&] (
const Option& o) {
323 if (o.fHelpOptionText) {
324 maxArgDescLen = max (maxArgDescLen, o.GetArgumentDescription ().length ());
327 options.
Apply ([&] (
const Option& o) {
328 if (o.fHelpOptionText) {
329 String argDesc = o.GetArgumentDescription ();
330 sb << kIndent_ << argDesc <<
" "_k.Repeat (static_cast<unsigned int> (kIndent_.length () + maxArgDescLen - argDesc.size ()))
331 <<
"/* " << *o.fHelpOptionText <<
" */\n";
348 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
349 if (not all.First ([&] (
const Option& o) {
350 auto oRes = ParseOneArg_ (o, &argi);
352 Throw (oRes.error ());
363 if (
auto o = unused.First ([] (
const Option& o) { return o.fRequired; })) {
364 return InvalidCommandLineArgument{
"Required command line argument "sv + o->GetArgumentDescription (
true) +
" was not provided"sv};
369String CommandLine::GetAppName (
bool onlyBaseName)
const
371 if (fArgs_.empty ()) {
375 filesystem::path p = fArgs_[0].As<filesystem::path> ();
381tuple<bool, Sequence<String>> CommandLine::Get (
const Option& o)
const
384 size_t nMore2Skip = o.fSkipFirstNArguments.value_or (0);
385 Assert (nMore2Skip == 0 or o.IsPositionArgument ());
387 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
388 if (optional<optional<String>> oRes =
ThrowIfFailed (ParseOneArg_ (o, &argi))) {
390 if (nMore2Skip == 0) {
393 if (not o.fRepeatable) {
403 if (o.fRequired and not found and arguments.
empty ()) {
406 if (found and o.fSupportsArgument and o.fIfSupportsArgumentThenRequired and arguments.
empty ()) {
409 return make_tuple (found, arguments);
412String CommandLine::ToString ()
const
414 return this->As<String> ();
420 Require (not argi->
Done ());
421 using OptionalArgument = optional<String>;
422 using OptionallyHasOption = optional<OptionalArgument>;
427 static const RT kMissingArgument_ = RT{OptionallyHasOption{OptionalArgument{}}};
428 static const RT kMissingOption_ = RT{OptionallyHasOption{}};
431 if (o.fSingleCharName and ai.
length () == 2 and ai[0] ==
'-' and ai[1] == o.fSingleCharName) {
432 if (o.fSupportsArgument) {
434 if ((*argi).Done ()) {
435 if (o.fIfSupportsArgumentThenRequired) {
437 "Command line argument requires an argument to it, but none provided (= or following argument)"sv, ai}};
439 return kMissingArgument_;
442 return RT{OptionallyHasOption{**argi}};
445 return kMissingArgument_;
448 String argiUpToEquals = ai;
449 if (optional<size_t> indexOfEquals = argiUpToEquals.
Find (
'=')) {
450 argiUpToEquals = argiUpToEquals.
SubString (0, *indexOfEquals);
452 if (o.fLongName and ai.
length () >= 2 + o.fLongName->size () and ai[0] ==
'-' and ai[1] ==
'-' and
455 if (o.fSupportsArgument) {
457 if (restOfArgi.
size () >= 1 and restOfArgi[0] ==
'=') {
458 return RT{OptionallyHasOption{restOfArgi.
SubString (1)}};
461 if ((*argi).Done ()) {
462 if (o.fIfSupportsArgumentThenRequired) {
464 "Command line argument requires an argument to it, but none provided (= or following argument)"sv, ai}};
466 return kMissingArgument_;
469 return RT{OptionallyHasOption{**argi}};
474 return kMissingArgument_;
479 if (o.IsPositionArgument () and not(ai.
size () >= 2 and ai.
StartsWith (
"-"sv))) {
481 return RT{OptionallyHasOption{**argi}};
483 return kMissingOption_;
487String CommandLine::As<String> ()
const
489 return As<String> (this->fShellStyleQuoting_);
493String CommandLine::As<String> (optional<CommandLine::StringShellQuoting> shellStyle)
const
498 return fArgs_.Join<
String> (
501 if (shellStyle == nullopt) {
502 if (i.ContainsAny ({
' ',
'\"'})) {
503 return "\"{}\""_f(i);
510 else if (shellStyle == StringShellQuoting::eWindowsCMD) {
512 if (i.ContainsAny ({
' ',
'\"'})) {
513 return "\"{}\""_f(i);
520 else if (shellStyle == StringShellQuoting::eBash) {
522 if (i.ContainsAny ({
' ',
'\"'})) {
523 return "\"{}\""_f(i);
#define AssertNotImplemented()
#define RequireNotReached()
#define RequireNotNull(p)
#define AssertNotReached()
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
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) 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 optional< InvalidCommandLineArgument > ValidateQuietly(const Iterable< Option > &options) const
like Validate - but return optional< InvalidCommandLineArgument> instead of throwing
nonvirtual void Validate(const 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...
EXPECTED::value_type ThrowIfFailed(const EXPECTED &e)
optional< filesystem::path > FindExecutableInPath(const filesystem::path &fn)
If fn refers to an executable - return it (using kPATH, and kPathEXT as appropriate)
constexpr bool IsPositionArgument() const
optional< String > fHelpArgName