blob: e415715c37cf37ef293f9a93625fe5d6b2890e56 [file] [log] [blame]
/*
* 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;
#ifndef SCREEN_WIDTH
#define SCREEN_WIDTH 80
#endif
void printWrap(std::ostream& os, int leftPad, const std::string& content) {
int len = content.size();
int space = SCREEN_WIDTH - leftPad;
std::string nextWord;
std::string pad(leftPad, ' ');
for (int i = 0; i <= len; ++i) {
if (i != len && content[i] != ' ') {
nextWord += content[i];
} else {
if (static_cast<int>(nextWord.size()) > space) {
os << '\n' << pad;
space = SCREEN_WIDTH - leftPad;
}
os << nextWord;
space -= nextWord.size() + 1;
if (space > 0) os << ' ';
nextWord.clear();
}
}
}
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";
printWrap(std::cerr, 0, description);
std::cerr << "\n\nOptions:\n";
size_t optionWidth = 0;
for (const auto& o : options) {
optionWidth =
std::max(optionWidth, o.longName.size() + o.shortName.size());
}
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, ' ');
printWrap(std::cerr, optionWidth + 4, o.description);
std::cerr << '\n';
}
std::cerr << '\n';
exit(EXIT_SUCCESS);
});
add("--debug", "-d", "Print debug information to stderr", Arguments::Zero,
[&](Options* o, const std::string& arguments) { debug = true; });
}
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;
}
}