summaryrefslogtreecommitdiff
path: root/src/support/command-line.cpp
diff options
context:
space:
mode:
authorJF Bastien <github@jfbastien.com>2016-01-04 10:46:28 -0800
committerJF Bastien <github@jfbastien.com>2016-01-04 10:46:28 -0800
commit7e6e3c78074101931bca83d9f7383fde55d6f380 (patch)
treecf80fffefea4d192e57f759a7ab37063ed431808 /src/support/command-line.cpp
parent2410eccb2a4bc7bbda87b299a6cd6fc434c77c01 (diff)
parentec87a4d54110965a2145349c697764a06db6bf21 (diff)
downloadbinaryen-7e6e3c78074101931bca83d9f7383fde55d6f380.tar.gz
binaryen-7e6e3c78074101931bca83d9f7383fde55d6f380.tar.bz2
binaryen-7e6e3c78074101931bca83d9f7383fde55d6f380.zip
Merge pull request #58 from WebAssembly/command-line
Generalize command-line parsing more.
Diffstat (limited to 'src/support/command-line.cpp')
-rw-r--r--src/support/command-line.cpp148
1 files changed, 110 insertions, 38 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;
}
}