| /* |
| * Copyright 2015 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 "support/command-line.h" |
| |
| using namespace wasm; |
| |
| Options::Options(const std::string &command, const std::string &description) |
| : debug(0), 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) { |
| bool long_n_short = o.longName.size() != 0 && o.shortName.size() != 0; |
| size_t pad = 1 + optionWidth - o.longName.size() - o.shortName.size(); |
| std::cerr << " " << o.longName << (long_n_short ? ',' : ' ') |
| << o.shortName << std::string(pad, ' ') << o.description |
| << '\n'; |
| } |
| std::cerr << '\n'; |
| exit(EXIT_SUCCESS); |
| }); |
| add("--debug", "-d", "Print debug information to stderr", Arguments::Optional, |
| [](Options *o, const std::string &arguments) { |
| o->debug = arguments.size() ? std::stoi(arguments) : 1; |
| }); |
| } |
| |
| 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; |
| } |
| |
| 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) { |
| 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: |
| case Arguments::Optional: |
| if (positionalsSeen) { |
| std::cerr << "Unexpected second positional argument '" |
| << currentOption << "' for " << positionalName << '\n'; |
| exit(EXIT_FAILURE); |
| } |
| // Fallthrough. |
| case Arguments::N: |
| positionalAction(this, currentOption); |
| ++positionalsSeen; |
| break; |
| } |
| 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); |
| } |
| 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]; |
| } |
| break; |
| case Arguments::Optional: |
| if (!argument.size()) { |
| if (i + 1 != e) argument = argv[++i]; |
| } |
| break; |
| } |
| option->action(this, argument); |
| ++option->seen; |
| } |
| } |