diff options
Diffstat (limited to 'src/support')
-rw-r--r-- | src/support/command-line.cpp | 148 | ||||
-rw-r--r-- | src/support/command-line.h | 50 |
2 files changed, 150 insertions, 48 deletions
diff --git a/src/support/command-line.cpp b/src/support/command-line.cpp index 3c03d48f4..40e11f60a 100644 --- a/src/support/command-line.cpp +++ b/src/support/command-line.cpp @@ -16,50 +16,122 @@ #include "support/command-line.h" -namespace { -bool optionIs(const char *arg, const char *LongOpt, const char *ShortOpt) { - return strcmp(arg, LongOpt) == 0 || strcmp(arg, ShortOpt) == 0; +using namespace wasm; + +Options::Options(const std::string &command, const std::string &description) + : debug(false), positional(Arguments::Zero) { + add("--help", "-h", "Show this help message and exit", Arguments::Zero, + [this, command, description](Options *o, const std::string &) { + std::cerr << command; + if (positional != Arguments::Zero) std::cerr << ' ' << positionalName; + std::cerr << "\n\n" << description << "\n\nOptions:\n"; + size_t optionWidth = 0; + for (const auto &o : options) { + optionWidth = + std::max(optionWidth, o.longName.size() + o.shortName.size()); + } + // TODO: line-wrap description. + for (const auto &o : options) { + size_t pad = 1 + optionWidth - o.longName.size() - o.shortName.size(); + std::cerr << " " << o.longName << ',' << o.shortName + << std::string(pad, ' ') << o.description << '\n'; + } + std::cerr << '\n'; + exit(EXIT_SUCCESS); + }); + add("--debug", "-d", "Print debug information to stderr", Arguments::Zero, + [](Options *o, const std::string &) { o->debug = true; }); } -bool startsWith(const char *str, const char *prefix) { - return strncmp(str, prefix, strlen(prefix)) == 0; + +Options::~Options() {} + +Options &Options::add(const std::string &longName, const std::string &shortName, + const std::string &description, Arguments arguments, + const Action &action) { + options.push_back({longName, shortName, description, arguments, action, 0}); + return *this; } -} // anonymous namespace -void wasm::processCommandLine(int argc, const char *argv[], Options *options, - const char *help) { +Options &Options::add_positional(const std::string &name, Arguments arguments, + const Action &action) { + positional = arguments; + positionalName = name; + positionalAction = action; + return *this; +} + +void Options::parse(int argc, const char *argv[]) { assert(argc > 0 && "expect at least program name as an argument"); + size_t positionalsSeen = 0; + auto dashes = [](const std::string &s) { + for (size_t i = 0;; ++i) { + if (s[i] != '-') return i; + } + }; for (size_t i = 1, e = argc; i != e; ++i) { - if (optionIs(argv[i], "--help", "-h")) { - std::cerr << help; - exit(EXIT_SUCCESS); - } else if (optionIs(argv[i], "--debug", "-d")) { - options->debug = true; - } else if (optionIs(argv[i], "--output", "-o")) { - if (i + 1 == e) { - std::cerr << "No output file" << std::endl; - exit(EXIT_FAILURE); - } - if (options->outfile.size()) { - std::cerr << "Expected only one output file, got '" << options->outfile - << "' and '" << argv[i] << "'" << std::endl; - exit(EXIT_FAILURE); - } - options->outfile = argv[++i]; - } else if (startsWith(argv[i], "--")) { - size_t j = 2; - std::string name; - while (argv[i][j] && argv[i][j] != '=') { - name += argv[i][j]; - j++; - } - options->extra[name] = argv[i][j] == '=' ? &argv[i][j + 1] : "(no value)"; - } else { - if (options->infile.size()) { - std::cerr << "Expected only one input file, got '" << options->infile - << "' and '" << argv[i] << "'" << std::endl; - exit(EXIT_FAILURE); + std::string currentOption = argv[i]; + + if (dashes(currentOption) == 0) { + // Positional. + switch (positional) { + case Arguments::Zero: + std::cerr << "Unexpected positional argument '" << currentOption + << "'\n"; + exit(EXIT_FAILURE); + case Arguments::One: + if (positionalsSeen) { + std::cerr << "Unexpected second positional argument '" + << currentOption << "' for " << positionalName << '\n'; + exit(EXIT_FAILURE); + } + // Fallthrough. + case Arguments::N: + positionalAction(this, currentOption); + ++positionalsSeen; } - options->infile = argv[i]; + continue; + } + + // Non-positional. + std::string argument; + auto equal = currentOption.find_first_of('='); + if (equal != std::string::npos) { + argument = currentOption.substr(equal + 1); + currentOption = currentOption.substr(0, equal - 1); + } + Option *option = nullptr; + for (auto &o : options) + if (o.longName == currentOption || o.shortName == currentOption) + option = &o; + if (!option) { + std::cerr << "Unknown option '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + switch (option->arguments) { + case Arguments::Zero: + if (argument.size()) { + std::cerr << "Unexpected argument '" << argument + << "' for option '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + break; + case Arguments::One: + if (option->seen) { + std::cerr << "Unexpected second argument '" << argument << "' for '" + << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + // Fallthrough. + case Arguments::N: + if (!argument.size()) { + if (i + 1 == e) { + std::cerr << "Couldn't find expected argument for '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + argument = argv[++i]; + } } + option->action(this, argument); + ++option->seen; } } diff --git a/src/support/command-line.h b/src/support/command-line.h index f57929b6b..14dbce57f 100644 --- a/src/support/command-line.h +++ b/src/support/command-line.h @@ -21,22 +21,52 @@ #ifndef wasm_support_command_line_h #define wasm_support_command_line_h +#include <functional> +#include <map> +#include <string> +#include <utility> +#include <vector> + #include "wasm.h" + namespace wasm { -struct Options { - // standard options +class Options { + public: + typedef std::function<void(Options *, const std::string &)> Action; + enum class Arguments { Zero, One, N }; + bool debug; - std::string infile; - std::string outfile; - // extra options - std::map<std::string, const char *> extra; - Options() : debug(false) {} -}; + std::map<std::string, std::string> extra; -void processCommandLine(int argc, const char *argv[], Options *options, - const char *help); + Options(const std::string &command, const std::string &description); + ~Options(); + Options &add(const std::string &longName, const std::string &shortName, + const std::string &description, Arguments arguments, + const Action &action); + Options &add_positional(const std::string &name, Arguments arguments, + const Action &action); + void parse(int argc, const char *argv[]); + + private: + Options() = delete; + Options(const Options &) = delete; + Options &operator=(const Options &) = delete; + + struct Option { + std::string longName; + std::string shortName; + std::string description; + Arguments arguments; + Action action; + size_t seen; + }; + std::vector<Option> options; + Arguments positional; + std::string positionalName; + Action positionalAction; +}; } // namespace wasm |