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