5#include "../conversion/stringbuilder.h"
6#include "../conversion/stringconversion.h"
7#include "../io/ansiescapecodes.h"
9#include "../misc/levenshtein.h"
10#include "../misc/parseerror.h"
20#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
25using namespace std::placeholders;
26using namespace std::literals;
66 : lastDetectedArg(reader.lastArg)
71struct ArgumentSuggestion {
72 ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
bool hasDashPrefix);
73 ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
size_t suggestionSize,
bool hasDashPrefix);
74 bool operator<(
const ArgumentSuggestion &other)
const;
75 bool operator==(
const ArgumentSuggestion &other)
const;
76 void addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const;
78 const char *
const suggestion;
79 const size_t suggestionSize;
80 const size_t editingDistance;
81 const bool hasDashPrefix;
84ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
size_t suggestionSize,
bool isOperation)
85 : suggestion(suggestion)
86 , suggestionSize(suggestionSize)
88 , hasDashPrefix(isOperation)
92ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
bool isOperation)
93 : ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
97bool ArgumentSuggestion::operator<(
const ArgumentSuggestion &other)
const
99 return editingDistance < other.editingDistance;
102void ArgumentSuggestion::addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const
104 if (suggestions.size() >= limit && !(*
this < *--suggestions.end())) {
107 suggestions.emplace(*
this);
108 while (suggestions.size() > limit) {
109 suggestions.erase(--suggestions.end());
128 , args(parser.m_mainArgs)
133 , argDenotation(nullptr)
134 , completionMode(completionMode)
163bool Argument::matchesDenotation(
const char *denotation,
size_t denotationLength)
const
165 return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) ==
'\0';
180 const vector<Argument *> &parentPath = parentArg ? parentArg->
path(parentArg->
occurrences() - 1) : vector<Argument *>();
183 vector<const char *> *values =
nullptr;
199 bool abbreviationFound =
false;
202 abbreviationFound =
false;
214 abbreviationFound =
false;
235 for (; argDenotationLength; matchingArg =
nullptr) {
239 if (arg->abbreviation() && arg->abbreviation() == *
argDenotation) {
241 abbreviationFound =
true;
247 if (arg->matchesDenotation(
argDenotation, argDenotationLength)) {
258 matchingArg->m_occurrences.emplace_back(
index, parentPath, parentArg);
261 values = &matchingArg->m_occurrences.back().values;
265 values->push_back(equationPos + 1);
272 lastArg = lastArgInLevel = matchingArg;
283 const char *
const *
const currentArgValue =
argv;
300 for (
auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
301 for (
Argument *
const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() :
parser.m_mainArgs)) {
302 if (sibling->occurrences() < sibling->maxOccurrences()) {
308 if (sibling->matchesDenotation(
argDenotation, argDenotationLength)) {
313 if (parentArgument == pathEnd) {
330 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *
argv)) {
331 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
343 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
344 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
345 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
353 if (lastArgInLevel == matchingArg) {
358 values = &matchingArg->m_occurrences.back().values;
362 lastArg = lastArgInLevel = matchingArg;
380 switch (
parser.m_unknownArgBehavior) {
382 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
408 const auto maxColumns = termSize.
columns ? termSize.
columns : numeric_limits<unsigned short>::max();
411 unsigned short currentCol = wrapper.m_indentation.
level;
412 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
413 const bool wrappingRequired = currentCol >= maxColumns;
414 if (wrappingRequired || *currentChar ==
'\n') {
418 if (wrapper.m_indentation.
level < maxColumns) {
419 os << wrapper.m_indentation;
420 currentCol = wrapper.m_indentation.
level;
425 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
437inline bool notEmpty(
const char *str)
462 , m_abbreviation(abbreviation)
463 , m_environmentVar(nullptr)
464 , m_description(description)
466 , m_minOccurrences(0)
467 , m_maxOccurrences(1)
468 , m_requiredValueCount(0)
470 , m_deprecatedBy(nullptr)
474 , m_preDefinedCompletionValues(nullptr)
494 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
495 return m_occurrences.front().values.front();
496 }
else if (m_environmentVar) {
497 return getenv(m_environmentVar);
526 if (notEmpty(
name())) {
540 unsigned int valueNamesPrint = 0;
542 os <<
' ' <<
'[' << *
i <<
']';
549 os <<
" [value " << (valueNamesPrint + 1) <<
']';
558 os <<
'\n' << ident <<
"particularities: mandatory";
560 os <<
" if parent argument is present";
567 bool hasSubArgs =
false;
569 if (arg->isDeprecated()) {
573 arg->printInfo(os, ident.
level);
576 if (ident.
level == 2 && hasSubArgs) {
592 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
617 for (
Argument *
const arg : m_subArgs) {
618 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
643 const auto requiredCap = m_subArgs.size() +
subArguments.size();
644 if (requiredCap < m_subArgs.capacity()) {
645 m_subArgs.reserve(requiredCap);
650 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
651 arg->m_parents.push_back(
this);
665 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
668 m_subArgs.push_back(arg);
669 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
670 arg->m_parents.push_back(
this);
683 for (
const Argument *parent : m_parents) {
684 if (parent->isPresent()) {
717 for (
Argument *parent : m_parents) {
719 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
734 if (arg->denotesOperation() && arg->isPresent()) {
748 arg->resetRecursively();
771 , m_executable(nullptr)
773 , m_defaultArg(nullptr)
794 arg->m_isMainArg =
true;
800 bool subArgsRequired =
false;
802 if (subArg->isRequired()) {
803 subArgsRequired =
true;
807 if (!subArgsRequired) {
820 argument->m_isMainArg =
true;
821 m_mainArgs.push_back(argument);
830 bool wroteLine =
false;
855 if (!m_mainArgs.empty()) {
856 bool hasOperations =
false, hasTopLevelOptions =
false;
857 for (
const Argument *
const arg : m_mainArgs) {
858 if (arg->denotesOperation()) {
859 hasOperations =
true;
860 }
else if (strcmp(arg->name(),
"help")) {
861 hasTopLevelOptions =
true;
863 if (hasOperations && hasTopLevelOptions) {
871 os <<
"Available operations:";
872 for (
const Argument *
const arg : m_mainArgs) {
873 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
879 if (hasTopLevelOptions) {
880 os <<
"\nAvailable top-level options:";
881 for (
const Argument *
const arg : m_mainArgs) {
882 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
891 os <<
"Available arguments:";
892 for (
const Argument *
const arg : m_mainArgs) {
893 if (arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
905 os <<
"Linked against: " << *
i;
906 for (++
i;
i != end; ++
i) {
907 os <<
',' <<
' ' << *
i;
950 invokeExit(EXIT_FAILURE);
976 m_executable =
nullptr;
979 m_executable = *argv;
985 m_defaultArg->m_occurrences.emplace_back(0);
991 const bool completionMode = !strcmp(*++argv,
"--bash-completion-for");
994 unsigned int currentWordIndex = 0, argcForReader;
995 if (completionMode) {
998 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
1004 currentWordIndex =
static_cast<unsigned int>(argc - 1);
1006 argcForReader =
min(
static_cast<unsigned int>(argc), currentWordIndex + 1);
1008 argcForReader =
static_cast<unsigned int>(argc);
1012 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
1013 const bool allArgsProcessed(reader.
read());
1014 m_noColorArg.
apply();
1017 if (!completionMode && !allArgsProcessed) {
1018 const auto suggestions(findSuggestions(argc, argv,
static_cast<unsigned int>(argc - 1), reader));
1023 if (completionMode) {
1024 printBashCompletion(argc, argv, currentWordIndex, reader);
1025 invokeExit(EXIT_SUCCESS);
1036 arg->resetRecursively();
1049 if (arg->denotesOperation() && arg->isPresent()) {
1061 for (
const Argument *arg : m_mainArgs) {
1062 if (!arg->isCombinable() && arg->isPresent()) {
1069#ifdef CPP_UTILITIES_DEBUG_BUILD
1086 vector<const Argument *> verifiedArgs;
1087 verifiedArgs.reserve(args.size());
1088 vector<char> abbreviations;
1089 abbreviations.reserve(abbreviations.size() + args.size());
1090 vector<const char *> names;
1091 names.reserve(names.size() + args.size());
1092 bool hasImplicit =
false;
1094 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1095 verifiedArgs.push_back(arg);
1096 assert(!arg->isImplicit() || !hasImplicit);
1097 hasImplicit |= arg->isImplicit();
1098 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1099 abbreviations.push_back(arg->abbreviation());
1100 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1101 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0 || (arg->flags() &
Argument::Flags::Greedy));
1102 names.emplace_back(arg->name());
1105 verifyArgs(arg->subArguments());
1124 return strcmp(arg1->
name(), arg2->
name()) < 0;
1134 bool onlyCombinable =
false;
1135 for (
const Argument *sibling : siblings) {
1136 if (sibling->isPresent() && !sibling->isCombinable()) {
1137 onlyCombinable =
true;
1141 for (
const Argument *sibling : siblings) {
1142 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1143 target.push_back(sibling);
1152 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1157 if (completion.lastDetectedArg) {
1158 completion.lastDetectedArgIndex =
static_cast<size_t>(reader.
lastArgDenotation - argv);
1159 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1164 completion.lastSpecifiedArgIndex =
static_cast<unsigned int>(argc) - 1;
1165 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1166 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1167 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1172 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1173 completion.nextArgumentOrValue =
true;
1179 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1180 if (!completion.nextArgumentOrValue) {
1182 completion.relevantArgs.push_back(completion.lastDetectedArg);
1188 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1190 completion.relevantPreDefinedValues.push_back(arg);
1199 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1201 if (currentValueCount) {
1202 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1203 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1204 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1206 currentValueCount = 0;
1212 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1214 if (child->isImplicit() && child->requiredValueCount()) {
1215 addValueCompletionsForArg(child);
1223 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1224 addValueCompletionsForArg(completion.lastDetectedArg);
1228 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1229 >= completion.lastDetectedArg->requiredValueCount()) {
1232 if (subArg->occurrences() < subArg->maxOccurrences()) {
1233 completion.relevantArgs.push_back(subArg);
1238 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1239 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1240 if (parentArgument == end) {
1252string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const
1255 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1258 const auto *unknownArg(*reader.
argv);
1259 auto unknownArgSize(strlen(unknownArg));
1261 if (unknownArgSize > 16) {
1265 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1267 unknownArgSize -= 2;
1271 multiset<ArgumentSuggestion> bestSuggestions;
1273 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1274 ArgumentSuggestion(unknownArg, unknownArgSize, arg->
name(), !arg->
denotesOperation()).addTo(bestSuggestions, 2);
1277 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1282 const char *
const wordStart(
i);
1283 const char *wordEnd(wordStart + 1);
1284 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1286 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart,
static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1292 string suggestionStr;
1293 if (
const auto suggestionCount = bestSuggestions.size()) {
1295 size_t requiredSize = 15;
1296 for (
const auto &suggestion : bestSuggestions) {
1297 requiredSize += suggestion.suggestionSize + 2;
1298 if (suggestion.hasDashPrefix) {
1302 suggestionStr.reserve(requiredSize);
1305 suggestionStr +=
"\nDid you mean ";
1307 for (
const auto &suggestion : bestSuggestions) {
1308 if (++
i == suggestionCount && suggestionCount != 1) {
1309 suggestionStr +=
" or ";
1311 suggestionStr +=
", ";
1313 if (suggestion.hasDashPrefix) {
1314 suggestionStr +=
"--";
1316 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1318 suggestionStr +=
'?';
1320 return suggestionStr;
1328void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1331 const auto completionInfo([&] {
1332 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1333 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1334 return clutteredCompletionInfo;
1338 const char *opening =
nullptr;
1339 string compoundOpening;
1340 size_t openingLen = 0, compoundOpeningStartLen = 0;
1341 unsigned char openingDenotationType =
Value;
1342 if (argc && completionInfo.nextArgumentOrValue) {
1343 if (currentWordIndex <
static_cast<unsigned int>(argc)) {
1344 opening = argv[currentWordIndex];
1350 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1351 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1352 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1353 compoundOpening = argv[currentWordIndex];
1354 compoundOpening +=
'=';
1355 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1356 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1357 compoundOpening = argv[currentWordIndex];
1358 compoundOpening +=
'=';
1359 compoundOpening += opening;
1361 if (!compoundOpening.empty()) {
1362 opening = compoundOpening.data();
1365 opening = *completionInfo.lastSpecifiedArg;
1367 if (*opening ==
'-') {
1369 ++openingDenotationType;
1370 if (*opening ==
'-') {
1372 ++openingDenotationType;
1375 openingLen = strlen(opening);
1379 cout <<
"COMPREPLY=(";
1381 bool noWhitespace =
false;
1382 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1390 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1391 if (openingDenotationType !=
Value) {
1394 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1395 size_t wordIndex = 0;
1398 const char *i1 =
i, *i2 = opening;
1399 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1401 if ((ok = (i2 == end))) {
1406 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1407 equationSignAlreadyPresent =
false;
1409 cout <<
'\'' <<
' ';
1413 }
else if (*
i ==
'=') {
1414 equationSignAlreadyPresent =
true;
1420 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1433 if (appendEquationSign && !equationSignAlreadyPresent) {
1435 noWhitespace =
true;
1436 equationSignAlreadyPresent =
false;
1445 bool equationSignAlreadyPresent =
false;
1455 equationSignAlreadyPresent =
true;
1460 if (appendEquationSign && !equationSignAlreadyPresent) {
1462 equationSignAlreadyPresent =
false;
1467 cout <<
' ' <<
'\'';
1472 cout <<
'\'' <<
' ';
1476 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1477 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1478 switch (openingDenotationType) {
1487 if (strncmp(arg->
name(), opening, openingLen)) {
1493 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1495 cout <<
'\'' <<
'-' << opening << arg->
abbreviation() <<
'\'' <<
' ';
1497 if (reader.
argv == reader.
end) {
1498 cout <<
'\'' << *(reader.
argv - 1) <<
'\'' <<
' ';
1501 cout <<
'\'' << arg->
name() <<
'\'' <<
' ';
1503 cout <<
'\'' <<
'-' <<
'-' << arg->
name() <<
'\'' <<
' ';
1508 string actualDir, actualFile;
1509 bool haveFileOrDirCompletions =
false;
1510 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1512 string unescapedOpening(opening);
1513 findAndReplace<string>(unescapedOpening,
"\\ ",
" ");
1514 findAndReplace<string>(unescapedOpening,
"\\,",
",");
1515 findAndReplace<string>(unescapedOpening,
"\\[",
"[");
1516 findAndReplace<string>(unescapedOpening,
"\\]",
"]");
1517 findAndReplace<string>(unescapedOpening,
"\\!",
"!");
1518 findAndReplace<string>(unescapedOpening,
"\\#",
"#");
1519 findAndReplace<string>(unescapedOpening,
"\\$",
"$");
1520 findAndReplace<string>(unescapedOpening,
"\\'",
"'");
1521 findAndReplace<string>(unescapedOpening,
"\\\"",
"\"");
1522 findAndReplace<string>(unescapedOpening,
"\\\\",
"\\");
1524 string dir =
directory(unescapedOpening);
1528 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1531 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1532 dir.erase(dir.size() - 2, 1);
1534 actualDir = std::move(dir);
1537 string file =
fileName(unescapedOpening);
1538 if (file[0] ==
'\"' || file[0] ==
'\'') {
1541 if (file.size() > 1 && (file[file.size() - 2] ==
'\"' || file[file.size() - 2] ==
'\'')) {
1542 file.erase(file.size() - 2, 1);
1544 actualFile = std::move(file);
1548#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1549 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1551 const auto replace =
"'"s, with =
"'\"'\"'"s;
1552 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1553 const auto dirEntries = [&] {
1554 filesystem::directory_iterator
i;
1556 i = filesystem::directory_iterator(actualDir);
1559 i = filesystem::directory_iterator(
".");
1563 for (
const auto &dirEntry : dirEntries) {
1564 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1567 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1570 auto dirEntryName = dirEntry.path().filename().string();
1571 auto hasStartingQuote =
false;
1577 hasStartingQuote =
true;
1578 if (actualDir !=
".") {
1583 if (!hasStartingQuote) {
1586 cout << dirEntryName <<
'\'' <<
' ';
1587 haveFileOrDirCompletions =
true;
1589 }
catch (
const filesystem::filesystem_error &) {
1597 if (haveFileOrDirCompletions) {
1598 cout <<
"; compopt -o filenames";
1603 cout <<
"; compopt -o nospace";
1625 Argument *conflictingArgument =
nullptr;
1633 if (conflictingArgument) {
1636 for (
size_t i = 0;
i != occurrences; ++
i) {
1640 stringstream ss(stringstream::in | stringstream::out);
1641 ss <<
"Not all parameters for argument \"" << arg->
name() <<
"\" ";
1643 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1645 ss <<
"provided. You have to provide the following parameters:";
1646 size_t valueNamesPrint = 0;
1647 for (
const auto &name : arg->m_valueNames) {
1652 while (valueNamesPrint < arg->m_requiredValueCount) {
1653 ss <<
"\nvalue " << (++valueNamesPrint);
1675 if (arg->m_callbackFunction) {
1676 for (
const auto &occurrence : arg->m_occurrences) {
1677 arg->m_callbackFunction(occurrence);
1688void ArgumentParser::invokeExit(
int code)
1690 if (m_exitFunction) {
1691 m_exitFunction(code);
1707 :
Argument(
"help",
'h',
"shows this information")
1750#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1751 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1753 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1756 setCombinable(
true);
1759 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1763 if (escapeCodesEnabled.has_value()) {
1774#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1785void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const
1788 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1789 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1790 targetTypeName,
"\" failed: ", errorMessage));
1796void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const
1799 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1800 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1801 " have been specified."));
#define CPP_UTILITIES_IF_DEBUG_BUILD(x)
Wraps debug-only lines conveniently.
The ArgumentParser class provides a means for handling command line arguments.
void checkConstraints()
Checks whether constraints are violated.
ArgumentParser()
Constructs a new ArgumentParser.
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const ArgumentVector & mainArguments() const
Returns the main arguments.
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
void printHelp(std::ostream &os) const
Prints help text for all assigned arguments.
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
void parseArgs(int argc, const char *const *argv, ParseArgumentBehavior behavior=ParseArgumentBehavior::CheckConstraints|ParseArgumentBehavior::InvokeCallbacks|ParseArgumentBehavior::ExitOnFailure)
Parses the specified command line arguments.
void invokeCallbacks()
Invokes all assigned callbacks.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
The ArgumentReader class internally encapsulates the process of reading command line arguments.
const char *const * lastArgDenotation
Points to the element in argv where lastArg was encountered. Unspecified if lastArg is not set.
const char * argDenotation
The currently processed abbreviation denotation (should be substring of one of the args in argv)....
ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode=false)
Initializes the internal reader for the specified parser and arguments.
ArgumentVector & args
The Argument instances to store the results. Sub arguments of args are considered as well.
const char *const * argv
Points to the first argument denotation and will be incremented when a denotation has been processed.
Argument * lastArg
The last Argument instance which could be detected. Set to nullptr in the initial call....
bool completionMode
Whether completion mode is enabled. In this case reading args will be continued even if an denotation...
unsigned char argDenotationType
The type of the currently processed abbreviation denotation. Unspecified if argDenotation is not set.
std::size_t index
An index which is incremented when an argument is encountered (the current index is stored in the occ...
ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
ArgumentParser & parser
The associated ArgumentParser instance.
bool read()
Reads the commands line arguments specified when constructing the object.
const char *const * end
Points to the end of the argv array.
The Argument class is a wrapper for command line argument information.
const char * description() const
Returns the description of the argument.
Argument(const char *name, char abbreviation='\0', const char *description=nullptr, const char *example=nullptr)
Constructs an Argument with the given name, abbreviation and description.
bool isParentPresent() const
Returns whether at least one parent argument is present.
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
static constexpr std::size_t varValueCount
Denotes a variable number of values.
const std::vector< Argument * > & path(std::size_t occurrence=0) const
Returns the path of the specified occurrence.
bool isMainArgument() const
Returns an indication whether the argument is used as main argument.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
const ArgumentVector & subArguments() const
Returns the secondary arguments for this argument.
const char * example() const
Returns the usage example of the argument.
Flags
The Flags enum specifies options for treating the argument in a special way.
bool isDeprecated() const
const std::vector< const char * > & valueNames() const
Returns the names of the required values.
void addSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this argument.
ValueCompletionBehavior valueCompletionBehaviour() const
Returns the items to be considered when generating completion for the values.
char abbreviation() const
Returns the abbreviation of the argument.
std::size_t occurrences() const
Returns how often the argument could be detected when parsing.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
const char * name() const
Returns the name of the argument.
bool isCombinable() const
Returns an indication whether the argument is combinable.
bool denotesOperation() const
Returns whether the argument denotes an operation.
void resetRecursively()
Resets this argument and all sub arguments recursively.
std::size_t maxOccurrences() const
Returns the maximum number of occurrences.
Argument::Flags flags() const
Returns Argument::Flags for the argument.
const char * preDefinedCompletionValues() const
Returns the assigned values used when generating completion for the values.
std::size_t requiredValueCount() const
Returns the number of values which are required to be given for this argument.
const char * environmentVariable() const
Returns the environment variable queried when firstValue() is called.
Argument * wouldConflictWithArgument() const
Checks if this argument would conflict with other arguments if it was present.
void reset()
Resets occurrences (indices, values and paths).
~Argument()
Destroys the Argument.
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
std::size_t minOccurrences() const
Returns the minimum number of occurrences.
bool allRequiredValuesPresent(std::size_t occurrence=0) const
Returns an indication whether all required values are present.
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this argument.
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
bool isRequired() const
Returns an indication whether the argument is mandatory.
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.
const char * firstValueOr(const char *fallback) const
Returns the first value like Argument::firstValue() but returns fallback instead of nullptr if there'...
The ConversionException class is thrown by the various conversion functions of this library when a co...
HelpArgument(ArgumentParser &parser)
Constructs a new help argument for the specified parser.
The Indentation class allows printing indentation conveniently, eg.
void apply() const
Sets EscapeCodes::enabled according to the presence of the first instantiation of NoColorArgument.
NoColorArgument()
Constructs a new NoColorArgument argument.
The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
The Wrapper class is internally used print text which might needs to be wrapped preserving the indent...
#define CMD_UTILS_START_CONSOLE
#define CPP_UTILITIES_EXPORT
Marks the symbol to be exported by the c++utilities library.
Encapsulates functions for formatted terminal output using ANSI escape codes.
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
void setStyle(std::ostream &stream, TextAttribute displayAttribute=TextAttribute::Reset)
Contains all utilities provides by the c++utilities library.
void findAndReplace(StringType1 &str, const StringType2 &find, const StringType3 &replace)
Replaces all occurrences of find with relpace in the specified str.
CPP_UTILITIES_EXPORT TerminalSize determineTerminalSize()
Returns the current size of the terminal.
std::vector< Argument * > ArgumentVector
Argument * firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
This function return the first present and uncombinable argument of the given list of arguments.
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
UnknownArgumentBehavior
The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown argume...
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
ParseArgumentBehavior
The ParseArgumentBehavior enum specifies the behavior when parsing arguments.
std::initializer_list< Argument * > ArgumentInitializerList
bool operator==(const AsHexNumber< T > &lhs, const AsHexNumber< T > &rhs)
Provides operator == required by CPPUNIT_ASSERT_EQUAL.
StringType argsToString(Args &&...args)
ValueCompletionBehavior
The ValueCompletionBehavior enum specifies the items to be considered when generating completion for ...
@ FileSystemIfNoPreDefinedValues
ArgumentDenotationType
The ArgumentDenotationType enum specifies the type of a given argument denotation.
CPP_UTILITIES_EXPORT std::optional< bool > isEnvVariableSet(const char *variableName)
Returns whether the specified env variable is set to a non-zero and non-white-space-only value.
constexpr T min(T first, T second)
Returns the smallest of the given items.
void insertSiblings(const ArgumentVector &siblings, list< const Argument * > &target)
Inserts the specified siblings in the target list.
bool compareArgs(const Argument *arg1, const Argument *arg2)
Returns whether arg1 should be listed before arg2 when printing completion.
CPP_UTILITIES_EXPORT std::size_t computeDamerauLevenshteinDistance(const char *str1, std::size_t size1, const char *str2, std::size_t size2)
CPP_UTILITIES_EXPORT ApplicationInfo applicationInfo
Stores global application info used by ArgumentParser::printHelp() and AboutDialog.
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
Stores information about an application.
std::vector< const char * > dependencyVersions
The ArgumentCompletionInfo struct holds information internally used for shell completion and suggesti...
list< const Argument * > relevantArgs
size_t lastDetectedArgIndex
ArgumentCompletionInfo(const ArgumentReader &reader)
Constructs a new completion info for the specified reader.
const char *const * lastSpecifiedArg
const Argument *const lastDetectedArg
vector< Argument * > lastDetectedArgPath
unsigned int lastSpecifiedArgIndex
list< const Argument * > relevantPreDefinedValues
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).
The TerminalSize struct describes a terminal size.
unsigned short columns
number of columns