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;
51 size_t lastDetectedArgIndex = 0;
55 const char *
const *lastSpecifiedArg =
nullptr;
56 unsigned int lastSpecifiedArgIndex = 0;
57 bool nextArgumentOrValue =
false;
58 bool completeFiles =
false, completeDirs =
false;
66 : lastDetectedArg(reader.lastArg)
75 void addTo(multiset<ArgumentSuggestion> &suggestions,
size_t limit)
const;
84 : suggestion(suggestion)
85 , suggestionSize(suggestionSize)
87 , hasDashPrefix(isOperation)
92 :
ArgumentSuggestion(unknownArg, unknownArgSize, suggestion, strlen(suggestion), isOperation)
103 if (suggestions.size() >= limit && !(*
this < *--suggestions.end())) {
106 suggestions.emplace(*
this);
107 while (suggestions.size() > limit) {
108 suggestions.erase(--suggestions.end());
126 , args(parser.m_mainArgs)
131 , argDenotation(nullptr)
132 , completionMode(completionMode)
161 bool Argument::matchesDenotation(
const char *denotation,
size_t denotationLength)
const
163 return m_name && !strncmp(m_name, denotation, denotationLength) && *(m_name + denotationLength) ==
'\0';
178 const vector<Argument *> &parentPath = parentArg ? parentArg->
path(parentArg->
occurrences() - 1) : vector<Argument *>();
181 vector<const char *> *values =
nullptr;
196 bool abbreviationFound =
false;
199 abbreviationFound =
false;
211 abbreviationFound =
false;
232 for (; argDenotationLength; matchingArg =
nullptr) {
236 if (arg->abbreviation() && arg->abbreviation() == *
argDenotation) {
238 abbreviationFound =
true;
244 if (arg->matchesDenotation(
argDenotation, argDenotationLength)) {
255 matchingArg->m_occurrences.emplace_back(
index, parentPath, parentArg);
258 values = &matchingArg->m_occurrences.back().values;
262 values->push_back(equationPos + 1);
269 lastArg = lastArgInLevel = matchingArg;
280 const char *
const *
const currentArgValue =
argv;
297 for (
auto parentArgument = parentPath.crbegin(), pathEnd = parentPath.crend();; ++parentArgument) {
298 for (
Argument *
const sibling : (parentArgument != pathEnd ? (*parentArgument)->subArguments() :
parser.m_mainArgs)) {
299 if (sibling->occurrences() < sibling->maxOccurrences()) {
305 if (sibling->matchesDenotation(
argDenotation, argDenotationLength)) {
310 if (parentArgument == pathEnd) {
327 if (arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *
argv)) {
328 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
340 if (arg->isImplicit() && !arg->isPresent() && !arg->wouldConflictWithArgument()
341 && (!uncombinableMainArgPresent || !arg->isMainArgument())) {
342 (matchingArg = arg)->m_occurrences.emplace_back(
index, parentPath, parentArg);
350 if (lastArgInLevel == matchingArg) {
355 values = &matchingArg->m_occurrences.back().values;
359 lastArg = lastArgInLevel = matchingArg;
377 switch (
parser.m_unknownArgBehavior) {
379 cerr << Phrases::Warning <<
"The specified argument \"" << *
argv <<
"\" is unknown and will be ignored." << Phrases::EndFlush;
408 unsigned short currentCol = wrapper.m_indentation.
level;
409 for (
const char *currentChar = wrapper.m_str; *currentChar; ++currentChar) {
410 const bool wrappingRequired = currentCol >= maxColumns;
411 if (wrappingRequired || *currentChar ==
'\n') {
415 if (wrapper.m_indentation.
level < maxColumns) {
416 os << wrapper.m_indentation;
417 currentCol = wrapper.m_indentation.
level;
422 if (*currentChar !=
'\n' && (!wrappingRequired || *currentChar !=
' ')) {
434 inline bool notEmpty(
const char *str)
457 Argument::Argument(
const char *name,
char abbreviation,
const char *description,
const char *example)
459 , m_abbreviation(abbreviation)
460 , m_environmentVar(nullptr)
461 , m_description(description)
463 , m_minOccurrences(0)
464 , m_maxOccurrences(1)
465 , m_requiredValueCount(0)
467 , m_deprecatedBy(nullptr)
471 , m_preDefinedCompletionValues(nullptr)
491 if (!m_occurrences.empty() && !m_occurrences.front().values.empty()) {
492 return m_occurrences.front().values.front();
493 }
else if (m_environmentVar) {
494 return getenv(m_environmentVar);
511 if (notEmpty(
name())) {
525 unsigned int valueNamesPrint = 0;
527 os <<
' ' <<
'[' << *
i <<
']';
534 os <<
" [value " << (valueNamesPrint + 1) <<
']';
543 os <<
'\n' << ident <<
"particularities: mandatory";
545 os <<
" if parent argument is present";
552 bool hasSubArgs =
false;
554 if (arg->isDeprecated()) {
558 arg->printInfo(os, ident.level);
561 if (ident.level == 2 && hasSubArgs) {
577 if (arg != except && arg->
isPresent() && !arg->isCombinable()) {
602 arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(),
this), arg->m_parents.end());
605 m_subArgs.assign(secondaryArguments);
609 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
610 arg->m_parents.push_back(
this);
624 if (find(m_subArgs.cbegin(), m_subArgs.cend(), arg) != m_subArgs.cend()) {
627 m_subArgs.push_back(arg);
628 if (find(arg->m_parents.cbegin(), arg->m_parents.cend(),
this) == arg->m_parents.cend()) {
629 arg->m_parents.push_back(
this);
642 for (
const Argument *parent : m_parents) {
643 if (parent->isPresent()) {
676 for (
Argument *parent : m_parents) {
678 if (sibling !=
this && sibling->isPresent() && !sibling->isCombinable()) {
693 if (arg->denotesOperation() && arg->isPresent()) {
707 arg->resetRecursively();
730 , m_executable(nullptr)
732 , m_defaultArg(nullptr)
753 arg->m_isMainArg =
true;
756 if (m_defaultArg || (*
mainArguments.begin())->requiredValueCount()) {
759 bool subArgsRequired =
false;
761 if (subArg->isRequired()) {
762 subArgsRequired =
true;
766 if (!subArgsRequired) {
779 argument->m_isMainArg =
true;
780 m_mainArgs.push_back(argument);
789 bool wroteLine =
false;
814 if (!m_mainArgs.empty()) {
815 bool hasOperations =
false;
816 for (
const Argument *
const arg : m_mainArgs) {
817 if (arg->denotesOperation()) {
818 hasOperations =
true;
826 os <<
"Available operations:";
827 for (
const Argument *
const arg : m_mainArgs) {
828 if (!arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
834 os <<
"\nAvailable top-level options:";
835 for (
const Argument *
const arg : m_mainArgs) {
836 if (arg->denotesOperation() || arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
844 os <<
"Available arguments:";
845 for (
const Argument *
const arg : m_mainArgs) {
846 if (arg->isDeprecated() || !strcmp(arg->name(),
"help")) {
858 os <<
"Linked against: " << *
i;
859 for (++
i;
i != end; ++
i) {
860 os <<
',' <<
' ' << *
i;
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 = 0, argcForReader;
948 if (completionMode) {
951 currentWordIndex = (--argc ? stringToNumber<unsigned int, string>(*(++argv)) : 0);
957 currentWordIndex = static_cast<unsigned int>(argc - 1);
959 argcForReader =
min(static_cast<unsigned int>(argc), currentWordIndex + 1);
961 argcForReader = static_cast<unsigned int>(argc);
965 ArgumentReader reader(*
this, argv, argv + argcForReader, completionMode);
966 const bool allArgsProcessed(reader.
read());
967 m_noColorArg.
apply();
970 if (!completionMode && !allArgsProcessed) {
971 const auto suggestions(findSuggestions(argc, argv, static_cast<unsigned int>(argc - 1), reader));
976 if (completionMode) {
977 printBashCompletion(argc, argv, currentWordIndex, reader);
989 arg->resetRecursively();
1002 if (arg->denotesOperation() && arg->isPresent()) {
1014 for (
const Argument *arg : m_mainArgs) {
1015 if (!arg->isCombinable() && arg->isPresent()) {
1022 #ifdef CPP_UTILITIES_DEBUG_BUILD
1039 vector<const Argument *> verifiedArgs;
1040 verifiedArgs.reserve(args.size());
1041 vector<char> abbreviations;
1042 abbreviations.reserve(abbreviations.size() + args.size());
1043 vector<const char *> names;
1044 names.reserve(names.size() + args.size());
1045 bool hasImplicit =
false;
1047 assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
1048 verifiedArgs.push_back(arg);
1049 assert(!arg->isImplicit() || !hasImplicit);
1050 hasImplicit |= arg->isImplicit();
1051 assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
1052 abbreviations.push_back(arg->abbreviation());
1053 assert(!arg->name() || find_if(names.cbegin(), names.cend(), [arg](
const char *name) {
return !strcmp(arg->name(), name); }) == names.cend());
1054 assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
1055 names.emplace_back(arg->name());
1058 verifyArgs(arg->subArguments());
1077 return strcmp(arg1->
name(), arg2->
name()) < 0;
1087 bool onlyCombinable =
false;
1088 for (
const Argument *sibling : siblings) {
1089 if (sibling->isPresent() && !sibling->isCombinable()) {
1090 onlyCombinable =
true;
1094 for (
const Argument *sibling : siblings) {
1095 if ((!onlyCombinable || sibling->isCombinable()) && sibling->occurrences() < sibling->maxOccurrences()) {
1096 target.push_back(sibling);
1104 ArgumentCompletionInfo ArgumentParser::determineCompletionInfo(
1105 int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1107 ArgumentCompletionInfo completion(reader);
1110 if (completion.lastDetectedArg) {
1111 completion.lastDetectedArgIndex = static_cast<size_t>(reader.lastArgDenotation - argv);
1112 completion.lastDetectedArgPath = completion.lastDetectedArg->path(completion.lastDetectedArg->occurrences() - 1);
1117 completion.lastSpecifiedArgIndex = static_cast<unsigned int>(argc) - 1;
1118 completion.lastSpecifiedArg = argv + completion.lastSpecifiedArgIndex;
1119 for (; completion.lastSpecifiedArg >= argv && **completion.lastSpecifiedArg ==
'\0';
1120 --completion.lastSpecifiedArg, --completion.lastSpecifiedArgIndex)
1125 if (!completion.lastDetectedArg || !completion.lastDetectedArg->isPresent()) {
1126 completion.nextArgumentOrValue =
true;
1132 completion.nextArgumentOrValue = currentWordIndex > completion.lastDetectedArgIndex;
1133 if (!completion.nextArgumentOrValue) {
1135 completion.relevantArgs.push_back(completion.lastDetectedArg);
1141 const auto addValueCompletionsForArg = [&completion](
const Argument *arg) {
1143 completion.relevantPreDefinedValues.push_back(arg);
1152 auto currentValueCount = completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size();
1154 if (currentValueCount) {
1155 const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - completion.lastDetectedArgIndex;
1156 if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
1157 currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
1159 currentValueCount = 0;
1165 if (!currentValueCount && !completion.lastDetectedArg->requiredValueCount()) {
1166 for (
const Argument *child : completion.lastDetectedArg->subArguments()) {
1167 if (child->isImplicit() && child->requiredValueCount()) {
1168 addValueCompletionsForArg(child);
1176 || (currentValueCount < completion.lastDetectedArg->requiredValueCount())) {
1177 addValueCompletionsForArg(completion.lastDetectedArg);
1181 || completion.lastDetectedArg->values(completion.lastDetectedArg->occurrences() - 1).size()
1182 >= completion.lastDetectedArg->requiredValueCount()) {
1184 for (
const Argument *subArg : completion.lastDetectedArg->subArguments()) {
1185 if (subArg->occurrences() < subArg->maxOccurrences()) {
1186 completion.relevantArgs.push_back(subArg);
1191 for (
auto parentArgument = completion.lastDetectedArgPath.crbegin(), end = completion.lastDetectedArgPath.crend();; ++parentArgument) {
1192 insertSiblings(parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs, completion.relevantArgs);
1193 if (parentArgument == end) {
1205 string ArgumentParser::findSuggestions(
int argc,
const char *
const *argv,
unsigned int cursorPos,
const ArgumentReader &reader)
const
1208 const auto completionInfo(determineCompletionInfo(argc, argv, cursorPos, reader));
1211 const auto *unknownArg(*reader.argv);
1212 auto unknownArgSize(strlen(unknownArg));
1214 if (unknownArgSize > 16) {
1218 if (unknownArgSize >= 2 && unknownArg[0] ==
'-' && unknownArg[1] ==
'-') {
1220 unknownArgSize -= 2;
1224 multiset<ArgumentSuggestion> bestSuggestions;
1226 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1227 ArgumentSuggestion(unknownArg, unknownArgSize, arg->name(), !arg->denotesOperation()).addTo(bestSuggestions, 2);
1230 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1231 if (!arg->preDefinedCompletionValues()) {
1234 for (
const char *
i = arg->preDefinedCompletionValues(); *
i; ++
i) {
1235 const char *
const wordStart(
i);
1236 const char *wordEnd(wordStart + 1);
1237 for (; *wordEnd && *wordEnd !=
' '; ++wordEnd)
1239 ArgumentSuggestion(unknownArg, unknownArgSize, wordStart, static_cast<size_t>(wordEnd - wordStart),
false).addTo(bestSuggestions, 2);
1245 string suggestionStr;
1246 if (
const auto suggestionCount = bestSuggestions.size()) {
1248 size_t requiredSize = 15;
1249 for (
const auto &suggestion : bestSuggestions) {
1250 requiredSize += suggestion.suggestionSize + 2;
1251 if (suggestion.hasDashPrefix) {
1255 suggestionStr.reserve(requiredSize);
1258 suggestionStr +=
"\nDid you mean ";
1260 for (
const auto &suggestion : bestSuggestions) {
1261 if (++
i == suggestionCount && suggestionCount != 1) {
1262 suggestionStr +=
" or ";
1264 suggestionStr +=
", ";
1266 if (suggestion.hasDashPrefix) {
1267 suggestionStr +=
"--";
1269 suggestionStr.append(suggestion.suggestion, suggestion.suggestionSize);
1271 suggestionStr +=
'?';
1273 return suggestionStr;
1281 void ArgumentParser::printBashCompletion(
int argc,
const char *
const *argv,
unsigned int currentWordIndex,
const ArgumentReader &reader)
const
1284 const auto completionInfo([&] {
1285 auto clutteredCompletionInfo(determineCompletionInfo(argc, argv, currentWordIndex, reader));
1286 clutteredCompletionInfo.relevantArgs.sort(
compareArgs);
1287 return clutteredCompletionInfo;
1291 const char *opening =
nullptr;
1292 string compoundOpening;
1293 size_t openingLen = 0, compoundOpeningStartLen = 0;
1294 unsigned char openingDenotationType =
Value;
1295 if (argc && completionInfo.nextArgumentOrValue) {
1296 if (currentWordIndex < static_cast<unsigned int>(argc)) {
1297 opening = argv[currentWordIndex];
1303 const size_t minCurrentWordIndex = (completionInfo.lastDetectedArg ? completionInfo.lastDetectedArgIndex : 0);
1304 if (currentWordIndex > minCurrentWordIndex && !strcmp(opening,
"=")) {
1305 compoundOpening.reserve(compoundOpeningStartLen = strlen(argv[--currentWordIndex]) + 1);
1306 compoundOpening = argv[currentWordIndex];
1307 compoundOpening +=
'=';
1308 }
else if (currentWordIndex > (minCurrentWordIndex + 1) && !strcmp(argv[currentWordIndex - 1],
"=")) {
1309 compoundOpening.reserve((compoundOpeningStartLen = strlen(argv[currentWordIndex -= 2]) + 1) + strlen(opening));
1310 compoundOpening = argv[currentWordIndex];
1311 compoundOpening +=
'=';
1312 compoundOpening += opening;
1314 if (!compoundOpening.empty()) {
1315 opening = compoundOpening.data();
1318 opening = *completionInfo.lastSpecifiedArg;
1320 if (*opening ==
'-') {
1322 ++openingDenotationType;
1323 if (*opening ==
'-') {
1325 ++openingDenotationType;
1328 openingLen = strlen(opening);
1332 cout <<
"COMPREPLY=(";
1334 bool noWhitespace =
false;
1335 for (
const Argument *
const arg : completionInfo.relevantPreDefinedValues) {
1337 arg->m_callbackFunction(arg->isPresent() ? arg->m_occurrences.front() : ArgumentOccurrence(
Argument::varValueCount));
1339 if (!arg->preDefinedCompletionValues()) {
1343 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1344 if (openingDenotationType !=
Value) {
1347 bool wordStart =
true, ok =
false, equationSignAlreadyPresent =
false;
1348 size_t wordIndex = 0;
1349 for (
const char *
i = arg->preDefinedCompletionValues(), *end = opening + openingLen; *
i;) {
1351 const char *i1 =
i, *i2 = opening;
1352 for (; *i1 && i2 != end && *i1 == *i2; ++i1, ++i2)
1354 if ((ok = (i2 == end))) {
1359 }
else if ((wordStart = (*
i ==
' ') || (*
i ==
'\n'))) {
1360 equationSignAlreadyPresent =
false;
1362 cout <<
'\'' <<
' ';
1366 }
else if (*
i ==
'=') {
1367 equationSignAlreadyPresent =
true;
1373 if (!compoundOpeningStartLen || wordIndex >= compoundOpeningStartLen) {
1386 if (appendEquationSign && !equationSignAlreadyPresent) {
1388 noWhitespace =
true;
1389 equationSignAlreadyPresent =
false;
1397 }
else if (
const char *
i = arg->preDefinedCompletionValues()) {
1398 bool equationSignAlreadyPresent =
false;
1408 equationSignAlreadyPresent =
true;
1413 if (appendEquationSign && !equationSignAlreadyPresent) {
1415 equationSignAlreadyPresent =
false;
1420 cout <<
' ' <<
'\'';
1425 cout <<
'\'' <<
' ';
1429 for (
const Argument *
const arg : completionInfo.relevantArgs) {
1430 if (argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening) {
1431 switch (openingDenotationType) {
1433 if (!arg->denotesOperation() || strncmp(arg->name(), opening, openingLen)) {
1440 if (strncmp(arg->name(), opening, openingLen)) {
1446 if (opening && openingDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1448 cout <<
'\'' <<
'-' << opening << arg->abbreviation() <<
'\'' <<
' ';
1449 }
else if (completionInfo.lastDetectedArg && reader.argDenotationType ==
Abbreviation && !completionInfo.nextArgumentOrValue) {
1450 if (reader.argv == reader.end) {
1451 cout <<
'\'' << *(reader.argv - 1) <<
'\'' <<
' ';
1453 }
else if (arg->denotesOperation()) {
1454 cout <<
'\'' << arg->name() <<
'\'' <<
' ';
1456 cout <<
'\'' <<
'-' <<
'-' << arg->name() <<
'\'' <<
' ';
1461 string actualDir, actualFile;
1462 bool haveFileOrDirCompletions =
false;
1463 if (argc && currentWordIndex == completionInfo.lastSpecifiedArgIndex && opening) {
1465 string unescapedOpening(opening);
1466 findAndReplace<string>(unescapedOpening,
"\\ ",
" ");
1467 findAndReplace<string>(unescapedOpening,
"\\,",
",");
1468 findAndReplace<string>(unescapedOpening,
"\\[",
"[");
1469 findAndReplace<string>(unescapedOpening,
"\\]",
"]");
1470 findAndReplace<string>(unescapedOpening,
"\\!",
"!");
1471 findAndReplace<string>(unescapedOpening,
"\\#",
"#");
1472 findAndReplace<string>(unescapedOpening,
"\\$",
"$");
1473 findAndReplace<string>(unescapedOpening,
"\\'",
"'");
1474 findAndReplace<string>(unescapedOpening,
"\\\"",
"\"");
1475 findAndReplace<string>(unescapedOpening,
"\\\\",
"\\");
1477 string dir =
directory(unescapedOpening);
1481 if (dir[0] ==
'\"' || dir[0] ==
'\'') {
1484 if (dir.size() > 1 && (dir[dir.size() - 2] ==
'\"' || dir[dir.size() - 2] ==
'\'')) {
1485 dir.erase(dir.size() - 2, 1);
1487 actualDir = move(dir);
1490 string file =
fileName(unescapedOpening);
1491 if (file[0] ==
'\"' || file[0] ==
'\'') {
1494 if (file.size() > 1 && (file[dir.size() - 2] ==
'\"' || dir[file.size() - 2] ==
'\'')) {
1495 file.erase(file.size() - 2, 1);
1497 actualFile = move(file);
1501 #ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
1502 if (completionInfo.completeFiles || completionInfo.completeDirs) {
1503 using namespace std::filesystem;
1504 const auto replace =
"'"s, with =
"'\"'\"'"s;
1505 const auto useActualDir = argc && currentWordIndex <= completionInfo.lastSpecifiedArgIndex && opening;
1506 const auto dirEntries = [&] {
1507 directory_iterator
i;
1509 i = directory_iterator(actualDir);
1512 i = directory_iterator(
".");
1516 for (
const auto &dirEntry : dirEntries) {
1517 if (!completionInfo.completeDirs && dirEntry.is_directory()) {
1520 if (!completionInfo.completeFiles && !dirEntry.is_directory()) {
1523 auto dirEntryName = dirEntry.path().filename().string();
1529 if (actualDir !=
".") {
1534 cout << dirEntryName <<
'\'' <<
' ';
1535 haveFileOrDirCompletions =
true;
1542 if (haveFileOrDirCompletions) {
1543 cout <<
"; compopt -o filenames";
1548 cout <<
"; compopt -o nospace";
1561 const auto occurrences = arg->occurrences();
1562 if (arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
1563 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" mustn't be specified more than ", arg->maxOccurrences(),
1564 (arg->maxOccurrences() == 1 ?
" time." :
" times.")));
1566 if (arg->isParentPresent() && occurrences < arg->minOccurrences()) {
1567 throw ParseError(
argsToString(
"The argument \"", arg->name(),
"\" must be specified at least ", arg->minOccurrences(),
1568 (arg->minOccurrences() == 1 ?
" time." :
" times.")));
1570 Argument *conflictingArgument =
nullptr;
1571 if (arg->isMainArgument()) {
1572 if (!arg->isCombinable() && arg->isPresent()) {
1576 conflictingArgument = arg->conflictsWithArgument();
1578 if (conflictingArgument) {
1579 throw ParseError(
argsToString(
"The argument \"", conflictingArgument->name(),
"\" can not be combined with \"", arg->name(),
"\"."));
1581 for (
size_t i = 0;
i != occurrences; ++
i) {
1582 if (arg->allRequiredValuesPresent(
i)) {
1585 stringstream ss(stringstream::in | stringstream::out);
1586 ss <<
"Not all parameter for argument \"" << arg->name() <<
"\" ";
1588 ss <<
" (" << (
i + 1) <<
" occurrence) ";
1590 ss <<
"provided. You have to provide the following parameter:";
1591 size_t valueNamesPrint = 0;
1592 for (
const auto &name : arg->m_valueNames) {
1597 while (valueNamesPrint < arg->m_requiredValueCount) {
1598 ss <<
"\nvalue " << (++valueNamesPrint);
1620 if (arg->m_callbackFunction) {
1621 for (
const auto &occurrence : arg->m_occurrences) {
1622 arg->m_callbackFunction(occurrence);
1633 void ArgumentParser::invokeExit(
int code)
1635 if (m_exitFunction) {
1636 m_exitFunction(code);
1652 :
Argument(
"help",
'h',
"shows this information")
1695 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1696 :
Argument(
"no-color",
'\0',
"disables formatted/colorized output")
1698 :
Argument(
"enable-color",
'\0',
"enables formatted/colorized output")
1701 setCombinable(
true);
1704 setEnvironmentVariable(
"ENABLE_ESCAPE_CODES");
1707 const char *envValue = getenv(environmentVariable());
1711 for (; *envValue; ++envValue) {
1712 switch (*envValue) {
1732 #ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
1743 void ValueConversion::Helper::ArgumentValueConversionError::throwFailure(
const std::vector<Argument *> &argumentPath)
const
1746 ?
argsToString(
"Conversion of top-level value \"", valueToConvert,
"\" to type \"", targetTypeName,
"\" failed: ", errorMessage)
1747 :
argsToString(
"Conversion of value \"", valueToConvert,
"\" (for argument --", argumentPath.back()->name(),
") to type \"",
1748 targetTypeName,
"\" failed: ", errorMessage));
1754 void ArgumentOccurrence::throwNumberOfValuesNotSufficient(
unsigned long valuesToConvert)
const
1757 ?
argsToString(
"Expected ", valuesToConvert,
" top-level values to be present but only ",
values.size(),
" have been specified.")
1758 :
argsToString(
"Expected ", valuesToConvert,
" values for argument --",
path.back()->name(),
" to be present but only ",
values.size(),
1759 " have been specified."));