5 #include "../conversion/stringbuilder.h"
6 #include "../conversion/stringconversion.h"
7 #include "../io/ansiescapecodes.h"
8 #include "../io/path.h"
9 #include "../misc/levenshtein.h"
10 #include "../misc/parseerror.h"
20 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
25 using namespace std::placeholders;
26 using namespace std::literals;
40 const char *envValue = std::getenv(variableName);
44 for (; *envValue; ++envValue) {
72 size_t lastDetectedArgIndex = 0;
76 const char *
const *lastSpecifiedArg =
nullptr;
77 unsigned int lastSpecifiedArgIndex = 0;
78 bool nextArgumentOrValue =
false;
79 bool completeFiles =
false, completeDirs =
false;
87 : lastDetectedArg(reader.lastArg)
92 struct 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;
105 ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
size_t suggestionSize,
bool isOperation)
106 : suggestion(suggestion)
107 , suggestionSize(suggestionSize)
109 , hasDashPrefix(isOperation)
113 ArgumentSuggestion::ArgumentSuggestion(
const char *unknownArg,
size_t unknownArgSize,
const char *suggestion,
bool isOperation)
114 : ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
118 bool ArgumentSuggestion::operator<(
const ArgumentSuggestion &other)
const
120 return editingDistance < other.editingDistance;
123 void 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)
184 bool 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;
219 bool abbreviationFound =
false;
222 abbreviationFound =
false;
234 abbreviationFound =
false;
255 for (; argDenotationLength; matchingArg =
nullptr) {
259 if (arg->abbreviation() && arg->abbreviation() == *
argDenotation) {
261 abbreviationFound =
true;
267 if (arg->matchesDenotation(
argDenotation, argDenotationLength)) {
278 matchingArg->m_occurrences.emplace_back(
index, parentPath, parentArg);
281 values = &matchingArg->m_occurrences.back().values;
285 values->push_back(equationPos + 1);
292 lastArg = lastArgInLevel = matchingArg;
303 const char *
const *
const currentArgValue =
argv;
320 for (
auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
321 for (
Argument *
const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() :
parser.m_mainArgs)) {
322 if (sibling->occurrences() < sibling->maxOccurrences()) {
328 if (sibling->matchesDenotation(
argDenotation, argDenotationLength)) {
333 if (parentArgument == pathEnd) {
350 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *
argv)) {
351 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
363 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
364 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
365 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
373 if (lastArgInLevel == matchingArg) {
378 values = &matchingArg->m_occurrences.back().values;
382 lastArg = lastArgInLevel = matchingArg;
400 switch (
parser.m_unknownArgBehavior) {
402 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
431 unsigned short currentCol = wrapper.m_indentation.
level;
432 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
433 const bool wrappingRequired = currentCol >= maxColumns;
434 if (wrappingRequired || *currentChar ==
'\n') {
438 if (wrapper.m_indentation.
level < maxColumns) {
439 os << wrapper.m_indentation;
440 currentCol = wrapper.m_indentation.
level;
445 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
457 inline bool notEmpty(
const char *str)
480 Argument::Argument(
const char *name,
char abbreviation,
const char *description,
const char *example)
482 , m_abbreviation(abbreviation)
483 , m_environmentVar(nullptr)
484 , m_description(description)
486 , m_minOccurrences(0)
487 , m_maxOccurrences(1)
488 , m_requiredValueCount(0)
490 , m_deprecatedBy(nullptr)
494 , m_preDefinedCompletionValues(nullptr)
514 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
515 return m_occurrences.front().values.front();
516 }
else if (m_environmentVar) {
517 return getenv(m_environmentVar);
546 if (notEmpty(
name())) {
560 unsigned int valueNamesPrint = 0;
562 os <<
' ' <<
'[' << *
i <<
']';
569 os <<
" [value " << (valueNamesPrint + 1) <<
']';
578 os <<
'\n' << ident <<
"particularities: mandatory";
580 os <<
" if parent argument is present";
587 bool hasSubArgs =
false;
589 if (arg->isDeprecated()) {
593 arg->printInfo(os, ident.
level);
596 if (ident.
level == 2 && hasSubArgs) {
612 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
637 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
640 m_subArgs.assign(secondaryArguments);
644 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
645 arg->m_parents.push_back(
this);
659 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
662 m_subArgs.push_back(arg);
663 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
664 arg->m_parents.push_back(
this);
677 for (
const Argument *parent : m_parents) {
678 if (parent->isPresent()) {
711 for (
Argument *parent : m_parents) {
713 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
728 if (arg->denotesOperation() && arg->isPresent()) {
742 arg->resetRecursively();
765 , m_executable(nullptr)
767 , m_defaultArg(nullptr)
788 arg->m_isMainArg =
true;
794 bool subArgsRequired =
false;
796 if (subArg->isRequired()) {
797 subArgsRequired =
true;
801 if (!subArgsRequired) {
814 argument->m_isMainArg =
true;
815 m_mainArgs.push_back(argument);
824 bool wroteLine =
false;
849 if (!m_mainArgs.empty()) {
850 bool hasOperations =
false, hasTopLevelOptions =
false;
851 for (
const Argument *
const arg : m_mainArgs) {
852 if (arg->denotesOperation()) {
853 hasOperations =
true;
854 }
else if (strcmp(arg->name(),
"help")) {
855 hasTopLevelOptions =
true;
857 if (hasOperations && hasTopLevelOptions) {
865 os <<
"Available operations:";
866 for (
const Argument *
const arg : m_mainArgs) {
867 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
873 if (hasTopLevelOptions) {
874 os <<
"\nAvailable top-level options:";
875 for (
const Argument *
const arg : m_mainArgs) {
876 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
885 os <<
"Available arguments:";
886 for (
const Argument *
const arg : m_mainArgs) {
887 if (arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
899 os <<
"Linked against: " << *
i;
900 for (++
i;
i != end; ++
i) {
901 os <<
',' <<
' ' << *
i;
970 m_executable =
nullptr;
973 m_executable = *argv;
979 m_defaultArg->m_occurrences.emplace_back(0);
985 const bool completionMode = !strcmp(*++argv,
"--bash-completion-for");
988 unsigned int currentWordIndex = 0, argcForReader;
989 if (completionMode) {
992 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
998 currentWordIndex =
static_cast<unsigned int>(argc - 1);
1000 argcForReader =
min(
static_cast<unsigned int>(argc), currentWordIndex + 1);
1002 argcForReader =
static_cast<unsigned int>(argc);
1006 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
1007 const bool allArgsProcessed(reader.
read());
1008 m_noColorArg.
apply();
1011 if (!completionMode && !allArgsProcessed) {
1012 const auto suggestions(findSuggestions(argc, argv,
static_cast<unsigned int>(argc - 1), reader));
1017 if (completionMode) {
1018 printBashCompletion(argc, argv, currentWordIndex, reader);
1030 arg->resetRecursively();
1043 if (arg->denotesOperation() && arg->isPresent()) {
1055 for (
const Argument *arg : m_mainArgs) {
1056 if (!arg->isCombinable() && arg->isPresent()) {
1063 #ifdef CPP_UTILITIES_DEBUG_BUILD
1080 vector<const Argument *> verifiedArgs;
1081 verifiedArgs.reserve(args.size());
1082 vector<char> abbreviations;
1083 abbreviations.reserve(abbreviations.size() + args.size());
1084 vector<const char *> names;
1085 names.reserve(names.size() + args.size());
1086 bool hasImplicit =
false;
1088 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1089 verifiedArgs.push_back(arg);
1090 assert(!arg->isImplicit() || !hasImplicit);
1091 hasImplicit |= arg->isImplicit();
1092 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1093 abbreviations.push_back(arg->abbreviation());
1094 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) { return !strcmp(arg->name(), name); }) == names.cend());
1095 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
1096 names.emplace_back(arg->name());
1098 for (
const Argument *arg : args) {
1099 verifyArgs(arg->subArguments());
1118 return strcmp(arg1->
name(), arg2->
name()) < 0;
1128 bool onlyCombinable =
false;
1129 for (
const Argument *sibling : siblings) {
1130 if (sibling->isPresent() && !sibling->isCombinable()) {
1131 onlyCombinable =
true;
1135 for (
const Argument *sibling : siblings) {
1136 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1137 target.push_back(sibling);
1145 ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1146 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1148 ArgumentCompletionInfo completion(reader);
1151 if (completion.lastDetectedArg) {
1152 completion.lastDetectedArgIndex =
static_cast<size_t>(reader.lastArgDenotation - argv);
1153 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1158 completion.lastSpecifiedArgIndex =
static_cast<unsigned int>(argc) - 1;
1159 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1160 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1161 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1166 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1167 completion.nextArgumentOrValue =
true;
1173 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1174 if (!completion.nextArgumentOrValue) {
1176 completion.relevantArgs.push_back(completion.lastDetectedArg);
1182 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1184 completion.relevantPreDefinedValues.push_back(arg);
1193 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1195 if (currentValueCount) {
1196 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1197 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1198 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1200 currentValueCount = 0;
1206 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1207 for (
const Argument *child : completion.lastDetectedArg->subArguments()) {
1208 if (child->isImplicit() && child->requiredValueCount()) {
1209 addValueCompletionsForArg(child);
1217 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1218 addValueCompletionsForArg(completion.lastDetectedArg);
1222 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1223 >= completion.lastDetectedArg->requiredValueCount()) {
1225 for (
const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1226 if (subArg->occurrences() < subArg->maxOccurrences()) {
1227 completion.relevantArgs.push_back(subArg);
1232 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1233 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1234 if (parentArgument == end) {
1246 string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const
1249 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1252 const auto *unknownArg(*reader.argv);
1253 auto unknownArgSize(strlen(unknownArg));
1255 if (unknownArgSize > 16) {
1259 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1261 unknownArgSize -= 2;
1265 multiset<ArgumentSuggestion> bestSuggestions;
1267 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1268 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1271 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1272 if (!arg->preDefinedCompletionValues()) {
1275 for (
const char *
i = arg->preDefinedCompletionValues(); *
i; ++
i) {
1276 const char *
const wordStart(
i);
1277 const char *wordEnd(wordStart + 1);
1278 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1280 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart,
static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1286 string suggestionStr;
1287 if (
const auto suggestionCount = bestSuggestions.size()) {
1289 size_t requiredSize = 15;
1290 for (
const auto &suggestion : bestSuggestions) {
1291 requiredSize += suggestion.suggestionSize + 2;
1292 if (suggestion.hasDashPrefix) {
1296 suggestionStr.reserve(requiredSize);
1299 suggestionStr +=
"\nDid you mean ";
1301 for (
const auto &suggestion : bestSuggestions) {
1302 if (++
i == suggestionCount && suggestionCount != 1) {
1303 suggestionStr +=
" or ";
1305 suggestionStr +=
", ";
1307 if (suggestion.hasDashPrefix) {
1308 suggestionStr +=
"--";
1310 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1312 suggestionStr +=
'?';
1314 return suggestionStr;
1322 void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1325 const auto completionInfo([&] {
1326 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1327 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1328 return clutteredCompletionInfo;
1332 const char *opening =
nullptr;
1333 string compoundOpening;
1334 size_t openingLen = 0, compoundOpeningStartLen = 0;
1335 unsigned char openingDenotationType =
Value;
1336 if (argc && completionInfo.nextArgumentOrValue) {
1337 if (currentWordIndex <
static_cast<unsigned int>(argc)) {
1338 opening = argv[currentWordIndex];
1344 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1345 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1346 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1347 compoundOpening = argv[currentWordIndex];
1348 compoundOpening +=
'=';
1349 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1350 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1351 compoundOpening = argv[currentWordIndex];
1352 compoundOpening +=
'=';
1353 compoundOpening += opening;
1355 if (!compoundOpening.empty()) {
1356 opening = compoundOpening.data();
1359 opening = *completionInfo.lastSpecifiedArg;
1361 if (*opening ==
'-') {
1363 ++openingDenotationType;
1364 if (*opening ==
'-') {
1366 ++openingDenotationType;
1369 openingLen = strlen(opening);
1373 cout <<
"COMPREPLY=(";
1375 bool noWhitespace =
false;
1376 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1378 arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(
Argument::varValueCount));
1380 if (!arg->preDefinedCompletionValues()) {
1384 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1385 if (openingDenotationType !=
Value) {
1388 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1389 size_t wordIndex = 0;
1390 for (
const char *
i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *
i;) {
1392 const char *i1 =
i, *i2 = opening;
1393 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1395 if ((ok = (i2 == end))) {
1400 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1401 equationSignAlreadyPresent =
false;
1403 cout <<
'\'' <<
' ';
1407 }
else if (*
i ==
'=') {
1408 equationSignAlreadyPresent =
true;
1414 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1427 if (appendEquationSign && !equationSignAlreadyPresent) {
1429 noWhitespace =
true;
1430 equationSignAlreadyPresent =
false;
1438 }
else if (
const char *
i = arg->preDefinedCompletionValues()) {
1439 bool equationSignAlreadyPresent =
false;
1449 equationSignAlreadyPresent =
true;
1454 if (appendEquationSign && !equationSignAlreadyPresent) {
1456 equationSignAlreadyPresent =
false;
1461 cout <<
' ' <<
'\'';
1466 cout <<
'\'' <<
' ';
1470 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1471 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1472 switch (openingDenotationType) {
1474 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1481 if (strncmp(arg->name(), opening, openingLen)) {
1487 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1489 cout <<
'\'' <<
'-' << opening << arg->abbreviation() <<
'\'' <<
' ';
1490 }
else if (completionInfo.lastDetectedArg && reader.argDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1491 if (reader.argv == reader.end) {
1492 cout <<
'\'' << *(reader.argv - 1) <<
'\'' <<
' ';
1494 }
else if (arg->denotesOperation()) {
1495 cout <<
'\'' << arg->name() <<
'\'' <<
' ';
1497 cout <<
'\'' <<
'-' <<
'-' << arg->name() <<
'\'' <<
' ';
1502 string actualDir, actualFile;
1503 bool haveFileOrDirCompletions =
false;
1504 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1506 string unescapedOpening(opening);
1507 findAndReplace<string>(unescapedOpening,
"\\ ",
" ");
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,
"\\\\",
"\\");
1518 string dir =
directory(unescapedOpening);
1522 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1525 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1526 dir.erase(dir.size() - 2, 1);
1528 actualDir = move(dir);
1531 string file =
fileName(unescapedOpening);
1532 if (file[0] ==
'\"' || file[0] ==
'\'') {
1535 if (file.size() > 1 && (file[file.size() - 2] ==
'\"' || file[file.size() - 2] ==
'\'')) {
1536 file.erase(file.size() - 2, 1);
1538 actualFile = move(file);
1542 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1543 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1545 const auto replace =
"'"s, with =
"'\"'\"'"s;
1546 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1547 const auto dirEntries = [&] {
1548 filesystem::directory_iterator
i;
1550 i = filesystem::directory_iterator(actualDir);
1553 i = filesystem::directory_iterator(
".");
1557 for (
const auto &dirEntry : dirEntries) {
1558 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1561 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1564 auto dirEntryName = dirEntry.path().filename().string();
1565 auto hasStartingQuote =
false;
1571 hasStartingQuote =
true;
1572 if (actualDir !=
".") {
1577 if (!hasStartingQuote) {
1580 cout << dirEntryName <<
'\'' <<
' ';
1581 haveFileOrDirCompletions =
true;
1583 }
catch (
const filesystem::filesystem_error &) {
1591 if (haveFileOrDirCompletions) {
1592 cout <<
"; compopt -o filenames";
1597 cout <<
"; compopt -o nospace";
1609 for (
const Argument *arg : args) {
1610 const auto occurrences = arg->occurrences();
1611 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1612 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" mustn't be specified more than ", arg->maxOccurrences(),
1613 (arg->maxOccurrences() == 1 ?
" time." :
" times.")));
1615 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1616 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" must be specified at least ", arg->minOccurrences(),
1617 (arg->minOccurrences() == 1 ?
" time." :
" times.")));
1619 Argument *conflictingArgument =
nullptr;
1620 if (arg->isMainArgument()) {
1621 if (!arg->isCombinable() && arg->isPresent()) {
1625 conflictingArgument = arg->conflictsWithArgument();
1627 if (conflictingArgument) {
1628 throw ParseError(
argsToString(
"The argument \"", conflictingArgument->name(),
"\" can not be combined with \"", arg->name(),
"\"."));
1630 for (
size_t i = 0;
i != occurrences; ++
i) {
1631 if (arg->allRequiredValuesPresent(
i)) {
1634 stringstream ss(stringstream::in | stringstream::out);
1635 ss <<
"Not all parameters for argument \"" << arg->name() <<
"\" ";
1637 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1639 ss <<
"provided. You have to provide the following parameters:";
1640 size_t valueNamesPrint = 0;
1641 for (
const auto &name : arg->m_valueNames) {
1646 while (valueNamesPrint < arg->m_requiredValueCount) {
1647 ss <<
"\nvalue " << (++valueNamesPrint);
1650 throw ParseError(ss.str());
1667 for (
const Argument *arg : args) {
1669 if (arg->m_callbackFunction) {
1670 for (
const auto &occurrence : arg->m_occurrences) {
1671 arg->m_callbackFunction(occurrence);
1682 void ArgumentParser::invokeExit(
int code)
1684 if (m_exitFunction) {
1685 m_exitFunction(code);
1701 :
Argument(
"help",
'h',
"shows this information")
1744 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1745 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1747 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1750 setCombinable(
true);
1753 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1757 if (escapeCodesEnabled.has_value()) {
1768 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1779 void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const
1782 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1783 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1784 targetTypeName,
"\" failed: ", errorMessage));
1790 void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const
1792 throw ParseError(
path.empty()
1793 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1794 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1795 " 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.
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.
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.
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
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
bool operator==(const AsHexNumber< T > &lhs, const AsHexNumber< T > &rhs)
Provides operator == required by CPPUNIT_ASSERT_EQUAL.
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
StringType argsToString(Args &&...args)
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
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.
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
const Argument *const lastDetectedArg
vector< Argument * > lastDetectedArgPath
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