C++ Utilities: application/argumentparser.cpp Source File

C++ Utilities 5.34.2 Useful C++ classes and routines such as argument parser, IO and conversion utilities

Loading...
Searching...
No Matches
argumentparser.cpp
Go to the documentation of this file.
1#include "./argumentparser.h"
2#include "./argumentparserprivate.h"
3#include "./commandlineutils.h"
4
5#include "../conversion/stringbuilder.h"
6#include "../conversion/stringconversion.h"
7#include "../io/ansiescapecodes.h"
8#include "../io/path.h"
9#include "../misc/levenshtein.h"
10#include "../misc/parseerror.h"
11
12#include <algorithm>
13#include <cstdlib>
14#include <cstring>
15#include <iostream>
16#include <set>
17#include <sstream>
18#include <string>
19
20#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
21#include <filesystem>
22#endif
23
24using namespace std;
25using namespace std::placeholders;
26using namespace std::literals;
27using namespace CppUtilities::EscapeCodes;
28
33namespace CppUtilities {
34
38 enum ArgumentDenotationType : unsigned char {
39 Value = 0,
40 Abbreviation = 1,
41 FullName = 2
42};
43
47 struct ArgumentCompletionInfo {
48 ArgumentCompletionInfo(const ArgumentReader &reader);
49
50 const Argument *const lastDetectedArg;
51 size_t lastDetectedArgIndex = 0;
52 vector<Argument *> lastDetectedArgPath;
53 list<const Argument *> relevantArgs;
54 list<const Argument *> relevantPreDefinedValues;
55 const char *const *lastSpecifiedArg = nullptr;
56 unsigned int lastSpecifiedArgIndex = 0;
57 bool nextArgumentOrValue = false;
58 bool completeFiles = false, completeDirs = false;
59};
60
65ArgumentCompletionInfo::ArgumentCompletionInfo(const ArgumentReader &reader)
66 : lastDetectedArg(reader.lastArg)
67{
68}
69
71struct ArgumentSuggestion {
72 ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool hasDashPrefix);
73 ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, size_t suggestionSize, bool hasDashPrefix);
74 bool operator<(const ArgumentSuggestion &other) const;
75 bool operator==(const ArgumentSuggestion &other) const;
76 void addTo(multiset<ArgumentSuggestion> &suggestions, size_t limit) const;
77
78 const char *const suggestion;
79 const size_t suggestionSize;
80 const size_t editingDistance;
81 const bool hasDashPrefix;
82};
83
84ArgumentSuggestion::ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, size_t suggestionSize, bool isOperation)
85 : suggestion(suggestion)
86 , suggestionSize(suggestionSize)
87 , editingDistance(computeDamerauLevenshteinDistance(unknownArg, unknownArgSize, suggestion, suggestionSize))
88 , hasDashPrefix(isOperation)
89{
90}
91
92ArgumentSuggestion::ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool isOperation)
93 : ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
94{
95}
96
97bool ArgumentSuggestion::operator<(const ArgumentSuggestion &other) const
98{
99 return editingDistance < other.editingDistance;
100}
101
102void ArgumentSuggestion::addTo(multiset<ArgumentSuggestion> &suggestions, size_t limit) const
103{
104 if (suggestions.size() >= limit && !(*this < *--suggestions.end())) {
105 return;
106 }
107 suggestions.emplace(*this);
108 while (suggestions.size() > limit) {
109 suggestions.erase(--suggestions.end());
110 }
111}
113
122
126ArgumentReader::ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode)
127 : parser(parser)
128 , args(parser.m_mainArgs)
129 , index(0)
130 , argv(argv)
131 , end(end)
132 , lastArg(nullptr)
133 , argDenotation(nullptr)
134 , completionMode(completionMode)
135{
136}
137
141 ArgumentReader &ArgumentReader::reset(const char *const *argv, const char *const *end)
142{
143 this->argv = argv;
144 this->end = end;
145 index = 0;
146 lastArg = nullptr;
147 argDenotation = nullptr;
148 return *this;
149}
150
155 bool ArgumentReader::read()
156{
157 return read(args);
158}
159
163bool Argument::matchesDenotation(const char *denotation, size_t denotationLength) const
164{
165 return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) == '\0';
166}
167
175 bool ArgumentReader::read(ArgumentVector &args)
176{
177 // method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
178 Argument *const parentArg = lastArg;
179 // determine the current path
180 const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>();
181
182 Argument *lastArgInLevel = nullptr;
183 vector<const char *> *values = nullptr;
184
185 // iterate through all argument denotations; loop might exit earlier when a denotation is unknown
186 while (argv != end) {
187 // check whether there are still values to read
188 if (values && ((lastArgInLevel->requiredValueCount() != Argument::varValueCount) || (lastArgInLevel->flags() & Argument::Flags::Greedy))
189 && values->size() < lastArgInLevel->requiredValueCount()) {
190 // read arg as value and continue with next arg
191 values->emplace_back(argDenotation ? argDenotation : *argv);
192 ++index;
193 ++argv;
194 argDenotation = nullptr;
195 continue;
196 }
197
198 // determine how denotation must be processed
199 bool abbreviationFound = false;
200 if (argDenotation) {
201 // continue reading children for abbreviation denotation already detected
202 abbreviationFound = false;
203 argDenotationType = Abbreviation;
204 } else {
205 // determine denotation type
206 argDenotation = *argv;
207 if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
208 // skip empty arguments
209 ++index;
210 ++argv;
211 argDenotation = nullptr;
212 continue;
213 }
214 abbreviationFound = false;
215 argDenotationType = Value;
216 if (*argDenotation == '-') {
217 ++argDenotation;
218 ++argDenotationType;
219 if (*argDenotation == '-') {
220 ++argDenotation;
221 ++argDenotationType;
222 }
223 }
224 }
225
226 // try to find matching Argument instance
227 Argument *matchingArg = nullptr;
228 if (argDenotationType != Value) {
229 // determine actual denotation length (everything before equation sign)
230 const char *const equationPos = strchr(argDenotation, '=');
231 const auto argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
232
233 // loop through each "part" of the denotation
234 // names are read at once, but for abbreviations each character is considered individually
235 for (; argDenotationLength; matchingArg = nullptr) {
236 // search for arguments by abbreviation or name depending on the previously determined denotation type
237 if (argDenotationType == Abbreviation) {
238 for (Argument *const arg : args) {
239 if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
240 matchingArg = arg;
241 abbreviationFound = true;
242 break;
243 }
244 }
245 } else {
246 for (Argument *const arg : args) {
247 if (arg->matchesDenotation(argDenotation, argDenotationLength)) {
248 matchingArg = arg;
249 break;
250 }
251 }
252 }
253 if (!matchingArg) {
254 break;
255 }
256
257 // an argument matched the specified denotation so add an occurrence
258 matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
259
260 // prepare reading parameter values
261 values = &matchingArg->m_occurrences.back().values;
262
263 // read value after equation sign
264 if ((argDenotationType != Abbreviation && equationPos) || (++argDenotation == equationPos)) {
265 values->push_back(equationPos + 1);
266 argDenotation = nullptr;
267 }
268
269 // read sub arguments, distinguish whether further abbreviations follow
270 ++index;
271 ++parser.m_actualArgc;
272 lastArg = lastArgInLevel = matchingArg;
273 lastArgDenotation = argv;
274 if (argDenotationType != Abbreviation || !argDenotation || !*argDenotation) {
275 // no further abbreviations follow -> read sub args for next argv
276 ++argv;
277 argDenotation = nullptr;
278 read(lastArg->m_subArgs);
279 argDenotation = nullptr;
280 break;
281 } else {
282 // further abbreviations follow -> remember current arg value
283 const char *const *const currentArgValue = argv;
284 // don't increment argv, keep processing outstanding chars of argDenotation
285 read(lastArg->m_subArgs);
286 // stop further processing if the denotation has been consumed or even the next value has already been loaded
287 if (!argDenotation || currentArgValue != argv) {
288 argDenotation = nullptr;
289 break;
290 }
291 }
292 }
293
294 // continue with next arg if we've got a match already
295 if (matchingArg) {
296 continue;
297 }
298
299 // unknown argument might be a sibling of the parent element
300 for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
301 for (Argument *const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
302 if (sibling->occurrences() < sibling->maxOccurrences()) {
303 // check whether the denoted abbreviation matches the sibling's abbreviatiopn
304 if (argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation)) {
305 return false;
306 }
307 // check whether the denoted name matches the sibling's name
308 if (sibling->matchesDenotation(argDenotation, argDenotationLength)) {
309 return false;
310 }
311 }
312 }
313 if (parentArgument == pathEnd) {
314 break;
315 }
316 }
317 }
318
319 // unknown argument might just be a parameter value of the last argument
320 if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
321 values->emplace_back(abbreviationFound ? argDenotation : *argv);
322 ++index;
323 ++argv;
324 argDenotation = nullptr;
325 continue;
326 }
327
328 // first value might denote "operation"
329 for (Argument *const arg : args) {
330 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
331 (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
332 lastArgDenotation = argv;
333 ++index;
334 ++argv;
335 break;
336 }
337 }
338
339 // use the first default argument which is not already present if there is still no match
340 if (!matchingArg && (!completionMode || (argv + 1 != end))) {
341 const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
342 for (Argument *const arg : args) {
343 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
344 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
345 (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
346 break;
347 }
348 }
349 }
350
351 if (matchingArg) {
352 // an argument matched the specified denotation
353 if (lastArgInLevel == matchingArg) {
354 break; // break required? -> TODO: add test for this condition
355 }
356
357 // prepare reading parameter values
358 values = &matchingArg->m_occurrences.back().values;
359
360 // read sub arguments
361 ++parser.m_actualArgc;
362 lastArg = lastArgInLevel = matchingArg;
363 argDenotation = nullptr;
364 if ((values->size() < matchingArg->requiredValueCount()) && (matchingArg->flags() & Argument::Flags::Greedy)) {
365 continue;
366 }
367 read(lastArg->m_subArgs);
368 argDenotation = nullptr;
369 continue;
370 }
371
372 // argument denotation is unknown -> handle error
373 if (parentArg) {
374 // continue with parent level
375 return false;
376 }
377 if (completionMode) {
378 // ignore unknown denotation
379 ++index;
380 ++argv;
381 argDenotation = nullptr;
382 } else {
383 switch (parser.m_unknownArgBehavior) {
384 case UnknownArgumentBehavior::Warn:
385 cerr << Phrases::Warning << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << Phrases::EndFlush;
386 [[fallthrough]];
387 case UnknownArgumentBehavior::Ignore:
388 // ignore unknown denotation
389 ++index;
390 ++argv;
391 argDenotation = nullptr;
392 break;
393 case UnknownArgumentBehavior::Fail:
394 return false;
395 }
396 }
397 } // while(argv != end)
398 return true;
399}
400
406
407ostream &operator<<(ostream &os, const Wrapper &wrapper)
408{
409 // determine max. number of columns
410 static const TerminalSize termSize(determineTerminalSize());
411 const auto maxColumns = termSize.columns ? termSize.columns : numeric_limits<unsigned short>::max();
412
413 // print wrapped string considering indentation
414 unsigned short currentCol = wrapper.m_indentation.level;
415 for (const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
416 const bool wrappingRequired = currentCol >= maxColumns;
417 if (wrappingRequired || *currentChar == '\n') {
418 // insert newline (TODO: wrap only at end of a word)
419 os << '\n';
420 // print indentation (if enough space)
421 if (wrapper.m_indentation.level < maxColumns) {
422 os << wrapper.m_indentation;
423 currentCol = wrapper.m_indentation.level;
424 } else {
425 currentCol = 0;
426 }
427 }
428 if (*currentChar != '\n' && (!wrappingRequired || *currentChar != ' ')) {
429 os << *currentChar;
430 ++currentCol;
431 }
432 }
433 return os;
434}
435
436 CPP_UTILITIES_EXPORT ApplicationInfo applicationInfo;
437
439
440inline bool notEmpty(const char *str)
441{
442 return str && *str;
443}
444
446
456
463Argument::Argument(const char *name, char abbreviation, const char *description, const char *example)
464 : m_name(name)
465 , m_abbreviation(abbreviation)
466 , m_environmentVar(nullptr)
467 , m_description(description)
468 , m_example(example)
469 , m_minOccurrences(0)
470 , m_maxOccurrences(1)
471 , m_requiredValueCount(0)
472 , m_flags(Flags::None)
473 , m_deprecatedBy(nullptr)
474 , m_isMainArg(false)
475 , m_valueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::Files | ValueCompletionBehavior::Directories
476 | ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
477 , m_preDefinedCompletionValues(nullptr)
478{
479}
480
484Argument::~Argument()
485{
486}
487
495 const char *Argument::firstValue() const
496{
497 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
498 return m_occurrences.front().values.front();
499 } else if (m_environmentVar) {
500 return getenv(m_environmentVar);
501 } else {
502 return nullptr;
503 }
504}
505
509 const char *Argument::firstValueOr(const char *fallback) const
510{
511 if (const auto *const v = firstValue()) {
512 return v;
513 } else {
514 return fallback;
515 }
516}
517
521 void Argument::printInfo(ostream &os, unsigned char indentation) const
522{
523 if (isDeprecated()) {
524 return;
525 }
526 Indentation ident(indentation);
527 os << ident;
528 EscapeCodes::setStyle(os, EscapeCodes::TextAttribute::Bold);
529 if (notEmpty(name())) {
530 if (!denotesOperation()) {
531 os << '-' << '-';
532 }
533 os << name();
534 }
535 if (notEmpty(name()) && abbreviation()) {
536 os << ',' << ' ';
537 }
538 if (abbreviation()) {
539 os << '-' << abbreviation();
540 }
541 EscapeCodes::setStyle(os);
542 if (requiredValueCount()) {
543 unsigned int valueNamesPrint = 0;
544 for (auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
545 os << ' ' << '[' << *i << ']';
546 ++valueNamesPrint;
547 }
548 if (requiredValueCount() == Argument::varValueCount) {
549 os << " ...";
550 } else {
551 for (; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
552 os << " [value " << (valueNamesPrint + 1) << ']';
553 }
554 }
555 }
556 ident.level += 2;
557 if (notEmpty(description())) {
558 os << '\n' << ident << Wrapper(description(), ident);
559 }
560 if (isRequired()) {
561 os << '\n' << ident << "particularities: mandatory";
562 if (!isMainArgument()) {
563 os << " if parent argument is present";
564 }
565 }
566 if (environmentVariable()) {
567 os << '\n' << ident << "default environment variable: " << Wrapper(environmentVariable(), ident + 30);
568 }
569 os << '\n';
570 bool hasSubArgs = false;
571 for (const auto *const arg : subArguments()) {
572 if (arg->isDeprecated()) {
573 continue;
574 }
575 hasSubArgs = true;
576 arg->printInfo(os, ident.level);
577 }
578 if (notEmpty(example())) {
579 if (ident.level == 2 && hasSubArgs) {
580 os << '\n';
581 }
582 os << ident << "example: " << Wrapper(example(), ident + 9);
583 os << '\n';
584 }
585}
586
592 Argument *firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
593{
594 for (Argument *arg : args) {
595 if (arg != except && arg->isPresent() && !arg->isCombinable()) {
596 return arg;
597 }
598 }
599 return nullptr;
600}
601
617 void Argument::setSubArguments(const ArgumentInitializerList &subArguments)
618{
619 // remove this argument from the parents list of the previous secondary arguments
620 for (Argument *const arg : m_subArgs) {
621 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
622 }
623 // clear currently assigned args before adding new ones
624 m_subArgs.clear();
625 addSubArguments(subArguments);
626}
627
643 void Argument::addSubArguments(const ArgumentInitializerList &subArguments)
644{
645 // append secondary arguments
646 const auto requiredCap = m_subArgs.size() + subArguments.size();
647 if (requiredCap < m_subArgs.capacity()) {
648 m_subArgs.reserve(requiredCap); // does insert this for us?
649 }
650 m_subArgs.insert(m_subArgs.end(), subArguments.begin(), subArguments.end());
651 // add this argument to the parents list of the assigned secondary arguments and set the parser
652 for (Argument *const arg : subArguments) {
653 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
654 arg->m_parents.push_back(this);
655 }
656 }
657}
658
666 void Argument::addSubArgument(Argument *arg)
667{
668 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
669 return;
670 }
671 m_subArgs.push_back(arg);
672 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
673 arg->m_parents.push_back(this);
674 }
675}
676
681 bool Argument::isParentPresent() const
682{
683 if (isMainArgument()) {
684 return true;
685 }
686 for (const Argument *parent : m_parents) {
687 if (parent->isPresent()) {
688 return true;
689 }
690 }
691 return false;
692}
693
702 Argument *Argument::conflictsWithArgument() const
703{
704 return isPresent() ? wouldConflictWithArgument() : nullptr;
705}
706
715 Argument *Argument::wouldConflictWithArgument() const
716{
717 if (isCombinable()) {
718 return nullptr;
719 }
720 for (Argument *parent : m_parents) {
721 for (Argument *sibling : parent->subArguments()) {
722 if (sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
723 return sibling;
724 }
725 }
726 }
727 return nullptr;
728}
729
734 Argument *Argument::specifiedOperation() const
735{
736 for (Argument *arg : m_subArgs) {
737 if (arg->denotesOperation() && arg->isPresent()) {
738 return arg;
739 }
740 }
741 return nullptr;
742}
743
748 void Argument::resetRecursively()
749{
750 for (Argument *arg : m_subArgs) {
751 arg->resetRecursively();
752 }
753 reset();
754}
755
768
772ArgumentParser::ArgumentParser()
773 : m_actualArgc(0)
774 , m_executable(nullptr)
775 , m_unknownArgBehavior(UnknownArgumentBehavior::Fail)
776 , m_defaultArg(nullptr)
777 , m_helpArg(*this)
778{
779}
780
790 void ArgumentParser::setMainArguments(const ArgumentInitializerList &mainArguments)
791{
792 if (!mainArguments.size()) {
793 m_mainArgs.clear();
794 return;
795 }
796 for (Argument *arg : mainArguments) {
797 arg->m_isMainArg = true;
798 }
799 m_mainArgs.assign(mainArguments);
800 if (m_defaultArg || (*mainArguments.begin())->requiredValueCount()) {
801 return;
802 }
803 bool subArgsRequired = false;
804 for (const Argument *subArg : (*mainArguments.begin())->subArguments()) {
805 if (subArg->isRequired()) {
806 subArgsRequired = true;
807 break;
808 }
809 }
810 if (!subArgsRequired) {
811 m_defaultArg = *mainArguments.begin();
812 }
813}
814
821 void ArgumentParser::addMainArgument(Argument *argument)
822{
823 argument->m_isMainArg = true;
824 m_mainArgs.push_back(argument);
825}
826
830 void ArgumentParser::printHelp(ostream &os) const
831{
832 EscapeCodes::setStyle(os, EscapeCodes::TextAttribute::Bold);
833 bool wroteLine = false;
834 if (applicationInfo.name && *applicationInfo.name) {
835 os << applicationInfo.name;
836 if (applicationInfo.version && *applicationInfo.version) {
837 os << ',' << ' ';
838 }
839 wroteLine = true;
840 }
841 if (applicationInfo.version && *applicationInfo.version) {
842 os << "version " << applicationInfo.version;
843 wroteLine = true;
844 }
845 if (wroteLine) {
846 os << '\n' << '\n';
847 }
848 EscapeCodes::setStyle(os);
849
850 if (applicationInfo.description && *applicationInfo.description) {
851 os << applicationInfo.description;
852 wroteLine = true;
853 }
854 if (wroteLine) {
855 os << '\n' << '\n';
856 }
857
858 if (!m_mainArgs.empty()) {
859 bool hasOperations = false, hasTopLevelOptions = false;
860 for (const Argument *const arg : m_mainArgs) {
861 if (arg->denotesOperation()) {
862 hasOperations = true;
863 } else if (strcmp(arg->name(), "help")) {
864 hasTopLevelOptions = true;
865 }
866 if (hasOperations && hasTopLevelOptions) {
867 break;
868 }
869 }
870
871 // check whether operations are available
872 if (hasOperations) {
873 // split top-level operations and other configurations
874 os << "Available operations:";
875 for (const Argument *const arg : m_mainArgs) {
876 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(), "help")) {
877 continue;
878 }
879 os << '\n';
880 arg->printInfo(os);
881 }
882 if (hasTopLevelOptions) {
883 os << "\nAvailable top-level options:";
884 for (const Argument *const arg : m_mainArgs) {
885 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(), "help")) {
886 continue;
887 }
888 os << '\n';
889 arg->printInfo(os);
890 }
891 }
892 } else {
893 // just show all args if no operations are available
894 os << "Available arguments:";
895 for (const Argument *const arg : m_mainArgs) {
896 if (arg->isDeprecated() || !strcmp(arg->name(), "help")) {
897 continue;
898 }
899 os << '\n';
900 arg->printInfo(os);
901 }
902 }
903 }
904
905 if (!applicationInfo.dependencyVersions.empty()) {
906 os << '\n';
907 auto i = applicationInfo.dependencyVersions.begin(), end = applicationInfo.dependencyVersions.end();
908 os << "Linked against: " << *i;
909 for (++i; i != end; ++i) {
910 os << ',' << ' ' << *i;
911 }
912 os << '\n';
913 }
914
915 if (applicationInfo.url && *applicationInfo.url) {
916 os << "\nProject website: " << applicationInfo.url << endl;
917 }
918}
919
936 void ArgumentParser::parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior)
937{
938 try {
939 readArgs(argc, argv);
940 if (!argc) {
941 return;
942 }
943 if (behavior & ParseArgumentBehavior::CheckConstraints) {
944 checkConstraints(m_mainArgs);
945 }
946 if (behavior & ParseArgumentBehavior::InvokeCallbacks) {
947 invokeCallbacks(m_mainArgs);
948 }
949 } catch (const ParseError &failure) {
950 if (behavior & ParseArgumentBehavior::ExitOnFailure) {
951 CMD_UTILS_START_CONSOLE;
952 cerr << failure;
953 invokeExit(EXIT_FAILURE);
954 }
955 throw;
956 }
957}
958
972 void ArgumentParser::readArgs(int argc, const char *const *argv)
973{
974 CPP_UTILITIES_IF_DEBUG_BUILD(verifyArgs(m_mainArgs);)
975 m_actualArgc = 0;
976
977 // the first argument is the executable name
978 if (!argc) {
979 m_executable = nullptr;
980 return;
981 }
982 m_executable = *argv;
983
984 // check for further arguments
985 if (!--argc) {
986 // no arguments specified -> flag default argument as present if one is assigned
987 assumeDefaultArgument();
988 return;
989 }
990
991 // check for completion mode: if first arg (after executable name) is "--bash-completion-for", bash completion for the following arguments is requested
992 const bool completionMode = !strcmp(*++argv, "--bash-completion-for");
993
994 // determine the index of the current word for completion and the number of arguments to be passed to ArgumentReader
995 unsigned int currentWordIndex = 0, argcForReader;
996 if (completionMode) {
997 // the first argument after "--bash-completion-for" is the index of the current word
998 try {
999 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
1000 if (argc) {
1001 ++argv;
1002 --argc;
1003 }
1004 } catch (const ConversionException &) {
1005 currentWordIndex = static_cast<unsigned int>(argc - 1);
1006 }
1007 argcForReader = min(static_cast<unsigned int>(argc), currentWordIndex + 1);
1008 } else {
1009 argcForReader = static_cast<unsigned int>(argc);
1010 }
1011
1012 // read specified arguments
1013 ArgumentReader reader(*this, argv, argv + argcForReader, completionMode);
1014 const bool allArgsProcessed(reader.read());
1015 m_noColorArg.apply();
1016
1017 // fail when not all arguments could be processed, except when in completion mode
1018 if (!completionMode && !allArgsProcessed) {
1019 const auto suggestions(findSuggestions(argc, argv, static_cast<unsigned int>(argc - 1), reader));
1020 throw ParseError(argsToString("The specified argument \"", *reader.argv, "\" is unknown.", suggestions));
1021 }
1022
1023 // print Bash completion and prevent the application to continue with the regular execution
1024 if (completionMode) {
1025 printBashCompletion(argc, argv, currentWordIndex, reader);
1026 invokeExit(EXIT_SUCCESS);
1027 }
1028}
1029
1034 void ArgumentParser::resetArgs()
1035{
1036 for (Argument *arg : m_mainArgs) {
1037 arg->resetRecursively();
1038 }
1039 m_actualArgc = 0;
1040}
1041
1052 void ArgumentParser::assumeDefaultArgument()
1053{
1054 if (m_defaultArg && m_defaultArg->m_occurrences.empty()) {
1055 m_defaultArg->m_occurrences.emplace_back(0);
1056 }
1057}
1058
1066 void ArgumentParser::ensureDefaultOperation()
1067{
1068 if (!m_defaultArg || m_defaultArg->isPresent() || !m_defaultArg->denotesOperation()) {
1069 return;
1070 }
1071 for (auto *const arg : m_mainArgs) {
1072 if (arg->isPresent() && (arg->denotesOperation() || arg == &m_helpArg)) {
1073 return;
1074 }
1075 }
1076 assumeDefaultArgument();
1077}
1078
1084 Argument *ArgumentParser::specifiedOperation() const
1085{
1086 for (Argument *arg : m_mainArgs) {
1087 if (arg->denotesOperation() && arg->isPresent()) {
1088 return arg;
1089 }
1090 }
1091 return nullptr;
1092}
1093
1097 bool ArgumentParser::isUncombinableMainArgPresent() const
1098{
1099 for (const Argument *arg : m_mainArgs) {
1100 if (!arg->isCombinable() && arg->isPresent()) {
1101 return true;
1102 }
1103 }
1104 return false;
1105}
1106
1107#ifdef CPP_UTILITIES_DEBUG_BUILD
1122void ArgumentParser::verifyArgs(const ArgumentVector &args)
1123{
1124 vector<const Argument *> verifiedArgs;
1125 verifiedArgs.reserve(args.size());
1126 vector<char> abbreviations;
1127 abbreviations.reserve(abbreviations.size() + args.size());
1128 vector<const char *> names;
1129 names.reserve(names.size() + args.size());
1130 bool hasImplicit = false;
1131 for (const Argument *arg : args) {
1132 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1133 verifiedArgs.push_back(arg);
1134 assert(!arg->isImplicit() || !hasImplicit);
1135 hasImplicit |= arg->isImplicit();
1136 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1137 abbreviations.push_back(arg->abbreviation());
1138 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1139 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0 || (arg->flags() & Argument::Flags::Greedy));
1140 names.emplace_back(arg->name());
1141 }
1142 for (const Argument *arg : args) {
1143 verifyArgs(arg->subArguments());
1144 }
1145}
1146#endif
1147
1155 bool compareArgs(const Argument *arg1, const Argument *arg2)
1156{
1157 if (arg1->denotesOperation() && !arg2->denotesOperation()) {
1158 return true;
1159 } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
1160 return false;
1161 } else {
1162 return strcmp(arg1->name(), arg2->name()) < 0;
1163 }
1164}
1165
1170 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
1171{
1172 bool onlyCombinable = false;
1173 for (const Argument *sibling : siblings) {
1174 if (sibling->isPresent() && !sibling->isCombinable()) {
1175 onlyCombinable = true;
1176 break;
1177 }
1178 }
1179 for (const Argument *sibling : siblings) {
1180 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1181 target.push_back(sibling);
1182 }
1183 }
1184}
1185
1189 ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1190 int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader) const
1191{
1192 ArgumentCompletionInfo completion(reader);
1193
1194 // determine last detected arg
1195 if (completion.lastDetectedArg) {
1196 completion.lastDetectedArgIndex = static_cast<size_t>(reader.lastArgDenotation - argv);
1197 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1198 }
1199
1200 // determine last arg, omitting trailing empty args
1201 if (argc) {
1202 completion.lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1203 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1204 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg == '\0';
1205 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1206 ;
1207 }
1208
1209 // just return main arguments if no args detected
1210 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1211 completion.nextArgumentOrValue = true;
1212 insertSiblings(m_mainArgs, completion.relevantArgs);
1213 completion.relevantArgs.sort(compareArgs);
1214 return completion;
1215 }
1216
1217 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1218 if (!completion.nextArgumentOrValue) {
1219 // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1220 completion.relevantArgs.push_back(completion.lastDetectedArg);
1221 completion.relevantArgs.sort(compareArgs);
1222 return completion;
1223 }
1224
1225 // define function to add parameter values of argument as possible completions
1226 const auto addValueCompletionsForArg = [&completion](const Argument *arg) {
1227 if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
1228 completion.relevantPreDefinedValues.push_back(arg);
1229 }
1230 if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues) || !arg->preDefinedCompletionValues()) {
1231 completion.completeFiles = completion.completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
1232 completion.completeDirs = completion.completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
1233 }
1234 };
1235
1236 // detect number of specified values
1237 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1238 // ignore values which are specified after the current word
1239 if (currentValueCount) {
1240 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1241 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1242 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1243 } else {
1244 currentValueCount = 0;
1245 }
1246 }
1247
1248 // add value completions for implicit child if there are no value specified and there are no values required by the
1249 // last detected argument itself
1250 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1251 for (const Argument *child : completion.lastDetectedArg->subArguments()) {
1252 if (child->isImplicit() && child->requiredValueCount()) {
1253 addValueCompletionsForArg(child);
1254 break;
1255 }
1256 }
1257 }
1258
1259 // add value completions for last argument if there are further values required
1260 if (completion.lastDetectedArg->requiredValueCount() == Argument::varValueCount
1261 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1262 addValueCompletionsForArg(completion.lastDetectedArg);
1263 }
1264
1265 if (completion.lastDetectedArg->requiredValueCount() == Argument::varValueCount
1266 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1267 >= completion.lastDetectedArg->requiredValueCount()) {
1268 // sub arguments of the last arg are possible completions
1269 for (const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1270 if (subArg->occurrences() < subArg->maxOccurrences()) {
1271 completion.relevantArgs.push_back(subArg);
1272 }
1273 }
1274
1275 // siblings of parents are possible completions as well
1276 for (auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1277 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1278 if (parentArgument == end) {
1279 break;
1280 }
1281 }
1282 }
1283
1284 return completion;
1285}
1286
1290string ArgumentParser::findSuggestions(int argc, const char *const *argv, unsigned int cursorPos, const ArgumentReader &reader) const
1291{
1292 // determine completion info
1293 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1294
1295 // determine the unknown/misspelled argument
1296 const auto *unknownArg(*reader.argv);
1297 auto unknownArgSize(strlen(unknownArg));
1298 // -> refuse suggestions for long args to prevent huge memory allocation for Damerau-Levenshtein algo
1299 if (unknownArgSize > 16) {
1300 return string();
1301 }
1302 // -> remove dashes since argument names internally don't have them
1303 if (unknownArgSize >= 2 && unknownArg[0] == '-' && unknownArg[1] == '-') {
1304 unknownArg += 2;
1305 unknownArgSize -= 2;
1306 }
1307
1308 // find best suggestions limiting the results to 2
1309 multiset<ArgumentSuggestion> bestSuggestions;
1310 // -> consider relevant arguments
1311 for (const Argument *const arg : completionInfo.relevantArgs) {
1312 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1313 }
1314 // -> consider relevant values
1315 for (const Argument *const arg : completionInfo.relevantPreDefinedValues) {
1316 if (!arg->preDefinedCompletionValues()) {
1317 continue;
1318 }
1319 for (const char *i = arg->preDefinedCompletionValues(); *i; ++i) {
1320 const char *const wordStart(i);
1321 const char *wordEnd(wordStart + 1);
1322 for (; *wordEnd && *wordEnd != ' '; ++wordEnd)
1323 ;
1324 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart, static_cast<size_t>(wordEnd - wordStart), false).addTo(bestSuggestions, 2);
1325 i = wordEnd;
1326 }
1327 }
1328
1329 // format suggestion
1330 string suggestionStr;
1331 if (const auto suggestionCount = bestSuggestions.size()) {
1332 // allocate memory
1333 size_t requiredSize = 15;
1334 for (const auto &suggestion : bestSuggestions) {
1335 requiredSize += suggestion.suggestionSize + 2;
1336 if (suggestion.hasDashPrefix) {
1337 requiredSize += 2;
1338 }
1339 }
1340 suggestionStr.reserve(requiredSize);
1341
1342 // add each suggestion to end up with something like "Did you mean status (1), pause (3), cat (4), edit (5) or rescan-all (8)?"
1343 suggestionStr += "\nDid you mean ";
1344 size_t i = 0;
1345 for (const auto &suggestion : bestSuggestions) {
1346 if (++i == suggestionCount && suggestionCount != 1) {
1347 suggestionStr += " or ";
1348 } else if (i > 1) {
1349 suggestionStr += ", ";
1350 }
1351 if (suggestion.hasDashPrefix) {
1352 suggestionStr += "--";
1353 }
1354 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1355 }
1356 suggestionStr += '?';
1357 }
1358 return suggestionStr;
1359}
1360
1364static std::string unescape(std::string_view escaped)
1365{
1366 auto unescaped = std::string();
1367 auto onEscaping = false;
1368 unescaped.reserve(escaped.size());
1369 for (const auto c : escaped) {
1370 if (!onEscaping && c == '\\') {
1371 onEscaping = true;
1372 } else {
1373 unescaped += c;
1374 onEscaping = false;
1375 }
1376 }
1377 return unescaped;
1378}
1379
1385void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader) const
1386{
1387 // determine completion info and sort relevant arguments
1388 const auto completionInfo([&] {
1389 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1390 clutteredCompletionInfo.relevantArgs.sort(compareArgs);
1391 return clutteredCompletionInfo;
1392 }());
1393
1394 // read the "opening" (started but not finished argument denotation)
1395 const char *opening = nullptr;
1396 string compoundOpening;
1397 size_t openingLen = 0, compoundOpeningStartLen = 0;
1398 unsigned char openingDenotationType = Value;
1399 if (argc && completionInfo.nextArgumentOrValue) {
1400 if (currentWordIndex < static_cast<unsigned int>(argc)) {
1401 opening = argv[currentWordIndex];
1402 // For some reason completions for eg. "set --values disk=1 tag=a" are split so the
1403 // equation sign is an own argument ("set --values disk = 1 tag = a").
1404 // This is not how values are treated by the argument parser. Hence the opening
1405 // must be joined again. In this case only the part after the equation sign needs to be
1406 // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1407 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1408 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1409 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1410 compoundOpening = argv[currentWordIndex];
1411 compoundOpening += '=';
1412 } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1413 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1414 compoundOpening = argv[currentWordIndex];
1415 compoundOpening += '=';
1416 compoundOpening += opening;
1417 }
1418 if (!compoundOpening.empty()) {
1419 opening = compoundOpening.data();
1420 }
1421 } else {
1422 opening = *completionInfo.lastSpecifiedArg;
1423 }
1424 if (*opening == '-') {
1425 ++opening;
1426 ++openingDenotationType;
1427 if (*opening == '-') {
1428 ++opening;
1429 ++openingDenotationType;
1430 }
1431 }
1432 openingLen = strlen(opening);
1433 }
1434
1435 // print "COMPREPLY" bash array
1436 cout << "COMPREPLY=(";
1437 // -> completions for parameter values
1438 bool noWhitespace = false;
1439 for (const Argument *const arg : completionInfo.relevantPreDefinedValues) {
1440 if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1441 arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1442 }
1443 if (!arg->preDefinedCompletionValues()) {
1444 continue;
1445 }
1446 const bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1447 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1448 if (openingDenotationType != Value) {
1449 continue;
1450 }
1451 bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1452 size_t wordIndex = 0;
1453 for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1454 if (wordStart) {
1455 const char *i1 = i, *i2 = opening;
1456 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1457 ;
1458 if ((ok = (i2 == end))) {
1459 cout << '\'';
1460 }
1461 wordStart = false;
1462 wordIndex = 0;
1463 } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1464 equationSignAlreadyPresent = false;
1465 if (ok) {
1466 cout << '\'' << ' ';
1467 }
1468 ++i;
1469 continue;
1470 } else if (*i == '=') {
1471 equationSignAlreadyPresent = true;
1472 }
1473 if (!ok) {
1474 ++i;
1475 continue;
1476 }
1477 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1478 if (*i == '\'') {
1479 cout << "'\"'\"'";
1480 } else {
1481 cout << *i;
1482 }
1483 }
1484 ++i;
1485 ++wordIndex;
1486 switch (*i) {
1487 case ' ':
1488 case '\n':
1489 case '\0':
1490 if (appendEquationSign && !equationSignAlreadyPresent) {
1491 cout << '=';
1492 noWhitespace = true;
1493 equationSignAlreadyPresent = false;
1494 }
1495 if (*i == '\0') {
1496 cout << '\'';
1497 }
1498 }
1499 }
1500 cout << ' ';
1501 } else if (const char *i = arg->preDefinedCompletionValues()) {
1502 bool equationSignAlreadyPresent = false;
1503 cout << '\'';
1504 while (*i) {
1505 if (*i == '\'') {
1506 cout << "'\"'\"'";
1507 } else {
1508 cout << *i;
1509 }
1510 switch (*(++i)) {
1511 case '=':
1512 equationSignAlreadyPresent = true;
1513 break;
1514 case ' ':
1515 case '\n':
1516 case '\0':
1517 if (appendEquationSign && !equationSignAlreadyPresent) {
1518 cout << '=';
1519 equationSignAlreadyPresent = false;
1520 }
1521 if (*i != '\0') {
1522 cout << '\'';
1523 if (*(++i)) {
1524 cout << ' ' << '\'';
1525 }
1526 }
1527 }
1528 }
1529 cout << '\'' << ' ';
1530 }
1531 }
1532 // -> completions for further arguments
1533 for (const Argument *const arg : completionInfo.relevantArgs) {
1534 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1535 switch (openingDenotationType) {
1536 case Value:
1537 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1538 continue;
1539 }
1540 break;
1541 case Abbreviation:
1542 break;
1543 case FullName:
1544 if (strncmp(arg->name(), opening, openingLen)) {
1545 continue;
1546 }
1547 }
1548 }
1549
1550 if (opening && openingDenotationType == Abbreviation && !completionInfo.nextArgumentOrValue) {
1551 // TODO: add test for this case
1552 cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1553 } else if (completionInfo.lastDetectedArg && reader.argDenotationType == Abbreviation && !completionInfo.nextArgumentOrValue) {
1554 if (reader.argv == reader.end) {
1555 cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1556 }
1557 } else if (arg->denotesOperation()) {
1558 cout << '\'' << arg->name() << '\'' << ' ';
1559 } else {
1560 cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1561 }
1562 }
1563 // -> completions for files and dirs
1564 // -> if there's already an "opening", determine the dir part and the file part
1565 auto actualDir = std::string(), actualFile = std::string();
1566 auto haveFileOrDirCompletions = false;
1567 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1568 // the "opening" might contain escaped characters which need to be unescaped first
1569 const auto unescapedOpening = unescape(opening);
1570 // determine the "directory" part
1571 auto dir = directory(unescapedOpening);
1572 if (dir.empty()) {
1573 actualDir = ".";
1574 } else {
1575 if (dir[0] == '\"' || dir[0] == '\'') {
1576 dir.erase(0, 1);
1577 }
1578 if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1579 dir.erase(dir.size() - 2, 1);
1580 }
1581 actualDir = std::move(dir);
1582 }
1583 // determine the "file" part
1584 auto file = fileName(unescapedOpening);
1585 if (file[0] == '\"' || file[0] == '\'') {
1586 file.erase(0, 1);
1587 }
1588 if (file.size() > 1 && (file[file.size() - 2] == '\"' || file[file.size() - 2] == '\'')) {
1589 file.erase(file.size() - 2, 1);
1590 }
1591 actualFile = std::move(file);
1592 }
1593
1594 // -> completion for files and dirs
1595#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1596 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1597 try {
1598 const auto replace = "'"s, with = "'\"'\"'"s;
1599 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1600 const auto dirEntries = [&] {
1601 filesystem::directory_iterator i;
1602 if (useActualDir) {
1603 i = filesystem::directory_iterator(actualDir);
1604 findAndReplace(actualDir, replace, with);
1605 } else {
1606 i = filesystem::directory_iterator(".");
1607 }
1608 return i;
1609 }();
1610 for (const auto &dirEntry : dirEntries) {
1611 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1612 continue;
1613 }
1614 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1615 continue;
1616 }
1617 auto dirEntryName = dirEntry.path().filename().string();
1618 auto hasStartingQuote = false;
1619 if (useActualDir) {
1620 if (!startsWith(dirEntryName, actualFile)) {
1621 continue;
1622 }
1623 cout << '\'';
1624 hasStartingQuote = true;
1625 if (actualDir != ".") {
1626 cout << actualDir;
1627 }
1628 }
1629 findAndReplace(dirEntryName, replace, with);
1630 if (!hasStartingQuote) {
1631 cout << '\'';
1632 }
1633 cout << dirEntryName << '\'' << ' ';
1634 haveFileOrDirCompletions = true;
1635 }
1636 } catch (const filesystem::filesystem_error &) {
1637 // ignore filesystem errors; there's no good way to report errors when printing bash completion
1638 }
1639 }
1640#endif
1641 cout << ')';
1642
1643 // ensure file or dir completions are formatted appropriately
1644 if (haveFileOrDirCompletions) {
1645 cout << "; compopt -o filenames";
1646 }
1647
1648 // ensure trailing whitespace is omitted
1649 if (noWhitespace) {
1650 cout << "; compopt -o nospace";
1651 }
1652
1653 cout << endl;
1654}
1655
1660void ArgumentParser::checkConstraints(const ArgumentVector &args)
1661{
1662 for (const Argument *arg : args) {
1663 const auto occurrences = arg->occurrences();
1664 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1665 throw ParseError(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1666 (arg->maxOccurrences() == 1 ? " time." : " times.")));
1667 }
1668 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1669 throw ParseError(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1670 (arg->minOccurrences() == 1 ? " time." : " times.")));
1671 }
1672 Argument *conflictingArgument = nullptr;
1673 if (arg->isMainArgument()) {
1674 if (!arg->isCombinable() && arg->isPresent()) {
1675 conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1676 }
1677 } else {
1678 conflictingArgument = arg->conflictsWithArgument();
1679 }
1680 if (conflictingArgument) {
1681 throw ParseError(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1682 }
1683 for (size_t i = 0; i != occurrences; ++i) {
1684 if (arg->allRequiredValuesPresent(i)) {
1685 continue;
1686 }
1687 stringstream ss(stringstream::in | stringstream::out);
1688 ss << "Not all parameters for argument \"" << arg->name() << "\" ";
1689 if (i) {
1690 ss << " (" << (i + 1) << " occurrence) ";
1691 }
1692 ss << "provided. You have to provide the following parameters:";
1693 size_t valueNamesPrint = 0;
1694 for (const auto &name : arg->m_valueNames) {
1695 ss << ' ' << name;
1696 ++valueNamesPrint;
1697 }
1698 if (arg->m_requiredValueCount != Argument::varValueCount) {
1699 while (valueNamesPrint < arg->m_requiredValueCount) {
1700 ss << "\nvalue " << (++valueNamesPrint);
1701 }
1702 }
1703 throw ParseError(ss.str());
1704 }
1705
1706 // check constraints of sub arguments recursively
1707 checkConstraints(arg->m_subArgs);
1708 }
1709}
1710
1718void ArgumentParser::invokeCallbacks(const ArgumentVector &args)
1719{
1720 for (const Argument *arg : args) {
1721 // invoke the callback for each occurrence of the argument
1722 if (arg->m_callbackFunction) {
1723 for (const auto &occurrence : arg->m_occurrences) {
1724 arg->m_callbackFunction(occurrence);
1725 }
1726 }
1727 // invoke the callbacks for sub arguments recursively
1728 invokeCallbacks(arg->m_subArgs);
1729 }
1730}
1731
1735void ArgumentParser::invokeExit(int code)
1736{
1737 if (m_exitFunction) {
1738 m_exitFunction(code);
1739 return;
1740 }
1741 std::exit(code);
1742}
1743
1749
1753HelpArgument::HelpArgument(ArgumentParser &parser)
1754 : Argument("help", 'h', "shows this information")
1755{
1756 setCallback([&parser](const ArgumentOccurrence &) {
1757 CMD_UTILS_START_CONSOLE;
1758 parser.printHelp(cout);
1759 });
1760}
1761
1766
1772
1791
1796NoColorArgument::NoColorArgument()
1797#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1798 : Argument("no-color", '\0', "disables formatted/colorized output")
1799#else
1800 : Argument("enable-color", '\0', "enables formatted/colorized output")
1801#endif
1802{
1803 setCombinable(true);
1804
1805 // set the environment variable (not directly used and just assigned for printing help)
1806 setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1807
1808 // initialize EscapeCodes::enabled from environment variable
1809 const auto escapeCodesEnabled = isEnvVariableSet(environmentVariable());
1810 if (escapeCodesEnabled.has_value()) {
1811 EscapeCodes::enabled = escapeCodesEnabled.value();
1812 }
1813}
1814
1818 void NoColorArgument::apply() const
1819{
1820 if (isPresent()) {
1821#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1822 EscapeCodes::enabled = false;
1823#else
1824 EscapeCodes::enabled = true;
1825#endif
1826 }
1827}
1828
1832void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(const std::vector<Argument *> &argumentPath) const
1833{
1834 throw ParseError(argumentPath.empty()
1835 ? argsToString("Conversion of top-level value \"", valueToConvert, "\" to type \"", targetTypeName, "\" failed: ", errorMessage)
1836 : argsToString("Conversion of value \"", valueToConvert, "\" (for argument --", argumentPath.back()->name(), ") to type \"",
1837 targetTypeName, "\" failed: ", errorMessage));
1838}
1839
1843void ArgumentOccurrence::throwNumberOfValuesNotSufficient(unsigned long valuesToConvert) const
1844{
1845 throw ParseError(path.empty()
1846 ? argsToString("Expected ", valuesToConvert, " top-level values to be present but only ", values.size(), " have been specified.")
1847 : argsToString("Expected ", valuesToConvert, " values for argument --", path.back()->name(), " to be present but only ", values.size(),
1848 " have been specified."));
1849}
1850
1851} // namespace CppUtilities
ansiescapecodes.h
CPP_UTILITIES_IF_DEBUG_BUILD
#define CPP_UTILITIES_IF_DEBUG_BUILD(x)
Wraps debug-only lines conveniently.
Definition global.h:118
argumentparser.h
argumentparserprivate.h
CppUtilities::ArgumentParser
The ArgumentParser class provides a means for handling command line arguments.
Definition argumentparser.h:457
CppUtilities::ArgumentParser::checkConstraints
void checkConstraints()
Checks whether constraints are violated.
Definition argumentparser.h:1161
CppUtilities::ArgumentParser::ArgumentParser
ArgumentParser()
Constructs a new ArgumentParser.
Definition argumentparser.cpp:772
CppUtilities::ArgumentParser::readArgs
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
Definition argumentparser.cpp:972
CppUtilities::ArgumentParser::mainArguments
const ArgumentVector & mainArguments() const
Returns the main arguments.
Definition argumentparser.h:1097
CppUtilities::ArgumentParser::isUncombinableMainArgPresent
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
Definition argumentparser.cpp:1097
CppUtilities::ArgumentParser::setMainArguments
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
Definition argumentparser.cpp:790
CppUtilities::ArgumentParser::printHelp
void printHelp(std::ostream &os) const
Prints help text for all assigned arguments.
Definition argumentparser.cpp:830
CppUtilities::ArgumentParser::addMainArgument
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
Definition argumentparser.cpp:821
CppUtilities::ArgumentParser::parseArgs
void parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior=ParseArgumentBehavior::CheckConstraints|ParseArgumentBehavior::InvokeCallbacks|ParseArgumentBehavior::ExitOnFailure)
Parses the specified command line arguments.
Definition argumentparser.cpp:936
CppUtilities::ArgumentParser::invokeCallbacks
void invokeCallbacks()
Invokes all assigned callbacks.
Definition argumentparser.h:1170
CppUtilities::ArgumentParser::resetArgs
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
Definition argumentparser.cpp:1034
CppUtilities::ArgumentParser::ensureDefaultOperation
void ensureDefaultOperation()
Ensures a main operation argument is present.
Definition argumentparser.cpp:1066
CppUtilities::ArgumentParser::specifiedOperation
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
Definition argumentparser.cpp:1084
CppUtilities::ArgumentParser::assumeDefaultArgument
void assumeDefaultArgument()
Assumes the default argument is present (if not already present).
Definition argumentparser.cpp:1052
CppUtilities::ArgumentReader
The ArgumentReader class internally encapsulates the process of reading command line arguments.
Definition argumentparserprivate.h:9
CppUtilities::ArgumentReader::lastArgDenotation
const char *const * lastArgDenotation
Points to the element in argv where lastArg was encountered. Unspecified if lastArg is not set.
Definition argumentparserprivate.h:29
CppUtilities::ArgumentReader::argDenotation
const char * argDenotation
The currently processed abbreviation denotation (should be substring of one of the args in argv)....
Definition argumentparserprivate.h:31
CppUtilities::ArgumentReader::ArgumentReader
ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode=false)
Initializes the internal reader for the specified parser and arguments.
Definition argumentparser.cpp:126
CppUtilities::ArgumentReader::args
ArgumentVector & args
The Argument instances to store the results. Sub arguments of args are considered as well.
Definition argumentparserprivate.h:19
CppUtilities::ArgumentReader::argv
const char *const * argv
Points to the first argument denotation and will be incremented when a denotation has been processed.
Definition argumentparserprivate.h:23
CppUtilities::ArgumentReader::lastArg
Argument * lastArg
The last Argument instance which could be detected. Set to nullptr in the initial call....
Definition argumentparserprivate.h:27
CppUtilities::ArgumentReader::completionMode
bool completionMode
Whether completion mode is enabled. In this case reading args will be continued even if an denotation...
Definition argumentparserprivate.h:35
CppUtilities::ArgumentReader::argDenotationType
unsigned char argDenotationType
The type of the currently processed abbreviation denotation. Unspecified if argDenotation is not set.
Definition argumentparserprivate.h:33
CppUtilities::ArgumentReader::index
std::size_t index
An index which is incremented when an argument is encountered (the current index is stored in the occ...
Definition argumentparserprivate.h:21
CppUtilities::ArgumentReader::reset
ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
Definition argumentparser.cpp:141
CppUtilities::ArgumentReader::parser
ArgumentParser & parser
The associated ArgumentParser instance.
Definition argumentparserprivate.h:17
CppUtilities::ArgumentReader::read
bool read()
Reads the commands line arguments specified when constructing the object.
Definition argumentparser.cpp:155
CppUtilities::ArgumentReader::end
const char *const * end
Points to the end of the argv array.
Definition argumentparserprivate.h:25
CppUtilities::Argument
The Argument class is a wrapper for command line argument information.
Definition argumentparser.h:263
CppUtilities::Argument::description
const char * description() const
Returns the description of the argument.
Definition argumentparser.h:594
CppUtilities::Argument::Argument
Argument(const char *name, char abbreviation='\0', const char *description=nullptr, const char *example=nullptr)
Constructs an Argument with the given name, abbreviation and description.
Definition argumentparser.cpp:463
CppUtilities::Argument::isParentPresent
bool isParentPresent() const
Returns whether at least one parent argument is present.
Definition argumentparser.cpp:681
CppUtilities::Argument::addSubArgument
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
Definition argumentparser.cpp:666
CppUtilities::Argument::varValueCount
static constexpr std::size_t varValueCount
Denotes a variable number of values.
Definition argumentparser.h:368
CppUtilities::Argument::path
const std::vector< Argument * > & path(std::size_t occurrence=0) const
Returns the path of the specified occurrence.
Definition argumentparser.h:826
CppUtilities::Argument::isMainArgument
bool isMainArgument() const
Returns an indication whether the argument is used as main argument.
Definition argumentparser.h:1002
CppUtilities::Argument::firstValue
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
Definition argumentparser.cpp:495
CppUtilities::Argument::subArguments
const ArgumentVector & subArguments() const
Returns the secondary arguments for this argument.
Definition argumentparser.h:965
CppUtilities::Argument::example
const char * example() const
Returns the usage example of the argument.
Definition argumentparser.h:614
CppUtilities::Argument::Flags
Flags
The Flags enum specifies options for treating the argument in a special way.
Definition argumentparser.h:272
CppUtilities::Argument::Flags::Greedy
@ Greedy
Definition argumentparser.h:279
CppUtilities::Argument::isDeprecated
bool isDeprecated() const
Definition argumentparser.h:788
CppUtilities::Argument::valueNames
const std::vector< const char * > & valueNames() const
Returns the names of the required values.
Definition argumentparser.h:683
CppUtilities::Argument::addSubArguments
void addSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this argument.
Definition argumentparser.cpp:643
CppUtilities::Argument::valueCompletionBehaviour
ValueCompletionBehavior valueCompletionBehaviour() const
Returns the items to be considered when generating completion for the values.
Definition argumentparser.h:1013
CppUtilities::Argument::abbreviation
char abbreviation() const
Returns the abbreviation of the argument.
Definition argumentparser.h:552
CppUtilities::Argument::occurrences
std::size_t occurrences() const
Returns how often the argument could be detected when parsing.
Definition argumentparser.h:755
CppUtilities::Argument::specifiedOperation
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
Definition argumentparser.cpp:734
CppUtilities::Argument::name
const char * name() const
Returns the name of the argument.
Definition argumentparser.h:522
CppUtilities::Argument::isCombinable
bool isCombinable() const
Returns an indication whether the argument is combinable.
Definition argumentparser.h:897
CppUtilities::Argument::denotesOperation
bool denotesOperation() const
Returns whether the argument denotes an operation.
Definition argumentparser.h:925
CppUtilities::Argument::resetRecursively
void resetRecursively()
Resets this argument and all sub arguments recursively.
Definition argumentparser.cpp:748
CppUtilities::Argument::maxOccurrences
std::size_t maxOccurrences() const
Returns the maximum number of occurrences.
Definition argumentparser.h:783
CppUtilities::Argument::flags
Argument::Flags flags() const
Returns Argument::Flags for the argument.
Definition argumentparser.h:866
CppUtilities::Argument::preDefinedCompletionValues
const char * preDefinedCompletionValues() const
Returns the assigned values used when generating completion for the values.
Definition argumentparser.h:1032
CppUtilities::Argument::requiredValueCount
std::size_t requiredValueCount() const
Returns the number of values which are required to be given for this argument.
Definition argumentparser.h:653
CppUtilities::Argument::environmentVariable
const char * environmentVariable() const
Returns the environment variable queried when firstValue() is called.
Definition argumentparser.h:575
CppUtilities::Argument::wouldConflictWithArgument
Argument * wouldConflictWithArgument() const
Checks if this argument would conflict with other arguments if it was present.
Definition argumentparser.cpp:715
CppUtilities::Argument::reset
void reset()
Resets occurrences (indices, values and paths).
Definition argumentparser.h:1050
CppUtilities::Argument::~Argument
~Argument()
Destroys the Argument.
Definition argumentparser.cpp:484
CppUtilities::Argument::isPresent
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
Definition argumentparser.h:747
CppUtilities::Argument::conflictsWithArgument
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
Definition argumentparser.cpp:702
CppUtilities::Argument::minOccurrences
std::size_t minOccurrences() const
Returns the minimum number of occurrences.
Definition argumentparser.h:773
CppUtilities::Argument::allRequiredValuesPresent
bool allRequiredValuesPresent(std::size_t occurrence=0) const
Returns an indication whether all required values are present.
Definition argumentparser.h:720
CppUtilities::Argument::setSubArguments
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this argument.
Definition argumentparser.cpp:617
CppUtilities::Argument::setCombinable
void setCombinable(bool combinable)
Sets whether this argument can be combined.
Definition argumentparser.h:910
CppUtilities::Argument::setCallback
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
Definition argumentparser.h:954
CppUtilities::Argument::isRequired
bool isRequired() const
Returns an indication whether the argument is mandatory.
Definition argumentparser.h:840
CppUtilities::Argument::printInfo
void printInfo(std::ostream &os, unsigned char indentation=0) const
Writes the name, the abbreviation and other information about the Argument to the give ostream.
Definition argumentparser.cpp:521
CppUtilities::Argument::setEnvironmentVariable
void setEnvironmentVariable(const char *environmentVariable)
Sets the environment variable queried when firstValue() is called.
Definition argumentparser.h:584
CppUtilities::Argument::firstValueOr
const char * firstValueOr(const char *fallback) const
Returns the first value like Argument::firstValue() but returns fallback instead of nullptr if there'...
Definition argumentparser.cpp:509
CppUtilities::ConversionException
The ConversionException class is thrown by the various conversion functions of this library when a co...
Definition conversionexception.h:11
CppUtilities::HelpArgument::HelpArgument
HelpArgument(ArgumentParser &parser)
Constructs a new help argument for the specified parser.
Definition argumentparser.cpp:1753
CppUtilities::Indentation
The Indentation class allows printing indentation conveniently, eg.
Definition commandlineutils.h:71
CppUtilities::Indentation::level
unsigned char level
Definition commandlineutils.h:84
CppUtilities::NoColorArgument::apply
void apply() const
Sets EscapeCodes::enabled according to the presence of the first instantiation of NoColorArgument.
Definition argumentparser.cpp:1818
CppUtilities::NoColorArgument::NoColorArgument
NoColorArgument()
Constructs a new NoColorArgument argument.
Definition argumentparser.cpp:1796
CppUtilities::ParseError
The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
Definition parseerror.h:11
CppUtilities::Wrapper
The Wrapper class is internally used print text which might needs to be wrapped preserving the indent...
Definition argumentparserprivate.h:42
CppUtilities::Wrapper::Wrapper
Wrapper(const char *str, Indentation currentIndentation=Indentation())
Definition argumentparserprivate.h:53
commandlineutils.h
CMD_UTILS_START_CONSOLE
#define CMD_UTILS_START_CONSOLE
Definition commandlineutils.h:35
CPP_UTILITIES_EXPORT
#define CPP_UTILITIES_EXPORT
Marks the symbol to be exported by the c++utilities library.
Definition global.h:14
levenshtein.h
CppUtilities::EscapeCodes
Encapsulates functions for formatted terminal output using ANSI escape codes.
Definition ansiescapecodes.h:12
CppUtilities::EscapeCodes::TextAttribute::Bold
@ Bold
Definition ansiescapecodes.h:22
CppUtilities::EscapeCodes::enabled
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
Definition ansiescapecodes.cpp:22
CppUtilities::EscapeCodes::setStyle
void setStyle(std::ostream &stream, TextAttribute displayAttribute=TextAttribute::Reset)
Definition ansiescapecodes.h:34
CppUtilities::EscapeCodes::Phrases::Warning
@ Warning
Definition ansiescapecodes.h:140
CppUtilities::EscapeCodes::Phrases::EndFlush
@ EndFlush
Definition ansiescapecodes.h:147
CppUtilities
Contains all utilities provided by the c++utilities library.
Definition argumentparser.h:18
CppUtilities::findAndReplace
void findAndReplace(StringType1 &str, const StringType2 &find, const StringType3 &replace)
Replaces all occurrences of find with relpace in the specified str.
Definition stringconversion.h:380
CppUtilities::determineTerminalSize
CPP_UTILITIES_EXPORT TerminalSize determineTerminalSize()
Returns the current size of the terminal.
Definition commandlineutils.cpp:75
CppUtilities::ArgumentInitializerList
std::initializer_list< Argument * > ArgumentInitializerList
Definition argumentparser.h:68
CppUtilities::firstPresentUncombinableArg
Argument * firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
This function return the first present and uncombinable argument of the given list of arguments.
Definition argumentparser.cpp:592
CppUtilities::ArgumentVector
std::vector< Argument * > ArgumentVector
Definition argumentparser.h:69
CppUtilities::operator<<
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
Definition commandlineutils.h:88
CppUtilities::UnknownArgumentBehavior
UnknownArgumentBehavior
The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown argume...
Definition argumentparser.h:76
CppUtilities::UnknownArgumentBehavior::Warn
@ Warn
Definition argumentparser.h:78
CppUtilities::UnknownArgumentBehavior::Fail
@ Fail
Definition argumentparser.h:79
CppUtilities::UnknownArgumentBehavior::Ignore
@ Ignore
Definition argumentparser.h:77
CppUtilities::startsWith
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
Definition stringconversion.h:274
CppUtilities::stringToNumber
IntegralType stringToNumber(const StringType &string, BaseType base=10)
Converts the given string to an unsigned/signed number assuming string uses the specified base.
Definition stringconversion.h:605
CppUtilities::ParseArgumentBehavior
ParseArgumentBehavior
The ParseArgumentBehavior enum specifies the behavior when parsing arguments.
Definition argumentparser.h:88
CppUtilities::ParseArgumentBehavior::CheckConstraints
@ CheckConstraints
Definition argumentparser.h:90
CppUtilities::ParseArgumentBehavior::ExitOnFailure
@ ExitOnFailure
Definition argumentparser.h:92
CppUtilities::ParseArgumentBehavior::InvokeCallbacks
@ InvokeCallbacks
Definition argumentparser.h:91
CppUtilities::operator==
bool operator==(const AsHexNumber< T > &lhs, const AsHexNumber< T > &rhs)
Provides operator == required by CPPUNIT_ASSERT_EQUAL.
Definition testutils.h:241
CppUtilities::argsToString
StringType argsToString(Args &&...args)
Definition stringbuilder.h:260
CppUtilities::ValueCompletionBehavior
ValueCompletionBehavior
The ValueCompletionBehavior enum specifies the items to be considered when generating completion for ...
Definition argumentparser.h:117
CppUtilities::ValueCompletionBehavior::AppendEquationSign
@ AppendEquationSign
Definition argumentparser.h:123
CppUtilities::ValueCompletionBehavior::None
@ None
Definition argumentparser.h:118
CppUtilities::ValueCompletionBehavior::PreDefinedValues
@ PreDefinedValues
Definition argumentparser.h:119
CppUtilities::ValueCompletionBehavior::Files
@ Files
Definition argumentparser.h:120
CppUtilities::ValueCompletionBehavior::FileSystemIfNoPreDefinedValues
@ FileSystemIfNoPreDefinedValues
Definition argumentparser.h:122
CppUtilities::ValueCompletionBehavior::InvokeCallback
@ InvokeCallback
Definition argumentparser.h:124
CppUtilities::ValueCompletionBehavior::Directories
@ Directories
Definition argumentparser.h:121
CppUtilities::ArgumentDenotationType
ArgumentDenotationType
The ArgumentDenotationType enum specifies the type of a given argument denotation.
Definition argumentparser.cpp:38
CppUtilities::FullName
@ FullName
Definition argumentparser.cpp:41
CppUtilities::Value
@ Value
Definition argumentparser.cpp:39
CppUtilities::Abbreviation
@ Abbreviation
Definition argumentparser.cpp:40
CppUtilities::isEnvVariableSet
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.
Definition commandlineutils.cpp:53
CppUtilities::min
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition math.h:84
CppUtilities::insertSiblings
void insertSiblings(const ArgumentVector &siblings, list< const Argument * > &target)
Inserts the specified siblings in the target list.
Definition argumentparser.cpp:1170
CppUtilities::compareArgs
bool compareArgs(const Argument *arg1, const Argument *arg2)
Returns whether arg1 should be listed before arg2 when printing completion.
Definition argumentparser.cpp:1155
CppUtilities::computeDamerauLevenshteinDistance
CPP_UTILITIES_EXPORT std::size_t computeDamerauLevenshteinDistance(const char *str1, std::size_t size1, const char *str2, std::size_t size2)
CppUtilities::applicationInfo
CPP_UTILITIES_EXPORT ApplicationInfo applicationInfo
Stores global application info used by ArgumentParser::printHelp() and AboutDialog.
Definition argumentparser.cpp:436
CppUtilities::fileName
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
Definition path.cpp:15
CppUtilities::directory
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
Definition path.cpp:23
std
STL namespace.
Fail
#define Fail(msg)
parseerror.h
path.h
stringbuilder.h
stringconversion.h
CppUtilities::ApplicationInfo
Stores information about an application.
Definition argumentparser.h:23
CppUtilities::ArgumentCompletionInfo
The ArgumentCompletionInfo struct holds information internally used for shell completion and suggesti...
Definition argumentparser.cpp:47
CppUtilities::ArgumentCompletionInfo::relevantArgs
list< const Argument * > relevantArgs
Definition argumentparser.cpp:53
CppUtilities::ArgumentCompletionInfo::lastDetectedArgIndex
size_t lastDetectedArgIndex
Definition argumentparser.cpp:51
CppUtilities::ArgumentCompletionInfo::nextArgumentOrValue
bool nextArgumentOrValue
Definition argumentparser.cpp:57
CppUtilities::ArgumentCompletionInfo::ArgumentCompletionInfo
ArgumentCompletionInfo(const ArgumentReader &reader)
Constructs a new completion info for the specified reader.
Definition argumentparser.cpp:65
CppUtilities::ArgumentCompletionInfo::lastSpecifiedArg
const char *const * lastSpecifiedArg
Definition argumentparser.cpp:55
CppUtilities::ArgumentCompletionInfo::lastDetectedArg
const Argument *const lastDetectedArg
Definition argumentparser.cpp:50
CppUtilities::ArgumentCompletionInfo::lastDetectedArgPath
vector< Argument * > lastDetectedArgPath
Definition argumentparser.cpp:52
CppUtilities::ArgumentCompletionInfo::lastSpecifiedArgIndex
unsigned int lastSpecifiedArgIndex
Definition argumentparser.cpp:56
CppUtilities::ArgumentCompletionInfo::relevantPreDefinedValues
list< const Argument * > relevantPreDefinedValues
Definition argumentparser.cpp:54
CppUtilities::ArgumentCompletionInfo::completeFiles
bool completeFiles
Definition argumentparser.cpp:58
CppUtilities::ArgumentCompletionInfo::completeDirs
bool completeDirs
Definition argumentparser.cpp:58
CppUtilities::ArgumentOccurrence
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
Definition argumentparser.h:194
CppUtilities::ArgumentOccurrence::values
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.
Definition argumentparser.h:206
CppUtilities::ArgumentOccurrence::path
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).
Definition argumentparser.h:212
CppUtilities::TerminalSize
The TerminalSize struct describes a terminal size.
Definition commandlineutils.h:45
CppUtilities::TerminalSize::columns
unsigned short columns
number of columns
Definition commandlineutils.h:51
i
constexpr int i
Definition traitstests.cpp:106