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