#include "config.h" #include "private/cli/commandparserandexecutor.hpp" #include #include #include #include #include #include "private/cli/commandmap.hpp" using namespace shareddatalayer; using namespace shareddatalayer::cli; namespace po = boost::program_options; namespace { void outputBashCompletion(std::ostream& os, const po::options_description& commonOpts, CommandMap& commandMap) { const auto commandNames(commandMap.getCommandNames()); std::set optionNames; for (const auto& i : commonOpts.options()) optionNames.insert(i->long_name()); for (const auto& i : commandNames) for (const auto& j : commandMap.getCommandOptions(i).options()) optionNames.insert(j->long_name()); os << "_sdltool()\n{\n COMPREPLY=()\n local commands=\""; for (const auto& i : commandNames) os << i << ' '; os << "\"\n local options=\""; for (const auto& i : optionNames) os << "--" << i << ' '; os << R"bash(" local cur="${COMP_WORDS[COMP_CWORD]}" if [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${options}" -- ${cur}) ) return 0 else COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) ) return 0 fi } complete -F _sdltool sdltool )bash" << std::flush; } void printShortHelp(std::ostream& os, const po::options_description& commonOpts, CommandMap& commandMap) { os << "Usage: sdltool [OPTIONS] COMMAND [COMMAND SPECIFIC OPTIONS]\n\n"; commonOpts.print(os); os << "\nAvailable commands:\n"; commandMap.shortHelp(os); } int privateParseAndExecute(int argc, char** argv, std::ostream& out, std::ostream& err, CommandMap& commandMap) { po::options_description commonHiddenOptsDescription("Common hidden options"); commonHiddenOptsDescription.add_options() ("pos-command", po::value(), "sdltool command to run") ("pos-subargs", po::value>(), "Arguments for command"); po::options_description commonVisibleOptsDescription("Common options"); commonVisibleOptsDescription.add_options() ("help", "Show help for COMMAND") ("repeat", po::value()->default_value(1U), "Times to repeat the command") ("bash-completion", "Generate bash completion script") ("version", "Show version information"); po::options_description commonOptsDescription("All common options"); commonOptsDescription.add(commonHiddenOptsDescription); commonOptsDescription.add(commonVisibleOptsDescription); po::positional_options_description commonPosOptsDescription; commonPosOptsDescription .add("pos-command", 1) .add("pos-subargs", -1); // all positional arguments after 'command' po::variables_map commonVarsMap; po::parsed_options commonParsed(po::command_line_parser(argc, argv) .options(commonOptsDescription) .positional(commonPosOptsDescription) .allow_unregistered() .run()); po::store(commonParsed, commonVarsMap); po::notify(commonVarsMap); if (!commonVarsMap.count("pos-command")) { if (commonVarsMap.count("help")) { printShortHelp(out, commonVisibleOptsDescription, commandMap); return EXIT_SUCCESS; } if (commonVarsMap.count("bash-completion")) { outputBashCompletion(out, commonVisibleOptsDescription, commandMap); return EXIT_SUCCESS; } if (commonVarsMap.count("version")) { out << PACKAGE_STRING << std::endl; return EXIT_SUCCESS; } err << "missing command\n\n"; printShortHelp(err, commonVisibleOptsDescription, commandMap); return EXIT_FAILURE; } auto leftOverOpts = po::collect_unrecognized(commonParsed.options, po::include_positional); if (!leftOverOpts.empty()) leftOverOpts.erase(leftOverOpts.begin()); const auto commandName(commonVarsMap["pos-command"].as()); if (commonVarsMap.count("help")) { bool found(false); try { commandMap.longHelp(out, commandName); out << '\n'; found = true; } catch (const CommandMap::UnknownCommandName&) { } if (!found) { err << "unknown command: \"" << commandName << "\"\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } const auto& commandOptions(commandMap.getCommandOptions(commandName)); po::variables_map subcmdVarsMap; auto subcmdParsed(po::command_line_parser(leftOverOpts).options(commandOptions).run()); po::store(subcmdParsed, subcmdVarsMap); po::notify(subcmdVarsMap); return commandMap.execute(commandName, out, err, subcmdVarsMap, commonVarsMap["repeat"].as()); } } int shareddatalayer::cli::parseAndExecute(int argc, char** argv, std::ostream& out, std::ostream& err, CommandMap& commandMap) { try { return privateParseAndExecute(argc, argv, out, err, commandMap); } catch (const std::exception& e) { err << e.what() << std::endl; return EXIT_FAILURE; } }