blob: a49a607091a45172a73178debc35fef03bd4958d [file] [log] [blame]
Mårten Kongstad02751232018-04-27 13:16:32 +02001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <algorithm>
18#include <iomanip>
19#include <iostream>
20#include <memory>
21#include <set>
22#include <string>
23#include <vector>
24
25#include "android-base/macros.h"
26
27#include "idmap2/CommandLineOptions.h"
28
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010029namespace android::idmap2 {
Mårten Kongstad02751232018-04-27 13:16:32 +020030
31std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
32 int argc, const char** argv) {
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010033 return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
Mårten Kongstad02751232018-04-27 13:16:32 +020034}
35
36CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
37 const std::string& description, bool* value) {
38 assert(value != nullptr);
39 auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
40 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
41 return *this;
42}
43
44CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
45 const std::string& description,
46 std::string* value) {
47 assert(value != nullptr);
48 auto func = [value](const std::string& arg) -> void { *value = arg; };
49 options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
50 return *this;
51}
52
53CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
54 const std::string& description,
55 std::vector<std::string>* value) {
56 assert(value != nullptr);
57 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
58 options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
59 return *this;
60}
61
62CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
63 const std::string& description,
64 std::string* value) {
65 assert(value != nullptr);
66 auto func = [value](const std::string& arg) -> void { *value = arg; };
67 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
68 return *this;
69}
70
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080071CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
72 const std::string& description,
73 std::vector<std::string>* value) {
74 assert(value != nullptr);
75 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
76 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
77 return *this;
78}
79
Mårten Kongstad02751232018-04-27 13:16:32 +020080bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
81 const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080082 return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +020083 });
84 std::set<std::string> mandatory_opts;
85 std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
86 [](const Option& opt) -> std::string { return opt.name; });
87
88 const size_t argv_size = argv.size();
89 for (size_t i = 0; i < argv_size; i++) {
90 const std::string arg = argv[i];
91 if ("--help" == arg || "-h" == arg) {
92 Usage(outError);
93 return false;
94 }
95 bool match = false;
96 for (const Option& opt : options_) {
97 if (opt.name == arg) {
98 match = true;
99
100 if (opt.argument) {
101 i++;
102 if (i >= argv_size) {
103 outError << "error: " << opt.name << ": missing argument" << std::endl;
104 Usage(outError);
105 return false;
106 }
107 }
108 opt.action(argv[i]);
109 mandatory_opts.erase(opt.name);
110 break;
111 }
112 }
113 if (!match) {
114 outError << "error: " << arg << ": unknown option" << std::endl;
115 Usage(outError);
116 return false;
117 }
118 }
119
120 if (!mandatory_opts.empty()) {
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100121 for (const auto& opt : mandatory_opts) {
122 outError << "error: " << opt << ": missing mandatory option" << std::endl;
Mårten Kongstad02751232018-04-27 13:16:32 +0200123 }
124 Usage(outError);
125 return false;
126 }
127 return true;
128}
129
130void CommandLineOptions::Usage(std::ostream& out) const {
131 size_t maxLength = 0;
132 out << "usage: " << name_;
133 for (const Option& opt : options_) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800134 const bool mandatory =
135 opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +0200136 out << " ";
137 if (!mandatory) {
138 out << "[";
139 }
140 if (opt.argument) {
141 out << opt.name << " arg";
142 maxLength = std::max(maxLength, opt.name.size() + 4);
143 } else {
144 out << opt.name;
145 maxLength = std::max(maxLength, opt.name.size());
146 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800147
148 if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
149 out << " [..]";
150 }
151
Mårten Kongstad02751232018-04-27 13:16:32 +0200152 if (!mandatory) {
153 out << "]";
154 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800155
Mårten Kongstad02751232018-04-27 13:16:32 +0200156 if (opt.count == Option::COUNT_ONCE_OR_MORE) {
157 out << " [" << opt.name << " arg [..]]";
158 }
159 }
160 out << std::endl << std::endl;
161 for (const Option& opt : options_) {
162 out << std::left << std::setw(maxLength);
163 if (opt.argument) {
164 out << (opt.name + " arg");
165 } else {
166 out << opt.name;
167 }
168 out << " " << opt.description;
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800169 if (opt.count == Option::COUNT_ONCE_OR_MORE ||
170 opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200171 out << " (can be provided multiple times)";
172 }
173 out << std::endl;
174 }
175}
176
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100177} // namespace android::idmap2