diff options
Diffstat (limited to 'src/option-parser.cc')
-rw-r--r-- | src/option-parser.cc | 268 |
1 files changed, 182 insertions, 86 deletions
diff --git a/src/option-parser.cc b/src/option-parser.cc index 173c5f54..e3eefeb2 100644 --- a/src/option-parser.cc +++ b/src/option-parser.cc @@ -28,20 +28,89 @@ namespace wabt { -static int option_match(const char* s, - const char* full, - HasArgument has_argument) { +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); }) { +} + +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::AddHelpOption() { + AddOption('h', "help", "Print this help message", [this]() { + PrintHelp(); + exit(0); + }); +} + +// 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 */ + // 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 == HasArgument::Yes && s[i] == '=')) + // 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; } @@ -53,28 +122,45 @@ static int option_match(const char* s, return i; } -static void WABT_PRINTF_FORMAT(2, 3) - error(OptionParser* parser, const char* format, ...) { +void OptionParser::Errorf(const char* format, ...) { WABT_SNPRINTF_ALLOCA(buffer, length, format); - parser->on_error(parser, buffer); + on_error_(buffer); +} + +void OptionParser::DefaultError(const std::string& message) { + WABT_FATAL("%s\n", message.c_str()); } -void parse_options(OptionParser* parser, int argc, char** argv) { - parser->argv0 = argv[0]; +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; for (int i = 1; i < argc; ++i) { - char* arg = argv[i]; + const char* arg = argv[i]; if (arg[0] == '-') { if (arg[1] == '-') { - /* long option */ + // Long option. int best_index = -1; int best_length = 0; int best_count = 0; - for (int j = 0; j < parser->num_options; ++j) { - Option* option = &parser->options[j]; - if (option->long_name) { + for (size_t j = 0; j < options_.size(); ++j) { + const Option& option = options_[j]; + if (!option.long_name.empty()) { int match_length = - option_match(&arg[2], option->long_name, option->has_argument); + Match(&arg[2], option.long_name, option.has_argument); if (match_length > best_length) { best_index = j; best_length = match_length; @@ -86,97 +172,115 @@ void parse_options(OptionParser* parser, int argc, char** argv) { } if (best_count > 1) { - error(parser, "ambiguous option \"%s\"", arg); + Errorf("ambiguous option '%s'", arg); continue; } else if (best_count == 0) { - error(parser, "unknown option \"%s\"", arg); + Errorf("unknown option '%s'", arg); continue; } - Option* best_option = &parser->options[best_index]; + const Option& best_option = options_[best_index]; const char* option_argument = nullptr; - if (best_option->has_argument == HasArgument::Yes) { + if (best_option.has_argument) { if (arg[best_length] == '=') { option_argument = &arg[best_length + 1]; } else { if (i + 1 == argc || argv[i + 1][0] == '-') { - error(parser, "option \"--%s\" requires argument", - best_option->long_name); + Errorf("option '--%s' requires argument", + best_option.long_name.c_str()); continue; } ++i; option_argument = argv[i]; } } - parser->on_option(parser, best_option, option_argument); + best_option.callback(option_argument); } else { - /* short option */ + // Short option. if (arg[1] == '\0') { - /* just "-" */ - parser->on_argument(parser, arg); + // Just "-". + HandleArgument(&arg_index, arg); continue; } - /* allow short names to be combined, e.g. "-d -v" => "-dv" */ + // Allow short names to be combined, e.g. "-d -v" => "-dv". for (int k = 1; arg[k]; ++k) { bool matched = false; - for (int j = 0; j < parser->num_options; ++j) { - Option* option = &parser->options[j]; - if (option->short_name && arg[k] == option->short_name) { + for (const Option& option: options_) { + if (option.short_name && arg[k] == option.short_name) { const char* option_argument = nullptr; - if (option->has_argument == HasArgument::Yes) { - /* a short option with a required argument cannot be followed - * by other short options */ + if (option.has_argument) { + // A short option with a required argument cannot be followed + // by other short options_. if (arg[k + 1] != '\0') { - error(parser, "option \"-%c\" requires argument", - option->short_name); + Errorf("option '-%c' requires argument", option.short_name); break; } if (i + 1 == argc || argv[i + 1][0] == '-') { - error(parser, "option \"-%c\" requires argument", - option->short_name); + Errorf("option '-%c' requires argument", option.short_name); break; } ++i; option_argument = argv[i]; } - parser->on_option(parser, option, option_argument); + option.callback(option_argument); matched = true; break; } } if (!matched) { - error(parser, "unknown option \"-%c\"", arg[k]); + Errorf("unknown option '-%c'", arg[k]); continue; } } } } else { - /* non-option argument */ - parser->on_argument(parser, arg); + // Non-option argument. + HandleArgument(&arg_index, arg); + } + } + + // 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) { + PrintHelp(); + for (size_t i = arg_index; i < arguments_.size(); ++i) { + Errorf("expected %s argument.\n", arguments_[i].name.c_str()); } } } -void print_help(OptionParser* parser, const char* program_name) { - /* TODO(binji): do something more generic for filename here */ - printf("usage: %s [options] filename\n\n", program_name); - printf("%s\n", parser->description); +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; + } + } + + printf("\n\n"); + printf("%s\n", description_.c_str()); printf("options:\n"); - const int extra_space = 8; - int longest_name_length = 0; - for (int i = 0; i < parser->num_options; ++i) { - Option* option = &parser->options[i]; - int length; - if (option->long_name) { - if (option->metavar) { - length = - snprintf(nullptr, 0, "%s=%s", option->long_name, option->metavar); - } else { - length = snprintf(nullptr, 0, "%s", option->long_name); + 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; @@ -186,41 +290,33 @@ void print_help(OptionParser* parser, const char* program_name) { longest_name_length = length; } - size_t buffer_size = longest_name_length + 1; - char* buffer = static_cast<char*>(alloca(buffer_size)); - - for (int i = 0; i < parser->num_options; ++i) { - Option* option = &parser->options[i]; - if (!option->short_name && !option->long_name) + for (const Option& option: options_) { + if (!option.short_name && option.long_name.empty()) continue; - if (option->short_name) - printf(" -%c, ", option->short_name); + std::string line; + if (option.short_name) + line += std::string(" -") + option.short_name + ", "; else - printf(" "); - - char format[20]; - if (option->long_name) { - snprintf(format, sizeof(format), "--%%-%ds", - longest_name_length + extra_space); + line += " "; - if (option->metavar) { - snprintf(buffer, buffer_size, "%s=%s", option->long_name, - option->metavar); - printf(format, buffer); + std::string flag; + if (!option.long_name.empty()) { + flag = "--"; + if (!option.metavar.empty()) { + flag += option.long_name + '=' + option.metavar; } else { - printf(format, option->long_name); + flag += option.long_name; } - } else { - /* +2 for the extra "--" above */ - snprintf(format, sizeof(format), "%%-%ds", - longest_name_length + extra_space + 2); - printf(format, ""); } - if (option->help) - printf("%s", option->help); - printf("\n"); + // +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()); } } |