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;
25InvalidCommandLineArgument::InvalidCommandLineArgument ()
29InvalidCommandLineArgument::InvalidCommandLineArgument (
const String& message)
34InvalidCommandLineArgument::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";
330 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
331 if (not all.First ([&] (
Option o) {
332 if (optional<pair<bool, optional<String>>> oRes = ParseOneArg_ (o, &argi)) {
341 if (
auto o = unused.First ([] (Option o) { return o.fRequired; })) {
342 return InvalidCommandLineArgument{
"Required command line argument "sv + o->GetArgumentDescription () +
" was not provided"sv};
347String CommandLine::GetAppName (
bool onlyBaseName)
const
349 if (fArgs_.empty ()) {
353 filesystem::path p = fArgs_[0].As<filesystem::path> ();
359tuple<bool, Sequence<String>> CommandLine::Get (
const Option& o)
const
363 for (
Iterator<String> argi = fArgs_.begin () + 1; argi != fArgs_.end (); ++argi) {
364 if (optional<pair<
bool, optional<String>>> oRes = ParseOneArg_ (o, &argi)) {
369 arguments += *oRes->second;
371 if (not o.fRepeatable) {
376 if (o.fRequired and not found and arguments.
empty ()) {
379 if (found and o.fSupportsArgument and o.fIfSupportsArgumentThenRequired and arguments.
empty ()) {
382 return make_tuple (found, arguments);
385String CommandLine::ToString ()
const
387 return this->As<String> ();
390optional<pair<bool, optional<String>>> CommandLine::ParseOneArg_ (
const Option& o,
Iterator<String>* argi)
393 Require (not argi->
Done ());
396 if (o.fSingleCharName and ai.
length () == 2 and ai[0] ==
'-' and ai[1] == o.fSingleCharName) {
397 if (o.fSupportsArgument) {
399 if ((*argi).Done ()) {
400 if (o.fIfSupportsArgumentThenRequired) {
403 return make_pair (
true, nullopt);
406 return make_pair (
true, **argi);
409 return make_pair (
true, nullopt);
414 if (o.fLongName and ai.
length () >= 2 + o.fLongName->size () and ai[0] ==
'-' and ai[1] ==
'-' and
416 if (o.fSupportsArgument) {
419 if (restOfArgi.
size () >= 1 and restOfArgi[0] ==
'=') {
420 return make_pair (
true, restOfArgi.
SubString (1));
424 if ((*argi).Done ()) {
425 if (o.fIfSupportsArgumentThenRequired) {
427 "Command line argument requires an argument to it, but none provided (= or following argument)"sv, ai});
429 return make_pair (
true, nullopt);
432 return make_pair (
true, **argi);
436 return make_pair (
true, nullopt);
439 if (not o.fSingleCharName and not o.fLongName and o.fSupportsArgument and not(ai.
size () >= 2 and ai.
StartsWith (
"-"sv))) {
441 return make_pair (
false, **argi);
447String CommandLine::As<String> ()
const
449 return As<String> (this->fShellStyleQuoting_);
453String CommandLine::As<String> (optional<CommandLine::StringShellQuoting> shellStyle)
const
458 return fArgs_.Join<
String> (
461 if (shellStyle == nullopt) {
462 if (i.ContainsAny ({
' ',
'\"'})) {
463 return "\"{}\""_f(i);
470 else if (shellStyle == StringShellQuoting::eWindowsCMD) {
472 if (i.ContainsAny ({
' ',
'\"'})) {
473 return "\"{}\""_f(i);
480 else if (shellStyle == StringShellQuoting::eBash) {
482 if (i.ContainsAny ({
' ',
'\"'})) {
483 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
nonvirtual optional< InvalidCommandLineArgument > ValidateQuietly(Iterable< Option > options) const
like Validate - but return optional< InvalidCommandLineArgument> instead of throwing
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