1 /*
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 "idmap2/CommandLineOptions.h"
18
19 #include <algorithm>
20 #include <iomanip>
21 #include <iostream>
22 #include <memory>
23 #include <set>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27
28 #include "android-base/macros.h"
29 #include "idmap2/Result.h"
30
31 namespace android::idmap2 {
32
ConvertArgvToVector(int argc,const char ** argv)33 std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
34 int argc, const char** argv) {
35 return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
36 }
37
OptionalFlag(const std::string & name,const std::string & description,bool * value)38 CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
39 const std::string& description, bool* value) {
40 assert(value != nullptr);
41 auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
42 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
43 return *this;
44 }
45
MandatoryOption(const std::string & name,const std::string & description,std::string * value)46 CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
47 const std::string& description,
48 std::string* value) {
49 assert(value != nullptr);
50 auto func = [value](const std::string& arg) -> void { *value = arg; };
51 options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
52 return *this;
53 }
54
MandatoryOption(const std::string & name,const std::string & description,std::vector<std::string> * value)55 CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
56 const std::string& description,
57 std::vector<std::string>* value) {
58 assert(value != nullptr);
59 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
60 options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
61 return *this;
62 }
63
OptionalOption(const std::string & name,const std::string & description,std::string * value)64 CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
65 const std::string& description,
66 std::string* value) {
67 assert(value != nullptr);
68 auto func = [value](const std::string& arg) -> void { *value = arg; };
69 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
70 return *this;
71 }
72
OptionalOption(const std::string & name,const std::string & description,std::vector<std::string> * value)73 CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
74 const std::string& description,
75 std::vector<std::string>* value) {
76 assert(value != nullptr);
77 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
78 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
79 return *this;
80 }
81
Parse(const std::vector<std::string> & argv) const82 Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
83 const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
84 return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
85 });
86 std::set<std::string> mandatory_opts;
87 std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
88 [](const Option& opt) -> std::string { return opt.name; });
89
90 const size_t argv_size = argv.size();
91 for (size_t i = 0; i < argv_size; i++) {
92 const std::string arg = argv[i];
93 if ("--help" == arg || "-h" == arg) {
94 std::stringstream stream;
95 Usage(stream);
96 return Error("%s", stream.str().c_str());
97 }
98 bool match = false;
99 for (const Option& opt : options_) {
100 if (opt.name == arg) {
101 match = true;
102
103 if (opt.argument) {
104 i++;
105 if (i >= argv_size) {
106 std::stringstream stream;
107 Usage(stream);
108 return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
109 }
110 }
111 opt.action(argv[i]);
112 mandatory_opts.erase(opt.name);
113 break;
114 }
115 }
116 if (!match) {
117 std::stringstream stream;
118 Usage(stream);
119 return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
120 }
121 }
122
123 if (!mandatory_opts.empty()) {
124 std::stringstream stream;
125 bool separator = false;
126 for (const auto& opt : mandatory_opts) {
127 if (separator) {
128 stream << ", ";
129 }
130 separator = true;
131 stream << opt << ": missing mandatory option";
132 }
133 stream << std::endl;
134 Usage(stream);
135 return Error("%s", stream.str().c_str());
136 }
137 return Unit{};
138 }
139
Usage(std::ostream & out) const140 void CommandLineOptions::Usage(std::ostream& out) const {
141 size_t maxLength = 0;
142 out << "usage: " << name_;
143 for (const Option& opt : options_) {
144 const bool mandatory =
145 opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
146 out << " ";
147 if (!mandatory) {
148 out << "[";
149 }
150 if (opt.argument) {
151 out << opt.name << " arg";
152 maxLength = std::max(maxLength, opt.name.size() + 4);
153 } else {
154 out << opt.name;
155 maxLength = std::max(maxLength, opt.name.size());
156 }
157
158 if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
159 out << " [..]";
160 }
161
162 if (!mandatory) {
163 out << "]";
164 }
165
166 if (opt.count == Option::COUNT_ONCE_OR_MORE) {
167 out << " [" << opt.name << " arg [..]]";
168 }
169 }
170 out << std::endl << std::endl;
171 for (const Option& opt : options_) {
172 out << std::left << std::setw(maxLength);
173 if (opt.argument) {
174 out << (opt.name + " arg");
175 } else {
176 out << opt.name;
177 }
178 out << " " << opt.description;
179 if (opt.count == Option::COUNT_ONCE_OR_MORE ||
180 opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
181 out << " (can be provided multiple times)";
182 }
183 out << std::endl;
184 }
185 }
186
187 } // namespace android::idmap2
188