diff options
Diffstat (limited to 'third_party/wasm2c/src/option-parser.cc')
-rw-r--r-- | third_party/wasm2c/src/option-parser.cc | 351 |
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 |