You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
6.3 KiB
188 lines
6.3 KiB
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* 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 "idmap2/CommandLineOptions.h"
|
|
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "android-base/macros.h"
|
|
#include "idmap2/Result.h"
|
|
|
|
namespace android::idmap2 {
|
|
|
|
std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
|
|
int argc, const char** argv) {
|
|
return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
|
|
}
|
|
|
|
CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
|
|
const std::string& description, bool* value) {
|
|
assert(value != nullptr);
|
|
auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
|
|
options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
|
|
return *this;
|
|
}
|
|
|
|
CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
|
|
const std::string& description,
|
|
std::string* value) {
|
|
assert(value != nullptr);
|
|
auto func = [value](const std::string& arg) -> void { *value = arg; };
|
|
options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
|
|
return *this;
|
|
}
|
|
|
|
CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
|
|
const std::string& description,
|
|
std::vector<std::string>* value) {
|
|
assert(value != nullptr);
|
|
auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
|
|
options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
|
|
return *this;
|
|
}
|
|
|
|
CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
|
|
const std::string& description,
|
|
std::string* value) {
|
|
assert(value != nullptr);
|
|
auto func = [value](const std::string& arg) -> void { *value = arg; };
|
|
options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
|
|
return *this;
|
|
}
|
|
|
|
CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
|
|
const std::string& description,
|
|
std::vector<std::string>* value) {
|
|
assert(value != nullptr);
|
|
auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
|
|
options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
|
|
return *this;
|
|
}
|
|
|
|
Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
|
|
const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
|
|
return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
|
|
});
|
|
std::set<std::string> mandatory_opts;
|
|
std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
|
|
[](const Option& opt) -> std::string { return opt.name; });
|
|
|
|
const size_t argv_size = argv.size();
|
|
for (size_t i = 0; i < argv_size; i++) {
|
|
const std::string arg = argv[i];
|
|
if ("--help" == arg || "-h" == arg) {
|
|
std::stringstream stream;
|
|
Usage(stream);
|
|
return Error("%s", stream.str().c_str());
|
|
}
|
|
bool match = false;
|
|
for (const Option& opt : options_) {
|
|
if (opt.name == arg) {
|
|
match = true;
|
|
|
|
if (opt.argument) {
|
|
i++;
|
|
if (i >= argv_size) {
|
|
std::stringstream stream;
|
|
Usage(stream);
|
|
return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
|
|
}
|
|
}
|
|
opt.action(argv[i]);
|
|
mandatory_opts.erase(opt.name);
|
|
break;
|
|
}
|
|
}
|
|
if (!match) {
|
|
std::stringstream stream;
|
|
Usage(stream);
|
|
return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
|
|
}
|
|
}
|
|
|
|
if (!mandatory_opts.empty()) {
|
|
std::stringstream stream;
|
|
bool separator = false;
|
|
for (const auto& opt : mandatory_opts) {
|
|
if (separator) {
|
|
stream << ", ";
|
|
}
|
|
separator = true;
|
|
stream << opt << ": missing mandatory option";
|
|
}
|
|
stream << std::endl;
|
|
Usage(stream);
|
|
return Error("%s", stream.str().c_str());
|
|
}
|
|
return Unit{};
|
|
}
|
|
|
|
void CommandLineOptions::Usage(std::ostream& out) const {
|
|
size_t maxLength = 0;
|
|
out << "usage: " << name_;
|
|
for (const Option& opt : options_) {
|
|
const bool mandatory =
|
|
opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
|
|
out << " ";
|
|
if (!mandatory) {
|
|
out << "[";
|
|
}
|
|
if (opt.argument) {
|
|
out << opt.name << " arg";
|
|
maxLength = std::max(maxLength, opt.name.size() + 4);
|
|
} else {
|
|
out << opt.name;
|
|
maxLength = std::max(maxLength, opt.name.size());
|
|
}
|
|
|
|
if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
|
|
out << " [..]";
|
|
}
|
|
|
|
if (!mandatory) {
|
|
out << "]";
|
|
}
|
|
|
|
if (opt.count == Option::COUNT_ONCE_OR_MORE) {
|
|
out << " [" << opt.name << " arg [..]]";
|
|
}
|
|
}
|
|
out << std::endl << std::endl;
|
|
for (const Option& opt : options_) {
|
|
out << std::left << std::setw(maxLength);
|
|
if (opt.argument) {
|
|
out << (opt.name + " arg");
|
|
} else {
|
|
out << opt.name;
|
|
}
|
|
out << " " << opt.description;
|
|
if (opt.count == Option::COUNT_ONCE_OR_MORE ||
|
|
opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
|
|
out << " (can be provided multiple times)";
|
|
}
|
|
out << std::endl;
|
|
}
|
|
}
|
|
|
|
} // namespace android::idmap2
|