C++ Utilities  4.12.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 #include "./failure.h"
5 
6 #include "../conversion/stringbuilder.h"
7 #include "../conversion/stringconversion.h"
8 #include "../io/ansiescapecodes.h"
9 #include "../io/path.h"
10 
11 #include <algorithm>
12 #include <cstdlib>
13 #include <cstring>
14 #include <iostream>
15 #include <sstream>
16 #include <string>
17 
18 using namespace std;
19 using namespace std::placeholders;
20 using namespace ConversionUtilities;
21 using namespace EscapeCodes;
22 using namespace IoUtilities;
23 
28 namespace ApplicationUtilities {
29 
33 enum ArgumentDenotationType : unsigned char {
34  Value = 0,
36  FullName = 2
37 };
38 
51 ArgumentReader::ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode)
52  : parser(parser)
53  , args(parser.m_mainArgs)
54  , index(0)
55  , argv(argv)
56  , end(end)
57  , lastArg(nullptr)
58  , argDenotation(nullptr)
59  , completionMode(completionMode)
60 {
61 }
62 
66 ArgumentReader &ArgumentReader::reset(const char *const *argv, const char *const *end)
67 {
68  this->argv = argv;
69  this->end = end;
70  index = 0;
71  lastArg = nullptr;
72  argDenotation = nullptr;
73  return *this;
74 }
75 
81 {
82  read(args);
83 }
84 
88 bool Argument::matchesDenotation(const char *denotation, size_t denotationLength) const
89 {
90  return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) == '\0';
91 }
92 
98 {
99  // method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
100  Argument *const parentArg = lastArg;
101  // determine the current path
102  const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>();
103 
104  Argument *lastArgInLevel = nullptr;
105  vector<const char *> *values = nullptr;
106 
107  // iterate through all argument denotations; loop might exit earlier when an denotation is unknown
108  while (argv != end) {
109  // check whether there are still values to read
110  if (values && lastArgInLevel->requiredValueCount() != Argument::varValueCount && values->size() < lastArgInLevel->requiredValueCount()) {
111  // read arg as value and continue with next arg
112  values->emplace_back(argDenotation ? argDenotation : *argv);
113  ++index, ++argv, argDenotation = nullptr;
114  continue;
115  }
116 
117  // determine how denotation must be processed
118  bool abbreviationFound = false;
119  if (argDenotation) {
120  // continue reading childs for abbreviation denotation already detected
121  abbreviationFound = false;
123  } else {
124  // determine denotation type
125  argDenotation = *argv;
126  if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
127  // skip empty arguments
128  ++index, ++argv, argDenotation = nullptr;
129  continue;
130  }
131  abbreviationFound = false;
134  }
135 
136  // try to find matching Argument instance
137  Argument *matchingArg = nullptr;
138  if (argDenotationType != Value) {
139  // determine actual denotation length (everything before equation sign)
140  const char *const equationPos = strchr(argDenotation, '=');
141  const auto argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation);
142 
143  // loop through each "part" of the denotation
144  // names are read at once, but for abbreviations each character is considered individually
145  for (; argDenotationLength; matchingArg = nullptr) {
146  // search for arguments by abbreviation or name depending on the previously determined denotation type
148  for (Argument *arg : args) {
149  if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
150  matchingArg = arg;
151  abbreviationFound = true;
152  break;
153  }
154  }
155  } else {
156  for (Argument *arg : args) {
157  if (arg->matchesDenotation(argDenotation, argDenotationLength)) {
158  matchingArg = arg;
159  break;
160  }
161  }
162  }
163  if (!matchingArg) {
164  break;
165  }
166 
167  // an argument matched the specified denotation so add an occurrence
168  matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
169 
170  // prepare reading parameter values
171  values = &matchingArg->m_occurrences.back().values;
172 
173  // read value after equation sigh
174  if ((argDenotationType != Abbreviation && equationPos) || (++argDenotation == equationPos)) {
175  values->push_back(equationPos + 1);
176  argDenotation = nullptr;
177  }
178 
179  // read sub arguments, distinguish whether further abbreviations follow
180  ++index, ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, lastArgDenotation = argv;
182  // no further abbreviations follow -> read sub args for next argv
183  ++argv, argDenotation = nullptr;
184  read(lastArg->m_subArgs);
185  argDenotation = nullptr;
186  break;
187  } else {
188  // further abbreviations follow -> remember current arg value
189  const char *const *currentArgValue = argv;
190  // don't increment argv, keep processing outstanding chars of argDenotation
191  read(lastArg->m_subArgs);
192 
193  // stop further processing if the denotation has been consumed or even the next value has already been loaded
194  if (!argDenotation || currentArgValue != argv) {
195  argDenotation = nullptr;
196  break;
197  }
198  }
199  }
200 
201  // continue with next arg if we've got a match already
202  if (matchingArg) {
203  continue;
204  }
205 
206  // unknown argument might be a sibling of the parent element
207  for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
208  for (Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
209  if (sibling->occurrences() < sibling->maxOccurrences()) {
210  // check whether the denoted abbreviation matches the sibling's abbreviatiopn
211  if (argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation)) {
212  return;
213  }
214  // check whether the denoted name matches the sibling's name
215  if (sibling->matchesDenotation(argDenotation, argDenotationLength)) {
216  return;
217  }
218  }
219  }
220  if (parentArgument == pathEnd) {
221  break;
222  }
223  };
224  }
225 
226  // unknown argument might just be a parameter value of the last argument
227  if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
228  values->emplace_back(abbreviationFound ? argDenotation : *argv);
229  ++index, ++argv, argDenotation = nullptr;
230  continue;
231  }
232 
233  // first value might denote "operation"
234  for (Argument *arg : args) {
235  if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
236  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
238  ++index, ++argv;
239  break;
240  }
241  }
242 
243  // use the first default argument which is not already present if there is still no match
244  if (!matchingArg && (!completionMode || (argv + 1 != end))) {
245  const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
246  for (Argument *arg : args) {
247  if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
248  && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
249  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
250  break;
251  }
252  }
253  }
254 
255  if (matchingArg) {
256  // an argument matched the specified denotation
257  if (lastArgInLevel == matchingArg) {
258  break; // break required? -> TODO: add test for this condition
259  }
260 
261  // prepare reading parameter values
262  values = &matchingArg->m_occurrences.back().values;
263 
264  // read sub arguments
265  ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, argDenotation = nullptr;
266  read(lastArg->m_subArgs);
267  argDenotation = nullptr;
268  continue;
269  }
270 
271  // argument denotation is unknown -> handle error
272  if (parentArg) {
273  // continue with parent level
274  return;
275  }
276  if (completionMode) {
277  // ignore unknown denotation
278  ++index, ++argv, argDenotation = nullptr;
279  } else {
280  switch (parser.m_unknownArgBehavior) {
282  cerr << Phrases::Warning << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << Phrases::EndFlush;
283  FALLTHROUGH;
285  // ignore unknown denotation
286  ++index, ++argv, argDenotation = nullptr;
287  break;
289  throw Failure(argsToString("The specified argument \"", *argv, "\" is unknown."));
290  }
291  }
292  } // while(argv != end)
293 }
294 
301 ostream &operator<<(ostream &os, const Wrapper &wrapper)
302 {
303  // determine max. number of columns
304  static const TerminalSize termSize(determineTerminalSize());
305  const auto maxColumns = termSize.columns ? termSize.columns : numeric_limits<unsigned short>::max();
306 
307  // print wrapped string considering indentation
308  unsigned short currentCol = wrapper.m_indentation.level;
309  for (const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
310  const bool wrappingRequired = currentCol >= maxColumns;
311  if (wrappingRequired || *currentChar == '\n') {
312  // insert newline (TODO: wrap only at end of a word)
313  os << '\n';
314  // print indentation (if enough space)
315  if (wrapper.m_indentation.level < maxColumns) {
316  os << wrapper.m_indentation;
317  currentCol = wrapper.m_indentation.level;
318  } else {
319  currentCol = 0;
320  }
321  }
322  if (*currentChar != '\n' && (!wrappingRequired || *currentChar != ' ')) {
323  os << *currentChar;
324  ++currentCol;
325  }
326  }
327  return os;
328 }
329 
331 const char *applicationName = nullptr;
333 const char *applicationAuthor = nullptr;
335 const char *applicationVersion = nullptr;
337 const char *applicationUrl = nullptr;
339 std::initializer_list<const char *> dependencyVersions;
340 
345 void (*exitFunction)(int) = &exit;
346 
348 
349 inline bool notEmpty(const char *str)
350 {
351  return str && *str;
352 }
353 
355 
372 Argument::Argument(const char *name, char abbreviation, const char *description, const char *example)
373  : m_name(name)
374  , m_abbreviation(abbreviation)
375  , m_environmentVar(nullptr)
376  , m_description(description)
377  , m_example(example)
378  , m_minOccurrences(0)
379  , m_maxOccurrences(1)
380  , m_combinable(false)
381  , m_denotesOperation(false)
382  , m_requiredValueCount(0)
383  , m_implicit(false)
384  , m_isMainArg(false)
387  , m_preDefinedCompletionValues(nullptr)
388 {
389 }
390 
395 {
396 }
397 
405 const char *Argument::firstValue() const
406 {
407  if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
408  return m_occurrences.front().values.front();
409  } else if (m_environmentVar) {
410  return getenv(m_environmentVar);
411  } else {
412  return nullptr;
413  }
414 }
415 
419 void Argument::printInfo(ostream &os, unsigned char indentation) const
420 {
421  Indentation ident(indentation);
422  os << ident;
424  if (notEmpty(name())) {
425  if (!denotesOperation()) {
426  os << '-' << '-';
427  }
428  os << name();
429  }
430  if (notEmpty(name()) && abbreviation()) {
431  os << ',' << ' ';
432  }
433  if (abbreviation()) {
434  os << '-' << abbreviation();
435  }
437  if (requiredValueCount()) {
438  unsigned int valueNamesPrint = 0;
439  for (auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
440  os << ' ' << '[' << *i << ']';
441  ++valueNamesPrint;
442  }
444  os << " ...";
445  } else {
446  for (; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
447  os << " [value " << (valueNamesPrint + 1) << ']';
448  }
449  }
450  }
451  ident.level += 2;
452  if (notEmpty(description())) {
453  os << '\n' << ident << Wrapper(description(), ident);
454  }
455  if (isRequired()) {
456  os << '\n' << ident << "particularities: mandatory";
457  if (!isMainArgument()) {
458  os << " if parent argument is present";
459  }
460  }
461  if (environmentVariable()) {
462  os << '\n' << ident << "default environment variable: " << Wrapper(environmentVariable(), ident + 30);
463  }
464  os << '\n';
465  for (const auto *arg : subArguments()) {
466  arg->printInfo(os, ident.level);
467  }
468  if (notEmpty(example())) {
469  if (ident.level == 2 && !subArguments().empty()) {
470  os << '\n';
471  }
472  os << ident << "example: " << Wrapper(example(), ident + 9);
473  os << '\n';
474  }
475 }
476 
483 {
484  for (Argument *arg : args) {
485  if (arg != except && arg->isPresent() && !arg->isCombinable()) {
486  return arg;
487  }
488  }
489  return nullptr;
490 }
491 
506 void Argument::setSubArguments(const ArgumentInitializerList &secondaryArguments)
507 {
508  // remove this argument from the parents list of the previous secondary arguments
509  for (Argument *arg : m_subArgs) {
510  arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
511  }
512  // assign secondary arguments
513  m_subArgs.assign(secondaryArguments);
514  // add this argument to the parents list of the assigned secondary arguments
515  // and set the parser
516  for (Argument *arg : m_subArgs) {
517  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
518  arg->m_parents.push_back(this);
519  }
520  }
521 }
522 
531 {
532  if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) == m_subArgs.cend()) {
533  m_subArgs.push_back(arg);
534  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
535  arg->m_parents.push_back(this);
536  }
537  }
538 }
539 
545 {
546  if (isMainArgument()) {
547  return true;
548  }
549  for (const Argument *parent : m_parents) {
550  if (parent->isPresent()) {
551  return true;
552  }
553  }
554  return false;
555 }
556 
566 {
567  return isPresent() ? wouldConflictWithArgument() : nullptr;
568 }
569 
579 {
580  if (!isCombinable()) {
581  for (Argument *parent : m_parents) {
582  for (Argument *sibling : parent->subArguments()) {
583  if (sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
584  return sibling;
585  }
586  }
587  }
588  }
589  return nullptr;
590 }
591 
597 {
598  for (Argument *arg : m_subArgs) {
599  if (arg->denotesOperation() && arg->isPresent()) {
600  return arg;
601  }
602  }
603  return nullptr;
604 }
605 
611 {
612  for (Argument *arg : m_subArgs) {
613  arg->resetRecursively();
614  }
615  reset();
616 }
617 
635  : m_actualArgc(0)
636  , m_executable(nullptr)
637  , m_unknownArgBehavior(UnknownArgumentBehavior::Fail)
638  , m_defaultArg(nullptr)
639 {
640 }
641 
652 {
653  if (mainArguments.size()) {
654  for (Argument *arg : mainArguments) {
655  arg->m_isMainArg = true;
656  }
657  m_mainArgs.assign(mainArguments);
658  if (!m_defaultArg) {
659  if (!(*mainArguments.begin())->requiredValueCount()) {
660  bool subArgsRequired = false;
661  for (const Argument *subArg : (*mainArguments.begin())->subArguments()) {
662  if (subArg->isRequired()) {
663  subArgsRequired = true;
664  break;
665  }
666  }
667  if (!subArgsRequired) {
668  m_defaultArg = *mainArguments.begin();
669  }
670  }
671  }
672  } else {
673  m_mainArgs.clear();
674  }
675 }
676 
684 {
685  argument->m_isMainArg = true;
686  m_mainArgs.push_back(argument);
687 }
688 
692 void ArgumentParser::printHelp(ostream &os) const
693 {
696  os << applicationName;
698  os << ',' << ' ';
699  }
700  }
702  os << "version " << applicationVersion;
703  }
704  if (dependencyVersions.size()) {
706  os << '\n';
708  }
709  auto i = dependencyVersions.begin(), end = dependencyVersions.end();
710  os << "Linked against: " << *i;
711  for (++i; i != end; ++i) {
712  os << ',' << ' ' << *i;
713  }
714  }
716  os << '\n' << '\n';
717  }
719  if (!m_mainArgs.empty()) {
720  os << "Available arguments:";
721  for (const Argument *arg : m_mainArgs) {
722  os << '\n';
723  arg->printInfo(os);
724  }
725  }
726  if (applicationUrl && *applicationUrl) {
727  os << "\nProject website: " << applicationUrl << endl;
728  }
729 }
730 
742 void ArgumentParser::parseArgs(int argc, const char *const *argv)
743 {
745 }
746 
756 void ArgumentParser::parseArgsOrExit(int argc, const char *const *argv)
757 {
758  parseArgsExt(argc, argv);
759 }
760 
778 void ArgumentParser::parseArgsExt(int argc, const char *const *argv, ParseArgumentBehavior behavior)
779 {
780  try {
781  readArgs(argc, argv);
782  if (!argc) {
783  return;
784  }
786  checkConstraints(m_mainArgs);
787  }
789  invokeCallbacks(m_mainArgs);
790  }
791  } catch (const Failure &failure) {
792  if (behavior & ParseArgumentBehavior::ExitOnFailure) {
794  cerr << failure;
795  exit(1);
796  }
797  throw;
798  }
799 }
800 
814 void ArgumentParser::readArgs(int argc, const char *const *argv)
815 {
816  IF_DEBUG_BUILD(verifyArgs(m_mainArgs, std::vector<char>(), std::vector<const char *>());)
817  m_actualArgc = 0;
818  if (argc) {
819  // the first argument is the executable name
820  m_executable = *argv;
821 
822  // check for further arguments
823  if (--argc) {
824  // if the first argument (after executable name) is "--bash-completion-for", bash completion for the following arguments is requested
825  bool completionMode = !strcmp(*++argv, "--bash-completion-for");
826  unsigned int currentWordIndex;
827  if (completionMode) {
828  // the first argument after "--bash-completion-for" is the index of the current word
829  try {
830  currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
831  if (argc) {
832  ++argv, --argc;
833  }
834  } catch (const ConversionException &) {
835  currentWordIndex = static_cast<unsigned int>(argc - 1);
836  }
837  }
838 
839  // read specified arguments
840  ArgumentReader reader(*this, argv,
841  argv + (completionMode ? min(static_cast<unsigned int>(argc), currentWordIndex + 1) : static_cast<unsigned int>(argc)),
842  completionMode);
843  try {
844  reader.read();
846  } catch (const Failure &) {
848  if (!completionMode) {
849  throw;
850  }
851  }
852 
853  if (completionMode) {
854  printBashCompletion(argc, argv, currentWordIndex, reader);
855  exitFunction(0); // prevent the applicaton to continue with the regular execution
856  }
857  } else {
858  // no arguments specified -> flag default argument as present if one is assigned
859  if (m_defaultArg) {
860  m_defaultArg->m_occurrences.emplace_back(0);
861  }
862  }
863  } else {
864  m_executable = nullptr;
865  }
866 }
867 
873 {
874  for (Argument *arg : m_mainArgs) {
875  arg->resetRecursively();
876  }
877  m_actualArgc = 0;
878 }
879 
886 {
887  for (Argument *arg : m_mainArgs) {
888  if (arg->denotesOperation() && arg->isPresent()) {
889  return arg;
890  }
891  }
892  return nullptr;
893 }
894 
899 {
900  for (const Argument *arg : m_mainArgs) {
901  if (!arg->isCombinable() && arg->isPresent()) {
902  return true;
903  }
904  }
905  return false;
906 }
907 
908 #ifdef DEBUG_BUILD
909 
925 void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args, vector<char>, vector<const char *>)
926 {
927  vector<const Argument *> verifiedArgs;
928  verifiedArgs.reserve(args.size());
929  vector<char> abbreviations;
930  abbreviations.reserve(abbreviations.size() + args.size());
931  vector<const char *> names;
932  names.reserve(names.size() + args.size());
933  bool hasImplicit = false;
934  for (const Argument *arg : args) {
935  assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
936  verifiedArgs.push_back(arg);
937  assert(!arg->isImplicit() || !hasImplicit);
938  hasImplicit |= arg->isImplicit();
939  assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
940  abbreviations.push_back(arg->abbreviation());
941  assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
942  assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
943  names.emplace_back(arg->name());
944  }
945  for (const Argument *arg : args) {
946  verifyArgs(arg->subArguments(), vector<char>(), vector<const char *>());
947  }
948 }
949 #endif
950 
958 bool compareArgs(const Argument *arg1, const Argument *arg2)
959 {
960  if (arg1->denotesOperation() && !arg2->denotesOperation()) {
961  return true;
962  } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
963  return false;
964  } else {
965  return strcmp(arg1->name(), arg2->name()) < 0;
966  }
967 }
968 
973 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
974 {
975  bool onlyCombinable = false;
976  for (const Argument *sibling : siblings) {
977  if (sibling->isPresent() && !sibling->isCombinable()) {
978  onlyCombinable = true;
979  break;
980  }
981  }
982  for (const Argument *sibling : siblings) {
983  if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
984  target.push_back(sibling);
985  }
986  }
987 }
988 
994 void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader)
995 {
996  // variables to store relevant completions (arguments, pre-defined values, files/dirs)
997  list<const Argument *> relevantArgs, relevantPreDefinedValues;
998  bool completeFiles = false, completeDirs = false, noWhitespace = false;
999 
1000  // get the last argument the argument parser was able to detect successfully
1001  const Argument *const lastDetectedArg = reader.lastArg;
1002  size_t lastDetectedArgIndex;
1003  vector<Argument *> lastDetectedArgPath;
1004  if (lastDetectedArg) {
1005  lastDetectedArgIndex = reader.lastArgDenotation - argv;
1006  lastDetectedArgPath = lastDetectedArg->path(lastDetectedArg->occurrences() - 1);
1007  }
1008 
1009  // determine last arg, omitting trailing empty args
1010  const char *const *lastSpecifiedArg;
1011  unsigned int lastSpecifiedArgIndex;
1012  if (argc) {
1013  lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1014  lastSpecifiedArg = argv + lastSpecifiedArgIndex;
1015  for (; lastSpecifiedArg >= argv && **lastSpecifiedArg == '\0'; --lastSpecifiedArg, --lastSpecifiedArgIndex)
1016  ;
1017  }
1018 
1019  // determine arguments relevant for completion
1020  bool nextArgumentOrValue;
1021  if (lastDetectedArg && lastDetectedArg->isPresent()) {
1022  if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
1023  // define function to add parameter values of argument as possible completions
1024  const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
1025  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
1026  relevantPreDefinedValues.push_back(arg);
1027  }
1028  if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
1029  || !arg->preDefinedCompletionValues()) {
1030  completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
1031  completeDirs = completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
1032  }
1033  };
1034 
1035  // detect number of specified values
1036  auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
1037  // ignore values which are specified after the current word
1038  if (currentValueCount) {
1039  const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - lastDetectedArgIndex;
1040  if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1041  currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1042  } else {
1043  currentValueCount = 0;
1044  }
1045  }
1046 
1047  // add value completions for implicit child if there are no value specified and there are no values required by the
1048  // last detected argument itself
1049  if (!currentValueCount && !lastDetectedArg->requiredValueCount()) {
1050  for (const Argument *child : lastDetectedArg->subArguments()) {
1051  if (child->isImplicit() && child->requiredValueCount()) {
1052  addValueCompletionsForArg(child);
1053  break;
1054  }
1055  }
1056  }
1057 
1058  // add value completions for last argument if there are further values required
1059  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount || (currentValueCount < lastDetectedArg->requiredValueCount())) {
1060  addValueCompletionsForArg(lastDetectedArg);
1061  }
1062 
1063  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount
1064  || lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
1065  // sub arguments of the last arg are possible completions
1066  for (const Argument *subArg : lastDetectedArg->subArguments()) {
1067  if (subArg->occurrences() < subArg->maxOccurrences()) {
1068  relevantArgs.push_back(subArg);
1069  }
1070  }
1071 
1072  // siblings of parents are possible completions as well
1073  for (auto parentArgument = lastDetectedArgPath.crbegin(), end = lastDetectedArgPath.crend();; ++parentArgument) {
1074  insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, relevantArgs);
1075  if (parentArgument == end) {
1076  break;
1077  }
1078  }
1079  }
1080  } else {
1081  // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1082  relevantArgs.push_back(lastDetectedArg);
1083  }
1084 
1085  } else {
1086  // no arguments detected -> just use main arguments for completion
1087  nextArgumentOrValue = true;
1088  insertSiblings(m_mainArgs, relevantArgs);
1089  }
1090 
1091  // read the "opening" (started but not finished argument denotation)
1092  const char *opening = nullptr;
1093  string compoundOpening;
1094  size_t openingLen, compoundOpeningStartLen = 0;
1095  unsigned char openingDenotationType = Value;
1096  if (argc && nextArgumentOrValue) {
1097  if (currentWordIndex < static_cast<unsigned int>(argc)) {
1098  opening = argv[currentWordIndex];
1099  // For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
1100  // equation sign is an own argument ("set --values disk = 1 tag = a").
1101  // This is not how values are treated by the argument parser. Hence the opening
1102  // must be joined again. In this case only the part after the equation sign needs to be
1103  // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1104  size_t minCurrentWordIndex = (lastDetectedArg ? lastDetectedArgIndex : 0);
1105  if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1106  compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1107  compoundOpening = argv[currentWordIndex];
1108  compoundOpening += '=';
1109  } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1110  compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1111  compoundOpening = argv[currentWordIndex];
1112  compoundOpening += '=';
1113  compoundOpening += opening;
1114  }
1115  if (!compoundOpening.empty()) {
1116  opening = compoundOpening.data();
1117  }
1118  } else {
1119  opening = *lastSpecifiedArg;
1120  }
1121  *opening == '-' && (++opening, ++openingDenotationType) && *opening == '-' && (++opening, ++openingDenotationType);
1122  openingLen = strlen(opening);
1123  }
1124 
1125  relevantArgs.sort(compareArgs);
1126 
1127  // print "COMPREPLY" bash array
1128  cout << "COMPREPLY=(";
1129  // -> completions for parameter values
1130  for (const Argument *arg : relevantPreDefinedValues) {
1131  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1132  arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1133  }
1134  if (arg->preDefinedCompletionValues()) {
1135  bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1136  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1137  if (openingDenotationType == Value) {
1138  bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1139  size_t wordIndex = 0;
1140  for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1141  if (wordStart) {
1142  const char *i1 = i, *i2 = opening;
1143  for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1144  ;
1145  if ((ok = (i2 == end))) {
1146  cout << '\'';
1147  }
1148  wordStart = false;
1149  wordIndex = 0;
1150  } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1151  equationSignAlreadyPresent = false;
1152  if (ok) {
1153  cout << '\'' << ' ';
1154  }
1155  ++i;
1156  continue;
1157  } else if (*i == '=') {
1158  equationSignAlreadyPresent = true;
1159  }
1160  if (ok) {
1161  if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1162  if (*i == '\'') {
1163  cout << "'\"'\"'";
1164  } else {
1165  cout << *i;
1166  }
1167  }
1168  ++i, ++wordIndex;
1169  switch (*i) {
1170  case ' ':
1171  case '\n':
1172  case '\0':
1173  if (appendEquationSign && !equationSignAlreadyPresent) {
1174  cout << '=';
1175  noWhitespace = true;
1176  equationSignAlreadyPresent = false;
1177  }
1178  if (*i == '\0') {
1179  cout << '\'';
1180  }
1181  }
1182  } else {
1183  ++i;
1184  }
1185  }
1186  cout << ' ';
1187  }
1188  } else if (const char *i = arg->preDefinedCompletionValues()) {
1189  bool equationSignAlreadyPresent = false;
1190  cout << '\'';
1191  while (*i) {
1192  if (*i == '\'') {
1193  cout << "'\"'\"'";
1194  } else {
1195  cout << *i;
1196  }
1197  switch (*(++i)) {
1198  case '=':
1199  equationSignAlreadyPresent = true;
1200  break;
1201  case ' ':
1202  case '\n':
1203  case '\0':
1204  if (appendEquationSign && !equationSignAlreadyPresent) {
1205  cout << '=';
1206  equationSignAlreadyPresent = false;
1207  }
1208  if (*i != '\0') {
1209  cout << '\'';
1210  if (*(++i)) {
1211  cout << ' ' << '\'';
1212  }
1213  }
1214  }
1215  }
1216  cout << '\'' << ' ';
1217  }
1218  }
1219  }
1220  // -> completions for further arguments
1221  for (const Argument *arg : relevantArgs) {
1222  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1223  switch (openingDenotationType) {
1224  case Value:
1225  if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1226  continue;
1227  }
1228  break;
1229  case Abbreviation:
1230  break;
1231  case FullName:
1232  if (strncmp(arg->name(), opening, openingLen)) {
1233  continue;
1234  }
1235  }
1236  }
1237 
1238  if (opening && openingDenotationType == Abbreviation && !nextArgumentOrValue) {
1239  cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1240  } else if (lastDetectedArg && reader.argDenotationType == Abbreviation && !nextArgumentOrValue) {
1241  if (reader.argv == reader.end) {
1242  cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1243  }
1244  } else if (arg->denotesOperation()) {
1245  cout << '\'' << arg->name() << '\'' << ' ';
1246  } else {
1247  cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1248  }
1249  }
1250  // -> completions for files and dirs
1251  // -> if there's already an "opening", determine the dir part and the file part
1252  string actualDir, actualFile;
1253  bool haveFileOrDirCompletions = false;
1254  if (argc && currentWordIndex == lastSpecifiedArgIndex && opening) {
1255  // the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
1256  string unescapedOpening(opening);
1257  findAndReplace<string>(unescapedOpening, "\\ ", " ");
1258  findAndReplace<string>(unescapedOpening, "\\,", ",");
1259  findAndReplace<string>(unescapedOpening, "\\[", "[");
1260  findAndReplace<string>(unescapedOpening, "\\]", "]");
1261  findAndReplace<string>(unescapedOpening, "\\!", "!");
1262  findAndReplace<string>(unescapedOpening, "\\#", "#");
1263  findAndReplace<string>(unescapedOpening, "\\$", "$");
1264  findAndReplace<string>(unescapedOpening, "\\'", "'");
1265  findAndReplace<string>(unescapedOpening, "\\\"", "\"");
1266  findAndReplace<string>(unescapedOpening, "\\\\", "\\");
1267  // determine the "directory" part
1268  string dir = directory(unescapedOpening);
1269  if (dir.empty()) {
1270  actualDir = ".";
1271  } else {
1272  if (dir[0] == '\"' || dir[0] == '\'') {
1273  dir.erase(0, 1);
1274  }
1275  if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1276  dir.erase(dir.size() - 2, 1);
1277  }
1278  actualDir = move(dir);
1279  }
1280  // determine the "file" part
1281  string file = fileName(unescapedOpening);
1282  if (file[0] == '\"' || file[0] == '\'') {
1283  file.erase(0, 1);
1284  }
1285  if (file.size() > 1 && (file[dir.size() - 2] == '\"' || dir[file.size() - 2] == '\'')) {
1286  file.erase(file.size() - 2, 1);
1287  }
1288  actualFile = move(file);
1289  }
1290 
1291  // -> completion for files and dirs
1293  if (completeFiles) {
1294  entryTypes |= DirectoryEntryType::File;
1295  }
1296  if (completeDirs) {
1297  entryTypes |= DirectoryEntryType::Directory;
1298  }
1299  if (entryTypes != DirectoryEntryType::None) {
1300  const string replace("'"), with("'\"'\"'");
1301  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1302  list<string> entries = directoryEntries(actualDir.c_str(), entryTypes);
1303  findAndReplace(actualDir, replace, with);
1304  for (string &dirEntry : entries) {
1305  if (startsWith(dirEntry, actualFile)) {
1306  cout << '\'';
1307  if (actualDir != ".") {
1308  cout << actualDir;
1309  }
1310  findAndReplace(dirEntry, replace, with);
1311  cout << dirEntry << '\'' << ' ';
1312  haveFileOrDirCompletions = true;
1313  }
1314  }
1315  } else {
1316  for (string &dirEntry : directoryEntries(".", entryTypes)) {
1317  findAndReplace(dirEntry, replace, with);
1318  cout << '\'' << dirEntry << '\'' << ' ';
1319  haveFileOrDirCompletions = true;
1320  }
1321  }
1322  }
1323  cout << ')';
1324 
1325  // ensure file or dir completions are formatted appropriately
1326  if (haveFileOrDirCompletions) {
1327  cout << "; compopt -o filenames";
1328  }
1329 
1330  // ensure trailing whitespace is ommitted
1331  if (noWhitespace) {
1332  cout << "; compopt -o nospace";
1333  }
1334 
1335  cout << endl;
1336 }
1337 
1343 {
1344  for (const Argument *arg : args) {
1345  const auto occurrences = arg->occurrences();
1346  if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1347  throw Failure(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1348  (arg->maxOccurrences() == 1 ? " time." : " times.")));
1349  }
1350  if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1351  throw Failure(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1352  (arg->minOccurrences() == 1 ? " time." : " times.")));
1353  }
1354  Argument *conflictingArgument = nullptr;
1355  if (arg->isMainArgument()) {
1356  if (!arg->isCombinable() && arg->isPresent()) {
1357  conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1358  }
1359  } else {
1360  conflictingArgument = arg->conflictsWithArgument();
1361  }
1362  if (conflictingArgument) {
1363  throw Failure(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1364  }
1365  for (size_t i = 0; i != occurrences; ++i) {
1366  if (!arg->allRequiredValuesPresent(i)) {
1367  stringstream ss(stringstream::in | stringstream::out);
1368  ss << "Not all parameter for argument \"" << arg->name() << "\" ";
1369  if (i) {
1370  ss << " (" << (i + 1) << " occurrence) ";
1371  }
1372  ss << "provided. You have to provide the following parameter:";
1373  size_t valueNamesPrint = 0;
1374  for (const auto &name : arg->m_valueNames) {
1375  ss << ' ' << name, ++valueNamesPrint;
1376  }
1377  if (arg->m_requiredValueCount != Argument::varValueCount) {
1378  while (valueNamesPrint < arg->m_requiredValueCount) {
1379  ss << "\nvalue " << (++valueNamesPrint);
1380  }
1381  }
1382  throw Failure(ss.str());
1383  }
1384  }
1385 
1386  // check contraints of sub arguments recursively
1387  checkConstraints(arg->m_subArgs);
1388  }
1389 }
1390 
1399 {
1400  for (const Argument *arg : args) {
1401  // invoke the callback for each occurrence of the argument
1402  if (arg->m_callbackFunction) {
1403  for (const auto &occurrence : arg->m_occurrences) {
1404  arg->m_callbackFunction(occurrence);
1405  }
1406  }
1407  // invoke the callbacks for sub arguments recursively
1408  invokeCallbacks(arg->m_subArgs);
1409  }
1410 }
1411 
1422  : Argument("help", 'h', "shows this information")
1423 {
1424  setCallback([&parser](const ArgumentOccurrence &) {
1426  parser.printHelp(cout);
1427  });
1428 }
1429 
1460 NoColorArgument *NoColorArgument::s_instance = nullptr;
1461 
1467 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1468  : Argument("no-color", '\0', "disables formatted/colorized output")
1469 #else
1470  : Argument("enable-color", '\0', "enables formatted/colorized output")
1471 #endif
1472 {
1473  setCombinable(true);
1474 
1475  if (s_instance) {
1476  return;
1477  }
1478  s_instance = this;
1479 
1480  // set the environmentvariable: note that this is not directly used and just assigned for printing help
1481  setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1482 
1483  // default-initialize EscapeCodes::enabled from environment variable
1484  const char *envValue = getenv(environmentVariable());
1485  if (!envValue) {
1486  return;
1487  }
1488  for (; *envValue; ++envValue) {
1489  switch (*envValue) {
1490  case '0':
1491  case ' ':
1492  break;
1493  default:
1494  // enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
1495  EscapeCodes::enabled = true;
1496  return;
1497  }
1498  }
1499  // disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
1500  EscapeCodes::enabled = false;
1501 }
1502 
1507 {
1508  if (s_instance == this) {
1509  s_instance = nullptr;
1510  }
1511 }
1512 
1517 {
1518  if (NoColorArgument::s_instance && NoColorArgument::s_instance->isPresent()) {
1519 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1520  EscapeCodes::enabled = false;
1521 #else
1522  EscapeCodes::enabled = true;
1523 #endif
1524  }
1525 }
1526 
1527 } // namespace ApplicationUtilities
bool isMainArgument() const
Returns an indication whether the argument is used as main argument.
CPP_UTILITIES_EXPORT const char * applicationUrl
Specifies the URL to the application website (used by ArgumentParser::printHelp()).
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
ValueCompletionBehavior
The ValueCompletionBehavior enum specifies the items to be considered when generating completion for ...
Argument * lastArg
The last Argument instance which could be detected. Set to nullptr in the initial call...
bool denotesOperation() const
Returns whether the argument denotes the operation.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
Encapsulates functions for formatted terminal output using ANSI escape codes.
std::initializer_list< Argument * > ArgumentInitializerList
#define IF_DEBUG_BUILD(x)
Wraps debug-only lines conveniently.
Definition: global.h:130
std::size_t requiredValueCount() const
Returns the number of values which are required to be given for this argument.
std::ostream & operator<<(std::ostream &os, const Wrapper &wrapper)
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
const char * description() const
Returns the description of the argument.
bool isParentPresent() const
Returns whether at least one parent argument is present.
static void apply()
Sets EscapeCodes::enabled according to the presense of the first instantiation of NoColorArgument...
unsigned char argDenotationType
The type of the currently processed abbreviation denotation. Unspecified if argDenotation is not set...
ArgumentParser()
Constructs a new ArgumentParser.
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
void setStyle(std::ostream &stream, TextAttribute displayAttribute=TextAttribute::Reset)
CPP_UTILITIES_EXPORT std::list< std::string > directoryEntries(const char *path, DirectoryEntryType types=DirectoryEntryType::All)
Returns the names of the directory entries in the specified path with the specified types...
Definition: path.cpp:181
The ConversionException class is thrown by the various conversion functions of this library when a co...
Contains currently only ArgumentParser and related classes.
constexpr StringType argsToString(Args &&... args)
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
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.
STL namespace.
bool isRequired() const
Returns an indication whether the argument is mandatory.
Argument CPP_UTILITIES_EXPORT * firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
This function return the first present and uncombinable argument of the given list of arguments...
The Wrapper class is internally used print text which might needs to be wrapped preserving the indent...
NoColorArgument()
Constructs a new NoColorArgument argument.
void parseArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
CPP_UTILITIES_EXPORT const char * applicationVersion
Specifies the version of the application (used by ArgumentParser::printHelp()).
void invokeCallbacks()
Invokes all assigned callbacks.
const char *const * argv
Points to the first argument denotation and will be incremented when a denotation has been processed...
~Argument()
Destroys the Argument.
The Indentation class allows printing indentation conveniently, eg.
#define CMD_UTILS_START_CONSOLE
The NoColorArgument class allows to specify whether use of escape codes or similar technique to provi...
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
void parseArgsOrExit(int argc, const char *const *argv)
Parses the specified command line arguments.
#define FALLTHROUGH
Prevents clang from warning about missing break in switch-case.
Definition: global.h:142
void checkConstraints()
Checks whether contraints are violated.
const char * name() const
Returns the name of the argument.
const std::vector< const char * > & valueNames() const
Returns the names of the requried values.
const char * argDenotation
The currently processed abbreviation denotation (should be substring of one of the args in argv)...
HelpArgument(ArgumentParser &parser)
Constructs a new help argument for the specified parser.
void parseArgsExt(int argc, const char *const *argv, ParseArgumentBehavior behavior=ParseArgumentBehavior::CheckConstraints|ParseArgumentBehavior::InvokeCallbacks|ParseArgumentBehavior::ExitOnFailure)
Parses the specified command line arguments.
ArgumentParser & parser
The associated ArgumentParser instance.
const ArgumentVector & subArguments() const
Returns the secondary arguments for this argument.
size_t index
An index which is incremented when an argument is encountered (the current index is stored in the occ...
Contains utility classes helping to read and write streams.
Definition: binaryreader.h:10
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes...
void printHelp(std::ostream &os) const
Prints help text for all assigned arguments.
void findAndReplace(StringType &str, const StringType &find, const StringType &replace)
Replaces all occurences of find with relpace in the specified str.
CPP_UTILITIES_EXPORT std::initializer_list< const char * > dependencyVersions
Specifies the dependency versions the application was linked against (used by ArgumentParser::printHe...
Contains several functions providing conversions between different data types.
The TerminalSize struct describes a terminal size.
CPP_UTILITIES_EXPORT void(* exitFunction)(int)
Specifies a function quit the application.
TerminalSize CPP_UTILITIES_EXPORT determineTerminalSize()
Returns the current size of the terminal.
ParseArgumentBehavior
The ParseArgumentBehavior enum specifies the behavior when parsing arguments.
Argument * wouldConflictWithArgument() const
Checks if this argument would conflict with other arguments if it was present.
std::size_t occurrences() const
Returns how often the argument could be detected when parsing.
The Argument class is a wrapper for command line argument information.
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
const std::vector< Argument * > & path(std::size_t occurrence=0) const
Returns the path of the specified occurrence.
static constexpr std::size_t varValueCount
Denotes a variable number of values.
bool isCombinable() const
Returns an indication whether the argument is combinable.
ApplicationUtilities::ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
ArgumentVector & args
The Argument instances to store the results. Sub arguments of args are considered as well...
ArgumentDenotationType
The ArgumentDenotationType enum specifies the type of a given argument denotation.
UnknownArgumentBehavior
The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown argume...
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...
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
Definition: path.cpp:52
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
const char *const * end
Points to the end of the argv array.
CPP_UTILITIES_EXPORT const char * applicationName
Specifies the name of the application (used by ArgumentParser::printHelp()).
unsigned short columns
number of columns
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
The Failure class is thrown by an ArgumentParser when a parsing error occurs.
Definition: failure.h:12
const ArgumentVector & mainArguments() const
Returns the main arguments.
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
char abbreviation() const
Returns the abbreviation of the argument.
const char * example() const
Returns the usage example of the argument.
void reset()
Resets occurrences (indices, values and paths).
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
Definition: path.cpp:32
const char *const * lastArgDenotation
Points to the element in argv where lastArg was encountered. Unspecified if lastArg is not set...
CPP_UTILITIES_EXPORT const char * applicationAuthor
Specifies the author of the application (used by ArgumentParser::printHelp()).
DirectoryEntryType
The DirectoryEntryType enum specifies the type of a directory entry (file, directory or symlink)...
Definition: path.h:26
The ArgumentReader class internally encapsulates the process of reading command line arguments...
const char * environmentVariable() const
Returns the environment variable queried when firstValue() is called.
bool completionMode
Whether completion mode is enabled. In this case reading args will be continued even if an denotation...
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
void read()
Reads the commands line arguments specified when constructing the object.
bool compareArgs(const Argument *arg1, const Argument *arg2)
Returns whether arg1 should be listed before arg2 when printing completion.
The ArgumentParser class provides a means for handling command line arguments.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
void resetRecursively()
Resets this argument and all sub arguments recursively.
void insertSiblings(const ArgumentVector &siblings, list< const Argument *> &target)
Inserts the specified siblings in the target list.
std::vector< Argument * > ArgumentVector