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;
40 const char *envValue = std::getenv(variableName);
44 for (; *envValue; ++envValue) {
87 : lastDetectedArg(reader.lastArg)
92struct ArgumentSuggestion {
93 ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
bool hasDashPrefix);
94 ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
size_t suggestionSize,
bool hasDashPrefix);
95 bool operator<(
const ArgumentSuggestion &other)
const;
96 bool operator==(
const ArgumentSuggestion &other)
const;
97 void addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const;
99 const char *
const suggestion;
100 const size_t suggestionSize;
101 const size_t editingDistance;
102 const bool hasDashPrefix;
105ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
size_t suggestionSize,
bool isOperation)
106 : suggestion(suggestion)
107 , suggestionSize(suggestionSize)
109 , hasDashPrefix(isOperation)
113ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
bool isOperation)
114 : ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
118bool ArgumentSuggestion::operator<(
const ArgumentSuggestion &other)
const
120 return editingDistance < other.editingDistance;
123void ArgumentSuggestion::addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const
125 if (suggestions.size() >= limit && !(*
this < *--suggestions.end())) {
128 suggestions.emplace(*
this);
129 while (suggestions.size() > limit) {
130 suggestions.erase(--suggestions.end());
149 , args(parser.m_mainArgs)
154 , argDenotation(nullptr)
155 , completionMode(completionMode)
184bool Argument::matchesDenotation(
const char *denotation,
size_t denotationLength)
const
186 return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) ==
'\0';
201 const vector<Argument *> &parentPath = parentArg ? parentArg->
path(parentArg->
occurrences() - 1) : vector<Argument *>();
204 vector<const char *> *values =
nullptr;
220 bool abbreviationFound =
false;
223 abbreviationFound =
false;
235 abbreviationFound =
false;
256 for (; argDenotationLength; matchingArg =
nullptr) {
260 if (arg->abbreviation() && arg->abbreviation() == *
argDenotation) {
262 abbreviationFound =
true;
268 if (arg->matchesDenotation(
argDenotation, argDenotationLength)) {
279 matchingArg->m_occurrences.emplace_back(
index, parentPath, parentArg);
282 values = &matchingArg->m_occurrences.back().values;
286 values->push_back(equationPos + 1);
293 lastArg = lastArgInLevel = matchingArg;
304 const char *
const *
const currentArgValue =
argv;
321 for (
auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
322 for (
Argument *
const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() :
parser.m_mainArgs)) {
323 if (sibling->occurrences() < sibling->maxOccurrences()) {
329 if (sibling->matchesDenotation(
argDenotation, argDenotationLength)) {
334 if (parentArgument == pathEnd) {
351 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *
argv)) {
352 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
364 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
365 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
366 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
374 if (lastArgInLevel == matchingArg) {
379 values = &matchingArg->m_occurrences.back().values;
383 lastArg = lastArgInLevel = matchingArg;
401 switch (
parser.m_unknownArgBehavior) {
403 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
432 unsigned short currentCol = wrapper.m_indentation.
level;
433 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
434 const bool wrappingRequired = currentCol >= maxColumns;
435 if (wrappingRequired || *currentChar ==
'\n') {
439 if (wrapper.m_indentation.
level < maxColumns) {
440 os << wrapper.m_indentation;
441 currentCol = wrapper.m_indentation.
level;
446 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
458inline bool notEmpty(
const char *str)
483 , m_abbreviation(abbreviation)
484 , m_environmentVar(nullptr)
485 , m_description(description)
487 , m_minOccurrences(0)
488 , m_maxOccurrences(1)
489 , m_requiredValueCount(0)
491 , m_deprecatedBy(nullptr)
495 , m_preDefinedCompletionValues(nullptr)
515 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
516 return m_occurrences.front().values.front();
517 }
else if (m_environmentVar) {
518 return getenv(m_environmentVar);
547 if (notEmpty(
name())) {
561 unsigned int valueNamesPrint = 0;
563 os <<
' ' <<
'[' << *
i <<
']';
570 os <<
" [value " << (valueNamesPrint + 1) <<
']';
579 os <<
'\n' << ident <<
"particularities: mandatory";
581 os <<
" if parent argument is present";
588 bool hasSubArgs =
false;
590 if (arg->isDeprecated()) {
594 arg->printInfo(os, ident.
level);
597 if (ident.
level == 2 && hasSubArgs) {
613 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
638 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
641 m_subArgs.assign(secondaryArguments);
645 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
646 arg->m_parents.push_back(
this);
660 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
663 m_subArgs.push_back(arg);
664 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
665 arg->m_parents.push_back(
this);
678 for (
const Argument *parent : m_parents) {
679 if (parent->isPresent()) {
712 for (
Argument *parent : m_parents) {
714 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
729 if (arg->denotesOperation() && arg->isPresent()) {
743 arg->resetRecursively();
766 , m_executable(nullptr)
768 , m_defaultArg(nullptr)
789 arg->m_isMainArg =
true;
795 bool subArgsRequired =
false;
797 if (subArg->isRequired()) {
798 subArgsRequired =
true;
802 if (!subArgsRequired) {
815 argument->m_isMainArg =
true;
816 m_mainArgs.push_back(argument);
825 bool wroteLine =
false;
850 if (!m_mainArgs.empty()) {
851 bool hasOperations =
false, hasTopLevelOptions =
false;
852 for (
const Argument *
const arg : m_mainArgs) {
853 if (arg->denotesOperation()) {
854 hasOperations =
true;
855 }
else if (strcmp(arg->name(),
"help")) {
856 hasTopLevelOptions =
true;
858 if (hasOperations && hasTopLevelOptions) {
866 os <<
"Available operations:";
867 for (
const Argument *
const arg : m_mainArgs) {
868 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
874 if (hasTopLevelOptions) {
875 os <<
"\nAvailable top-level options:";
876 for (
const Argument *
const arg : m_mainArgs) {
877 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
886 os <<
"Available arguments:";
887 for (
const Argument *
const arg : m_mainArgs) {
888 if (arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
900 os <<
"Linked against: " << *
i;
901 for (++
i;
i != end; ++
i) {
902 os <<
',' <<
' ' << *
i;
945 invokeExit(EXIT_FAILURE);
971 m_executable =
nullptr;
974 m_executable = *argv;
980 m_defaultArg->m_occurrences.emplace_back(0);
986 const bool completionMode = !strcmp(*++argv,
"--bash-completion-for");
989 unsigned int currentWordIndex = 0, argcForReader;
990 if (completionMode) {
993 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
999 currentWordIndex =
static_cast<unsigned int>(argc - 1);
1001 argcForReader =
min(
static_cast<unsigned int>(argc), currentWordIndex + 1);
1003 argcForReader =
static_cast<unsigned int>(argc);
1007 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
1008 const bool allArgsProcessed(reader.
read());
1009 m_noColorArg.
apply();
1012 if (!completionMode && !allArgsProcessed) {
1013 const auto suggestions(findSuggestions(argc, argv,
static_cast<unsigned int>(argc - 1), reader));
1018 if (completionMode) {
1019 printBashCompletion(argc, argv, currentWordIndex, reader);
1020 invokeExit(EXIT_SUCCESS);
1031 arg->resetRecursively();
1044 if (arg->denotesOperation() && arg->isPresent()) {
1056 for (
const Argument *arg : m_mainArgs) {
1057 if (!arg->isCombinable() && arg->isPresent()) {
1064#ifdef CPP_UTILITIES_DEBUG_BUILD
1081 vector<const Argument *> verifiedArgs;
1082 verifiedArgs.reserve(args.size());
1083 vector<char> abbreviations;
1084 abbreviations.reserve(abbreviations.size() + args.size());
1085 vector<const char *> names;
1086 names.reserve(names.size() + args.size());
1087 bool hasImplicit =
false;
1089 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1090 verifiedArgs.push_back(arg);
1091 assert(!arg->isImplicit() || !hasImplicit);
1092 hasImplicit |= arg->isImplicit();
1093 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1094 abbreviations.push_back(arg->abbreviation());
1095 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1096 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0 || (arg->flags() &
Argument::Flags::Greedy));
1097 names.emplace_back(arg->name());
1099 for (
const Argument *arg : args) {
1100 verifyArgs(arg->subArguments());
1119 return strcmp(arg1->
name(), arg2->
name()) < 0;
1129 bool onlyCombinable =
false;
1130 for (
const Argument *sibling : siblings) {
1131 if (sibling->isPresent() && !sibling->isCombinable()) {
1132 onlyCombinable =
true;
1136 for (
const Argument *sibling : siblings) {
1137 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1138 target.push_back(sibling);
1146ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1147 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1149 ArgumentCompletionInfo completion(reader);
1152 if (completion.lastDetectedArg) {
1153 completion.lastDetectedArgIndex =
static_cast<size_t>(reader.lastArgDenotation - argv);
1154 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1159 completion.lastSpecifiedArgIndex =
static_cast<unsigned int>(argc) - 1;
1160 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1161 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1162 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1167 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1168 completion.nextArgumentOrValue =
true;
1174 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1175 if (!completion.nextArgumentOrValue) {
1177 completion.relevantArgs.push_back(completion.lastDetectedArg);
1183 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1185 completion.relevantPreDefinedValues.push_back(arg);
1194 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1196 if (currentValueCount) {
1197 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1198 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1199 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1201 currentValueCount = 0;
1207 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1208 for (
const Argument *child : completion.lastDetectedArg->subArguments()) {
1209 if (child->isImplicit() && child->requiredValueCount()) {
1210 addValueCompletionsForArg(child);
1218 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1219 addValueCompletionsForArg(completion.lastDetectedArg);
1223 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1224 >= completion.lastDetectedArg->requiredValueCount()) {
1226 for (
const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1227 if (subArg->occurrences() < subArg->maxOccurrences()) {
1228 completion.relevantArgs.push_back(subArg);
1233 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1234 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1235 if (parentArgument == end) {
1247string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const
1250 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1253 const auto *unknownArg(*reader.argv);
1254 auto unknownArgSize(strlen(unknownArg));
1256 if (unknownArgSize > 16) {
1260 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1262 unknownArgSize -= 2;
1266 multiset<ArgumentSuggestion> bestSuggestions;
1268 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1269 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1272 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1273 if (!arg->preDefinedCompletionValues()) {
1276 for (
const char *
i = arg->preDefinedCompletionValues(); *
i; ++
i) {
1277 const char *
const wordStart(
i);
1278 const char *wordEnd(wordStart + 1);
1279 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1281 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart,
static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1287 string suggestionStr;
1288 if (
const auto suggestionCount = bestSuggestions.size()) {
1290 size_t requiredSize = 15;
1291 for (
const auto &suggestion : bestSuggestions) {
1292 requiredSize += suggestion.suggestionSize + 2;
1293 if (suggestion.hasDashPrefix) {
1297 suggestionStr.reserve(requiredSize);
1300 suggestionStr +=
"\nDid you mean ";
1302 for (
const auto &suggestion : bestSuggestions) {
1303 if (++
i == suggestionCount && suggestionCount != 1) {
1304 suggestionStr +=
" or ";
1306 suggestionStr +=
", ";
1308 if (suggestion.hasDashPrefix) {
1309 suggestionStr +=
"--";
1311 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1313 suggestionStr +=
'?';
1315 return suggestionStr;
1323void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1326 const auto completionInfo([&] {
1327 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1328 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1329 return clutteredCompletionInfo;
1333 const char *opening =
nullptr;
1334 string compoundOpening;
1335 size_t openingLen = 0, compoundOpeningStartLen = 0;
1336 unsigned char openingDenotationType =
Value;
1337 if (argc && completionInfo.nextArgumentOrValue) {
1338 if (currentWordIndex <
static_cast<unsigned int>(argc)) {
1339 opening = argv[currentWordIndex];
1345 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1346 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1347 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1348 compoundOpening = argv[currentWordIndex];
1349 compoundOpening +=
'=';
1350 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1351 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1352 compoundOpening = argv[currentWordIndex];
1353 compoundOpening +=
'=';
1354 compoundOpening += opening;
1356 if (!compoundOpening.empty()) {
1357 opening = compoundOpening.data();
1360 opening = *completionInfo.lastSpecifiedArg;
1362 if (*opening ==
'-') {
1364 ++openingDenotationType;
1365 if (*opening ==
'-') {
1367 ++openingDenotationType;
1370 openingLen = strlen(opening);
1374 cout <<
"COMPREPLY=(";
1376 bool noWhitespace =
false;
1377 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1379 arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(
Argument::varValueCount));
1381 if (!arg->preDefinedCompletionValues()) {
1385 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1386 if (openingDenotationType !=
Value) {
1389 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1390 size_t wordIndex = 0;
1391 for (
const char *
i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *
i;) {
1393 const char *i1 =
i, *i2 = opening;
1394 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1396 if ((ok = (i2 == end))) {
1401 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1402 equationSignAlreadyPresent =
false;
1404 cout <<
'\'' <<
' ';
1408 }
else if (*
i ==
'=') {
1409 equationSignAlreadyPresent =
true;
1415 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1428 if (appendEquationSign && !equationSignAlreadyPresent) {
1430 noWhitespace =
true;
1431 equationSignAlreadyPresent =
false;
1439 }
else if (
const char *
i = arg->preDefinedCompletionValues()) {
1440 bool equationSignAlreadyPresent =
false;
1450 equationSignAlreadyPresent =
true;
1455 if (appendEquationSign && !equationSignAlreadyPresent) {
1457 equationSignAlreadyPresent =
false;
1462 cout <<
' ' <<
'\'';
1467 cout <<
'\'' <<
' ';
1471 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1472 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1473 switch (openingDenotationType) {
1475 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1482 if (strncmp(arg->name(), opening, openingLen)) {
1488 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1490 cout <<
'\'' <<
'-' << opening << arg->abbreviation() <<
'\'' <<
' ';
1491 }
else if (completionInfo.lastDetectedArg && reader.argDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1492 if (reader.argv == reader.end) {
1493 cout <<
'\'' << *(reader.argv - 1) <<
'\'' <<
' ';
1495 }
else if (arg->denotesOperation()) {
1496 cout <<
'\'' << arg->name() <<
'\'' <<
' ';
1498 cout <<
'\'' <<
'-' <<
'-' << arg->name() <<
'\'' <<
' ';
1503 string actualDir, actualFile;
1504 bool haveFileOrDirCompletions =
false;
1505 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1507 string unescapedOpening(opening);
1508 findAndReplace<string>(unescapedOpening,
"\\ ",
" ");
1509 findAndReplace<string>(unescapedOpening,
"\\,",
",");
1510 findAndReplace<string>(unescapedOpening,
"\\[",
"[");
1511 findAndReplace<string>(unescapedOpening,
"\\]",
"]");
1512 findAndReplace<string>(unescapedOpening,
"\\!",
"!");
1513 findAndReplace<string>(unescapedOpening,
"\\#",
"#");
1514 findAndReplace<string>(unescapedOpening,
"\\$",
"$");
1515 findAndReplace<string>(unescapedOpening,
"\\'",
"'");
1516 findAndReplace<string>(unescapedOpening,
"\\\"",
"\"");
1517 findAndReplace<string>(unescapedOpening,
"\\\\",
"\\");
1519 string dir =
directory(unescapedOpening);
1523 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1526 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1527 dir.erase(dir.size() - 2, 1);
1529 actualDir = move(dir);
1532 string file =
fileName(unescapedOpening);
1533 if (file[0] ==
'\"' || file[0] ==
'\'') {
1536 if (file.size() > 1 && (file[file.size() - 2] ==
'\"' || file[file.size() - 2] ==
'\'')) {
1537 file.erase(file.size() - 2, 1);
1539 actualFile = move(file);
1543#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1544 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1546 const auto replace =
"'"s, with =
"'\"'\"'"s;
1547 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1548 const auto dirEntries = [&] {
1549 filesystem::directory_iterator
i;
1551 i = filesystem::directory_iterator(actualDir);
1554 i = filesystem::directory_iterator(
".");
1558 for (
const auto &dirEntry : dirEntries) {
1559 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1562 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1565 auto dirEntryName = dirEntry.path().filename().string();
1566 auto hasStartingQuote =
false;
1572 hasStartingQuote =
true;
1573 if (actualDir !=
".") {
1578 if (!hasStartingQuote) {
1581 cout << dirEntryName <<
'\'' <<
' ';
1582 haveFileOrDirCompletions =
true;
1584 }
catch (
const filesystem::filesystem_error &) {
1592 if (haveFileOrDirCompletions) {
1593 cout <<
"; compopt -o filenames";
1598 cout <<
"; compopt -o nospace";
1610 for (
const Argument *arg : args) {
1611 const auto occurrences = arg->occurrences();
1612 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1613 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" mustn't be specified more than ", arg->maxOccurrences(),
1614 (arg->maxOccurrences() == 1 ?
" time." :
" times.")));
1616 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1617 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" must be specified at least ", arg->minOccurrences(),
1618 (arg->minOccurrences() == 1 ?
" time." :
" times.")));
1620 Argument *conflictingArgument =
nullptr;
1621 if (arg->isMainArgument()) {
1622 if (!arg->isCombinable() && arg->isPresent()) {
1626 conflictingArgument = arg->conflictsWithArgument();
1628 if (conflictingArgument) {
1629 throw ParseError(
argsToString(
"The argument \"", conflictingArgument->name(),
"\" can not be combined with \"", arg->name(),
"\"."));
1631 for (
size_t i = 0;
i != occurrences; ++
i) {
1632 if (arg->allRequiredValuesPresent(
i)) {
1635 stringstream ss(stringstream::in | stringstream::out);
1636 ss <<
"Not all parameters for argument \"" << arg->name() <<
"\" ";
1638 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1640 ss <<
"provided. You have to provide the following parameters:";
1641 size_t valueNamesPrint = 0;
1642 for (
const auto &name : arg->m_valueNames) {
1647 while (valueNamesPrint < arg->m_requiredValueCount) {
1648 ss <<
"\nvalue " << (++valueNamesPrint);
1651 throw ParseError(ss.str());
1668 for (
const Argument *arg : args) {
1670 if (arg->m_callbackFunction) {
1671 for (
const auto &occurrence : arg->m_occurrences) {
1672 arg->m_callbackFunction(occurrence);
1683void ArgumentParser::invokeExit(
int code)
1685 if (m_exitFunction) {
1686 m_exitFunction(code);
1702 :
Argument(
"help",
'h',
"shows this information")
1745#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1746 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1748 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1751 setCombinable(
true);
1754 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1758 if (escapeCodesEnabled.has_value()) {
1769#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1780void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const
1783 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1784 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1785 targetTypeName,
"\" failed: ", errorMessage));
1791void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const
1793 throw ParseError(
path.empty()
1794 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1795 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1796 " 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.
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.
Argument::Flags flags() const
Returns Argument::Flags for the argument.
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.
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
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)
constexpr T max(T first, T second)
Returns the greatest of the given items.
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.
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