| /* |
| * 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 <cassert> |
| #include <iomanip> |
| #include <iterator> |
| #include <memory> |
| #include <ostream> |
| #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 << '\n'; |
| 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 << "\n\n"; |
| 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 << '\n'; |
| } |
| } |
| |
| } // namespace android::idmap2 |