C++ Utilities  5.0.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
argumentparser.cpp
Go to the documentation of this file.
1 #include "./argumentparser.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 
24 using namespace std;
25 using namespace std::placeholders;
26 using namespace std::literals;
27 using namespace CppUtilities::EscapeCodes;
28 
33 namespace CppUtilities {
34 
38 enum ArgumentDenotationType : unsigned char {
39  Value = 0,
41  FullName = 2
42 };
43 
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 
65 ArgumentCompletionInfo::ArgumentCompletionInfo(const ArgumentReader &reader)
66  : lastDetectedArg(reader.lastArg)
67 {
68 }
69 
71  ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool hasDashPrefix);
72  ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, size_t suggestionSize, bool hasDashPrefix);
73  bool operator<(const ArgumentSuggestion &other) const;
74  bool operator==(const ArgumentSuggestion &other) const;
75  void addTo(multiset<ArgumentSuggestion> &suggestions, size_t limit) const;
76 
77  const char *const suggestion;
78  const size_t suggestionSize;
79  const size_t editingDistance;
80  const bool hasDashPrefix;
81 };
82 
83 ArgumentSuggestion::ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, size_t suggestionSize, bool isOperation)
84  : suggestion(suggestion)
85  , suggestionSize(suggestionSize)
86  , editingDistance(computeDamerauLevenshteinDistance(unknownArg, unknownArgSize, suggestion, suggestionSize))
87  , hasDashPrefix(isOperation)
88 {
89 }
90 
91 ArgumentSuggestion::ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool isOperation)
92  : ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
93 {
94 }
95 
97 {
98  return editingDistance < other.editingDistance;
99 }
100 
101 void ArgumentSuggestion::addTo(multiset<ArgumentSuggestion> &suggestions, size_t limit) const
102 {
103  if (suggestions.size() >= limit && !(*this < *--suggestions.end())) {
104  return;
105  }
106  suggestions.emplace(*this);
107  while (suggestions.size() > limit) {
108  suggestions.erase(--suggestions.end());
109  }
110 }
111 
124 ArgumentReader::ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode)
125  : parser(parser)
126  , args(parser.m_mainArgs)
127  , index(0)
128  , argv(argv)
129  , end(end)
130  , lastArg(nullptr)
131  , argDenotation(nullptr)
132  , completionMode(completionMode)
133 {
134 }
135 
139 ArgumentReader &ArgumentReader::reset(const char *const *argv, const char *const *end)
140 {
141  this->argv = argv;
142  this->end = end;
143  index = 0;
144  lastArg = nullptr;
145  argDenotation = nullptr;
146  return *this;
147 }
148 
154 {
155  return read(args);
156 }
157 
161 bool Argument::matchesDenotation(const char *denotation, size_t denotationLength) const
162 {
163  return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) == '\0';
164 }
165 
174 {
175  // method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
176  Argument *const parentArg = lastArg;
177  // determine the current path
178  const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>();
179 
180  Argument *lastArgInLevel = nullptr;
181  vector<const char *> *values = nullptr;
182 
183  // iterate through all argument denotations; loop might exit earlier when an denotation is unknown
184  while (argv != end) {
185  // check whether there are still values to read
186  if (values && lastArgInLevel->requiredValueCount() != Argument::varValueCount && values->size() < lastArgInLevel->requiredValueCount()) {
187  // read arg as value and continue with next arg
188  values->emplace_back(argDenotation ? argDenotation : *argv);
189  ++index;
190  ++argv;
191  argDenotation = nullptr;
192  continue;
193  }
194 
195  // determine how denotation must be processed
196  bool abbreviationFound = false;
197  if (argDenotation) {
198  // continue reading childs for abbreviation denotation already detected
199  abbreviationFound = false;
201  } else {
202  // determine denotation type
203  argDenotation = *argv;
204  if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
205  // skip empty arguments
206  ++index;
207  ++argv;
208  argDenotation = nullptr;
209  continue;
210  }
211  abbreviationFound = false;
213  if (*argDenotation == '-') {
214  ++argDenotation;
216  if (*argDenotation == '-') {
217  ++argDenotation;
219  }
220  }
221  }
222 
223  // try to find matching Argument instance
224  Argument *matchingArg = nullptr;
225  if (argDenotationType != Value) {
226  // determine actual denotation length (everything before equation sign)
227  const char *const equationPos = strchr(argDenotation, '=');
228  const auto argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
229 
230  // loop through each "part" of the denotation
231  // names are read at once, but for abbreviations each character is considered individually
232  for (; argDenotationLength; matchingArg = nullptr) {
233  // search for arguments by abbreviation or name depending on the previously determined denotation type
235  for (Argument *const arg : args) {
236  if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
237  matchingArg = arg;
238  abbreviationFound = true;
239  break;
240  }
241  }
242  } else {
243  for (Argument *const arg : args) {
244  if (arg->matchesDenotation(argDenotation, argDenotationLength)) {
245  matchingArg = arg;
246  break;
247  }
248  }
249  }
250  if (!matchingArg) {
251  break;
252  }
253 
254  // an argument matched the specified denotation so add an occurrence
255  matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
256 
257  // prepare reading parameter values
258  values = &matchingArg->m_occurrences.back().values;
259 
260  // read value after equation sign
261  if ((argDenotationType != Abbreviation && equationPos) || (++argDenotation == equationPos)) {
262  values->push_back(equationPos + 1);
263  argDenotation = nullptr;
264  }
265 
266  // read sub arguments, distinguish whether further abbreviations follow
267  ++index;
268  ++parser.m_actualArgc;
269  lastArg = lastArgInLevel = matchingArg;
272  // no further abbreviations follow -> read sub args for next argv
273  ++argv;
274  argDenotation = nullptr;
275  read(lastArg->m_subArgs);
276  argDenotation = nullptr;
277  break;
278  } else {
279  // further abbreviations follow -> remember current arg value
280  const char *const *const currentArgValue = argv;
281  // don't increment argv, keep processing outstanding chars of argDenotation
282  read(lastArg->m_subArgs);
283  // stop further processing if the denotation has been consumed or even the next value has already been loaded
284  if (!argDenotation || currentArgValue != argv) {
285  argDenotation = nullptr;
286  break;
287  }
288  }
289  }
290 
291  // continue with next arg if we've got a match already
292  if (matchingArg) {
293  continue;
294  }
295 
296  // unknown argument might be a sibling of the parent element
297  for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
298  for (Argument *const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
299  if (sibling->occurrences() < sibling->maxOccurrences()) {
300  // check whether the denoted abbreviation matches the sibling's abbreviatiopn
301  if (argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation)) {
302  return false;
303  }
304  // check whether the denoted name matches the sibling's name
305  if (sibling->matchesDenotation(argDenotation, argDenotationLength)) {
306  return false;
307  }
308  }
309  }
310  if (parentArgument == pathEnd) {
311  break;
312  }
313  }
314  }
315 
316  // unknown argument might just be a parameter value of the last argument
317  if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
318  values->emplace_back(abbreviationFound ? argDenotation : *argv);
319  ++index;
320  ++argv;
321  argDenotation = nullptr;
322  continue;
323  }
324 
325  // first value might denote "operation"
326  for (Argument *const arg : args) {
327  if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
328  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
330  ++index;
331  ++argv;
332  break;
333  }
334  }
335 
336  // use the first default argument which is not already present if there is still no match
337  if (!matchingArg && (!completionMode || (argv + 1 != end))) {
338  const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
339  for (Argument *const arg : args) {
340  if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
341  && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
342  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
343  break;
344  }
345  }
346  }
347 
348  if (matchingArg) {
349  // an argument matched the specified denotation
350  if (lastArgInLevel == matchingArg) {
351  break; // break required? -> TODO: add test for this condition
352  }
353 
354  // prepare reading parameter values
355  values = &matchingArg->m_occurrences.back().values;
356 
357  // read sub arguments
358  ++parser.m_actualArgc;
359  lastArg = lastArgInLevel = matchingArg;
360  argDenotation = nullptr;
361  read(lastArg->m_subArgs);
362  argDenotation = nullptr;
363  continue;
364  }
365 
366  // argument denotation is unknown -> handle error
367  if (parentArg) {
368  // continue with parent level
369  return false;
370  }
371  if (completionMode) {
372  // ignore unknown denotation
373  ++index;
374  ++argv;
375  argDenotation = nullptr;
376  } else {
377  switch (parser.m_unknownArgBehavior) {
379  cerr << Phrases::Warning << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << Phrases::EndFlush;
380  [[fallthrough]];
382  // ignore unknown denotation
383  ++index;
384  ++argv;
385  argDenotation = nullptr;
386  break;
388  return false;
389  }
390  }
391  } // while(argv != end)
392  return true;
393 }
394 
401 ostream &operator<<(ostream &os, const Wrapper &wrapper)
402 {
403  // determine max. number of columns
404  static const TerminalSize termSize(determineTerminalSize());
405  const auto maxColumns = termSize.columns ? termSize.columns : numeric_limits<unsigned short>::max();
406 
407  // print wrapped string considering indentation
408  unsigned short currentCol = wrapper.m_indentation.level;
409  for (const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
410  const bool wrappingRequired = currentCol >= maxColumns;
411  if (wrappingRequired || *currentChar == '\n') {
412  // insert newline (TODO: wrap only at end of a word)
413  os << '\n';
414  // print indentation (if enough space)
415  if (wrapper.m_indentation.level < maxColumns) {
416  os << wrapper.m_indentation;
417  currentCol = wrapper.m_indentation.level;
418  } else {
419  currentCol = 0;
420  }
421  }
422  if (*currentChar != '\n' && (!wrappingRequired || *currentChar != ' ')) {
423  os << *currentChar;
424  ++currentCol;
425  }
426  }
427  return os;
428 }
429 
431 
433 
434 inline bool notEmpty(const char *str)
435 {
436  return str && *str;
437 }
438 
440 
457 Argument::Argument(const char *name, char abbreviation, const char *description, const char *example)
458  : m_name(name)
459  , m_abbreviation(abbreviation)
460  , m_environmentVar(nullptr)
461  , m_description(description)
462  , m_example(example)
463  , m_minOccurrences(0)
464  , m_maxOccurrences(1)
465  , m_requiredValueCount(0)
466  , m_flags(Flags::None)
467  , m_deprecatedBy(nullptr)
468  , m_isMainArg(false)
471  , m_preDefinedCompletionValues(nullptr)
472 {
473 }
474 
479 {
480 }
481 
489 const char *Argument::firstValue() const
490 {
491  if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
492  return m_occurrences.front().values.front();
493  } else if (m_environmentVar) {
494  return getenv(m_environmentVar);
495  } else {
496  return nullptr;
497  }
498 }
499 
503 void Argument::printInfo(ostream &os, unsigned char indentation) const
504 {
505  if (isDeprecated()) {
506  return;
507  }
508  Indentation ident(indentation);
509  os << ident;
511  if (notEmpty(name())) {
512  if (!denotesOperation()) {
513  os << '-' << '-';
514  }
515  os << name();
516  }
517  if (notEmpty(name()) && abbreviation()) {
518  os << ',' << ' ';
519  }
520  if (abbreviation()) {
521  os << '-' << abbreviation();
522  }
524  if (requiredValueCount()) {
525  unsigned int valueNamesPrint = 0;
526  for (auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
527  os << ' ' << '[' << *i << ']';
528  ++valueNamesPrint;
529  }
531  os << " ...";
532  } else {
533  for (; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
534  os << " [value " << (valueNamesPrint + 1) << ']';
535  }
536  }
537  }
538  ident.level += 2;
539  if (notEmpty(description())) {
540  os << '\n' << ident << Wrapper(description(), ident);
541  }
542  if (isRequired()) {
543  os << '\n' << ident << "particularities: mandatory";
544  if (!isMainArgument()) {
545  os << " if parent argument is present";
546  }
547  }
548  if (environmentVariable()) {
549  os << '\n' << ident << "default environment variable: " << Wrapper(environmentVariable(), ident + 30);
550  }
551  os << '\n';
552  bool hasSubArgs = false;
553  for (const auto *const arg : subArguments()) {
554  if (arg->isDeprecated()) {
555  continue;
556  }
557  hasSubArgs = true;
558  arg->printInfo(os, ident.level);
559  }
560  if (notEmpty(example())) {
561  if (ident.level == 2 && hasSubArgs) {
562  os << '\n';
563  }
564  os << ident << "example: " << Wrapper(example(), ident + 9);
565  os << '\n';
566  }
567 }
568 
575 {
576  for (Argument *arg : args) {
577  if (arg != except && arg->isPresent() && !arg->isCombinable()) {
578  return arg;
579  }
580  }
581  return nullptr;
582 }
583 
598 void Argument::setSubArguments(const ArgumentInitializerList &secondaryArguments)
599 {
600  // remove this argument from the parents list of the previous secondary arguments
601  for (Argument *arg : m_subArgs) {
602  arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
603  }
604  // assign secondary arguments
605  m_subArgs.assign(secondaryArguments);
606  // add this argument to the parents list of the assigned secondary arguments
607  // and set the parser
608  for (Argument *arg : m_subArgs) {
609  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
610  arg->m_parents.push_back(this);
611  }
612  }
613 }
614 
623 {
624  if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
625  return;
626  }
627  m_subArgs.push_back(arg);
628  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
629  arg->m_parents.push_back(this);
630  }
631 }
632 
638 {
639  if (isMainArgument()) {
640  return true;
641  }
642  for (const Argument *parent : m_parents) {
643  if (parent->isPresent()) {
644  return true;
645  }
646  }
647  return false;
648 }
649 
659 {
660  return isPresent() ? wouldConflictWithArgument() : nullptr;
661 }
662 
672 {
673  if (isCombinable()) {
674  return nullptr;
675  }
676  for (Argument *parent : m_parents) {
677  for (Argument *sibling : parent->subArguments()) {
678  if (sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
679  return sibling;
680  }
681  }
682  }
683  return nullptr;
684 }
685 
691 {
692  for (Argument *arg : m_subArgs) {
693  if (arg->denotesOperation() && arg->isPresent()) {
694  return arg;
695  }
696  }
697  return nullptr;
698 }
699 
705 {
706  for (Argument *arg : m_subArgs) {
707  arg->resetRecursively();
708  }
709  reset();
710 }
711 
729  : m_actualArgc(0)
730  , m_executable(nullptr)
731  , m_unknownArgBehavior(UnknownArgumentBehavior::Fail)
732  , m_defaultArg(nullptr)
733  , m_helpArg(*this)
734 {
735 }
736 
747 {
748  if (!mainArguments.size()) {
749  m_mainArgs.clear();
750  return;
751  }
752  for (Argument *arg : mainArguments) {
753  arg->m_isMainArg = true;
754  }
755  m_mainArgs.assign(mainArguments);
756  if (m_defaultArg || (*mainArguments.begin())->requiredValueCount()) {
757  return;
758  }
759  bool subArgsRequired = false;
760  for (const Argument *subArg : (*mainArguments.begin())->subArguments()) {
761  if (subArg->isRequired()) {
762  subArgsRequired = true;
763  break;
764  }
765  }
766  if (!subArgsRequired) {
767  m_defaultArg = *mainArguments.begin();
768  }
769 }
770 
778 {
779  argument->m_isMainArg = true;
780  m_mainArgs.push_back(argument);
781 }
782 
786 void ArgumentParser::printHelp(ostream &os) const
787 {
789  bool wroteLine = false;
791  os << applicationInfo.name;
793  os << ',' << ' ';
794  }
795  wroteLine = true;
796  }
798  os << "version " << applicationInfo.version;
799  wroteLine = true;
800  }
801  if (wroteLine) {
802  os << '\n' << '\n';
803  }
805 
808  wroteLine = true;
809  }
810  if (wroteLine) {
811  os << '\n' << '\n';
812  }
813 
814  if (!m_mainArgs.empty()) {
815  bool hasOperations = false;
816  for (const Argument *const arg : m_mainArgs) {
817  if (arg->denotesOperation()) {
818  hasOperations = true;
819  break;
820  }
821  }
822 
823  // check whether operations are available
824  if (hasOperations) {
825  // split top-level operations and other configurations
826  os << "Available operations:";
827  for (const Argument *const arg : m_mainArgs) {
828  if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(), "help")) {
829  continue;
830  }
831  os << '\n';
832  arg->printInfo(os);
833  }
834  os << "\nAvailable top-level options:";
835  for (const Argument *const arg : m_mainArgs) {
836  if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(), "help")) {
837  continue;
838  }
839  os << '\n';
840  arg->printInfo(os);
841  }
842  } else {
843  // just show all args if no operations are available
844  os << "Available arguments:";
845  for (const Argument *const arg : m_mainArgs) {
846  if (arg->isDeprecated() || !strcmp(arg->name(), "help")) {
847  continue;
848  }
849  os << '\n';
850  arg->printInfo(os);
851  }
852  }
853  }
854 
855  if (!applicationInfo.dependencyVersions.empty()) {
856  os << '\n';
858  os << "Linked against: " << *i;
859  for (++i; i != end; ++i) {
860  os << ',' << ' ' << *i;
861  }
862  os << '\n';
863  }
864 
866  os << "\nProject website: " << applicationInfo.url << endl;
867  }
868 }
869 
886 void ArgumentParser::parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior)
887 {
888  try {
889  readArgs(argc, argv);
890  if (!argc) {
891  return;
892  }
894  checkConstraints(m_mainArgs);
895  }
897  invokeCallbacks(m_mainArgs);
898  }
899  } catch (const ParseError &failure) {
900  if (behavior & ParseArgumentBehavior::ExitOnFailure) {
902  cerr << failure;
903  invokeExit(1);
904  }
905  throw;
906  }
907 }
908 
922 void ArgumentParser::readArgs(int argc, const char *const *argv)
923 {
924  CPP_UTILITIES_IF_DEBUG_BUILD(verifyArgs(m_mainArgs);)
925  m_actualArgc = 0;
926 
927  // the first argument is the executable name
928  if (!argc) {
929  m_executable = nullptr;
930  return;
931  }
932  m_executable = *argv;
933 
934  // check for further arguments
935  if (!--argc) {
936  // no arguments specified -> flag default argument as present if one is assigned
937  if (m_defaultArg) {
938  m_defaultArg->m_occurrences.emplace_back(0);
939  }
940  return;
941  }
942 
943  // check for completion mode: if first arg (after executable name) is "--bash-completion-for", bash completion for the following arguments is requested
944  const bool completionMode = !strcmp(*++argv, "--bash-completion-for");
945 
946  // determine the index of the current word for completion and the number of arguments to be passed to ArgumentReader
947  unsigned int currentWordIndex = 0, argcForReader;
948  if (completionMode) {
949  // the first argument after "--bash-completion-for" is the index of the current word
950  try {
951  currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
952  if (argc) {
953  ++argv;
954  --argc;
955  }
956  } catch (const ConversionException &) {
957  currentWordIndex = static_cast<unsigned int>(argc - 1);
958  }
959  argcForReader = min(static_cast<unsigned int>(argc), currentWordIndex + 1);
960  } else {
961  argcForReader = static_cast<unsigned int>(argc);
962  }
963 
964  // read specified arguments
965  ArgumentReader reader(*this, argv, argv + argcForReader, completionMode);
966  const bool allArgsProcessed(reader.read());
967  m_noColorArg.apply();
968 
969  // fail when not all arguments could be processed, except when in completion mode
970  if (!completionMode && !allArgsProcessed) {
971  const auto suggestions(findSuggestions(argc, argv, static_cast<unsigned int>(argc - 1), reader));
972  throw ParseError(argsToString("The specified argument \"", *reader.argv, "\" is unknown.", suggestions));
973  }
974 
975  // print Bash completion and prevent the applicaton to continue with the regular execution
976  if (completionMode) {
977  printBashCompletion(argc, argv, currentWordIndex, reader);
978  invokeExit(0);
979  }
980 }
981 
987 {
988  for (Argument *arg : m_mainArgs) {
989  arg->resetRecursively();
990  }
991  m_actualArgc = 0;
992 }
993 
1000 {
1001  for (Argument *arg : m_mainArgs) {
1002  if (arg->denotesOperation() && arg->isPresent()) {
1003  return arg;
1004  }
1005  }
1006  return nullptr;
1007 }
1008 
1013 {
1014  for (const Argument *arg : m_mainArgs) {
1015  if (!arg->isCombinable() && arg->isPresent()) {
1016  return true;
1017  }
1018  }
1019  return false;
1020 }
1021 
1022 #ifdef CPP_UTILITIES_DEBUG_BUILD
1023 
1037 void ArgumentParser::verifyArgs(const ArgumentVector &args)
1038 {
1039  vector<const Argument *> verifiedArgs;
1040  verifiedArgs.reserve(args.size());
1041  vector<char> abbreviations;
1042  abbreviations.reserve(abbreviations.size() + args.size());
1043  vector<const char *> names;
1044  names.reserve(names.size() + args.size());
1045  bool hasImplicit = false;
1046  for (const Argument *arg : args) {
1047  assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1048  verifiedArgs.push_back(arg);
1049  assert(!arg->isImplicit() || !hasImplicit);
1050  hasImplicit |= arg->isImplicit();
1051  assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1052  abbreviations.push_back(arg->abbreviation());
1053  assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1054  assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
1055  names.emplace_back(arg->name());
1056  }
1057  for (const Argument *arg : args) {
1058  verifyArgs(arg->subArguments());
1059  }
1060 }
1061 #endif
1062 
1070 bool compareArgs(const Argument *arg1, const Argument *arg2)
1071 {
1072  if (arg1->denotesOperation() && !arg2->denotesOperation()) {
1073  return true;
1074  } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
1075  return false;
1076  } else {
1077  return strcmp(arg1->name(), arg2->name()) < 0;
1078  }
1079 }
1080 
1085 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
1086 {
1087  bool onlyCombinable = false;
1088  for (const Argument *sibling : siblings) {
1089  if (sibling->isPresent() && !sibling->isCombinable()) {
1090  onlyCombinable = true;
1091  break;
1092  }
1093  }
1094  for (const Argument *sibling : siblings) {
1095  if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1096  target.push_back(sibling);
1097  }
1098  }
1099 }
1100 
1104 ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1105  int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader) const
1106 {
1107  ArgumentCompletionInfo completion(reader);
1108 
1109  // determine last detected arg
1110  if (completion.lastDetectedArg) {
1111  completion.lastDetectedArgIndex = static_cast<size_t>(reader.lastArgDenotation - argv);
1112  completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1113  }
1114 
1115  // determine last arg, omitting trailing empty args
1116  if (argc) {
1117  completion.lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1118  completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1119  for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg == '\0';
1120  --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1121  ;
1122  }
1123 
1124  // just return main arguments if no args detected
1125  if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1126  completion.nextArgumentOrValue = true;
1127  insertSiblings(m_mainArgs, completion.relevantArgs);
1128  completion.relevantArgs.sort(compareArgs);
1129  return completion;
1130  }
1131 
1132  completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1133  if (!completion.nextArgumentOrValue) {
1134  // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1135  completion.relevantArgs.push_back(completion.lastDetectedArg);
1136  completion.relevantArgs.sort(compareArgs);
1137  return completion;
1138  }
1139 
1140  // define function to add parameter values of argument as possible completions
1141  const auto addValueCompletionsForArg = [&completion](const Argument *arg) {
1142  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
1143  completion.relevantPreDefinedValues.push_back(arg);
1144  }
1145  if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues) || !arg->preDefinedCompletionValues()) {
1146  completion.completeFiles = completion.completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
1147  completion.completeDirs = completion.completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
1148  }
1149  };
1150 
1151  // detect number of specified values
1152  auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1153  // ignore values which are specified after the current word
1154  if (currentValueCount) {
1155  const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1156  if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1157  currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1158  } else {
1159  currentValueCount = 0;
1160  }
1161  }
1162 
1163  // add value completions for implicit child if there are no value specified and there are no values required by the
1164  // last detected argument itself
1165  if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1166  for (const Argument *child : completion.lastDetectedArg->subArguments()) {
1167  if (child->isImplicit() && child->requiredValueCount()) {
1168  addValueCompletionsForArg(child);
1169  break;
1170  }
1171  }
1172  }
1173 
1174  // add value completions for last argument if there are further values required
1175  if (completion.lastDetectedArg->requiredValueCount() == Argument::varValueCount
1176  || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1177  addValueCompletionsForArg(completion.lastDetectedArg);
1178  }
1179 
1180  if (completion.lastDetectedArg->requiredValueCount() == Argument::varValueCount
1181  || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1182  >= completion.lastDetectedArg->requiredValueCount()) {
1183  // sub arguments of the last arg are possible completions
1184  for (const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1185  if (subArg->occurrences() < subArg->maxOccurrences()) {
1186  completion.relevantArgs.push_back(subArg);
1187  }
1188  }
1189 
1190  // siblings of parents are possible completions as well
1191  for (auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1192  insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1193  if (parentArgument == end) {
1194  break;
1195  }
1196  }
1197  }
1198 
1199  return completion;
1200 }
1201 
1205 string ArgumentParser::findSuggestions(int argc, const char *const *argv, unsigned int cursorPos, const ArgumentReader &reader) const
1206 {
1207  // determine completion info
1208  const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1209 
1210  // determine the unknown/misspelled argument
1211  const auto *unknownArg(*reader.argv);
1212  auto unknownArgSize(strlen(unknownArg));
1213  // -> refuse suggestions for long args to prevent huge memory allocation for Damerau-Levenshtein algo
1214  if (unknownArgSize > 16) {
1215  return string();
1216  }
1217  // -> remove dashes since argument names internally don't have them
1218  if (unknownArgSize >= 2 && unknownArg[0] == '-' && unknownArg[1] == '-') {
1219  unknownArg += 2;
1220  unknownArgSize -= 2;
1221  }
1222 
1223  // find best suggestions limiting the results to 2
1224  multiset<ArgumentSuggestion> bestSuggestions;
1225  // -> consider relevant arguments
1226  for (const Argument *const arg : completionInfo.relevantArgs) {
1227  ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1228  }
1229  // -> consider relevant values
1230  for (const Argument *const arg : completionInfo.relevantPreDefinedValues) {
1231  if (!arg->preDefinedCompletionValues()) {
1232  continue;
1233  }
1234  for (const char *i = arg->preDefinedCompletionValues(); *i; ++i) {
1235  const char *const wordStart(i);
1236  const char *wordEnd(wordStart + 1);
1237  for (; *wordEnd && *wordEnd != ' '; ++wordEnd)
1238  ;
1239  ArgumentSuggestion(unknownArg, unknownArgSize, wordStart, static_cast<size_t>(wordEnd - wordStart), false).addTo(bestSuggestions, 2);
1240  i = wordEnd;
1241  }
1242  }
1243 
1244  // format suggestion
1245  string suggestionStr;
1246  if (const auto suggestionCount = bestSuggestions.size()) {
1247  // allocate memory
1248  size_t requiredSize = 15;
1249  for (const auto &suggestion : bestSuggestions) {
1250  requiredSize += suggestion.suggestionSize + 2;
1251  if (suggestion.hasDashPrefix) {
1252  requiredSize += 2;
1253  }
1254  }
1255  suggestionStr.reserve(requiredSize);
1256 
1257  // add each suggestion to end up with something like "Did you mean status (1), pause (3), cat (4), edit (5) or rescan-all (8)?"
1258  suggestionStr += "\nDid you mean ";
1259  size_t i = 0;
1260  for (const auto &suggestion : bestSuggestions) {
1261  if (++i == suggestionCount && suggestionCount != 1) {
1262  suggestionStr += " or ";
1263  } else if (i > 1) {
1264  suggestionStr += ", ";
1265  }
1266  if (suggestion.hasDashPrefix) {
1267  suggestionStr += "--";
1268  }
1269  suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1270  }
1271  suggestionStr += '?';
1272  }
1273  return suggestionStr;
1274 }
1275 
1281 void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader) const
1282 {
1283  // determine completion info and sort relevant arguments
1284  const auto completionInfo([&] {
1285  auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1286  clutteredCompletionInfo.relevantArgs.sort(compareArgs);
1287  return clutteredCompletionInfo;
1288  }());
1289 
1290  // read the "opening" (started but not finished argument denotation)
1291  const char *opening = nullptr;
1292  string compoundOpening;
1293  size_t openingLen = 0, compoundOpeningStartLen = 0;
1294  unsigned char openingDenotationType = Value;
1295  if (argc && completionInfo.nextArgumentOrValue) {
1296  if (currentWordIndex < static_cast<unsigned int>(argc)) {
1297  opening = argv[currentWordIndex];
1298  // For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
1299  // equation sign is an own argument ("set --values disk = 1 tag = a").
1300  // This is not how values are treated by the argument parser. Hence the opening
1301  // must be joined again. In this case only the part after the equation sign needs to be
1302  // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1303  const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1304  if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1305  compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1306  compoundOpening = argv[currentWordIndex];
1307  compoundOpening += '=';
1308  } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1309  compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1310  compoundOpening = argv[currentWordIndex];
1311  compoundOpening += '=';
1312  compoundOpening += opening;
1313  }
1314  if (!compoundOpening.empty()) {
1315  opening = compoundOpening.data();
1316  }
1317  } else {
1318  opening = *completionInfo.lastSpecifiedArg;
1319  }
1320  if (*opening == '-') {
1321  ++opening;
1322  ++openingDenotationType;
1323  if (*opening == '-') {
1324  ++opening;
1325  ++openingDenotationType;
1326  }
1327  }
1328  openingLen = strlen(opening);
1329  }
1330 
1331  // print "COMPREPLY" bash array
1332  cout << "COMPREPLY=(";
1333  // -> completions for parameter values
1334  bool noWhitespace = false;
1335  for (const Argument *const arg : completionInfo.relevantPreDefinedValues) {
1336  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1337  arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1338  }
1339  if (!arg->preDefinedCompletionValues()) {
1340  continue;
1341  }
1342  const bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1343  if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1344  if (openingDenotationType != Value) {
1345  continue;
1346  }
1347  bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1348  size_t wordIndex = 0;
1349  for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1350  if (wordStart) {
1351  const char *i1 = i, *i2 = opening;
1352  for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1353  ;
1354  if ((ok = (i2 == end))) {
1355  cout << '\'';
1356  }
1357  wordStart = false;
1358  wordIndex = 0;
1359  } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1360  equationSignAlreadyPresent = false;
1361  if (ok) {
1362  cout << '\'' << ' ';
1363  }
1364  ++i;
1365  continue;
1366  } else if (*i == '=') {
1367  equationSignAlreadyPresent = true;
1368  }
1369  if (!ok) {
1370  ++i;
1371  continue;
1372  }
1373  if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1374  if (*i == '\'') {
1375  cout << "'\"'\"'";
1376  } else {
1377  cout << *i;
1378  }
1379  }
1380  ++i;
1381  ++wordIndex;
1382  switch (*i) {
1383  case ' ':
1384  case '\n':
1385  case '\0':
1386  if (appendEquationSign && !equationSignAlreadyPresent) {
1387  cout << '=';
1388  noWhitespace = true;
1389  equationSignAlreadyPresent = false;
1390  }
1391  if (*i == '\0') {
1392  cout << '\'';
1393  }
1394  }
1395  }
1396  cout << ' ';
1397  } else if (const char *i = arg->preDefinedCompletionValues()) {
1398  bool equationSignAlreadyPresent = false;
1399  cout << '\'';
1400  while (*i) {
1401  if (*i == '\'') {
1402  cout << "'\"'\"'";
1403  } else {
1404  cout << *i;
1405  }
1406  switch (*(++i)) {
1407  case '=':
1408  equationSignAlreadyPresent = true;
1409  break;
1410  case ' ':
1411  case '\n':
1412  case '\0':
1413  if (appendEquationSign && !equationSignAlreadyPresent) {
1414  cout << '=';
1415  equationSignAlreadyPresent = false;
1416  }
1417  if (*i != '\0') {
1418  cout << '\'';
1419  if (*(++i)) {
1420  cout << ' ' << '\'';
1421  }
1422  }
1423  }
1424  }
1425  cout << '\'' << ' ';
1426  }
1427  }
1428  // -> completions for further arguments
1429  for (const Argument *const arg : completionInfo.relevantArgs) {
1430  if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1431  switch (openingDenotationType) {
1432  case Value:
1433  if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1434  continue;
1435  }
1436  break;
1437  case Abbreviation:
1438  break;
1439  case FullName:
1440  if (strncmp(arg->name(), opening, openingLen)) {
1441  continue;
1442  }
1443  }
1444  }
1445 
1446  if (opening && openingDenotationType == Abbreviation && !completionInfo.nextArgumentOrValue) {
1447  // TODO: add test for this case
1448  cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1449  } else if (completionInfo.lastDetectedArg && reader.argDenotationType == Abbreviation && !completionInfo.nextArgumentOrValue) {
1450  if (reader.argv == reader.end) {
1451  cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1452  }
1453  } else if (arg->denotesOperation()) {
1454  cout << '\'' << arg->name() << '\'' << ' ';
1455  } else {
1456  cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1457  }
1458  }
1459  // -> completions for files and dirs
1460  // -> if there's already an "opening", determine the dir part and the file part
1461  string actualDir, actualFile;
1462  bool haveFileOrDirCompletions = false;
1463  if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1464  // the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
1465  string unescapedOpening(opening);
1466  findAndReplace<string>(unescapedOpening, "\\ ", " ");
1467  findAndReplace<string>(unescapedOpening, "\\,", ",");
1468  findAndReplace<string>(unescapedOpening, "\\[", "[");
1469  findAndReplace<string>(unescapedOpening, "\\]", "]");
1470  findAndReplace<string>(unescapedOpening, "\\!", "!");
1471  findAndReplace<string>(unescapedOpening, "\\#", "#");
1472  findAndReplace<string>(unescapedOpening, "\\$", "$");
1473  findAndReplace<string>(unescapedOpening, "\\'", "'");
1474  findAndReplace<string>(unescapedOpening, "\\\"", "\"");
1475  findAndReplace<string>(unescapedOpening, "\\\\", "\\");
1476  // determine the "directory" part
1477  string dir = directory(unescapedOpening);
1478  if (dir.empty()) {
1479  actualDir = ".";
1480  } else {
1481  if (dir[0] == '\"' || dir[0] == '\'') {
1482  dir.erase(0, 1);
1483  }
1484  if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1485  dir.erase(dir.size() - 2, 1);
1486  }
1487  actualDir = move(dir);
1488  }
1489  // determine the "file" part
1490  string file = fileName(unescapedOpening);
1491  if (file[0] == '\"' || file[0] == '\'') {
1492  file.erase(0, 1);
1493  }
1494  if (file.size() > 1 && (file[dir.size() - 2] == '\"' || dir[file.size() - 2] == '\'')) {
1495  file.erase(file.size() - 2, 1);
1496  }
1497  actualFile = move(file);
1498  }
1499 
1500  // -> completion for files and dirs
1501 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1502  if (completionInfo.completeFiles || completionInfo.completeDirs) {
1503  using namespace std::filesystem;
1504  const auto replace = "'"s, with = "'\"'\"'"s;
1505  const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1506  const auto dirEntries = [&] {
1507  directory_iterator i;
1508  if (useActualDir) {
1509  i = directory_iterator(actualDir);
1510  findAndReplace(actualDir, replace, with);
1511  } else {
1512  i = directory_iterator(".");
1513  }
1514  return i;
1515  }();
1516  for (const auto &dirEntry : dirEntries) {
1517  if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1518  continue;
1519  }
1520  if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1521  continue;
1522  }
1523  auto dirEntryName = dirEntry.path().filename().string();
1524  if (useActualDir) {
1525  if (!startsWith(dirEntryName, actualFile)) {
1526  continue;
1527  }
1528  cout << '\'';
1529  if (actualDir != ".") {
1530  cout << actualDir;
1531  }
1532  }
1533  findAndReplace(dirEntryName, replace, with);
1534  cout << dirEntryName << '\'' << ' ';
1535  haveFileOrDirCompletions = true;
1536  }
1537  }
1538 #endif
1539  cout << ')';
1540 
1541  // ensure file or dir completions are formatted appropriately
1542  if (haveFileOrDirCompletions) {
1543  cout << "; compopt -o filenames";
1544  }
1545 
1546  // ensure trailing whitespace is ommitted
1547  if (noWhitespace) {
1548  cout << "; compopt -o nospace";
1549  }
1550 
1551  cout << endl;
1552 }
1553 
1559 {
1560  for (const Argument *arg : args) {
1561  const auto occurrences = arg->occurrences();
1562  if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1563  throw ParseError(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1564  (arg->maxOccurrences() == 1 ? " time." : " times.")));
1565  }
1566  if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1567  throw ParseError(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1568  (arg->minOccurrences() == 1 ? " time." : " times.")));
1569  }
1570  Argument *conflictingArgument = nullptr;
1571  if (arg->isMainArgument()) {
1572  if (!arg->isCombinable() && arg->isPresent()) {
1573  conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1574  }
1575  } else {
1576  conflictingArgument = arg->conflictsWithArgument();
1577  }
1578  if (conflictingArgument) {
1579  throw ParseError(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1580  }
1581  for (size_t i = 0; i != occurrences; ++i) {
1582  if (arg->allRequiredValuesPresent(i)) {
1583  continue;
1584  }
1585  stringstream ss(stringstream::in | stringstream::out);
1586  ss << "Not all parameter for argument \"" << arg->name() << "\" ";
1587  if (i) {
1588  ss << " (" << (i + 1) << " occurrence) ";
1589  }
1590  ss << "provided. You have to provide the following parameter:";
1591  size_t valueNamesPrint = 0;
1592  for (const auto &name : arg->m_valueNames) {
1593  ss << ' ' << name;
1594  ++valueNamesPrint;
1595  }
1596  if (arg->m_requiredValueCount != Argument::varValueCount) {
1597  while (valueNamesPrint < arg->m_requiredValueCount) {
1598  ss << "\nvalue " << (++valueNamesPrint);
1599  }
1600  }
1601  throw ParseError(ss.str());
1602  }
1603 
1604  // check contraints of sub arguments recursively
1605  checkConstraints(arg->m_subArgs);
1606  }
1607 }
1608 
1617 {
1618  for (const Argument *arg : args) {
1619  // invoke the callback for each occurrence of the argument
1620  if (arg->m_callbackFunction) {
1621  for (const auto &occurrence : arg->m_occurrences) {
1622  arg->m_callbackFunction(occurrence);
1623  }
1624  }
1625  // invoke the callbacks for sub arguments recursively
1626  invokeCallbacks(arg->m_subArgs);
1627  }
1628 }
1629 
1633 void ArgumentParser::invokeExit(int code)
1634 {
1635  if (m_exitFunction) {
1636  m_exitFunction(code);
1637  return;
1638  }
1639  std::exit(code);
1640 }
1641 
1652  : Argument("help", 'h', "shows this information")
1653 {
1654  setCallback([&parser](const ArgumentOccurrence &) {
1656  parser.printHelp(cout);
1657  });
1658 }
1659 
1695 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1696  : Argument("no-color", '\0', "disables formatted/colorized output")
1697 #else
1698  : Argument("enable-color", '\0', "enables formatted/colorized output")
1699 #endif
1700 {
1701  setCombinable(true);
1702 
1703  // set the environmentvariable: note that this is not directly used and just assigned for printing help
1704  setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1705 
1706  // default-initialize EscapeCodes::enabled from environment variable
1707  const char *envValue = getenv(environmentVariable());
1708  if (!envValue) {
1709  return;
1710  }
1711  for (; *envValue; ++envValue) {
1712  switch (*envValue) {
1713  case '0':
1714  case ' ':
1715  break;
1716  default:
1717  // enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
1718  EscapeCodes::enabled = true;
1719  return;
1720  }
1721  }
1722  // disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
1723  EscapeCodes::enabled = false;
1724 }
1725 
1730 {
1731  if (isPresent()) {
1732 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1733  EscapeCodes::enabled = false;
1734 #else
1735  EscapeCodes::enabled = true;
1736 #endif
1737  }
1738 }
1739 
1743 void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(const std::vector<Argument *> &argumentPath) const
1744 {
1745  throw ParseError(argumentPath.empty()
1746  ? argsToString("Conversion of top-level value \"", valueToConvert, "\" to type \"", targetTypeName, "\" failed: ", errorMessage)
1747  : argsToString("Conversion of value \"", valueToConvert, "\" (for argument --", argumentPath.back()->name(), ") to type \"",
1748  targetTypeName, "\" failed: ", errorMessage));
1749 }
1750 
1754 void ArgumentOccurrence::throwNumberOfValuesNotSufficient(unsigned long valuesToConvert) const
1755 {
1756  throw ParseError(path.empty()
1757  ? argsToString("Expected ", valuesToConvert, " top-level values to be present but only ", values.size(), " have been specified.")
1758  : argsToString("Expected ", valuesToConvert, " values for argument --", path.back()->name(), " to be present but only ", values.size(),
1759  " have been specified."));
1760 }
1761 
1762 } // namespace CppUtilities
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:457
CppUtilities::Argument::setSubArguments
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
Definition: argumentparser.cpp:598
CppUtilities::Argument::varValueCount
static constexpr std::size_t varValueCount
Denotes a variable number of values.
Definition: argumentparser.h:361
CppUtilities::EscapeCodes::TextAttribute::Bold
Argument
The Argument class is a wrapper for command line argument information.
CppUtilities::ApplicationInfo::dependencyVersions
std::vector< const char * > dependencyVersions
Definition: argumentparser.h:31
CppUtilities::Argument
Definition: argumentparser.h:262
CppUtilities::FullName
Definition: argumentparser.cpp:41
CppUtilities::ArgumentCompletionInfo
The ArgumentCompletionInfo struct holds information internally used for shell completion and suggesti...
Definition: argumentparser.cpp:47
CppUtilities::ArgumentReader::read
bool read()
Reads the commands line arguments specified when constructing the object.
Definition: argumentparser.cpp:153
CppUtilities::ArgumentParser::checkConstraints
void checkConstraints()
Checks whether contraints are violated.
Definition: argumentparser.h:1152
CppUtilities::Indentation
The Indentation class allows printing indentation conveniently, eg.
Definition: commandlineutils.h:66
CppUtilities::Argument::isParentPresent
bool isParentPresent() const
Returns whether at least one parent argument is present.
Definition: argumentparser.cpp:637
CppUtilities::ParseError
Definition: parseerror.h:11
CppUtilities::ValueCompletionBehavior
ValueCompletionBehavior
The ValueCompletionBehavior enum specifies the items to be considered when generating completion for ...
Definition: argumentparser.h:116
CppUtilities::ArgumentParser::resetArgs
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
Definition: argumentparser.cpp:986
CppUtilities::startsWith
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
Definition: stringconversion.h:210
CppUtilities::Argument::name
const char * name() const
Returns the name of the argument.
Definition: argumentparser.h:513
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::ValueCompletionBehavior::None
CppUtilities::applicationInfo
CPP_UTILITIES_EXPORT ApplicationInfo applicationInfo
Stores global application info used by ArgumentParser::printHelp() and AboutDialog.
Definition: argumentparser.cpp:430
CppUtilities::ApplicationInfo
Stores information about an application.
Definition: argumentparser.h:22
CppUtilities::Argument::occurrences
std::size_t occurrences() const
Returns how often the argument could be detected when parsing.
Definition: argumentparser.h:746
CppUtilities::ArgumentCompletionInfo::lastDetectedArgPath
vector< Argument * > lastDetectedArgPath
Definition: argumentparser.cpp:52
CppUtilities::HelpArgument::HelpArgument
HelpArgument(ArgumentParser &parser)
Constructs a new help argument for the specified parser.
Definition: argumentparser.cpp:1651
CppUtilities::Argument::conflictsWithArgument
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
Definition: argumentparser.cpp:658
CppUtilities::Argument::environmentVariable
const char * environmentVariable() const
Returns the environment variable queried when firstValue() is called.
Definition: argumentparser.h:566
CppUtilities::ArgumentParser::setMainArguments
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
Definition: argumentparser.cpp:746
CppUtilities::ParseArgumentBehavior::ExitOnFailure
CppUtilities::Abbreviation
Definition: argumentparser.cpp:40
CppUtilities::ArgumentDenotationType
ArgumentDenotationType
The ArgumentDenotationType enum specifies the type of a given argument denotation.
Definition: argumentparser.cpp:38
CppUtilities::ArgumentParser::mainArguments
const ArgumentVector & mainArguments() const
Returns the main arguments.
Definition: argumentparser.h:1088
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:886
CppUtilities::Argument::isDeprecated
bool isDeprecated() const
Definition: argumentparser.h:779
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:503
CppUtilities::Indentation::level
unsigned char level
Definition: commandlineutils.h:79
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:574
CppUtilities::ArgumentCompletionInfo::lastDetectedArg
const Argument *const lastDetectedArg
Definition: argumentparser.cpp:50
CppUtilities::ArgumentSuggestion::suggestion
const char *const suggestion
Definition: argumentparser.cpp:77
CppUtilities::Argument::example
const char * example() const
Returns the usage example of the argument.
Definition: argumentparser.h:605
CppUtilities::Argument::subArguments
const ArgumentVector & subArguments() const
Returns the secondary arguments for this argument.
Definition: argumentparser.h:956
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:945
CppUtilities::TerminalSize::columns
unsigned short columns
number of columns
Definition: commandlineutils.h:46
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::Argument::requiredValueCount
std::size_t requiredValueCount() const
Returns the number of values which are required to be given for this argument.
Definition: argumentparser.h:644
CppUtilities::UnknownArgumentBehavior::Warn
CppUtilities::TerminalSize
The TerminalSize struct describes a terminal size.
Definition: commandlineutils.h:40
CppUtilities::ArgumentSuggestion::addTo
void addTo(multiset< ArgumentSuggestion > &suggestions, size_t limit) const
Definition: argumentparser.cpp:101
CppUtilities::operator<<
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
Definition: commandlineutils.h:83
CppUtilities::ValueCompletionBehavior::PreDefinedValues
CppUtilities::ArgumentSuggestion::ArgumentSuggestion
ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool hasDashPrefix)
Definition: argumentparser.cpp:91
CppUtilities::NoColorArgument::apply
void apply() const
Sets EscapeCodes::enabled according to the presense of the first instantiation of NoColorArgument.
Definition: argumentparser.cpp:1729
CppUtilities::max
constexpr T max(T first, T second)
Returns the greatest of the given items.
Definition: math.h:100
CppUtilities::insertSiblings
void insertSiblings(const ArgumentVector &siblings, list< const Argument * > &target)
Inserts the specified siblings in the target list.
Definition: argumentparser.cpp:1085
CppUtilities::ArgumentInitializerList
std::initializer_list< Argument * > ArgumentInitializerList
Definition: argumentparser.h:67
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:690
CppUtilities::ArgumentReader::reset
ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
Definition: argumentparser.cpp:139
CppUtilities::ArgumentParser::invokeCallbacks
void invokeCallbacks()
Invokes all assigned callbacks.
Definition: argumentparser.h:1161
CppUtilities::Argument::isRequired
bool isRequired() const
Returns an indication whether the argument is mandatory.
Definition: argumentparser.h:831
CppUtilities::argsToString
StringType argsToString(Args &&... args)
Definition: stringbuilder.h:147
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:35
CppUtilities::ValueCompletionBehavior::InvokeCallback
CppUtilities::Argument::firstValue
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
Definition: argumentparser.cpp:489
CppUtilities::ParseArgumentBehavior
ParseArgumentBehavior
The ParseArgumentBehavior enum specifies the behavior when parsing arguments.
Definition: argumentparser.h:87
CppUtilities::Argument::addSubArgument
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
Definition: argumentparser.cpp:622
CppUtilities::Argument::description
const char * description() const
Returns the description of the argument.
Definition: argumentparser.h:585
CppUtilities::findAndReplace
void findAndReplace(StringType &str, const StringType &find, const StringType &replace)
Replaces all occurences of find with relpace in the specified str.
Definition: stringconversion.h:276
CppUtilities::ArgumentSuggestion::operator==
bool operator==(const ArgumentSuggestion &other) const
CppUtilities::NoColorArgument::NoColorArgument
NoColorArgument()
Constructs a new NoColorArgument argument.
Definition: argumentparser.cpp:1694
CppUtilities::ApplicationInfo::url
const char * url
Definition: argumentparser.h:26
CppUtilities::ArgumentReader::parser
ArgumentParser & parser
The associated ArgumentParser instance.
Definition: argumentparserprivate.h:17
CppUtilities
Contains all utilities provides by the c++utilities library.
Definition: argumentparser.h:17
i
constexpr int i
Definition: traitstests.cpp:101
CppUtilities::ApplicationInfo::version
const char * version
Definition: argumentparser.h:25
CppUtilities::ArgumentSuggestion
Definition: argumentparser.cpp:70
CppUtilities::ArgumentParser::addMainArgument
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
Definition: argumentparser.cpp:777
CppUtilities::EscapeCodes
Encapsulates functions for formatted terminal output using ANSI escape codes.
Definition: ansiescapecodes.h:11
CppUtilities::Argument::isPresent
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
Definition: argumentparser.h:738
CppUtilities::ArgumentSuggestion::hasDashPrefix
const bool hasDashPrefix
Definition: argumentparser.cpp:80
CppUtilities::ArgumentReader::end
const char *const * end
Points to the end of the argv array.
Definition: argumentparserprivate.h:25
CppUtilities::UnknownArgumentBehavior
UnknownArgumentBehavior
The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown argume...
Definition: argumentparser.h:75
CppUtilities::ValueCompletionBehavior::AppendEquationSign
CppUtilities::Value
Definition: argumentparser.cpp:39
CppUtilities::Argument::reset
void reset()
Resets occurrences (indices, values and paths).
Definition: argumentparser.h:1041
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::description
const char * description
Definition: argumentparser.h:28
CppUtilities::Argument::isCombinable
bool isCombinable() const
Returns an indication whether the argument is combinable.
Definition: argumentparser.h:888
CppUtilities::compareArgs
bool compareArgs(const Argument *arg1, const Argument *arg2)
Returns whether arg1 should be listed before arg2 when printing completion.
Definition: argumentparser.cpp:1070
CppUtilities::ArgumentReader
The ArgumentReader class internally encapsulates the process of reading command line arguments.
Definition: argumentparserprivate.h:9
CppUtilities::Argument::path
const std::vector< Argument * > & path(std::size_t occurrence=0) const
Returns the path of the specified occurrence.
Definition: argumentparser.h:817
CppUtilities::ConversionException
Definition: conversionexception.h:11
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::ArgumentParser::isUncombinableMainArgPresent
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
Definition: argumentparser.cpp:1012
CppUtilities::Argument::Flags
Flags
Definition: argumentparser.h:270
CppUtilities::UnknownArgumentBehavior::Fail
CppUtilities::min
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition: math.h:88
CppUtilities::ArgumentOccurrence::values
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.
Definition: argumentparser.h:205
argumentparser.h
CppUtilities::ArgumentParser::printHelp
void printHelp(std::ostream &os) const
Prints help text for all assigned arguments.
Definition: argumentparser.cpp:786
CppUtilities::ArgumentSuggestion::suggestionSize
const size_t suggestionSize
Definition: argumentparser.cpp:78
CppUtilities::Argument::valueNames
const std::vector< const char * > & valueNames() const
Returns the names of the requried values.
Definition: argumentparser.h:674
CppUtilities::Wrapper
The Wrapper class is internally used print text which might needs to be wrapped preserving the indent...
Definition: argumentparserprivate.h:42
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::ValueCompletionBehavior::FileSystemIfNoPreDefinedValues
CppUtilities::Argument::resetRecursively
void resetRecursively()
Resets this argument and all sub arguments recursively.
Definition: argumentparser.cpp:704
CppUtilities::Argument::abbreviation
char abbreviation() const
Returns the abbreviation of the argument.
Definition: argumentparser.h:543
CppUtilities::ArgumentSuggestion::editingDistance
const size_t editingDistance
Definition: argumentparser.cpp:79
CppUtilities::ArgumentParser::readArgs
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
Definition: argumentparser.cpp:922
CppUtilities::Argument::wouldConflictWithArgument
Argument * wouldConflictWithArgument() const
Checks if this argument would conflict with other arguments if it was present.
Definition: argumentparser.cpp:671
CMD_UTILS_START_CONSOLE
#define CMD_UTILS_START_CONSOLE
Definition: commandlineutils.h:31
CppUtilities::ArgumentCompletionInfo::relevantArgs
list< const Argument * > relevantArgs
Definition: argumentparser.cpp:53
CppUtilities::ArgumentOccurrence
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
Definition: argumentparser.h:193
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::ArgumentVector
std::vector< Argument * > ArgumentVector
Definition: argumentparser.h:68
CPP_UTILITIES_EXPORT
#define CPP_UTILITIES_EXPORT
Marks the symbol to be exported by the c++utilities library.
CppUtilities::Argument::isMainArgument
bool isMainArgument() const
Returns an indication whether the argument is used as main argument.
Definition: argumentparser.h:993
commandlineutils.h
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:999
argumentparserprivate.h
CppUtilities::ArgumentOccurrence::path
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).
Definition: argumentparser.h:211
ParseError
The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
CppUtilities::EscapeCodes::setStyle
void setStyle(std::ostream &stream, TextAttribute displayAttribute=TextAttribute::Reset)
Definition: ansiescapecodes.h:33
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:124
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::ArgumentParser
Definition: argumentparser.h:450
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::determineTerminalSize
TerminalSize CPP_UTILITIES_EXPORT determineTerminalSize()
Returns the current size of the terminal.
Definition: commandlineutils.cpp:46
CppUtilities::ArgumentReader::index
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::UnknownArgumentBehavior::Ignore
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::ApplicationInfo::name
const char * name
Definition: argumentparser.h:23
CppUtilities::ParseArgumentBehavior::CheckConstraints
CPP_UTILITIES_IF_DEBUG_BUILD
#define CPP_UTILITIES_IF_DEBUG_BUILD(x)
Wraps debug-only lines conveniently.
Definition: global.h:102
CppUtilities::ParseArgumentBehavior::InvokeCallbacks
CppUtilities::Argument::~Argument
~Argument()
Destroys the Argument.
Definition: argumentparser.cpp:478
CppUtilities::ArgumentCompletionInfo::relevantPreDefinedValues
list< const Argument * > relevantPreDefinedValues
Definition: argumentparser.cpp:54
CppUtilities::ArgumentSuggestion::operator<
bool operator<(const ArgumentSuggestion &other) const
Definition: argumentparser.cpp:96
CppUtilities::ArgumentParser::ArgumentParser
ArgumentParser()
Constructs a new ArgumentParser.
Definition: argumentparser.cpp:728
CppUtilities::ValueCompletionBehavior::Directories
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::Argument::denotesOperation
bool denotesOperation() const
Returns whether the argument denotes an operation.
Definition: argumentparser.h:916
CppUtilities::ValueCompletionBehavior::Files