summaryrefslogtreecommitdiff
path: root/src/support
diff options
context:
space:
mode:
Diffstat (limited to 'src/support')
-rw-r--r--src/support/command-line.cpp148
-rw-r--r--src/support/command-line.h50
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