in libraries/utilities/src/CommandLineParser.cpp [137:290]
void CommandLineParser::Parse()
{
if (_originalArgs.size() == 0)
return;
bool hasHelpOption = HasOption("help");
bool hasVerboseOption = HasOption("verbose");
if (!hasHelpOption || !hasVerboseOption)
{
AddDocumentationString("");
AddDocumentationString("General options");
}
bool printHelpAndExit = false;
if (!hasHelpOption)
{
if (HasShortName("h"))
{
AddOption(printHelpAndExit, "help", "", "Print help and exit", false);
}
else
{
AddOption(printHelpAndExit, "help", "h", "Print help and exit", false);
}
}
bool setVerbose = false;
if (!hasVerboseOption)
{
if (HasShortName("v"))
{
AddOption(setVerbose, "verbose", "", "Verbose output", false);
}
else
{
AddOption(setVerbose, "verbose", "v", "Verbose output", false);
}
}
// While we're parsing the arguments, we may add new conditional options. If we do so, we need
// to reparse the inputs in case some earlier commandline option referred to one of these new
// options. So we repeatedly parse the command line text until we haven't added any new conditional options.
bool needsReparse = true;
while (needsReparse)
{
std::set<std::string, case_insensitive_comparer> unset_args;
for (const auto& opt : _options)
{
if (opt.second.enabled)
{
unset_args.insert(opt.first);
}
}
needsReparse = false;
size_t argc = _originalArgs.size();
for (size_t index = 1; index < argc; index++)
{
std::string arg = _originalArgs[index];
if (arg == "--") // "--" is the special separator for passthrough args
{
while (++index < argc)
{
arg = _originalArgs[index];
_passthroughArgs.push_back(arg);
}
}
else if (arg[0] == '-') // it's an option
{
std::string option;
if (arg[1] == '-') // long name
{
option = std::string(arg.begin() + 2, arg.end());
}
else // short name
{
std::string shortName = std::string(arg.begin() + 1, arg.end());
if (!HasShortName(shortName))
{
throw CommandLineParserErrorException("Unknown option", { std::string("Error: unknown option ") + arg });
}
option = _shortToLongNameMap[shortName];
}
if (!HasOption(option) || !_options[option].enabled)
{
throw CommandLineParserErrorException("Unknown option", { std::string("Error: unknown option ") + arg });
}
else
{
unset_args.erase(option);
if (index < argc - 1)
{
std::string val = _originalArgs[index + 1];
if (val[0] == '-')
{
// next token in an option, so use the default unset-value string
needsReparse = SetOption(option) || needsReparse;
}
else
{
needsReparse = SetOption(option, val) || needsReparse;
index++;
}
}
else // this is the last thing on the line --- assume it's a shortcut for --option true
{
needsReparse = SetOption(option) || needsReparse;
}
}
}
else
{
_positionalArgs.push_back(arg);
}
}
// Need to set default args here, in case one of them enables a conditional argument set
needsReparse = SetDefaultArgs(unset_args) || needsReparse;
}
// Finally, invoke the post-parse callbacks
bool isValid = true;
std::vector<ParseError> parseErrors;
for (const auto& callback : _postParseCallbacks)
{
auto callbackResult = callback(*this);
if (!callbackResult) // callbackResult is an error
{
isValid = false;
for (auto message : callbackResult._messages)
{
parseErrors.emplace_back(message);
}
}
}
if (printHelpAndExit)
{
throw CommandLineParserPrintHelpException(GetHelpString());
}
if (setVerbose)
{
logging::ShouldLog() = true;
}
if (!isValid)
{
throw CommandLineParserErrorException("Error in parse callback", parseErrors);
}
}