summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src/option-parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/wasm2c/src/option-parser.cc')
-rw-r--r--third_party/wasm2c/src/option-parser.cc351
1 files changed, 351 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/option-parser.cc b/third_party/wasm2c/src/option-parser.cc
new file mode 100644
index 0000000000..88fe75780b
--- /dev/null
+++ b/third_party/wasm2c/src/option-parser.cc
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/option-parser.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+
+#include "wabt/config.h"
+
+namespace wabt {
+
+OptionParser::Option::Option(char short_name,
+ const std::string& long_name,
+ const std::string& metavar,
+ HasArgument has_argument,
+ const std::string& help,
+ const Callback& callback)
+ : short_name(short_name),
+ long_name(long_name),
+ metavar(metavar),
+ has_argument(has_argument == HasArgument::Yes),
+ help(help),
+ callback(callback) {}
+
+OptionParser::Argument::Argument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback)
+ : name(name), count(count), callback(callback) {}
+
+OptionParser::OptionParser(const char* program_name, const char* description)
+ : program_name_(program_name),
+ description_(description),
+ on_error_([this](const std::string& message) { DefaultError(message); }) {
+ // Add common options
+ AddOption("help", "Print this help message", [this]() {
+ PrintHelp();
+ exit(0);
+ });
+ AddOption("version", "Print version information", []() {
+ printf("%s\n", WABT_VERSION_STRING);
+ exit(0);
+ });
+}
+
+void OptionParser::AddOption(const Option& option) {
+ options_.emplace_back(option);
+}
+
+void OptionParser::AddArgument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback) {
+ arguments_.emplace_back(name, count, callback);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option(short_name, long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option('\0', long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* metavar,
+ const char* help,
+ const Callback& callback) {
+ Option option(short_name, long_name, metavar, HasArgument::Yes, help,
+ callback);
+ AddOption(option);
+}
+
+void OptionParser::SetErrorCallback(const Callback& callback) {
+ on_error_ = callback;
+}
+
+// static
+int OptionParser::Match(const char* s,
+ const std::string& full,
+ bool has_argument) {
+ int i;
+ for (i = 0;; i++) {
+ if (full[i] == '\0') {
+ // Perfect match. Return +1, so it will be preferred over a longer option
+ // with the same prefix.
+ if (s[i] == '\0') {
+ return i + 1;
+ }
+
+ // We want to fail if s is longer than full, e.g. --foobar vs. --foo.
+ // However, if s ends with an '=', it's OK.
+ if (!(has_argument && s[i] == '=')) {
+ return -1;
+ }
+ break;
+ }
+ if (s[i] == '\0') {
+ break;
+ }
+ if (s[i] != full[i]) {
+ return -1;
+ }
+ }
+ return i;
+}
+
+void OptionParser::Errorf(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ std::string msg(program_name_);
+ msg += ": ";
+ msg += buffer;
+ msg += "\nTry '--help' for more information.";
+ on_error_(msg.c_str());
+}
+
+void OptionParser::DefaultError(const std::string& message) {
+ WABT_FATAL("%s\n", message.c_str());
+}
+
+void OptionParser::HandleArgument(size_t* arg_index, const char* arg_value) {
+ if (*arg_index >= arguments_.size()) {
+ Errorf("unexpected argument '%s'", arg_value);
+ return;
+ }
+ Argument& argument = arguments_[*arg_index];
+ argument.callback(arg_value);
+ argument.handled_count++;
+
+ if (argument.count == ArgumentCount::One) {
+ (*arg_index)++;
+ }
+}
+
+void OptionParser::Parse(int argc, char* argv[]) {
+ size_t arg_index = 0;
+ bool processing_options = true;
+
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (!processing_options || arg[0] != '-') {
+ // Non-option argument.
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ // -- on its own means stop processing args, everything should
+ // be treated as positional.
+ processing_options = false;
+ continue;
+ }
+ // Long option.
+ int best_index = -1;
+ int best_length = 0;
+ int best_count = 0;
+ for (size_t j = 0; j < options_.size(); ++j) {
+ const Option& option = options_[j];
+ if (!option.long_name.empty()) {
+ int match_length =
+ Match(&arg[2], option.long_name, option.has_argument);
+ if (match_length > best_length) {
+ best_index = j;
+ best_length = match_length;
+ best_count = 1;
+ } else if (match_length == best_length && best_length > 0) {
+ best_count++;
+ }
+ }
+ }
+
+ if (best_count > 1) {
+ Errorf("ambiguous option '%s'", arg);
+ continue;
+ } else if (best_count == 0) {
+ Errorf("unknown option '%s'", arg);
+ continue;
+ }
+
+ const Option& best_option = options_[best_index];
+ const char* option_argument = nullptr;
+ if (best_option.has_argument) {
+ if (arg[best_length + 1] != 0 && // This byte is 0 on a full match.
+ arg[best_length + 2] == '=') { // +2 to skip "--".
+ option_argument = &arg[best_length + 3];
+ } else {
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '--%s' requires argument",
+ best_option.long_name.c_str());
+ continue;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ }
+ best_option.callback(option_argument);
+ } else {
+ // Short option.
+ if (arg[1] == '\0') {
+ // Just "-".
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ // Allow short names to be combined, e.g. "-d -v" => "-dv".
+ for (int k = 1; arg[k]; ++k) {
+ bool matched = false;
+ for (const Option& option : options_) {
+ if (option.short_name && arg[k] == option.short_name) {
+ const char* option_argument = nullptr;
+ if (option.has_argument) {
+ // A short option with a required argument cannot be followed
+ // by other short options_.
+ if (arg[k + 1] != '\0') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ option.callback(option_argument);
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ Errorf("unknown option '-%c'", arg[k]);
+ continue;
+ }
+ }
+ }
+ }
+
+ // For now, all arguments must be provided. Check that the last Argument was
+ // handled at least once.
+ if (!arguments_.empty() && arguments_.back().handled_count == 0) {
+ for (size_t i = arg_index; i < arguments_.size(); ++i) {
+ if (arguments_[i].count != ArgumentCount::ZeroOrMore) {
+ Errorf("expected %s argument.", arguments_[i].name.c_str());
+ }
+ }
+ }
+}
+
+void OptionParser::PrintHelp() {
+ printf("usage: %s [options]", program_name_.c_str());
+
+ for (size_t i = 0; i < arguments_.size(); ++i) {
+ Argument& argument = arguments_[i];
+ switch (argument.count) {
+ case ArgumentCount::One:
+ printf(" %s", argument.name.c_str());
+ break;
+
+ case ArgumentCount::OneOrMore:
+ printf(" %s+", argument.name.c_str());
+ break;
+
+ case ArgumentCount::ZeroOrMore:
+ printf(" [%s]...", argument.name.c_str());
+ break;
+ }
+ }
+
+ printf("\n\n");
+ printf("%s\n", description_.c_str());
+ printf("options:\n");
+
+ const size_t kExtraSpace = 8;
+ size_t longest_name_length = 0;
+ for (const Option& option : options_) {
+ size_t length;
+ if (!option.long_name.empty()) {
+ length = option.long_name.size();
+ if (!option.metavar.empty()) {
+ // +1 for '='.
+ length += option.metavar.size() + 1;
+ }
+ } else {
+ continue;
+ }
+
+ if (length > longest_name_length) {
+ longest_name_length = length;
+ }
+ }
+
+ for (const Option& option : options_) {
+ if (!option.short_name && option.long_name.empty()) {
+ continue;
+ }
+
+ std::string line;
+ if (option.short_name) {
+ line += std::string(" -") + option.short_name + ", ";
+ } else {
+ line += " ";
+ }
+
+ std::string flag;
+ if (!option.long_name.empty()) {
+ flag = "--";
+ if (!option.metavar.empty()) {
+ flag += option.long_name + '=' + option.metavar;
+ } else {
+ flag += option.long_name;
+ }
+ }
+
+ // +2 for "--" of the long flag name.
+ size_t remaining = longest_name_length + kExtraSpace + 2 - flag.size();
+ line += flag + std::string(remaining, ' ');
+
+ if (!option.help.empty()) {
+ line += option.help;
+ }
+ printf("%s\n", line.c_str());
+ }
+}
+
+} // namespace wabt