6 #include "../conversion/stringbuilder.h" 7 #include "../conversion/stringconversion.h" 8 #include "../io/ansiescapecodes.h" 9 #include "../io/path.h" 10 #include "../misc/levenshtein.h" 21 using namespace std::placeholders;
48 size_t lastDetectedArgIndex = 0;
52 const char *
const *lastSpecifiedArg =
nullptr;
53 unsigned int lastSpecifiedArgIndex = 0;
54 bool nextArgumentOrValue =
false;
55 bool completeFiles =
false, completeDirs =
false;
63 : lastDetectedArg(reader.lastArg)
72 void addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const;
81 : suggestion(suggestion)
82 , suggestionSize(suggestionSize)
84 , hasDashPrefix(isOperation)
89 :
ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
100 if (suggestions.size() >= limit && !(*
this < *--suggestions.end())) {
103 suggestions.emplace(*
this);
104 while (suggestions.size() > limit) {
105 suggestions.erase(--suggestions.end());
123 , args(parser.m_mainArgs)
128 , argDenotation(nullptr)
129 , completionMode(completionMode)
158 bool Argument::matchesDenotation(
const char *denotation,
size_t denotationLength)
const 160 return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) ==
'\0';
175 const vector<Argument *> &parentPath = parentArg ? parentArg->
path(parentArg->
occurrences() - 1) : vector<Argument *>();
178 vector<const char *> *values =
nullptr;
191 bool abbreviationFound =
false;
194 abbreviationFound =
false;
204 abbreviationFound =
false;
218 for (; argDenotationLength; matchingArg =
nullptr) {
222 if (arg->abbreviation() && arg->abbreviation() == *
argDenotation) {
224 abbreviationFound =
true;
230 if (arg->matchesDenotation(
argDenotation, argDenotationLength)) {
241 matchingArg->m_occurrences.emplace_back(
index, parentPath, parentArg);
244 values = &matchingArg->m_occurrences.back().values;
248 values->push_back(equationPos + 1);
262 const char *
const *
const currentArgValue =
argv;
279 for (
auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
280 for (
Argument *
const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() :
parser.m_mainArgs)) {
281 if (sibling->occurrences() < sibling->maxOccurrences()) {
287 if (sibling->matchesDenotation(
argDenotation, argDenotationLength)) {
292 if (parentArgument == pathEnd) {
307 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *
argv)) {
308 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
319 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
320 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
321 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
329 if (lastArgInLevel == matchingArg) {
334 values = &matchingArg->m_occurrences.back().values;
352 switch (
parser.m_unknownArgBehavior) {
354 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
381 unsigned short currentCol = wrapper.m_indentation.
level;
382 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
383 const bool wrappingRequired = currentCol >= maxColumns;
384 if (wrappingRequired || *currentChar ==
'\n') {
388 if (wrapper.m_indentation.
level < maxColumns) {
389 os << wrapper.m_indentation;
390 currentCol = wrapper.m_indentation.
level;
395 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
427 inline bool notEmpty(
const char *str)
450 Argument::Argument(
const char *name,
char abbreviation,
const char *description,
const char *example)
452 , m_abbreviation(abbreviation)
453 , m_environmentVar(nullptr)
454 , m_description(description)
456 , m_minOccurrences(0)
457 , m_maxOccurrences(1)
458 , m_combinable(false)
459 , m_denotesOperation(false)
460 , m_requiredValueCount(0)
465 , m_preDefinedCompletionValues(nullptr)
485 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
486 return m_occurrences.front().values.front();
487 }
else if (m_environmentVar) {
488 return getenv(m_environmentVar);
502 if (notEmpty(
name())) {
516 unsigned int valueNamesPrint = 0;
518 os <<
' ' <<
'[' << *
i <<
']';
525 os <<
" [value " << (valueNamesPrint + 1) <<
']';
534 os <<
'\n' << ident <<
"particularities: mandatory";
536 os <<
" if parent argument is present";
544 arg->printInfo(os, ident.level);
563 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
588 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
591 m_subArgs.assign(secondaryArguments);
595 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
596 arg->m_parents.push_back(
this);
610 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) == m_subArgs.cend()) {
611 m_subArgs.push_back(arg);
612 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
613 arg->m_parents.push_back(
this);
627 for (
const Argument *parent : m_parents) {
628 if (parent->isPresent()) {
659 for (
Argument *parent : m_parents) {
661 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
677 if (arg->denotesOperation() && arg->isPresent()) {
691 arg->resetRecursively();
714 , m_executable(nullptr)
716 , m_defaultArg(nullptr)
733 arg->m_isMainArg =
true;
738 bool subArgsRequired =
false;
740 if (subArg->isRequired()) {
741 subArgsRequired =
true;
745 if (!subArgsRequired) {
763 argument->m_isMainArg =
true;
764 m_mainArgs.push_back(argument);
788 os <<
"Linked against: " << *
i;
789 for (++
i;
i != end; ++
i) {
790 os <<
',' <<
' ' << *
i;
797 if (!m_mainArgs.empty()) {
798 bool hasOperations =
false;
799 for (
const Argument *arg : m_mainArgs) {
800 if (arg->denotesOperation()) {
801 hasOperations =
true;
809 os <<
"Available operations:";
810 for (
const Argument *arg : m_mainArgs) {
811 if (arg->denotesOperation() && strcmp(arg->name(),
"help")) {
816 os <<
"\nAvailable top-level options:";
817 for (
const Argument *arg : m_mainArgs) {
818 if (!arg->denotesOperation() && strcmp(arg->name(),
"help")) {
825 os <<
"Available arguments:";
826 for (
const Argument *arg : m_mainArgs) {
827 if (strcmp(arg->name(),
"help")) {
899 }
catch (
const Failure &failure) {
929 m_executable =
nullptr;
932 m_executable = *argv;
938 m_defaultArg->m_occurrences.emplace_back(0);
944 const bool completionMode = !strcmp(*++argv,
"--bash-completion-for");
947 unsigned int currentWordIndex, argcForReader;
948 if (completionMode) {
951 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
956 currentWordIndex = static_cast<unsigned int>(argc - 1);
958 argcForReader =
min(static_cast<unsigned int>(argc), currentWordIndex + 1);
960 argcForReader = static_cast<unsigned int>(argc);
964 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
965 const bool allArgsProcessed(reader.
read());
969 if (!completionMode && !allArgsProcessed) {
970 const auto suggestions(findSuggestions(argc, argv, static_cast<unsigned int>(argc - 1), reader));
975 if (completionMode) {
976 printBashCompletion(argc, argv, currentWordIndex, reader);
988 arg->resetRecursively();
1001 if (arg->denotesOperation() && arg->isPresent()) {
1013 for (
const Argument *arg : m_mainArgs) {
1014 if (!arg->isCombinable() && arg->isPresent()) {
1036 void ApplicationUtilities::ArgumentParser::verifyArgs(
const ArgumentVector &args)
1038 vector<const Argument *> verifiedArgs;
1039 verifiedArgs.reserve(args.size());
1040 vector<char> abbreviations;
1041 abbreviations.reserve(abbreviations.size() + args.size());
1042 vector<const char *> names;
1043 names.reserve(names.size() + args.size());
1044 bool hasImplicit =
false;
1046 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1047 verifiedArgs.push_back(arg);
1048 assert(!arg->isImplicit() || !hasImplicit);
1049 hasImplicit |= arg->isImplicit();
1050 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1051 abbreviations.push_back(arg->abbreviation());
1052 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) {
return !strcmp(arg->name(), name); }) == names.cend());
1053 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
1054 names.emplace_back(arg->name());
1056 for (
const Argument *arg : args) {
1057 verifyArgs(arg->subArguments());
1076 return strcmp(arg1->
name(), arg2->
name()) < 0;
1086 bool onlyCombinable =
false;
1087 for (
const Argument *sibling : siblings) {
1088 if (sibling->isPresent() && !sibling->isCombinable()) {
1089 onlyCombinable =
true;
1093 for (
const Argument *sibling : siblings) {
1094 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1095 target.push_back(sibling);
1103 ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1104 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const 1106 ArgumentCompletionInfo completion(reader);
1109 if (completion.lastDetectedArg) {
1110 completion.lastDetectedArgIndex = reader.lastArgDenotation - argv;
1111 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1116 completion.lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1117 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1118 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1119 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1124 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1125 completion.nextArgumentOrValue =
true;
1131 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1132 if (!completion.nextArgumentOrValue) {
1134 completion.relevantArgs.push_back(completion.lastDetectedArg);
1140 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1142 completion.relevantPreDefinedValues.push_back(arg);
1151 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1153 if (currentValueCount) {
1154 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1155 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1156 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1158 currentValueCount = 0;
1164 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1165 for (
const Argument *child : completion.lastDetectedArg->subArguments()) {
1166 if (child->isImplicit() && child->requiredValueCount()) {
1167 addValueCompletionsForArg(child);
1175 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1176 addValueCompletionsForArg(completion.lastDetectedArg);
1180 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1181 >= completion.lastDetectedArg->requiredValueCount()) {
1183 for (
const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1184 if (subArg->occurrences() < subArg->maxOccurrences()) {
1185 completion.relevantArgs.push_back(subArg);
1190 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1191 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1192 if (parentArgument == end) {
1204 string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const 1207 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1210 const auto *unknownArg(*reader.argv);
1211 auto unknownArgSize(strlen(unknownArg));
1213 if (unknownArgSize > 16) {
1217 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1219 unknownArgSize -= 2;
1223 multiset<ArgumentSuggestion> bestSuggestions;
1225 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1226 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1229 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1230 if (!arg->preDefinedCompletionValues()) {
1233 for (
const char *
i = arg->preDefinedCompletionValues(); *
i; ++
i) {
1234 const char *
const wordStart(
i);
1235 const char *wordEnd(wordStart + 1);
1236 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1238 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart, static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1244 string suggestionStr;
1245 if (
const auto suggestionCount = bestSuggestions.size()) {
1247 size_t requiredSize = 15;
1248 for (
const auto &suggestion : bestSuggestions) {
1249 requiredSize += suggestion.suggestionSize + 2;
1250 if (suggestion.hasDashPrefix) {
1254 suggestionStr.reserve(requiredSize);
1257 suggestionStr +=
"\nDid you mean ";
1259 for (
const auto &suggestion : bestSuggestions) {
1260 if (++
i == suggestionCount && suggestionCount != 1) {
1261 suggestionStr +=
" or ";
1263 suggestionStr +=
", ";
1265 if (suggestion.hasDashPrefix) {
1266 suggestionStr +=
"--";
1268 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1270 suggestionStr +=
'?';
1272 return suggestionStr;
1280 void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const 1283 const auto completionInfo([&] {
1284 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1285 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1286 return clutteredCompletionInfo;
1290 const char *opening =
nullptr;
1291 string compoundOpening;
1292 size_t openingLen = 0, compoundOpeningStartLen = 0;
1293 unsigned char openingDenotationType =
Value;
1294 if (argc && completionInfo.nextArgumentOrValue) {
1295 if (currentWordIndex < static_cast<unsigned int>(argc)) {
1296 opening = argv[currentWordIndex];
1302 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1303 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1304 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1305 compoundOpening = argv[currentWordIndex];
1306 compoundOpening +=
'=';
1307 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1308 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1309 compoundOpening = argv[currentWordIndex];
1310 compoundOpening +=
'=';
1311 compoundOpening += opening;
1313 if (!compoundOpening.empty()) {
1314 opening = compoundOpening.data();
1317 opening = *completionInfo.lastSpecifiedArg;
1319 *opening ==
'-' && (++opening, ++openingDenotationType) && *opening ==
'-' && (++opening, ++openingDenotationType);
1320 openingLen = strlen(opening);
1324 cout <<
"COMPREPLY=(";
1326 bool noWhitespace =
false;
1327 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1329 arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(
Argument::varValueCount));
1331 if (!arg->preDefinedCompletionValues()) {
1335 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1336 if (openingDenotationType !=
Value) {
1339 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1340 size_t wordIndex = 0;
1341 for (
const char *
i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *
i;) {
1343 const char *i1 =
i, *i2 = opening;
1344 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1346 if ((ok = (i2 == end))) {
1351 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1352 equationSignAlreadyPresent =
false;
1354 cout <<
'\'' <<
' ';
1358 }
else if (*
i ==
'=') {
1359 equationSignAlreadyPresent =
true;
1365 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1377 if (appendEquationSign && !equationSignAlreadyPresent) {
1379 noWhitespace =
true;
1380 equationSignAlreadyPresent =
false;
1388 }
else if (
const char *
i = arg->preDefinedCompletionValues()) {
1389 bool equationSignAlreadyPresent =
false;
1399 equationSignAlreadyPresent =
true;
1404 if (appendEquationSign && !equationSignAlreadyPresent) {
1406 equationSignAlreadyPresent =
false;
1411 cout <<
' ' <<
'\'';
1416 cout <<
'\'' <<
' ';
1420 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1421 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1422 switch (openingDenotationType) {
1424 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1431 if (strncmp(arg->name(), opening, openingLen)) {
1437 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1439 cout <<
'\'' <<
'-' << opening << arg->abbreviation() <<
'\'' <<
' ';
1440 }
else if (completionInfo.lastDetectedArg && reader.argDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1441 if (reader.argv == reader.end) {
1442 cout <<
'\'' << *(reader.argv - 1) <<
'\'' <<
' ';
1444 }
else if (arg->denotesOperation()) {
1445 cout <<
'\'' << arg->name() <<
'\'' <<
' ';
1447 cout <<
'\'' <<
'-' <<
'-' << arg->name() <<
'\'' <<
' ';
1452 string actualDir, actualFile;
1453 bool haveFileOrDirCompletions =
false;
1454 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1456 string unescapedOpening(opening);
1457 findAndReplace<string>(unescapedOpening,
"\\ ",
" ");
1458 findAndReplace<string>(unescapedOpening,
"\\,",
",");
1459 findAndReplace<string>(unescapedOpening,
"\\[",
"[");
1460 findAndReplace<string>(unescapedOpening,
"\\]",
"]");
1461 findAndReplace<string>(unescapedOpening,
"\\!",
"!");
1462 findAndReplace<string>(unescapedOpening,
"\\#",
"#");
1463 findAndReplace<string>(unescapedOpening,
"\\$",
"$");
1464 findAndReplace<string>(unescapedOpening,
"\\'",
"'");
1465 findAndReplace<string>(unescapedOpening,
"\\\"",
"\"");
1466 findAndReplace<string>(unescapedOpening,
"\\\\",
"\\");
1468 string dir =
directory(unescapedOpening);
1472 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1475 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1476 dir.erase(dir.size() - 2, 1);
1478 actualDir = move(dir);
1481 string file =
fileName(unescapedOpening);
1482 if (file[0] ==
'\"' || file[0] ==
'\'') {
1485 if (file.size() > 1 && (file[dir.size() - 2] ==
'\"' || dir[file.size() - 2] ==
'\'')) {
1486 file.erase(file.size() - 2, 1);
1488 actualFile = move(file);
1493 if (completionInfo.completeFiles) {
1494 entryTypes |= DirectoryEntryType::File;
1496 if (completionInfo.completeDirs) {
1497 entryTypes |= DirectoryEntryType::Directory;
1500 const string replace(
"'"), with(
"'\"'\"'");
1501 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1504 for (
string &dirEntry : entries) {
1509 if (actualDir !=
".") {
1513 cout << dirEntry <<
'\'' <<
' ';
1514 haveFileOrDirCompletions =
true;
1519 cout <<
'\'' << dirEntry <<
'\'' <<
' ';
1520 haveFileOrDirCompletions =
true;
1527 if (haveFileOrDirCompletions) {
1528 cout <<
"; compopt -o filenames";
1533 cout <<
"; compopt -o nospace";
1545 for (
const Argument *arg : args) {
1546 const auto occurrences = arg->occurrences();
1547 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1548 throw Failure(
argsToString(
"The argument \"", arg->name(),
"\" mustn't be specified more than ", arg->maxOccurrences(),
1549 (arg->maxOccurrences() == 1 ?
" time." :
" times.")));
1551 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1552 throw Failure(
argsToString(
"The argument \"", arg->name(),
"\" must be specified at least ", arg->minOccurrences(),
1553 (arg->minOccurrences() == 1 ?
" time." :
" times.")));
1555 Argument *conflictingArgument =
nullptr;
1556 if (arg->isMainArgument()) {
1557 if (!arg->isCombinable() && arg->isPresent()) {
1561 conflictingArgument = arg->conflictsWithArgument();
1563 if (conflictingArgument) {
1564 throw Failure(
argsToString(
"The argument \"", conflictingArgument->name(),
"\" can not be combined with \"", arg->name(),
"\"."));
1566 for (
size_t i = 0;
i != occurrences; ++
i) {
1567 if (!arg->allRequiredValuesPresent(
i)) {
1568 stringstream ss(stringstream::in | stringstream::out);
1569 ss <<
"Not all parameter for argument \"" << arg->name() <<
"\" ";
1571 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1573 ss <<
"provided. You have to provide the following parameter:";
1574 size_t valueNamesPrint = 0;
1575 for (
const auto &name : arg->m_valueNames) {
1576 ss <<
' ' << name, ++valueNamesPrint;
1579 while (valueNamesPrint < arg->m_requiredValueCount) {
1580 ss <<
"\nvalue " << (++valueNamesPrint);
1583 throw Failure(ss.str());
1601 for (
const Argument *arg : args) {
1603 if (arg->m_callbackFunction) {
1604 for (
const auto &occurrence : arg->m_occurrences) {
1605 arg->m_callbackFunction(occurrence);
1623 :
Argument(
"help",
'h',
"shows this information")
1668 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT 1669 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1671 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1674 setCombinable(
true);
1682 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1685 const char *envValue = getenv(environmentVariable());
1689 for (; *envValue; ++envValue) {
1690 switch (*envValue) {
1709 if (s_instance ==
this) {
1710 s_instance =
nullptr;
1719 if (NoColorArgument::s_instance && NoColorArgument::s_instance->
isPresent()) {
1720 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT 1731 void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const 1733 throw Failure(argumentPath.empty()
1734 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1735 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1736 targetTypeName,
"\" failed: ", errorMessage));
1742 void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const 1744 throw Failure(
path.empty()
1745 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1746 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1747 " have been specified."));
bool isMainArgument() const
Returns an indication whether the argument is used as main argument.
CPP_UTILITIES_EXPORT const char * applicationUrl
Specifies the URL to the application website (used by ArgumentParser::printHelp()).
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
ValueCompletionBehavior
The ValueCompletionBehavior enum specifies the items to be considered when generating completion for ...
Argument * lastArg
The last Argument instance which could be detected. Set to nullptr in the initial call....
bool denotesOperation() const
Returns whether the argument denotes an operation.
void resetArgs()
Resets all Argument instances assigned as mainArguments() and sub arguments.
Encapsulates functions for formatted terminal output using ANSI escape codes.
#define IF_DEBUG_BUILD(x)
Wraps debug-only lines conveniently.
std::size_t requiredValueCount() const
Returns the number of values which are required to be given for this argument.
vector< Argument * > lastDetectedArgPath
Argument * conflictsWithArgument() const
Checks if this arguments conflicts with other arguments.
const char * description() const
Returns the description of the argument.
bool isParentPresent() const
Returns whether at least one parent argument is present.
static void apply()
Sets EscapeCodes::enabled according to the presense of the first instantiation of NoColorArgument.
unsigned char argDenotationType
The type of the currently processed abbreviation denotation. Unspecified if argDenotation is not set.
ArgumentParser()
Constructs a new ArgumentParser.
std::vector< Argument * > ArgumentVector
void readArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
void setStyle(std::ostream &stream, TextAttribute displayAttribute=TextAttribute::Reset)
CPP_UTILITIES_EXPORT std::list< std::string > directoryEntries(const char *path, DirectoryEntryType types=DirectoryEntryType::All)
Returns the names of the directory entries in the specified path with the specified types.
The ConversionException class is thrown by the various conversion functions of this library when a co...
Contains currently only ArgumentParser and related classes.
constexpr StringType argsToString(Args &&... args)
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
Argument(const char *name, char abbreviation='\0', const char *description=nullptr, const char *example=nullptr)
Constructs an Argument with the given name, abbreviation and description.
const size_t suggestionSize
bool isRequired() const
Returns an indication whether the argument is mandatory.
Argument CPP_UTILITIES_EXPORT * firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
This function return the first present and uncombinable argument of the given list of arguments.
The Wrapper class is internally used print text which might needs to be wrapped preserving the indent...
NoColorArgument()
Constructs a new NoColorArgument argument.
void parseArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
CPP_UTILITIES_EXPORT const char * applicationVersion
Specifies the version of the application (used by ArgumentParser::printHelp()).
void invokeCallbacks()
Invokes all assigned callbacks.
const char *const * argv
Points to the first argument denotation and will be incremented when a denotation has been processed.
~Argument()
Destroys the Argument.
The Indentation class allows printing indentation conveniently, eg.
ArgumentReader(ArgumentParser &parser, const char *const *argv, const char *const *end, bool completionMode=false)
Initializes the internal reader for the specified parser and arguments.
#define CMD_UTILS_START_CONSOLE
The NoColorArgument class allows to specify whether use of escape codes or similar technique to provi...
void addMainArgument(Argument *argument)
Adds the specified argument to the main argument.
void parseArgsOrExit(int argc, const char *const *argv)
Parses the specified command line arguments.
const Argument *const lastDetectedArg
#define FALLTHROUGH
Prevents clang from warning about missing break in switch-case.
void checkConstraints()
Checks whether contraints are violated.
const char * name() const
Returns the name of the argument.
const std::vector< const char * > & valueNames() const
Returns the names of the requried values.
const char * argDenotation
The currently processed abbreviation denotation (should be substring of one of the args in argv)....
HelpArgument(ArgumentParser &parser)
Constructs a new help argument for the specified parser.
void parseArgsExt(int argc, const char *const *argv, ParseArgumentBehavior behavior=ParseArgumentBehavior::CheckConstraints|ParseArgumentBehavior::InvokeCallbacks|ParseArgumentBehavior::ExitOnFailure)
Parses the specified command line arguments.
ArgumentParser & parser
The associated ArgumentParser instance.
const ArgumentVector & subArguments() const
Returns the secondary arguments for this argument.
constexpr T max(T first, T second)
Returns the greatest of the given items.
std::initializer_list< Argument * > ArgumentInitializerList
size_t index
An index which is incremented when an argument is encountered (the current index is stored in the occ...
bool operator==(const ArgumentSuggestion &other) const
Contains utility classes helping to read and write streams.
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
bool operator<(const ArgumentSuggestion &other) const
void printHelp(std::ostream &os) const
Prints help text for all assigned arguments.
void findAndReplace(StringType &str, const StringType &find, const StringType &replace)
Replaces all occurences of find with relpace in the specified str.
CPP_UTILITIES_EXPORT std::initializer_list< const char * > dependencyVersions
Specifies the dependency versions the application was linked against (used by ArgumentParser::printHe...
Contains several functions providing conversions between different data types.
The TerminalSize struct describes a terminal size.
CPP_UTILITIES_EXPORT void(* exitFunction)(int)
Specifies a function quit the application.
TerminalSize CPP_UTILITIES_EXPORT determineTerminalSize()
Returns the current size of the terminal.
ParseArgumentBehavior
The ParseArgumentBehavior enum specifies the behavior when parsing arguments.
Argument * wouldConflictWithArgument() const
Checks if this argument would conflict with other arguments if it was present.
std::size_t occurrences() const
Returns how often the argument could be detected when parsing.
The Argument class is a wrapper for command line argument information.
void setCallback(CallbackFunction callback)
Sets a callback function which will be called by the parser if the argument could be found and no par...
const std::vector< Argument * > & path(std::size_t occurrence=0) const
Returns the path of the specified occurrence.
static constexpr std::size_t varValueCount
Denotes a variable number of values.
bool isCombinable() const
Returns an indication whether the argument is combinable.
const size_t editingDistance
ApplicationUtilities::ArgumentReader & reset(const char *const *argv, const char *const *end)
Resets the ArgumentReader to continue reading new argv.
list< const Argument * > relevantArgs
The ArgumentCompletionInfo struct holds information internally used for shell completion and suggesti...
std::vector< Argument * > path
The "path" of the occurrence (the parent elements which have been specified before).
ArgumentVector & args
The Argument instances to store the results. Sub arguments of args are considered as well.
constexpr T min(T first, T second)
Returns the smallest of the given items.
ArgumentDenotationType
The ArgumentDenotationType enum specifies the type of a given argument denotation.
UnknownArgumentBehavior
The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown argume...
void printInfo(std::ostream &os, unsigned char indentation=0) const
Writes the name, the abbreviation and other information about the Argument to the give ostream.
bool read()
Reads the commands line arguments specified when constructing the object.
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
void addSubArgument(Argument *arg)
Adds arg as a secondary argument for this argument.
CPP_UTILITIES_EXPORT std::size_t computeDamerauLevenshteinDistance(const char *str1, std::size_t size1, const char *str2, std::size_t size2)
The ArgumentOccurrence struct holds argument values for an occurrence of an argument.
const char *const suggestion
const char *const * end
Points to the end of the argv array.
CPP_UTILITIES_EXPORT const char * applicationName
Specifies the name of the application (used by ArgumentParser::printHelp()).
list< const Argument * > relevantPreDefinedValues
unsigned short columns
number of columns
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
The Failure class is thrown by an ArgumentParser when a parsing error occurs.
~NoColorArgument()
Destroys the object.
CPP_UTILITIES_EXPORT std::vector< const char * > dependencyVersions2
Specifies the dependency versions the application was linked against (used by ArgumentParser::printHe...
const ArgumentVector & mainArguments() const
Returns the main arguments.
bool isUncombinableMainArgPresent() const
Checks whether at least one uncombinable main argument is present.
char abbreviation() const
Returns the abbreviation of the argument.
const char * example() const
Returns the usage example of the argument.
ArgumentSuggestion(const char *unknownArg, size_t unknownArgSize, const char *suggestion, bool hasDashPrefix)
void reset()
Resets occurrences (indices, values and paths).
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
Contains various utilities such as computing Damerau–Levenshtein distance and N-dimensional arrays.
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
const char *const * lastArgDenotation
Points to the element in argv where lastArg was encountered. Unspecified if lastArg is not set.
CPP_UTILITIES_EXPORT const char * applicationAuthor
Specifies the author of the application (used by ArgumentParser::printHelp()).
DirectoryEntryType
The DirectoryEntryType enum specifies the type of a directory entry (file, directory or symlink).
void insertSiblings(const ArgumentVector &siblings, list< const Argument * > &target)
Inserts the specified siblings in the target list.
The ArgumentReader class internally encapsulates the process of reading command line arguments.
const char * environmentVariable() const
Returns the environment variable queried when firstValue() is called.
bool completionMode
Whether completion mode is enabled. In this case reading args will be continued even if an denotation...
void setSubArguments(const ArgumentInitializerList &subArguments)
Sets the secondary arguments for this arguments.
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
void addTo(multiset< ArgumentSuggestion > &suggestions, size_t limit) const
bool compareArgs(const Argument *arg1, const Argument *arg2)
Returns whether arg1 should be listed before arg2 when printing completion.
The ArgumentParser class provides a means for handling command line arguments.
Argument * specifiedOperation() const
Returns the first operation argument specified by the user or nullptr if no operation has been specif...
void resetRecursively()
Resets this argument and all sub arguments recursively.
std::vector< const char * > values
The parameter values which have been specified after the occurrence of the argument.