C++ Utilities  4.12.1
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, std::vector<char>(), std::vector<const char *>());)
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 
956 void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args, vector<char>, vector<const char *>)
957 {
958  vector<const Argument *> verifiedArgs;
959  verifiedArgs.reserve(args.size());
960  vector<char> abbreviations;
961  abbreviations.reserve(abbreviations.size() + args.size());
962  vector<const char *> names;
963  names.reserve(names.size() + args.size());
964  bool hasImplicit = false;
965  for (const Argument *arg : args) {
966  assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
967  verifiedArgs.push_back(arg);
968  assert(!arg->isImplicit() || !hasImplicit);
969  hasImplicit |= arg->isImplicit();
970  assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
971  abbreviations.push_back(arg->abbreviation());
972  assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
973  assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
974  names.emplace_back(arg->name());
975  }
976  for (const Argument *arg : args) {
977  verifyArgs(arg->subArguments(), vector<char>(), vector<const char *>());
978  }
979 }
980 #endif
981 
989 bool compareArgs(const Argument *arg1, const Argument *arg2)
990 {
991  if (arg1->denotesOperation() && !arg2->denotesOperation()) {
992  return true;
993  } else if (!arg1->denotesOperation() && arg2->denotesOperation()) {
994  return false;
995  } else {
996  return strcmp(arg1->name(), arg2->name()) < 0;
997  }
998 }
999 
1004 void insertSiblings(const ArgumentVector &siblings, list<const Argument *> &target)
1005 {
1006  bool onlyCombinable = false;
1007  for (const Argument *sibling : siblings) {
1008  if (sibling->isPresent() && !sibling->isCombinable()) {
1009  onlyCombinable = true;
1010  break;
1011  }
1012  }
1013  for (const Argument *sibling : siblings) {
1014  if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1015  target.push_back(sibling);
1016  }
1017  }
1018 }
1019 
1025 void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsigned int currentWordIndex, const ArgumentReader &reader)
1026 {
1027  // variables to store relevant completions (arguments, pre-defined values, files/dirs)
1028  list<const Argument *> relevantArgs, relevantPreDefinedValues;
1029  bool completeFiles = false, completeDirs = false, noWhitespace = false;
1030 
1031  // get the last argument the argument parser was able to detect successfully
1032  const Argument *const lastDetectedArg = reader.lastArg;
1033  size_t lastDetectedArgIndex;
1034  vector<Argument *> lastDetectedArgPath;
1035  if (lastDetectedArg) {
1036  lastDetectedArgIndex = reader.lastArgDenotation - argv;
1037  lastDetectedArgPath = lastDetectedArg->path(lastDetectedArg->occurrences() - 1);
1038  }
1039 
1040  // determine last arg, omitting trailing empty args
1041  const char *const *lastSpecifiedArg;
1042  unsigned int lastSpecifiedArgIndex;
1043  if (argc) {
1044  lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1045  lastSpecifiedArg = argv + lastSpecifiedArgIndex;
1046  for (; lastSpecifiedArg >= argv && **lastSpecifiedArg == '\0'; --lastSpecifiedArg, --lastSpecifiedArgIndex)
1047  ;
1048  }
1049 
1050  // determine arguments relevant for completion
1051  bool nextArgumentOrValue;
1052  if (lastDetectedArg && lastDetectedArg->isPresent()) {
1053  if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
1054  // define function to add parameter values of argument as possible completions
1055  const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
1056  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
1057  relevantPreDefinedValues.push_back(arg);
1058  }
1059  if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
1060  || !arg->preDefinedCompletionValues()) {
1061  completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
1062  completeDirs = completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
1063  }
1064  };
1065 
1066  // detect number of specified values
1067  auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
1068  // ignore values which are specified after the current word
1069  if (currentValueCount) {
1070  const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - lastDetectedArgIndex;
1071  if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1072  currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1073  } else {
1074  currentValueCount = 0;
1075  }
1076  }
1077 
1078  // add value completions for implicit child if there are no value specified and there are no values required by the
1079  // last detected argument itself
1080  if (!currentValueCount && !lastDetectedArg->requiredValueCount()) {
1081  for (const Argument *child : lastDetectedArg->subArguments()) {
1082  if (child->isImplicit() && child->requiredValueCount()) {
1083  addValueCompletionsForArg(child);
1084  break;
1085  }
1086  }
1087  }
1088 
1089  // add value completions for last argument if there are further values required
1090  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount || (currentValueCount < lastDetectedArg->requiredValueCount())) {
1091  addValueCompletionsForArg(lastDetectedArg);
1092  }
1093 
1094  if (lastDetectedArg->requiredValueCount() == Argument::varValueCount
1095  || lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
1096  // sub arguments of the last arg are possible completions
1097  for (const Argument *subArg : lastDetectedArg->subArguments()) {
1098  if (subArg->occurrences() < subArg->maxOccurrences()) {
1099  relevantArgs.push_back(subArg);
1100  }
1101  }
1102 
1103  // siblings of parents are possible completions as well
1104  for (auto parentArgument = lastDetectedArgPath.crbegin(), end = lastDetectedArgPath.crend();; ++parentArgument) {
1105  insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, relevantArgs);
1106  if (parentArgument == end) {
1107  break;
1108  }
1109  }
1110  }
1111  } else {
1112  // since the argument could be detected (hopefully unambiguously?) just return it for "final completion"
1113  relevantArgs.push_back(lastDetectedArg);
1114  }
1115 
1116  } else {
1117  // no arguments detected -> just use main arguments for completion
1118  nextArgumentOrValue = true;
1119  insertSiblings(m_mainArgs, relevantArgs);
1120  }
1121 
1122  // read the "opening" (started but not finished argument denotation)
1123  const char *opening = nullptr;
1124  string compoundOpening;
1125  size_t openingLen, compoundOpeningStartLen = 0;
1126  unsigned char openingDenotationType = Value;
1127  if (argc && nextArgumentOrValue) {
1128  if (currentWordIndex < static_cast<unsigned int>(argc)) {
1129  opening = argv[currentWordIndex];
1130  // For some reason completions for eg. "set --values disk=1 tag=a" are splitted so the
1131  // equation sign is an own argument ("set --values disk = 1 tag = a").
1132  // This is not how values are treated by the argument parser. Hence the opening
1133  // must be joined again. In this case only the part after the equation sign needs to be
1134  // provided for completion so compoundOpeningStartLen is set to number of characters to skip.
1135  size_t minCurrentWordIndex = (lastDetectedArg ? lastDetectedArgIndex : 0);
1136  if (currentWordIndex > minCurrentWordIndex && !strcmp(opening, "=")) {
1137  compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1138  compoundOpening = argv[currentWordIndex];
1139  compoundOpening += '=';
1140  } else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1], "=")) {
1141  compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1142  compoundOpening = argv[currentWordIndex];
1143  compoundOpening += '=';
1144  compoundOpening += opening;
1145  }
1146  if (!compoundOpening.empty()) {
1147  opening = compoundOpening.data();
1148  }
1149  } else {
1150  opening = *lastSpecifiedArg;
1151  }
1152  *opening == '-' && (++opening, ++openingDenotationType) && *opening == '-' && (++opening, ++openingDenotationType);
1153  openingLen = strlen(opening);
1154  }
1155 
1156  relevantArgs.sort(compareArgs);
1157 
1158  // print "COMPREPLY" bash array
1159  cout << "COMPREPLY=(";
1160  // -> completions for parameter values
1161  for (const Argument *arg : relevantPreDefinedValues) {
1162  if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::InvokeCallback && arg->m_callbackFunction) {
1163  arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(Argument::varValueCount));
1164  }
1165  if (arg->preDefinedCompletionValues()) {
1166  bool appendEquationSign = arg->valueCompletionBehaviour() & ValueCompletionBehavior::AppendEquationSign;
1167  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1168  if (openingDenotationType == Value) {
1169  bool wordStart = true, ok = false, equationSignAlreadyPresent = false;
1170  size_t wordIndex = 0;
1171  for (const char *i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *i;) {
1172  if (wordStart) {
1173  const char *i1 = i, *i2 = opening;
1174  for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1175  ;
1176  if ((ok = (i2 == end))) {
1177  cout << '\'';
1178  }
1179  wordStart = false;
1180  wordIndex = 0;
1181  } else if ((wordStart = (*i == ' ') || (*i == '\n'))) {
1182  equationSignAlreadyPresent = false;
1183  if (ok) {
1184  cout << '\'' << ' ';
1185  }
1186  ++i;
1187  continue;
1188  } else if (*i == '=') {
1189  equationSignAlreadyPresent = true;
1190  }
1191  if (ok) {
1192  if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1193  if (*i == '\'') {
1194  cout << "'\"'\"'";
1195  } else {
1196  cout << *i;
1197  }
1198  }
1199  ++i, ++wordIndex;
1200  switch (*i) {
1201  case ' ':
1202  case '\n':
1203  case '\0':
1204  if (appendEquationSign && !equationSignAlreadyPresent) {
1205  cout << '=';
1206  noWhitespace = true;
1207  equationSignAlreadyPresent = false;
1208  }
1209  if (*i == '\0') {
1210  cout << '\'';
1211  }
1212  }
1213  } else {
1214  ++i;
1215  }
1216  }
1217  cout << ' ';
1218  }
1219  } else if (const char *i = arg->preDefinedCompletionValues()) {
1220  bool equationSignAlreadyPresent = false;
1221  cout << '\'';
1222  while (*i) {
1223  if (*i == '\'') {
1224  cout << "'\"'\"'";
1225  } else {
1226  cout << *i;
1227  }
1228  switch (*(++i)) {
1229  case '=':
1230  equationSignAlreadyPresent = true;
1231  break;
1232  case ' ':
1233  case '\n':
1234  case '\0':
1235  if (appendEquationSign && !equationSignAlreadyPresent) {
1236  cout << '=';
1237  equationSignAlreadyPresent = false;
1238  }
1239  if (*i != '\0') {
1240  cout << '\'';
1241  if (*(++i)) {
1242  cout << ' ' << '\'';
1243  }
1244  }
1245  }
1246  }
1247  cout << '\'' << ' ';
1248  }
1249  }
1250  }
1251  // -> completions for further arguments
1252  for (const Argument *arg : relevantArgs) {
1253  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1254  switch (openingDenotationType) {
1255  case Value:
1256  if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1257  continue;
1258  }
1259  break;
1260  case Abbreviation:
1261  break;
1262  case FullName:
1263  if (strncmp(arg->name(), opening, openingLen)) {
1264  continue;
1265  }
1266  }
1267  }
1268 
1269  if (opening && openingDenotationType == Abbreviation && !nextArgumentOrValue) {
1270  // TODO: add test for this case
1271  cout << '\'' << '-' << opening << arg->abbreviation() << '\'' << ' ';
1272  } else if (lastDetectedArg && reader.argDenotationType == Abbreviation && !nextArgumentOrValue) {
1273  if (reader.argv == reader.end) {
1274  cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
1275  }
1276  } else if (arg->denotesOperation()) {
1277  cout << '\'' << arg->name() << '\'' << ' ';
1278  } else {
1279  cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';
1280  }
1281  }
1282  // -> completions for files and dirs
1283  // -> if there's already an "opening", determine the dir part and the file part
1284  string actualDir, actualFile;
1285  bool haveFileOrDirCompletions = false;
1286  if (argc && currentWordIndex == lastSpecifiedArgIndex && opening) {
1287  // the "opening" might contain escaped characters which need to be unescaped first (let's hope this covers all possible escapings)
1288  string unescapedOpening(opening);
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  findAndReplace<string>(unescapedOpening, "\\\"", "\"");
1298  findAndReplace<string>(unescapedOpening, "\\\\", "\\");
1299  // determine the "directory" part
1300  string dir = directory(unescapedOpening);
1301  if (dir.empty()) {
1302  actualDir = ".";
1303  } else {
1304  if (dir[0] == '\"' || dir[0] == '\'') {
1305  dir.erase(0, 1);
1306  }
1307  if (dir.size() > 1 && (dir[dir.size() - 2] == '\"' || dir[dir.size() - 2] == '\'')) {
1308  dir.erase(dir.size() - 2, 1);
1309  }
1310  actualDir = move(dir);
1311  }
1312  // determine the "file" part
1313  string file = fileName(unescapedOpening);
1314  if (file[0] == '\"' || file[0] == '\'') {
1315  file.erase(0, 1);
1316  }
1317  if (file.size() > 1 && (file[dir.size() - 2] == '\"' || dir[file.size() - 2] == '\'')) {
1318  file.erase(file.size() - 2, 1);
1319  }
1320  actualFile = move(file);
1321  }
1322 
1323  // -> completion for files and dirs
1325  if (completeFiles) {
1326  entryTypes |= DirectoryEntryType::File;
1327  }
1328  if (completeDirs) {
1329  entryTypes |= DirectoryEntryType::Directory;
1330  }
1331  if (entryTypes != DirectoryEntryType::None) {
1332  const string replace("'"), with("'\"'\"'");
1333  if (argc && currentWordIndex <= lastSpecifiedArgIndex && opening) {
1334  list<string> entries = directoryEntries(actualDir.c_str(), entryTypes);
1335  findAndReplace(actualDir, replace, with);
1336  for (string &dirEntry : entries) {
1337  if (startsWith(dirEntry, actualFile)) {
1338  cout << '\'';
1339  if (actualDir != ".") {
1340  cout << actualDir;
1341  }
1342  findAndReplace(dirEntry, replace, with);
1343  cout << dirEntry << '\'' << ' ';
1344  haveFileOrDirCompletions = true;
1345  }
1346  }
1347  } else {
1348  for (string &dirEntry : directoryEntries(".", entryTypes)) {
1349  findAndReplace(dirEntry, replace, with);
1350  cout << '\'' << dirEntry << '\'' << ' ';
1351  haveFileOrDirCompletions = true;
1352  }
1353  }
1354  }
1355  cout << ')';
1356 
1357  // ensure file or dir completions are formatted appropriately
1358  if (haveFileOrDirCompletions) {
1359  cout << "; compopt -o filenames";
1360  }
1361 
1362  // ensure trailing whitespace is ommitted
1363  if (noWhitespace) {
1364  cout << "; compopt -o nospace";
1365  }
1366 
1367  cout << endl;
1368 }
1369 
1375 {
1376  for (const Argument *arg : args) {
1377  const auto occurrences = arg->occurrences();
1378  if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1379  throw Failure(argsToString("The argument \"", arg->name(), "\" mustn't be specified more than ", arg->maxOccurrences(),
1380  (arg->maxOccurrences() == 1 ? " time." : " times.")));
1381  }
1382  if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1383  throw Failure(argsToString("The argument \"", arg->name(), "\" must be specified at least ", arg->minOccurrences(),
1384  (arg->minOccurrences() == 1 ? " time." : " times.")));
1385  }
1386  Argument *conflictingArgument = nullptr;
1387  if (arg->isMainArgument()) {
1388  if (!arg->isCombinable() && arg->isPresent()) {
1389  conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
1390  }
1391  } else {
1392  conflictingArgument = arg->conflictsWithArgument();
1393  }
1394  if (conflictingArgument) {
1395  throw Failure(argsToString("The argument \"", conflictingArgument->name(), "\" can not be combined with \"", arg->name(), "\"."));
1396  }
1397  for (size_t i = 0; i != occurrences; ++i) {
1398  if (!arg->allRequiredValuesPresent(i)) {
1399  stringstream ss(stringstream::in | stringstream::out);
1400  ss << "Not all parameter for argument \"" << arg->name() << "\" ";
1401  if (i) {
1402  ss << " (" << (i + 1) << " occurrence) ";
1403  }
1404  ss << "provided. You have to provide the following parameter:";
1405  size_t valueNamesPrint = 0;
1406  for (const auto &name : arg->m_valueNames) {
1407  ss << ' ' << name, ++valueNamesPrint;
1408  }
1409  if (arg->m_requiredValueCount != Argument::varValueCount) {
1410  while (valueNamesPrint < arg->m_requiredValueCount) {
1411  ss << "\nvalue " << (++valueNamesPrint);
1412  }
1413  }
1414  throw Failure(ss.str());
1415  }
1416  }
1417 
1418  // check contraints of sub arguments recursively
1419  checkConstraints(arg->m_subArgs);
1420  }
1421 }
1422 
1431 {
1432  for (const Argument *arg : args) {
1433  // invoke the callback for each occurrence of the argument
1434  if (arg->m_callbackFunction) {
1435  for (const auto &occurrence : arg->m_occurrences) {
1436  arg->m_callbackFunction(occurrence);
1437  }
1438  }
1439  // invoke the callbacks for sub arguments recursively
1440  invokeCallbacks(arg->m_subArgs);
1441  }
1442 }
1443 
1454  : Argument("help", 'h', "shows this information")
1455 {
1456  setCallback([&parser](const ArgumentOccurrence &) {
1458  parser.printHelp(cout);
1459  });
1460 }
1461 
1492 NoColorArgument *NoColorArgument::s_instance = nullptr;
1493 
1499 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1500  : Argument("no-color", '\0', "disables formatted/colorized output")
1501 #else
1502  : Argument("enable-color", '\0', "enables formatted/colorized output")
1503 #endif
1504 {
1505  setCombinable(true);
1506 
1507  if (s_instance) {
1508  return;
1509  }
1510  s_instance = this;
1511 
1512  // set the environmentvariable: note that this is not directly used and just assigned for printing help
1513  setEnvironmentVariable("ENABLE_ESCAPE_CODES");
1514 
1515  // default-initialize EscapeCodes::enabled from environment variable
1516  const char *envValue = getenv(environmentVariable());
1517  if (!envValue) {
1518  return;
1519  }
1520  for (; *envValue; ++envValue) {
1521  switch (*envValue) {
1522  case '0':
1523  case ' ':
1524  break;
1525  default:
1526  // enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
1527  EscapeCodes::enabled = true;
1528  return;
1529  }
1530  }
1531  // disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
1532  EscapeCodes::enabled = false;
1533 }
1534 
1539 {
1540  if (s_instance == this) {
1541  s_instance = nullptr;
1542  }
1543 }
1544 
1549 {
1550  if (NoColorArgument::s_instance && NoColorArgument::s_instance->isPresent()) {
1551 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1552  EscapeCodes::enabled = false;
1553 #else
1554  EscapeCodes::enabled = true;
1555 #endif
1556  }
1557 }
1558 
1559 } // 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