void CommandLineParser::Parse()

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);
        }
    }