C++ Utilities  4.11.0
Common C++ classes and routines used by my applications 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 
90 {
91  // method is called recursively for sub args to the last argument (which is nullptr in the initial call) is the current parent argument
92  Argument *const parentArg = lastArg;
93  // determine the current path
94  const vector<Argument *> &parentPath = parentArg ? parentArg->path(parentArg->occurrences() - 1) : vector<Argument *>();
95 
96  Argument *lastArgInLevel = nullptr;
97  vector<const char *> *values = nullptr;
98 
99  // iterate through all argument denotations; loop might exit earlier when an denotation is unknown
100  while (argv != end) {
101  // check whether there are still values to read
102  if (values && lastArgInLevel->requiredValueCount() != Argument::varValueCount && values->size() < lastArgInLevel->requiredValueCount()) {
103  // read arg as value and continue with next arg
104  values->emplace_back(argDenotation ? argDenotation : *argv);
105  ++index, ++argv, argDenotation = nullptr;
106  continue;
107  }
108 
109  // determine how denotation must be processed
110  bool abbreviationFound = false;
111  if (argDenotation) {
112  // continue reading childs for abbreviation denotation already detected
113  abbreviationFound = false;
115  } else {
116  // determine denotation type
117  argDenotation = *argv;
118  if (!*argDenotation && (!lastArgInLevel || values->size() >= lastArgInLevel->requiredValueCount())) {
119  // skip empty arguments
120  ++index, ++argv, argDenotation = nullptr;
121  continue;
122  }
123  abbreviationFound = false;
126  }
127 
128  // try to find matching Argument instance
129  Argument *matchingArg = nullptr;
130  size_t argDenotationLength;
131  if (argDenotationType != Value) {
132  const char *const equationPos = strchr(argDenotation, '=');
133  for (argDenotationLength = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation); argDenotationLength;
134  matchingArg = nullptr) {
135  // search for arguments by abbreviation or name depending on the previously determined denotation type
137  for (Argument *arg : args) {
138  if (arg->abbreviation() && arg->abbreviation() == *argDenotation) {
139  matchingArg = arg;
140  abbreviationFound = true;
141  break;
142  }
143  }
144  } else {
145  for (Argument *arg : args) {
146  if (arg->name() && !strncmp(arg->name(), argDenotation, argDenotationLength)
147  && *(arg->name() + argDenotationLength) == '\0') {
148  matchingArg = arg;
149  break;
150  }
151  }
152  }
153 
154  if (matchingArg) {
155  // an argument matched the specified denotation so add an occurrence
156  matchingArg->m_occurrences.emplace_back(index, parentPath, parentArg);
157 
158  // prepare reading parameter values
159  values = &matchingArg->m_occurrences.back().values;
160  if (equationPos) {
161  values->push_back(equationPos + 1);
162  }
163 
164  // read sub arguments
165  ++index, ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, lastArgDenotation = argv;
166  if (argDenotationType != Abbreviation || (++argDenotation != equationPos)) {
168  // no further abbreviations follow -> read sub args for next argv
169  ++argv, argDenotation = nullptr;
170  read(lastArg->m_subArgs);
171  argDenotation = nullptr;
172  } else {
173  // further abbreviations follow -> don't increment argv, keep processing outstanding chars of argDenotation
174  read(lastArg->m_subArgs);
175  }
176  break;
177  } // else: another abbreviated argument follows (and it is not present in the sub args)
178  } else {
179  break;
180  }
181  }
182  }
183 
184  // continue with next arg if we've got a match already
185  if (matchingArg) {
186  continue;
187  }
188 
189  // unknown argument might be a sibling of the parent element
190  if (argDenotationType != Value) {
191  for (auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
192  for (Argument *sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() : parser.m_mainArgs)) {
193  if (sibling->occurrences() < sibling->maxOccurrences()) {
194  if ((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation))
195  || (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenotationLength))) {
196  return;
197  }
198  }
199  }
200  if (parentArgument == pathEnd) {
201  break;
202  }
203  };
204  }
205 
206  // unknown argument might just be a parameter value of the last argument
207  if (lastArgInLevel && values->size() < lastArgInLevel->requiredValueCount()) {
208  values->emplace_back(abbreviationFound ? argDenotation : *argv);
209  ++index, ++argv, argDenotation = nullptr;
210  continue;
211  }
212 
213  // first value might denote "operation"
214  for (Argument *arg : args) {
215  if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
216  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
218  ++index, ++argv;
219  break;
220  }
221  }
222 
223  // use the first default argument which is not already present if there is still no match
224  if (!matchingArg && (!completionMode || (argv + 1 != end))) {
225  const bool uncombinableMainArgPresent = parentArg ? false : parser.isUncombinableMainArgPresent();
226  for (Argument *arg : args) {
227  if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
228  && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
229  (matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
230  break;
231  }
232  }
233  }
234 
235  if (matchingArg) {
236  // an argument matched the specified denotation
237  if (lastArgInLevel == matchingArg) {
238  break; // break required? -> TODO: add test for this condition
239  }
240 
241  // prepare reading parameter values
242  values = &matchingArg->m_occurrences.back().values;
243 
244  // read sub arguments
245  ++parser.m_actualArgc, lastArg = lastArgInLevel = matchingArg, argDenotation = nullptr;
246  read(lastArg->m_subArgs);
247  argDenotation = nullptr;
248  continue;
249  }
250 
251  // argument denotation is unknown -> handle error
252  if (parentArg) {
253  // continue with parent level
254  return;
255  }
256  if (completionMode) {
257  // ignore unknown denotation
258  ++index, ++argv, argDenotation = nullptr;
259  } else {
260  switch (parser.m_unknownArgBehavior) {
262  cerr << Phrases::Warning << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << Phrases::EndFlush;
263  FALLTHROUGH;
265  // ignore unknown denotation
266  ++index, ++argv, argDenotation = nullptr;
267  break;
269  throw Failure(argsToString("The specified argument \"", *argv, "\" is unknown."));
270  }
271  }
272  } // while(argv != end)
273 }
274 
281 ostream &operator<<(ostream &os, const Wrapper &wrapper)
282 {
283  // determine max. number of columns
284  static const TerminalSize termSize(determineTerminalSize());
285  const auto maxColumns = termSize.columns ? termSize.columns : numeric_limits<unsigned short>::max();
286 
287  // print wrapped string considering indentation
288  unsigned short currentCol = wrapper.m_indentation.level;
289  for (const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
290  const bool wrappingRequired = currentCol >= maxColumns;
291  if (wrappingRequired || *currentChar == '\n') {
292  // insert newline (TODO: wrap only at end of a word)
293  os << '\n';
294  // print indentation (if enough space)
295  if (wrapper.m_indentation.level < maxColumns) {
296  os << wrapper.m_indentation;
297  currentCol = wrapper.m_indentation.level;
298  } else {
299  currentCol = 0;
300  }
301  }
302  if (*currentChar != '\n' && (!wrappingRequired || *currentChar != ' ')) {
303  os << *currentChar;
304  ++currentCol;
305  }
306  }
307  return os;
308 }
309 
311 const char *applicationName = nullptr;
313 const char *applicationAuthor = nullptr;
315 const char *applicationVersion = nullptr;
317 const char *applicationUrl = nullptr;
319 std::initializer_list<const char *> dependencyVersions;
320 
325 void (*exitFunction)(int) = &exit;
326 
328 
329 inline bool notEmpty(const char *str)
330 {
331  return str && *str;
332 }
333 
335 
352 Argument::Argument(const char *name, char abbreviation, const char *description, const char *example)
353  : m_name(name)
354  , m_abbreviation(abbreviation)
355  , m_environmentVar(nullptr)
356  , m_description(description)
357  , m_example(example)
358  , m_minOccurrences(0)
359  , m_maxOccurrences(1)
360  , m_combinable(false)
361  , m_denotesOperation(false)
362  , m_requiredValueCount(0)
363  , m_implicit(false)
364  , m_isMainArg(false)
367  , m_preDefinedCompletionValues(nullptr)
368 {
369 }
370 
375 {
376 }
377 
385 const char *Argument::firstValue() const
386 {
387  if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
388  return m_occurrences.front().values.front();
389  } else if (m_environmentVar) {
390  return getenv(m_environmentVar);
391  } else {
392  return nullptr;
393  }
394 }
395 
399 void Argument::printInfo(ostream &os, unsigned char indentation) const
400 {
401  Indentation ident(indentation);
402  os << ident;
404  if (notEmpty(name())) {
405  if (!denotesOperation()) {
406  os << '-' << '-';
407  }
408  os << name();
409  }
410  if (notEmpty(name()) && abbreviation()) {
411  os << ',' << ' ';
412  }
413  if (abbreviation()) {
414  os << '-' << abbreviation();
415  }
417  if (requiredValueCount()) {
418  unsigned int valueNamesPrint = 0;
419  for (auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
420  os << ' ' << '[' << *i << ']';
421  ++valueNamesPrint;
422  }
424  os << " ...";
425  } else {
426  for (; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
427  os << " [value " << (valueNamesPrint + 1) << ']';
428  }
429  }
430  }
431  ident.level += 2;
432  if (notEmpty(description())) {
433  os << '\n' << ident << Wrapper(description(), ident);
434  }
435  if (isRequired()) {
436  os << '\n' << ident << "particularities: mandatory";
437  if (!isMainArgument()) {
438  os << " if parent argument is present";
439  }
440  }
441  if (environmentVariable()) {
442  os << '\n' << ident << "default environment variable: " << Wrapper(environmentVariable(), ident + 30);
443  }
444  os << '\n';
445  for (const auto *arg : subArguments()) {
446  arg->printInfo(os, ident.level);
447  }
448  if (notEmpty(example())) {
449  if (ident.level == 2 && !subArguments().empty()) {
450  os << '\n';
451  }
452  os << ident << "example: " << Wrapper(example(), ident + 9);
453  os << '\n';
454  }
455 }
456 
463 {
464  for (Argument *arg : args) {
465  if (arg != except && arg->isPresent() && !arg->isCombinable()) {
466  return arg;
467  }
468  }
469  return nullptr;
470 }
471 
486 void Argument::setSubArguments(const ArgumentInitializerList &secondaryArguments)
487 {
488  // remove this argument from the parents list of the previous secondary arguments
489  for (Argument *arg : m_subArgs) {
490  arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
491  }
492  // assign secondary arguments
493  m_subArgs.assign(secondaryArguments);
494  // add this argument to the parents list of the assigned secondary arguments
495  // and set the parser
496  for (Argument *arg : m_subArgs) {
497  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
498  arg->m_parents.push_back(this);
499  }
500  }
501 }
502 
511 {
512  if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) == m_subArgs.cend()) {
513  m_subArgs.push_back(arg);
514  if (find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
515  arg->m_parents.push_back(this);
516  }
517  }
518 }
519 
525 {
526  if (isMainArgument()) {
527  return true;
528  }
529  for (const Argument *parent : m_parents) {
530  if (parent->isPresent()) {
531  return true;
532  }
533  }
534  return false;
535 }
536 
546 {
547  return isPresent() ? wouldConflictWithArgument() : nullptr;
548 }
549 
559 {
560  if (!isCombinable()) {
561  for (Argument *parent : m_parents) {
562  for (Argument *sibling : parent->subArguments()) {
563  if (sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
564  return sibling;
565  }
566  }
567  }
568  }
569  return nullptr;
570 }
571 
577 {
578  for (Argument *arg : m_subArgs) {
579  if (arg->denotesOperation() && arg->isPresent()) {
580  return arg;
581  }
582  }
583  return nullptr;
584 }
585 
591 {
592  for (Argument *arg : m_subArgs) {
593  arg->resetRecursively();
594  }
595  reset();
596 }
597 
615  : m_actualArgc(0)
616  , m_executable(nullptr)
617  , m_unknownArgBehavior(UnknownArgumentBehavior::Fail)
618  , m_defaultArg(nullptr)
619 {
620 }
621 
632 {
633  if (mainArguments.size()) {
634  for (Argument *arg : mainArguments) {
635  arg->m_isMainArg = true;
636  }
637  m_mainArgs.assign(mainArguments);
638  if (!m_defaultArg) {
639  if (!(*mainArguments.begin())->requiredValueCount()) {
640  bool subArgsRequired = false;
641  for (const Argument *subArg : (*mainArguments.begin())->subArguments()) {
642  if (subArg->isRequired()) {
643  subArgsRequired = true;
644  break;
645  }
646  }
647  if (!subArgsRequired) {
648  m_defaultArg = *mainArguments.begin();
649  }
650  }
651  }
652  } else {
653  m_mainArgs.clear();
654  }
655 }
656 
664 {
665  argument->m_isMainArg = true;
666  m_mainArgs.push_back(argument);
667 }
668 
672 void ArgumentParser::printHelp(ostream &os) const
673 {
675  if (applicationName && *applicationName) {
676  os << applicationName;
677  if (applicationVersion && *applicationVersion) {
678  os << ',' << ' ';
679  }
680  }
681  if (applicationVersion && *applicationVersion) {
682  os << "version " << applicationVersion;
683  }
684  if (dependencyVersions.size()) {
685  if ((applicationName && *applicationName) || (applicationVersion && *applicationVersion)) {
686  os << '\n';
688  }
689  auto i = dependencyVersions.begin(), end = dependencyVersions.end();
690  os << "Linked against: " << *i;
691  for (++i; i != end; ++i) {
692  os << ',' << ' ' << *i;
693  }
694  }
695  if ((applicationName && *applicationName) || (applicationVersion && *applicationVersion) || dependencyVersions.size()) {
696  os << '\n' << '\n';
697  }
699  if (!m_mainArgs.empty()) {
700  os << "Available arguments:";
701  for (const Argument *arg : m_mainArgs) {
702  os << '\n';
703  arg->printInfo(os);
704  }
705  }
706  if (applicationUrl && *applicationUrl) {
707  os << "\nProject website: " << applicationUrl << endl;
708  }
709 }
710 
722 void ArgumentParser::parseArgs(int argc, const char *const *argv)
723 {
724  readArgs(argc, argv);
725  if (argc) {
726  checkConstraints(m_mainArgs);
727  invokeCallbacks(m_mainArgs);
728  }
729 }
730 
738 void ArgumentParser::parseArgsOrExit(int argc, const char *const *argv)
739 {
740  try {
741  parseArgs(argc, argv);
742  } catch (const Failure &failure) {
744  cerr << failure;
745  exit(1);
746  }
747 }
748 
760 void ArgumentParser::readArgs(int argc, const char *const *argv)
761 {
762  IF_DEBUG_BUILD(verifyArgs(m_mainArgs, std::vector<char>(), std::vector<const char *>());)
763  m_actualArgc = 0;
764  if (argc) {
765  // the first argument is the executable name
766  m_executable = *argv;
767 
768  // check for further arguments
769  if (--argc) {
770  // if the first argument (after executable name) is "--bash-completion-for", bash completion for the following arguments is requested
771  bool completionMode = !strcmp(*++argv, "--bash-completion-for");
772  unsigned int currentWordIndex;
773  if (completionMode) {
774  // the first argument after "--bash-completion-for" is the index of the current word
775  try {
776  currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
777  if (argc) {
778  ++argv, --argc;
779  }
780  } catch (const ConversionException &) {
781  currentWordIndex = static_cast<unsigned int>(argc - 1);
782  }
783  }
784 
785  // read specified arguments
786  ArgumentReader reader(*this, argv,
787  argv + (completionMode ? min(static_cast<unsigned int>(argc), currentWordIndex + 1) : static_cast<unsigned int>(argc)),
788  completionMode);
789  try {
790  reader.read();
792  } catch (const Failure &) {
794  if (!completionMode) {
795  throw;
796  }
797  }
798 
799  if (completionMode) {
800  printBashCompletion(argc, argv, currentWordIndex, reader);
801  exitFunction(0); // prevent the applicaton to continue with the regular execution
802  }
803  } else {
804  // no arguments specified -> flag default argument as present if one is assigned
805  if (m_defaultArg) {
806  m_defaultArg->m_occurrences.emplace_back(0);
807  }
808  }
809  } else {
810  m_executable = nullptr;
811  }
812 }
813 
819 {
820  for (Argument *arg : m_mainArgs) {
821  arg->resetRecursively();
822  }
823  m_actualArgc = 0;
824 }
825 
832 {
833  for (Argument *arg : m_mainArgs) {
834  if (arg->denotesOperation() && arg->isPresent()) {
835  return arg;
836  }
837  }
838  return nullptr;
839 }
840 
845 {
846  for (const Argument *arg : m_mainArgs) {
847  if (!arg->isCombinable() && arg->isPresent()) {
848  return true;
849  }
850  }
851  return false;
852 }
853 
854 #ifdef DEBUG_BUILD
855 
871 void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args, vector<char>, vector<const char *>)
872 {
873  vector<const Argument *> verifiedArgs;
874  verifiedArgs.reserve(args.size());
875  vector<char> abbreviations;
876  abbreviations.reserve(abbreviations.size() + args.size());
877  vector<const char *> names;
878  names.reserve(names.size() + args.size());
879  bool hasImplicit = false;
880  for (const Argument *arg : args) {
881  assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
882  verifiedArgs.push_back(arg);
883  assert(!arg->isImplicit() || !hasImplicit);
884  hasImplicit |= arg->isImplicit();
885  assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
886  abbreviations.push_back(arg->abbreviation());
887  assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
888  assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
889  names.emplace_back(arg->name());
890  }
891  for (const Argument *arg : args) {
892  verifyArgs(arg->subArguments(), vector<char>(), vector<const char *>());
893  }
894 }
895 #endif
896 
904 bool compareArgs(const Argument *arg1, const Argument *arg2)
905 {
906  if (arg1->denotesOperation() && !arg2->denotesOperation()) {
907  return true;
908  } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
909  return false;
910  } else {
911  return strcmp(arg1->name(), arg2->name()) < 0;
912  }
913 }
914 
919 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
920 {
921  bool onlyCombinable = false;
922  for (const Argument *sibling : siblings) {
923  if (sibling->isPresent() && !sibling->isCombinable()) {
924  onlyCombinable = true;
925  break;
926  }
927  }
928  for (const Argument *sibling : siblings) {
929  if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
930  target.push_back(sibling);
931  }
932  }
933 }
934 
940 void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader)
941 {
942  // variables to store relevant completions (arguments, pre-defined values, files/dirs)
943  list<const Argument *> relevantArgs, relevantPreDefinedValues;
944  bool completeFiles = false, completeDirs = false, noWhitespace = false;
945 
946  // get the last argument the argument parser was able to detect successfully
947  const Argument *const lastDetectedArg = reader.lastArg;
948  size_t lastDetectedArgIndex;
949  vector<Argument *> lastDetectedArgPath;
950  if (lastDetectedArg) {
951  lastDetectedArgIndex = reader.lastArgDenotation - argv;
952  lastDetectedArgPath = lastDetectedArg->path(lastDetectedArg->occurrences() - 1);
953  }
954 
955  // determine last arg, omitting trailing empty args
956  const char *const *lastSpecifiedArg;
957  unsigned int lastSpecifiedArgIndex;
958  if (argc) {
959  lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
960  lastSpecifiedArg = argv + lastSpecifiedArgIndex;
961  for (; lastSpecifiedArg >= argv && **lastSpecifiedArg == '\0'; --lastSpecifiedArg, --lastSpecifiedArgIndex)
962  ;
963  }
964 
965  // determine arguments relevant for completion
966  bool nextArgumentOrValue;
967  if (lastDetectedArg && lastDetectedArg->isPresent()) {
968  if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
969  // define function to add parameter values of argument as possible completions
970  const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
971  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
972  relevantPreDefinedValues.push_back(arg);
973  }
974  if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
975  || !arg->preDefinedCompletionValues()) {
976  completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
977  completeDirs = completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
978  }
979  };
980 
981  // detect number of specified values
982  auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
983  // ignore values which are specified after the current word
984  if (currentValueCount) {
985  const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - lastDetectedArgIndex;
986  if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
987  currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
988  } else {
989  currentValueCount = 0;
990  }
991  }
992 
993  // add value completions for implicit child if there are no value specified and there are no values required by the
994  // last detected argument itself
995  if (!currentValueCount && !lastDetectedArg->requiredValueCount()) {
996  for (const Argument *child : lastDetectedArg->subArguments()) {
997  if (child->isImplicit() && child->requiredValueCount()) {
998  addValueCompletionsForArg(child);
999  break;
1000  }
1001  }
1002  }
1003 
1004  // add value completions for last argument if there are further values required
1005  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount || (currentValueCount < lastDetectedArg->requiredValueCount())) {
1006  addValueCompletionsForArg(lastDetectedArg);
1007  }
1008 
1009  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount
1010  || lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
1011  // sub arguments of the last arg are possible completions
1012  for (const Argument *subArg : lastDetectedArg->subArguments()) {
1013  if (subArg->occurrences() < subArg->maxOccurrences()) {
1014  relevantArgs.push_back(subArg);
1015  }
1016  }
1017 
1018  // siblings of parents are possible completions as well
1019  for (auto parentArgument = lastDetectedArgPath.crbegin(), end = lastDetectedArgPath.crend();; ++parentArgument) {
1020  insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, relevantArgs);
1021  if (parentArgument == end) {
1022  break;
1023  }
1024  }
1025  }
1026  } else {
1027  // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1028  relevantArgs.push_back(lastDetectedArg);
1029  }
1030 
1031  } else {
1032  // no arguments detected -> just use main arguments for completion
1033  nextArgumentOrValue = true;
1034  insertSiblings(m_mainArgs, relevantArgs);
1035  }
1036 
1037  // read the "opening" (started but not finished argument denotation)
1038  const char *opening = nullptr;
1039  string compoundOpening;
1040  size_t openingLen, compoundOpeningStartLen = 0;
1041  unsigned char openingDenotationType = Value;
1042  if (argc && nextArgumentOrValue) {
1043  if (currentWordIndex < static_cast<unsigned int>(argc)) {
1044  opening = argv[currentWordIndex];
1045  // For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
1046  // equation sign is an own argument ("set --values disk = 1 tag = a").
1047  // This is not how values are treated by the argument parser. Hence the opening
1048  // must be joined again. In this case only the part after the equation sign needs to be
1049  // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1050  size_t minCurrentWordIndex = (lastDetectedArg ? lastDetectedArgIndex : 0);
1051  if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1052  compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1053  compoundOpening = argv[currentWordIndex];
1054  compoundOpening += '=';
1055  } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1056  compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1057  compoundOpening = argv[currentWordIndex];
1058  compoundOpening += '=';
1059  compoundOpening += opening;
1060  }
1061  if (!compoundOpening.empty()) {
1062  opening = compoundOpening.data();
1063  }
1064  } else {
1065  opening = *lastSpecifiedArg;
1066  }
1067  *opening == '-' && (++opening, ++openingDenotationType) && *opening == '-' && (++opening, ++openingDenotationType);
1068  openingLen = strlen(opening);
1069  }
1070 
1071  relevantArgs.sort(compareArgs);
1072 
1073  // print "COMPREPLY" bash array
1074  cout << "COMPREPLY=(";
1075  // -> completions for parameter values
1076  for (const Argument *arg : relevantPreDefinedValues) {
1077  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1078  arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1079  }
1080  if (arg->preDefinedCompletionValues()) {
1081  bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1082  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1083  if (openingDenotationType == Value) {
1084  bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1085  size_t wordIndex = 0;
1086  for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1087  if (wordStart) {
1088  const char *i1 = i, *i2 = opening;
1089  for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1090  ;
1091  if ((ok = (i2 == end))) {
1092  cout << '\'';
1093  }
1094  wordStart = false;
1095  wordIndex = 0;
1096  } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1097  equationSignAlreadyPresent = false;
1098  if (ok) {
1099  cout << '\'' << ' ';
1100  }
1101  ++i;
1102  continue;
1103  } else if (*i == '=') {
1104  equationSignAlreadyPresent = true;
1105  }
1106  if (ok) {
1107  if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1108  if (*i == '\'') {
1109  cout << "'\"'\"'";
1110  } else {
1111  cout << *i;
1112  }
1113  }
1114  ++i, ++wordIndex;
1115  switch (*i) {
1116  case ' ':
1117  case '\n':
1118  case '\0':
1119  if (appendEquationSign && !equationSignAlreadyPresent) {
1120  cout << '=';
1121  noWhitespace = true;
1122  equationSignAlreadyPresent = false;
1123  }
1124  if (*i == '\0') {
1125  cout << '\'';
1126  }
1127  }
1128  } else {
1129  ++i;
1130  }
1131  }
1132  cout << ' ';
1133  }
1134  } else if (const char *i = arg->preDefinedCompletionValues()) {
1135  bool equationSignAlreadyPresent = false;
1136  cout << '\'';
1137  while (*i) {
1138  if (*i == '\'') {
1139  cout << "'\"'\"'";
1140  } else {
1141  cout << *i;
1142  }
1143  switch (*(++i)) {
1144  case '=':
1145  equationSignAlreadyPresent = true;
1146  break;
1147  case ' ':
1148  case '\n':
1149  case '\0':
1150  if (appendEquationSign && !equationSignAlreadyPresent) {
1151  cout << '=';
1152  equationSignAlreadyPresent = false;
1153  }
1154  if (*i != '\0') {
1155  cout << '\'';
1156  if (*(++i)) {
1157  cout << ' ' << '\'';
1158  }
1159  }
1160  }
1161  }
1162  cout << '\'' << ' ';
1163  }
1164  }
1165  }
1166  // -> completions for further arguments
1167  for (const Argument *arg : relevantArgs) {
1168  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1169  switch (openingDenotationType) {
1170  case Value:
1171  if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1172  continue;
1173  }
1174  break;
1175  case Abbreviation:
1176  break;
1177  case FullName:
1178  if (strncmp(arg->name(), opening, openingLen)) {
1179  continue;
1180  }
1181  }
1182  }
1183 
1184  if (opening && openingDenotationType == Abbreviation && !nextArgumentOrValue) {
1185  cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1186  } else if (lastDetectedArg && reader.argDenotationType == Abbreviation && !nextArgumentOrValue) {
1187  if (reader.argv == reader.end) {
1188  cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1189  }
1190  } else if (arg->denotesOperation()) {
1191  cout << '\'' << arg->name() << '\'' << ' ';
1192  } else {
1193  cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1194  }
1195  }
1196  // -> completions for files and dirs
1197  // -> if there's already an "opening", determine the dir part and the file part
1198  string actualDir, actualFile;
1199  bool haveFileOrDirCompletions = false;
1200  if (argc && currentWordIndex == lastSpecifiedArgIndex && opening) {
1201  // the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
1202  string unescapedOpening(opening);
1203  findAndReplace<string>(unescapedOpening, "\\ ", " ");
1204  findAndReplace<string>(unescapedOpening, "\\,", ",");
1205  findAndReplace<string>(unescapedOpening, "\\[", "[");
1206  findAndReplace<string>(unescapedOpening, "\\]", "]");
1207  findAndReplace<string>(unescapedOpening, "\\!", "!");
1208  findAndReplace<string>(unescapedOpening, "\\#", "#");
1209  findAndReplace<string>(unescapedOpening, "\\$", "$");
1210  findAndReplace<string>(unescapedOpening, "\\'", "'");
1211  findAndReplace<string>(unescapedOpening, "\\\"", "\"");
1212  findAndReplace<string>(unescapedOpening, "\\\\", "\\");
1213  // determine the "directory" part
1214  string dir = directory(unescapedOpening);
1215  if (dir.empty()) {
1216  actualDir = ".";
1217  } else {
1218  if (dir[0] == '\"' || dir[0] == '\'') {
1219  dir.erase(0, 1);
1220  }
1221  if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1222  dir.erase(dir.size() - 2, 1);
1223  }
1224  actualDir = move(dir);
1225  }
1226  // determine the "file" part
1227  string file = fileName(unescapedOpening);
1228  if (file[0] == '\"' || file[0] == '\'') {
1229  file.erase(0, 1);
1230  }
1231  if (file.size() > 1 && (file[dir.size() - 2] == '\"' || dir[file.size() - 2] == '\'')) {
1232  file.erase(file.size() - 2, 1);
1233  }
1234  actualFile = move(file);
1235  }
1236 
1237  // -> completion for files and dirs
1239  if (completeFiles) {
1240  entryTypes |= DirectoryEntryType::File;
1241  }
1242  if (completeDirs) {
1243  entryTypes |= DirectoryEntryType::Directory;
1244  }
1245  if (entryTypes != DirectoryEntryType::None) {
1246  const string replace("'"), with("'\"'\"'");
1247  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1248  list<string> entries = directoryEntries(actualDir.c_str(), entryTypes);
1249  findAndReplace(actualDir, replace, with);
1250  for (string &dirEntry : entries) {
1251  if (startsWith(dirEntry, actualFile)) {
1252  cout << '\'';
1253  if (actualDir != ".") {
1254  cout << actualDir;
1255  }
1256  findAndReplace(dirEntry, replace, with);
1257  cout << dirEntry << '\'' << ' ';
1258  haveFileOrDirCompletions = true;
1259  }
1260  }
1261  } else {
1262  for (string &dirEntry : directoryEntries(".", entryTypes)) {
1263  findAndReplace(dirEntry, replace, with);
1264  cout << '\'' << dirEntry << '\'' << ' ';
1265  haveFileOrDirCompletions = true;
1266  }
1267  }
1268  }
1269  cout << ')';
1270 
1271  // ensure file or dir completions are formatted appropriately
1272  if (haveFileOrDirCompletions) {
1273  cout << "; compopt -o filenames";
1274  }
1275 
1276  // ensure trailing whitespace is ommitted
1277  if (noWhitespace) {
1278  cout << "; compopt -o nospace";
1279  }
1280 
1281  cout << endl;
1282 }
1283 
1289 {
1290  for (const Argument *arg : args) {
1291  const auto occurrences = arg->occurrences();
1292  if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1293  throw Failure(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1294  (arg->maxOccurrences() == 1 ? " time." : " times.")));
1295  }
1296  if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1297  throw Failure(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1298  (arg->minOccurrences() == 1 ? " time." : " times.")));
1299  }
1300  Argument *conflictingArgument = nullptr;
1301  if (arg->isMainArgument()) {
1302  if (!arg->isCombinable() && arg->isPresent()) {
1303  conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1304  }
1305  } else {
1306  conflictingArgument = arg->conflictsWithArgument();
1307  }
1308  if (conflictingArgument) {
1309  throw Failure(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1310  }
1311  for (size_t i = 0; i != occurrences; ++i) {
1312  if (!arg->allRequiredValuesPresent(i)) {
1313  stringstream ss(stringstream::in | stringstream::out);
1314  ss << "Not all parameter for argument \"" << arg->name() << "\" ";
1315  if (i) {
1316  ss << " (" << (i + 1) << " occurrence) ";
1317  }
1318  ss << "provided. You have to provide the following parameter:";
1319  size_t valueNamesPrint = 0;
1320  for (const auto &name : arg->m_valueNames) {
1321  ss << ' ' << name, ++valueNamesPrint;
1322  }
1323  if (arg->m_requiredValueCount != static_cast<size_t>(-1)) {
1324  while (valueNamesPrint < arg->m_requiredValueCount) {
1325  ss << "\nvalue " << (++valueNamesPrint);
1326  }
1327  }
1328  throw Failure(ss.str());
1329  }
1330  }
1331 
1332  // check contraints of sub arguments recursively
1333  checkConstraints(arg->m_subArgs);
1334  }
1335 }
1336 
1345 {
1346  for (const Argument *arg : args) {
1347  // invoke the callback for each occurrence of the argument
1348  if (arg->m_callbackFunction) {
1349  for (const auto &occurrence : arg->m_occurrences) {
1350  arg->m_callbackFunction(occurrence);
1351  }
1352  }
1353  // invoke the callbacks for sub arguments recursively
1354  invokeCallbacks(arg->m_subArgs);
1355  }
1356 }
1357 
1368  : Argument("help", 'h', "shows this information")
1369 {
1370  setCallback([&parser](const ArgumentOccurrence &) {
1372  parser.printHelp(cout);
1373  });
1374 }
1375 
1406 NoColorArgument *NoColorArgument::s_instance = nullptr;
1407 
1413 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1414  : Argument("no-color", '\0', "disables formatted/colorized output")
1415 #else
1416  : Argument("enable-color", '\0', "enables formatted/colorized output")
1417 #endif
1418 {
1419  setCombinable(true);
1420 
1421  if (s_instance) {
1422  return;
1423  }
1424  s_instance = this;
1425 
1426  // set the environmentvariable: note that this is not directly used and just assigned for printing help
1427  setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1428 
1429  // default-initialize EscapeCodes::enabled from environment variable
1430  const char *envValue = getenv(environmentVariable());
1431  if (!envValue) {
1432  return;
1433  }
1434  for (; *envValue; ++envValue) {
1435  switch (*envValue) {
1436  case '0':
1437  case ' ':
1438  break;
1439  default:
1440  // enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
1441  EscapeCodes::enabled = true;
1442  return;
1443  }
1444  }
1445  // disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
1446  EscapeCodes::enabled = false;
1447 }
1448 
1453 {
1454  if (s_instance == this) {
1455  s_instance = nullptr;
1456  }
1457 }
1458 
1463 {
1464  if (NoColorArgument::s_instance && NoColorArgument::s_instance->isPresent()) {
1465 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1466  EscapeCodes::enabled = false;
1467 #else
1468  EscapeCodes::enabled = true;
1469 #endif
1470  }
1471 }
1472 
1473 } // 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...
void setCombinable(bool value)
Sets whether this argument can be combined.
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.
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...
const std::vector< const char * > & values(std::size_t occurrence=0) const
Returns the parameter values for the specified occurrence of argument.
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.
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.
void setEnvironmentVariable(const char *environmentVariable)
Sets the environment variable queried when firstValue() is called.
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