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;
383 switch (
parser.m_unknownArgBehavior) {
385 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
411 const auto maxColumns = termSize.
columns ? termSize.
columns : numeric_limits<unsigned short>::max();
414 unsigned short currentCol = wrapper.m_indentation.
level;
415 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
416 const bool wrappingRequired = currentCol >= maxColumns;
417 if (wrappingRequired || *currentChar ==
'\n') {
421 if (wrapper.m_indentation.
level < maxColumns) {
422 os << wrapper.m_indentation;
423 currentCol = wrapper.m_indentation.
level;
428 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
440inline bool notEmpty(
const char *str)
465 , m_abbreviation(abbreviation)
466 , m_environmentVar(nullptr)
467 , m_description(description)
469 , m_minOccurrences(0)
470 , m_maxOccurrences(1)
471 , m_requiredValueCount(0)
473 , m_deprecatedBy(nullptr)
477 , m_preDefinedCompletionValues(nullptr)
497 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
498 return m_occurrences.front().values.front();
499 }
else if (m_environmentVar) {
500 return getenv(m_environmentVar);
529 if (notEmpty(
name())) {
543 unsigned int valueNamesPrint = 0;
545 os <<
' ' <<
'[' << *
i <<
']';
552 os <<
" [value " << (valueNamesPrint + 1) <<
']';
561 os <<
'\n' << ident <<
"particularities: mandatory";
563 os <<
" if parent argument is present";
570 bool hasSubArgs =
false;
572 if (arg->isDeprecated()) {
576 arg->printInfo(os, ident.
level);
579 if (ident.
level == 2 && hasSubArgs) {
595 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
620 for (
Argument *
const arg : m_subArgs) {
621 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
646 const auto requiredCap = m_subArgs.size() +
subArguments.size();
647 if (requiredCap < m_subArgs.capacity()) {
648 m_subArgs.reserve(requiredCap);
653 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
654 arg->m_parents.push_back(
this);
668 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
671 m_subArgs.push_back(arg);
672 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
673 arg->m_parents.push_back(
this);
686 for (
const Argument *parent : m_parents) {
687 if (parent->isPresent()) {
720 for (
Argument *parent : m_parents) {
721 for (
Argument *sibling : parent->subArguments()) {
722 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
737 if (arg->denotesOperation() && arg->isPresent()) {
774 , m_executable(nullptr)
776 , m_defaultArg(nullptr)
797 arg->m_isMainArg =
true;
803 bool subArgsRequired =
false;
805 if (subArg->isRequired()) {
806 subArgsRequired =
true;
810 if (!subArgsRequired) {
823 argument->m_isMainArg =
true;
824 m_mainArgs.push_back(argument);
833 bool wroteLine =
false;
858 if (!m_mainArgs.empty()) {
859 bool hasOperations =
false, hasTopLevelOptions =
false;
860 for (
const Argument *
const arg : m_mainArgs) {
861 if (arg->denotesOperation()) {
862 hasOperations =
true;
863 }
else if (strcmp(arg->name(),
"help")) {
864 hasTopLevelOptions =
true;
866 if (hasOperations && hasTopLevelOptions) {
874 os <<
"Available operations:";
875 for (
const Argument *
const arg : m_mainArgs) {
876 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
882 if (hasTopLevelOptions) {
883 os <<
"\nAvailable top-level options:";
884 for (
const Argument *
const arg : m_mainArgs) {
885 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
894 os <<
"Available arguments:";
895 for (
const Argument *
const arg : m_mainArgs) {
896 if (arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
908 os <<
"Linked against: " << *
i;
909 for (++
i;
i != end; ++
i) {
910 os <<
',' <<
' ' << *
i;
953 invokeExit(EXIT_FAILURE);
979 m_executable =
nullptr;
982 m_executable = *argv;
988 m_defaultArg->m_occurrences.emplace_back(0);
994 const bool completionMode = !strcmp(*++argv,
"--bash-completion-for");
997 unsigned int currentWordIndex = 0, argcForReader;
998 if (completionMode) {
1001 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
1007 currentWordIndex =
static_cast<unsigned int>(argc - 1);
1009 argcForReader =
min(
static_cast<unsigned int>(argc), currentWordIndex + 1);
1011 argcForReader =
static_cast<unsigned int>(argc);
1015 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
1016 const bool allArgsProcessed(reader.
read());
1017 m_noColorArg.
apply();
1020 if (!completionMode && !allArgsProcessed) {
1021 const auto suggestions(findSuggestions(argc, argv,
static_cast<unsigned int>(argc - 1), reader));
1026 if (completionMode) {
1027 printBashCompletion(argc, argv, currentWordIndex, reader);
1028 invokeExit(EXIT_SUCCESS);
1039 arg->resetRecursively();
1052 if (arg->denotesOperation() && arg->isPresent()) {
1064 for (
const Argument *arg : m_mainArgs) {
1065 if (!arg->isCombinable() && arg->isPresent()) {
1072#ifdef CPP_UTILITIES_DEBUG_BUILD
1089 vector<const Argument *> verifiedArgs;
1090 verifiedArgs.reserve(args.size());
1091 vector<char> abbreviations;
1092 abbreviations.reserve(abbreviations.size() + args.size());
1093 vector<const char *> names;
1094 names.reserve(names.size() + args.size());
1095 bool hasImplicit =
false;
1097 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1098 verifiedArgs.push_back(arg);
1099 assert(!arg->isImplicit() || !hasImplicit);
1100 hasImplicit |= arg->isImplicit();
1101 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1102 abbreviations.push_back(arg->abbreviation());
1103 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1104 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0 || (arg->flags() &
Argument::Flags::Greedy));
1105 names.emplace_back(arg->name());
1108 verifyArgs(arg->subArguments());
1127 return strcmp(arg1->
name(), arg2->
name()) < 0;
1137 bool onlyCombinable =
false;
1138 for (
const Argument *sibling : siblings) {
1139 if (sibling->isPresent() && !sibling->isCombinable()) {
1140 onlyCombinable =
true;
1144 for (
const Argument *sibling : siblings) {
1145 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1146 target.push_back(sibling);
1155 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1160 if (completion.lastDetectedArg) {
1161 completion.lastDetectedArgIndex =
static_cast<size_t>(reader.
lastArgDenotation - argv);
1162 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1167 completion.lastSpecifiedArgIndex =
static_cast<unsigned int>(argc) - 1;
1168 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1169 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1170 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1175 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1176 completion.nextArgumentOrValue =
true;
1182 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1183 if (!completion.nextArgumentOrValue) {
1185 completion.relevantArgs.push_back(completion.lastDetectedArg);
1191 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1193 completion.relevantPreDefinedValues.push_back(arg);
1202 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1204 if (currentValueCount) {
1205 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1206 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1207 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1209 currentValueCount = 0;
1215 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1216 for (
const Argument *child : completion.lastDetectedArg->subArguments()) {
1217 if (child->isImplicit() && child->requiredValueCount()) {
1218 addValueCompletionsForArg(child);
1226 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1227 addValueCompletionsForArg(completion.lastDetectedArg);
1231 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1232 >= completion.lastDetectedArg->requiredValueCount()) {
1234 for (
const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1235 if (subArg->occurrences() < subArg->maxOccurrences()) {
1236 completion.relevantArgs.push_back(subArg);
1241 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1242 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1243 if (parentArgument == end) {
1255string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const
1258 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1261 const auto *unknownArg(*reader.
argv);
1262 auto unknownArgSize(strlen(unknownArg));
1264 if (unknownArgSize > 16) {
1268 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1270 unknownArgSize -= 2;
1274 multiset<ArgumentSuggestion> bestSuggestions;
1276 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1277 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1280 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1281 if (!arg->preDefinedCompletionValues()) {
1284 for (
const char *
i = arg->preDefinedCompletionValues(); *
i; ++
i) {
1285 const char *
const wordStart(
i);
1286 const char *wordEnd(wordStart + 1);
1287 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1289 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart,
static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1295 string suggestionStr;
1296 if (
const auto suggestionCount = bestSuggestions.size()) {
1298 size_t requiredSize = 15;
1299 for (
const auto &suggestion : bestSuggestions) {
1300 requiredSize += suggestion.suggestionSize + 2;
1301 if (suggestion.hasDashPrefix) {
1305 suggestionStr.reserve(requiredSize);
1308 suggestionStr +=
"\nDid you mean ";
1310 for (
const auto &suggestion : bestSuggestions) {
1311 if (++
i == suggestionCount && suggestionCount != 1) {
1312 suggestionStr +=
" or ";
1314 suggestionStr +=
", ";
1316 if (suggestion.hasDashPrefix) {
1317 suggestionStr +=
"--";
1319 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1321 suggestionStr +=
'?';
1323 return suggestionStr;
1331void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1334 const auto completionInfo([&] {
1335 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1336 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1337 return clutteredCompletionInfo;
1341 const char *opening =
nullptr;
1342 string compoundOpening;
1343 size_t openingLen = 0, compoundOpeningStartLen = 0;
1344 unsigned char openingDenotationType =
Value;
1345 if (argc && completionInfo.nextArgumentOrValue) {
1346 if (currentWordIndex <
static_cast<unsigned int>(argc)) {
1347 opening = argv[currentWordIndex];
1353 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1354 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1355 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1356 compoundOpening = argv[currentWordIndex];
1357 compoundOpening +=
'=';
1358 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1359 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1360 compoundOpening = argv[currentWordIndex];
1361 compoundOpening +=
'=';
1362 compoundOpening += opening;
1364 if (!compoundOpening.empty()) {
1365 opening = compoundOpening.data();
1368 opening = *completionInfo.lastSpecifiedArg;
1370 if (*opening ==
'-') {
1372 ++openingDenotationType;
1373 if (*opening ==
'-') {
1375 ++openingDenotationType;
1378 openingLen = strlen(opening);
1382 cout <<
"COMPREPLY=(";
1384 bool noWhitespace =
false;
1385 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1389 if (!arg->preDefinedCompletionValues()) {
1393 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1394 if (openingDenotationType !=
Value) {
1397 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1398 size_t wordIndex = 0;
1399 for (
const char *
i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *
i;) {
1401 const char *i1 =
i, *i2 = opening;
1402 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1404 if ((ok = (i2 == end))) {
1409 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1410 equationSignAlreadyPresent =
false;
1412 cout <<
'\'' <<
' ';
1416 }
else if (*
i ==
'=') {
1417 equationSignAlreadyPresent =
true;
1423 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1436 if (appendEquationSign && !equationSignAlreadyPresent) {
1438 noWhitespace =
true;
1439 equationSignAlreadyPresent =
false;
1447 }
else if (
const char *
i = arg->preDefinedCompletionValues()) {
1448 bool equationSignAlreadyPresent =
false;
1458 equationSignAlreadyPresent =
true;
1463 if (appendEquationSign && !equationSignAlreadyPresent) {
1465 equationSignAlreadyPresent =
false;
1470 cout <<
' ' <<
'\'';
1475 cout <<
'\'' <<
' ';
1479 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1480 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1481 switch (openingDenotationType) {
1483 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1490 if (strncmp(arg->name(), opening, openingLen)) {
1496 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1498 cout <<
'\'' <<
'-' << opening << arg->abbreviation() <<
'\'' <<
' ';
1500 if (reader.
argv == reader.
end) {
1501 cout <<
'\'' << *(reader.
argv - 1) <<
'\'' <<
' ';
1503 }
else if (arg->denotesOperation()) {
1504 cout <<
'\'' << arg->name() <<
'\'' <<
' ';
1506 cout <<
'\'' <<
'-' <<
'-' << arg->name() <<
'\'' <<
' ';
1511 string actualDir, actualFile;
1512 bool haveFileOrDirCompletions =
false;
1513 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1515 string unescapedOpening(opening);
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,
"\\$",
"$");
1523 findAndReplace<string>(unescapedOpening,
"\\'",
"'");
1524 findAndReplace<string>(unescapedOpening,
"\\\"",
"\"");
1525 findAndReplace<string>(unescapedOpening,
"\\\\",
"\\");
1527 string dir =
directory(unescapedOpening);
1531 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1534 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1535 dir.erase(dir.size() - 2, 1);
1537 actualDir = std::move(dir);
1540 string file =
fileName(unescapedOpening);
1541 if (file[0] ==
'\"' || file[0] ==
'\'') {
1544 if (file.size() > 1 && (file[file.size() - 2] ==
'\"' || file[file.size() - 2] ==
'\'')) {
1545 file.erase(file.size() - 2, 1);
1547 actualFile = std::move(file);
1551#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1552 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1554 const auto replace =
"'"s, with =
"'\"'\"'"s;
1555 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1556 const auto dirEntries = [&] {
1557 filesystem::directory_iterator
i;
1559 i = filesystem::directory_iterator(actualDir);
1562 i = filesystem::directory_iterator(
".");
1566 for (
const auto &dirEntry : dirEntries) {
1567 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1570 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1573 auto dirEntryName = dirEntry.path().filename().string();
1574 auto hasStartingQuote =
false;
1580 hasStartingQuote =
true;
1581 if (actualDir !=
".") {
1586 if (!hasStartingQuote) {
1589 cout << dirEntryName <<
'\'' <<
' ';
1590 haveFileOrDirCompletions =
true;
1592 }
catch (
const filesystem::filesystem_error &) {
1600 if (haveFileOrDirCompletions) {
1601 cout <<
"; compopt -o filenames";
1606 cout <<
"; compopt -o nospace";
1619 const auto occurrences = arg->occurrences();
1620 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1621 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" mustn't be specified more than ", arg->maxOccurrences(),
1622 (arg->maxOccurrences() == 1 ?
" time." :
" times.")));
1624 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1625 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" must be specified at least ", arg->minOccurrences(),
1626 (arg->minOccurrences() == 1 ?
" time." :
" times.")));
1628 Argument *conflictingArgument =
nullptr;
1629 if (arg->isMainArgument()) {
1630 if (!arg->isCombinable() && arg->isPresent()) {
1636 if (conflictingArgument) {
1637 throw ParseError(
argsToString(
"The argument \"", conflictingArgument->
name(),
"\" can not be combined with \"", arg->name(),
"\"."));
1639 for (
size_t i = 0;
i != occurrences; ++
i) {
1640 if (arg->allRequiredValuesPresent(
i)) {
1643 stringstream ss(stringstream::in | stringstream::out);
1644 ss <<
"Not all parameters for argument \"" << arg->name() <<
"\" ";
1646 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1648 ss <<
"provided. You have to provide the following parameters:";
1649 size_t valueNamesPrint = 0;
1650 for (
const auto &name : arg->m_valueNames) {
1655 while (valueNamesPrint < arg->m_requiredValueCount) {
1656 ss <<
"\nvalue " << (++valueNamesPrint);
1678 if (arg->m_callbackFunction) {
1679 for (
const auto &occurrence : arg->m_occurrences) {
1680 arg->m_callbackFunction(occurrence);
1691void ArgumentParser::invokeExit(
int code)
1693 if (m_exitFunction) {
1694 m_exitFunction(code);
1710 :
Argument(
"help",
'h',
"shows this information")
1753#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1754 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1756 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1759 setCombinable(
true);
1762 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1766 if (escapeCodesEnabled.has_value()) {
1777#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1788void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const
1791 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1792 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1793 targetTypeName,
"\" failed: ", errorMessage));
1799void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const
1802 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1803 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1804 " 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.
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 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