C++ Utilities  4.13.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  bool hasOperations = false;
721  for (const Argument *arg : m_mainArgs) {
722  if (arg->denotesOperation()) {
723  hasOperations = true;
724  break;
725  }
726  }
727 
728  // check whether operations are available
729  if (hasOperations) {
730  // split top-level operations and other configurations
731  os << "Available operations:";
732  for (const Argument *arg : m_mainArgs) {
733  if (arg->denotesOperation() && strcmp(arg->name(), "help")) {
734  os << '\n';
735  arg->printInfo(os);
736  }
737  }
738  os << "\nAvailable top-level options:";
739  for (const Argument *arg : m_mainArgs) {
740  if (!arg->denotesOperation() && strcmp(arg->name(), "help")) {
741  os << '\n';
742  arg->printInfo(os);
743  }
744  }
745  } else {
746  // just show all args if no operations are available
747  os << "Available arguments:";
748  for (const Argument *arg : m_mainArgs) {
749  if (strcmp(arg->name(), "help")) {
750  os << '\n';
751  arg->printInfo(os);
752  }
753  }
754  }
755  }
756  if (applicationUrl && *applicationUrl) {
757  os << "\nProject website: " << applicationUrl << endl;
758  }
759 }
760 
772 void ArgumentParser::parseArgs(int argc, const char *const *argv)
773 {
775 }
776 
786 void ArgumentParser::parseArgsOrExit(int argc, const char *const *argv)
787 {
788  parseArgsExt(argc, argv);
789 }
790 
808 void ArgumentParser::parseArgsExt(int argc, const char *const *argv, ParseArgumentBehavior behavior)
809 {
810  try {
811  readArgs(argc, argv);
812  if (!argc) {
813  return;
814  }
816  checkConstraints(m_mainArgs);
817  }
819  invokeCallbacks(m_mainArgs);
820  }
821  } catch (const Failure &failure) {
822  if (behavior & ParseArgumentBehavior::ExitOnFailure) {
824  cerr << failure;
825  exit(1);
826  }
827  throw;
828  }
829 }
830 
844 void ArgumentParser::readArgs(int argc, const char *const *argv)
845 {
846  IF_DEBUG_BUILD(verifyArgs(m_mainArgs);)
847  m_actualArgc = 0;
848 
849  // the first argument is the executable name
850  if (!argc) {
851  m_executable = nullptr;
852  return;
853  }
854  m_executable = *argv;
855 
856  // check for further arguments
857  if (!--argc) {
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  return;
863  }
864 
865  // if the first argument (after executable name) is "--bash-completion-for", bash completion for the following arguments is requested
866  bool completionMode = !strcmp(*++argv, "--bash-completion-for");
867  unsigned int currentWordIndex;
868  if (completionMode) {
869  // the first argument after "--bash-completion-for" is the index of the current word
870  try {
871  currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
872  if (argc) {
873  ++argv, --argc;
874  }
875  } catch (const ConversionException &) {
876  currentWordIndex = static_cast<unsigned int>(argc - 1);
877  }
878  }
879 
880  // read specified arguments
881  ArgumentReader reader(*this, argv,
882  argv + (completionMode ? min(static_cast<unsigned int>(argc), currentWordIndex + 1) : static_cast<unsigned int>(argc)), completionMode);
883  try {
884  reader.read();
886  } catch (const Failure &) {
888  if (!completionMode) {
889  throw;
890  }
891  }
892 
893  if (completionMode) {
894  printBashCompletion(argc, argv, currentWordIndex, reader);
895  exitFunction(0); // prevent the applicaton to continue with the regular execution
896  }
897 }
898 
904 {
905  for (Argument *arg : m_mainArgs) {
906  arg->resetRecursively();
907  }
908  m_actualArgc = 0;
909 }
910 
917 {
918  for (Argument *arg : m_mainArgs) {
919  if (arg->denotesOperation() && arg->isPresent()) {
920  return arg;
921  }
922  }
923  return nullptr;
924 }
925 
930 {
931  for (const Argument *arg : m_mainArgs) {
932  if (!arg->isCombinable() && arg->isPresent()) {
933  return true;
934  }
935  }
936  return false;
937 }
938 
939 #ifdef DEBUG_BUILD
940 
954 void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args)
955 {
956  vector<const Argument *> verifiedArgs;
957  verifiedArgs.reserve(args.size());
958  vector<char> abbreviations;
959  abbreviations.reserve(abbreviations.size() + args.size());
960  vector<const char *> names;
961  names.reserve(names.size() + args.size());
962  bool hasImplicit = false;
963  for (const Argument *arg : args) {
964  assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
965  verifiedArgs.push_back(arg);
966  assert(!arg->isImplicit() || !hasImplicit);
967  hasImplicit |= arg->isImplicit();
968  assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
969  abbreviations.push_back(arg->abbreviation());
970  assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
971  assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
972  names.emplace_back(arg->name());
973  }
974  for (const Argument *arg : args) {
975  verifyArgs(arg->subArguments());
976  }
977 }
978 #endif
979 
987 bool compareArgs(const Argument *arg1, const Argument *arg2)
988 {
989  if (arg1->denotesOperation() && !arg2->denotesOperation()) {
990  return true;
991  } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
992  return false;
993  } else {
994  return strcmp(arg1->name(), arg2->name()) < 0;
995  }
996 }
997 
1002 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
1003 {
1004  bool onlyCombinable = false;
1005  for (const Argument *sibling : siblings) {
1006  if (sibling->isPresent() && !sibling->isCombinable()) {
1007  onlyCombinable = true;
1008  break;
1009  }
1010  }
1011  for (const Argument *sibling : siblings) {
1012  if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1013  target.push_back(sibling);
1014  }
1015  }
1016 }
1017 
1023 void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader)
1024 {
1025  // variables to store relevant completions (arguments, pre-defined values, files/dirs)
1026  list<const Argument *> relevantArgs, relevantPreDefinedValues;
1027  bool completeFiles = false, completeDirs = false, noWhitespace = false;
1028 
1029  // get the last argument the argument parser was able to detect successfully
1030  const Argument *const lastDetectedArg = reader.lastArg;
1031  size_t lastDetectedArgIndex;
1032  vector<Argument *> lastDetectedArgPath;
1033  if (lastDetectedArg) {
1034  lastDetectedArgIndex = reader.lastArgDenotation - argv;
1035  lastDetectedArgPath = lastDetectedArg->path(lastDetectedArg->occurrences() - 1);
1036  }
1037 
1038  // determine last arg, omitting trailing empty args
1039  const char *const *lastSpecifiedArg;
1040  unsigned int lastSpecifiedArgIndex;
1041  if (argc) {
1042  lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1043  lastSpecifiedArg = argv + lastSpecifiedArgIndex;
1044  for (; lastSpecifiedArg >= argv && **lastSpecifiedArg == '\0'; --lastSpecifiedArg, --lastSpecifiedArgIndex)
1045  ;
1046  }
1047 
1048  // determine arguments relevant for completion
1049  bool nextArgumentOrValue;
1050  if (lastDetectedArg && lastDetectedArg->isPresent()) {
1051  if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
1052  // define function to add parameter values of argument as possible completions
1053  const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
1054  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
1055  relevantPreDefinedValues.push_back(arg);
1056  }
1057  if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
1058  || !arg->preDefinedCompletionValues()) {
1059  completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
1060  completeDirs = completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
1061  }
1062  };
1063 
1064  // detect number of specified values
1065  auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
1066  // ignore values which are specified after the current word
1067  if (currentValueCount) {
1068  const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - lastDetectedArgIndex;
1069  if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1070  currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1071  } else {
1072  currentValueCount = 0;
1073  }
1074  }
1075 
1076  // add value completions for implicit child if there are no value specified and there are no values required by the
1077  // last detected argument itself
1078  if (!currentValueCount && !lastDetectedArg->requiredValueCount()) {
1079  for (const Argument *child : lastDetectedArg->subArguments()) {
1080  if (child->isImplicit() && child->requiredValueCount()) {
1081  addValueCompletionsForArg(child);
1082  break;
1083  }
1084  }
1085  }
1086 
1087  // add value completions for last argument if there are further values required
1088  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount || (currentValueCount < lastDetectedArg->requiredValueCount())) {
1089  addValueCompletionsForArg(lastDetectedArg);
1090  }
1091 
1092  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount
1093  || lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
1094  // sub arguments of the last arg are possible completions
1095  for (const Argument *subArg : lastDetectedArg->subArguments()) {
1096  if (subArg->occurrences() < subArg->maxOccurrences()) {
1097  relevantArgs.push_back(subArg);
1098  }
1099  }
1100 
1101  // siblings of parents are possible completions as well
1102  for (auto parentArgument = lastDetectedArgPath.crbegin(), end = lastDetectedArgPath.crend();; ++parentArgument) {
1103  insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, relevantArgs);
1104  if (parentArgument == end) {
1105  break;
1106  }
1107  }
1108  }
1109  } else {
1110  // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1111  relevantArgs.push_back(lastDetectedArg);
1112  }
1113 
1114  } else {
1115  // no arguments detected -> just use main arguments for completion
1116  nextArgumentOrValue = true;
1117  insertSiblings(m_mainArgs, relevantArgs);
1118  }
1119 
1120  // read the "opening" (started but not finished argument denotation)
1121  const char *opening = nullptr;
1122  string compoundOpening;
1123  size_t openingLen, compoundOpeningStartLen = 0;
1124  unsigned char openingDenotationType = Value;
1125  if (argc && nextArgumentOrValue) {
1126  if (currentWordIndex < static_cast<unsigned int>(argc)) {
1127  opening = argv[currentWordIndex];
1128  // For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
1129  // equation sign is an own argument ("set --values disk = 1 tag = a").
1130  // This is not how values are treated by the argument parser. Hence the opening
1131  // must be joined again. In this case only the part after the equation sign needs to be
1132  // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1133  size_t minCurrentWordIndex = (lastDetectedArg ? lastDetectedArgIndex : 0);
1134  if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1135  compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1136  compoundOpening = argv[currentWordIndex];
1137  compoundOpening += '=';
1138  } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1139  compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1140  compoundOpening = argv[currentWordIndex];
1141  compoundOpening += '=';
1142  compoundOpening += opening;
1143  }
1144  if (!compoundOpening.empty()) {
1145  opening = compoundOpening.data();
1146  }
1147  } else {
1148  opening = *lastSpecifiedArg;
1149  }
1150  *opening == '-' && (++opening, ++openingDenotationType) && *opening == '-' && (++opening, ++openingDenotationType);
1151  openingLen = strlen(opening);
1152  }
1153 
1154  relevantArgs.sort(compareArgs);
1155 
1156  // print "COMPREPLY" bash array
1157  cout << "COMPREPLY=(";
1158  // -> completions for parameter values
1159  for (const Argument *arg : relevantPreDefinedValues) {
1160  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1161  arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1162  }
1163  if (arg->preDefinedCompletionValues()) {
1164  bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1165  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1166  if (openingDenotationType == Value) {
1167  bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1168  size_t wordIndex = 0;
1169  for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1170  if (wordStart) {
1171  const char *i1 = i, *i2 = opening;
1172  for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1173  ;
1174  if ((ok = (i2 == end))) {
1175  cout << '\'';
1176  }
1177  wordStart = false;
1178  wordIndex = 0;
1179  } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1180  equationSignAlreadyPresent = false;
1181  if (ok) {
1182  cout << '\'' << ' ';
1183  }
1184  ++i;
1185  continue;
1186  } else if (*i == '=') {
1187  equationSignAlreadyPresent = true;
1188  }
1189  if (ok) {
1190  if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1191  if (*i == '\'') {
1192  cout << "'\"'\"'";
1193  } else {
1194  cout << *i;
1195  }
1196  }
1197  ++i, ++wordIndex;
1198  switch (*i) {
1199  case ' ':
1200  case '\n':
1201  case '\0':
1202  if (appendEquationSign && !equationSignAlreadyPresent) {
1203  cout << '=';
1204  noWhitespace = true;
1205  equationSignAlreadyPresent = false;
1206  }
1207  if (*i == '\0') {
1208  cout << '\'';
1209  }
1210  }
1211  } else {
1212  ++i;
1213  }
1214  }
1215  cout << ' ';
1216  }
1217  } else if (const char *i = arg->preDefinedCompletionValues()) {
1218  bool equationSignAlreadyPresent = false;
1219  cout << '\'';
1220  while (*i) {
1221  if (*i == '\'') {
1222  cout << "'\"'\"'";
1223  } else {
1224  cout << *i;
1225  }
1226  switch (*(++i)) {
1227  case '=':
1228  equationSignAlreadyPresent = true;
1229  break;
1230  case ' ':
1231  case '\n':
1232  case '\0':
1233  if (appendEquationSign && !equationSignAlreadyPresent) {
1234  cout << '=';
1235  equationSignAlreadyPresent = false;
1236  }
1237  if (*i != '\0') {
1238  cout << '\'';
1239  if (*(++i)) {
1240  cout << ' ' << '\'';
1241  }
1242  }
1243  }
1244  }
1245  cout << '\'' << ' ';
1246  }
1247  }
1248  }
1249  // -> completions for further arguments
1250  for (const Argument *arg : relevantArgs) {
1251  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1252  switch (openingDenotationType) {
1253  case Value:
1254  if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1255  continue;
1256  }
1257  break;
1258  case Abbreviation:
1259  break;
1260  case FullName:
1261  if (strncmp(arg->name(), opening, openingLen)) {
1262  continue;
1263  }
1264  }
1265  }
1266 
1267  if (opening && openingDenotationType == Abbreviation && !nextArgumentOrValue) {
1268  // TODO: add test for this case
1269  cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1270  } else if (lastDetectedArg && reader.argDenotationType == Abbreviation && !nextArgumentOrValue) {
1271  if (reader.argv == reader.end) {
1272  cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1273  }
1274  } else if (arg->denotesOperation()) {
1275  cout << '\'' << arg->name() << '\'' << ' ';
1276  } else {
1277  cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1278  }
1279  }
1280  // -> completions for files and dirs
1281  // -> if there's already an "opening", determine the dir part and the file part
1282  string actualDir, actualFile;
1283  bool haveFileOrDirCompletions = false;
1284  if (argc && currentWordIndex == lastSpecifiedArgIndex && opening) {
1285  // the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
1286  string unescapedOpening(opening);
1287  findAndReplace<string>(unescapedOpening, "\\ ", " ");
1288  findAndReplace<string>(unescapedOpening, "\\,", ",");
1289  findAndReplace<string>(unescapedOpening, "\\[", "[");
1290  findAndReplace<string>(unescapedOpening, "\\]", "]");
1291  findAndReplace<string>(unescapedOpening, "\\!", "!");
1292  findAndReplace<string>(unescapedOpening, "\\#", "#");
1293  findAndReplace<string>(unescapedOpening, "\\$", "$");
1294  findAndReplace<string>(unescapedOpening, "\\'", "'");
1295  findAndReplace<string>(unescapedOpening, "\\\"", "\"");
1296  findAndReplace<string>(unescapedOpening, "\\\\", "\\");
1297  // determine the "directory" part
1298  string dir = directory(unescapedOpening);
1299  if (dir.empty()) {
1300  actualDir = ".";
1301  } else {
1302  if (dir[0] == '\"' || dir[0] == '\'') {
1303  dir.erase(0, 1);
1304  }
1305  if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1306  dir.erase(dir.size() - 2, 1);
1307  }
1308  actualDir = move(dir);
1309  }
1310  // determine the "file" part
1311  string file = fileName(unescapedOpening);
1312  if (file[0] == '\"' || file[0] == '\'') {
1313  file.erase(0, 1);
1314  }
1315  if (file.size() > 1 && (file[dir.size() - 2] == '\"' || dir[file.size() - 2] == '\'')) {
1316  file.erase(file.size() - 2, 1);
1317  }
1318  actualFile = move(file);
1319  }
1320 
1321  // -> completion for files and dirs
1323  if (completeFiles) {
1324  entryTypes |= DirectoryEntryType::File;
1325  }
1326  if (completeDirs) {
1327  entryTypes |= DirectoryEntryType::Directory;
1328  }
1329  if (entryTypes != DirectoryEntryType::None) {
1330  const string replace("'"), with("'\"'\"'");
1331  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1332  list<string> entries = directoryEntries(actualDir.c_str(), entryTypes);
1333  findAndReplace(actualDir, replace, with);
1334  for (string &dirEntry : entries) {
1335  if (startsWith(dirEntry, actualFile)) {
1336  cout << '\'';
1337  if (actualDir != ".") {
1338  cout << actualDir;
1339  }
1340  findAndReplace(dirEntry, replace, with);
1341  cout << dirEntry << '\'' << ' ';
1342  haveFileOrDirCompletions = true;
1343  }
1344  }
1345  } else {
1346  for (string &dirEntry : directoryEntries(".", entryTypes)) {
1347  findAndReplace(dirEntry, replace, with);
1348  cout << '\'' << dirEntry << '\'' << ' ';
1349  haveFileOrDirCompletions = true;
1350  }
1351  }
1352  }
1353  cout << ')';
1354 
1355  // ensure file or dir completions are formatted appropriately
1356  if (haveFileOrDirCompletions) {
1357  cout << "; compopt -o filenames";
1358  }
1359 
1360  // ensure trailing whitespace is ommitted
1361  if (noWhitespace) {
1362  cout << "; compopt -o nospace";
1363  }
1364 
1365  cout << endl;
1366 }
1367 
1373 {
1374  for (const Argument *arg : args) {
1375  const auto occurrences = arg->occurrences();
1376  if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1377  throw Failure(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1378  (arg->maxOccurrences() == 1 ? " time." : " times.")));
1379  }
1380  if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1381  throw Failure(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1382  (arg->minOccurrences() == 1 ? " time." : " times.")));
1383  }
1384  Argument *conflictingArgument = nullptr;
1385  if (arg->isMainArgument()) {
1386  if (!arg->isCombinable() && arg->isPresent()) {
1387  conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1388  }
1389  } else {
1390  conflictingArgument = arg->conflictsWithArgument();
1391  }
1392  if (conflictingArgument) {
1393  throw Failure(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1394  }
1395  for (size_t i = 0; i != occurrences; ++i) {
1396  if (!arg->allRequiredValuesPresent(i)) {
1397  stringstream ss(stringstream::in | stringstream::out);
1398  ss << "Not all parameter for argument \"" << arg->name() << "\" ";
1399  if (i) {
1400  ss << " (" << (i + 1) << " occurrence) ";
1401  }
1402  ss << "provided. You have to provide the following parameter:";
1403  size_t valueNamesPrint = 0;
1404  for (const auto &name : arg->m_valueNames) {
1405  ss << ' ' << name, ++valueNamesPrint;
1406  }
1407  if (arg->m_requiredValueCount != Argument::varValueCount) {
1408  while (valueNamesPrint < arg->m_requiredValueCount) {
1409  ss << "\nvalue " << (++valueNamesPrint);
1410  }
1411  }
1412  throw Failure(ss.str());
1413  }
1414  }
1415 
1416  // check contraints of sub arguments recursively
1417  checkConstraints(arg->m_subArgs);
1418  }
1419 }
1420 
1429 {
1430  for (const Argument *arg : args) {
1431  // invoke the callback for each occurrence of the argument
1432  if (arg->m_callbackFunction) {
1433  for (const auto &occurrence : arg->m_occurrences) {
1434  arg->m_callbackFunction(occurrence);
1435  }
1436  }
1437  // invoke the callbacks for sub arguments recursively
1438  invokeCallbacks(arg->m_subArgs);
1439  }
1440 }
1441 
1452  : Argument("help", 'h', "shows this information")
1453 {
1454  setCallback([&parser](const ArgumentOccurrence &) {
1456  parser.printHelp(cout);
1457  });
1458 }
1459 
1490 NoColorArgument *NoColorArgument::s_instance = nullptr;
1491 
1497 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1498  : Argument("no-color", '\0', "disables formatted/colorized output")
1499 #else
1500  : Argument("enable-color", '\0', "enables formatted/colorized output")
1501 #endif
1502 {
1503  setCombinable(true);
1504 
1505  if (s_instance) {
1506  return;
1507  }
1508  s_instance = this;
1509 
1510  // set the environmentvariable: note that this is not directly used and just assigned for printing help
1511  setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1512 
1513  // default-initialize EscapeCodes::enabled from environment variable
1514  const char *envValue = getenv(environmentVariable());
1515  if (!envValue) {
1516  return;
1517  }
1518  for (; *envValue; ++envValue) {
1519  switch (*envValue) {
1520  case '0':
1521  case ' ':
1522  break;
1523  default:
1524  // enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
1525  EscapeCodes::enabled = true;
1526  return;
1527  }
1528  }
1529  // disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
1530  EscapeCodes::enabled = false;
1531 }
1532 
1537 {
1538  if (s_instance == this) {
1539  s_instance = nullptr;
1540  }
1541 }
1542 
1547 {
1548  if (NoColorArgument::s_instance && NoColorArgument::s_instance->isPresent()) {
1549 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1550  EscapeCodes::enabled = false;
1551 #else
1552  EscapeCodes::enabled = true;
1553 #endif
1554  }
1555 }
1556 
1557 } // 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