1 /* <!-- copyright */
2 /*
3  * aria2 - The high speed download utility
4  *
5  * Copyright (C) 2006 Tatsuhiro Tsujikawa
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of portions of this program with the
23  * OpenSSL library under certain conditions as described in each
24  * individual source file, and distribute linked combinations
25  * including the two.
26  * You must obey the GNU General Public License in all respects
27  * for all of the code used other than OpenSSL.  If you modify
28  * file(s) with this exception, you may extend this exception to your
29  * version of the file(s), but you are not obligated to do so.  If you
30  * do not wish to do so, delete this exception statement from your
31  * version.  If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  */
34 /* copyright --> */
35 #include "OptionParser.h"
36 
37 #include <unistd.h>
38 #include <getopt.h>
39 
40 #include <cstring>
41 #include <cassert>
42 #include <istream>
43 #include <utility>
44 
45 #include "util.h"
46 #include "OptionHandlerImpl.h"
47 #include "Option.h"
48 #include "A2STR.h"
49 #include "a2functional.h"
50 #include "array_fun.h"
51 #include "OptionHandlerFactory.h"
52 #include "DlAbortEx.h"
53 #include "error_code.h"
54 #include "UnknownOptionException.h"
55 #include "LogFactory.h"
56 #include "fmt.h"
57 
58 namespace aria2 {
59 
OptionParser()60 OptionParser::OptionParser()
61     : handlers_(option::countOption(), nullptr), shortOpts_(256)
62 {
63 }
64 
~OptionParser()65 OptionParser::~OptionParser()
66 {
67   std::for_each(handlers_.begin(), handlers_.end(), Deleter());
68 }
69 
70 namespace {
71 template <typename InputIterator>
countPublicOption(InputIterator first,InputIterator last)72 size_t countPublicOption(InputIterator first, InputIterator last)
73 {
74   size_t count = 0;
75   for (; first != last; ++first) {
76     if (*first && !(*first)->isHidden()) {
77       ++count;
78     }
79   }
80   return count;
81 }
82 } // namespace
83 
84 namespace {
85 template <typename InputIterator>
putOptions(struct option * longOpts,int * plopt,InputIterator first,InputIterator last)86 void putOptions(struct option* longOpts, int* plopt, InputIterator first,
87                 InputIterator last)
88 {
89   for (; first != last; ++first) {
90     if (*first && !(*first)->isHidden()) {
91 #ifdef HAVE_OPTION_CONST_NAME
92       (*longOpts).name = (*first)->getName();
93 #else  // !HAVE_OPTION_CONST_NAME
94       (*longOpts).name = strdup((*first)->getName());
95       if ((*longOpts).name == nullptr) {
96         auto errNum = errno;
97         A2_LOG_ERROR(
98             fmt("strdup() failed: %s", util::safeStrerror(errNum).c_str()));
99         exit(EXIT_FAILURE);
100       }
101 #endif // !HAVE_OPTION_CONST_NAME
102       switch ((*first)->getArgType()) {
103       case OptionHandler::REQ_ARG:
104         (*longOpts).has_arg = required_argument;
105         break;
106       case OptionHandler::OPT_ARG:
107         (*longOpts).has_arg = optional_argument;
108         break;
109       case OptionHandler::NO_ARG:
110         (*longOpts).has_arg = no_argument;
111         break;
112       default:
113         abort();
114       }
115       if ((*first)->getShortName() == 0) {
116         (*longOpts).flag = plopt;
117         (*longOpts).val = (*first)->getPref()->i;
118       }
119       else {
120         (*longOpts).flag = nullptr;
121         (*longOpts).val = (*first)->getShortName();
122       }
123       ++longOpts;
124     }
125   }
126   (*longOpts).name = nullptr;
127   (*longOpts).has_arg = 0;
128   (*longOpts).flag = nullptr;
129   (*longOpts).val = 0;
130 }
131 } // namespace
132 
133 namespace {
134 template <typename InputIterator>
createOptstring(InputIterator first,InputIterator last)135 std::string createOptstring(InputIterator first, InputIterator last)
136 {
137   std::string str = "";
138   for (; first != last; ++first) {
139     if (*first && !(*first)->isHidden()) {
140       if ((*first)->getShortName() != 0) {
141         str += (*first)->getShortName();
142         if ((*first)->getArgType() == OptionHandler::REQ_ARG) {
143           str += ":";
144         }
145         else if ((*first)->getArgType() == OptionHandler::OPT_ARG) {
146           str += "::";
147         }
148       }
149     }
150   }
151   return str;
152 }
153 } // namespace
154 
parseArg(std::ostream & out,std::vector<std::string> & nonopts,int argc,char * argv[]) const155 void OptionParser::parseArg(std::ostream& out,
156                             std::vector<std::string>& nonopts, int argc,
157                             char* argv[]) const
158 {
159   size_t numPublicOption =
160       countPublicOption(handlers_.begin(), handlers_.end());
161   int lopt;
162   auto longOpts = make_unique<struct option[]>(numPublicOption + 1);
163   putOptions(longOpts.get(), &lopt, handlers_.begin(), handlers_.end());
164   std::string optstring = createOptstring(handlers_.begin(), handlers_.end());
165   while (1) {
166     int c = getopt_long(argc, argv, optstring.c_str(), longOpts.get(), nullptr);
167     if (c == -1) {
168       break;
169     }
170     const OptionHandler* op = nullptr;
171     if (c == 0) {
172       op = findById(lopt);
173     }
174     else if (c != '?') {
175       op = findByShortName(c);
176     }
177     else {
178       assert(c == '?');
179       if (optind == 1) {
180         throw DL_ABORT_EX2("Failed to parse command-line options.",
181                            error_code::OPTION_ERROR);
182       }
183       int optlen = strlen(argv[optind - 1]);
184       const char* optstr = argv[optind - 1];
185       for (; *optstr == '-'; ++optstr)
186         ;
187       int optstrlen = strlen(optstr);
188       if (optstrlen + 1 >= optlen) {
189         // If this is short option form (1 '-' prefix), just throw
190         // error here.
191         throw DL_ABORT_EX2("Failed to parse command-line options.",
192                            error_code::OPTION_ERROR);
193       }
194       // There are 3 situations: 1) completely unknown option 2)
195       // getopt_long() complained because too few arguments.  3)
196       // option is ambiguous.
197       int ambiguous = 0;
198       for (int i = 1, len = option::countOption(); i < len; ++i) {
199         PrefPtr pref = option::i2p(i);
200         const OptionHandler* h = find(pref);
201         if (h && !h->isHidden()) {
202           if (strcmp(pref->k, optstr) == 0) {
203             // Exact match, this means getopt_long detected error
204             // while handling this option.
205             throw DL_ABORT_EX2("Failed to parse command-line options.",
206                                error_code::OPTION_ERROR);
207           }
208           else if (util::startsWith(pref->k, pref->k + strlen(pref->k), optstr,
209                                     optstr + optstrlen)) {
210             ++ambiguous;
211           }
212         }
213       }
214       if (ambiguous == 1) {
215         // This is successfully abbreviated option. So it must be case
216         // 2).
217         throw DL_ABORT_EX2("Failed to parse command-line options.",
218                            error_code::OPTION_ERROR);
219       }
220       throw UNKNOWN_OPTION_EXCEPTION(argv[optind - 1]);
221     }
222     assert(op);
223     out << op->getName() << "=";
224     if (optarg) {
225       out << optarg;
226       if (op->getEraseAfterParse()) {
227         for (char* p = optarg; *p != '\0'; ++p) {
228           *p = '*';
229         }
230       }
231     }
232     out << "\n";
233   }
234   std::copy(argv + optind, argv + argc, std::back_inserter(nonopts));
235 }
236 
parse(Option & option,std::istream & is) const237 void OptionParser::parse(Option& option, std::istream& is) const
238 {
239   std::string line;
240   while (getline(is, line)) {
241     if (line.empty() || line[0] == '#') {
242       continue;
243     }
244     auto nv = util::divide(std::begin(line), std::end(line), '=');
245     if (nv.first.first == nv.first.second) {
246       continue;
247     }
248     PrefPtr pref = option::k2p(std::string(nv.first.first, nv.first.second));
249     const OptionHandler* handler = find(pref);
250     if (handler) {
251       handler->parse(option, std::string(nv.second.first, nv.second.second));
252     }
253     else {
254       A2_LOG_WARN(fmt("Unknown option: %s", line.c_str()));
255     }
256   }
257 }
258 
parse(Option & option,const KeyVals & options) const259 void OptionParser::parse(Option& option, const KeyVals& options) const
260 {
261   for (const auto& o : options) {
262     auto pref = option::k2p(o.first);
263     const OptionHandler* handler = find(pref);
264     if (handler) {
265       handler->parse(option, o.second);
266     }
267     else {
268       A2_LOG_WARN(fmt("Unknown option: %s", o.first.c_str()));
269     }
270   }
271 }
272 
setOptionHandlers(const std::vector<OptionHandler * > & handlers)273 void OptionParser::setOptionHandlers(
274     const std::vector<OptionHandler*>& handlers)
275 {
276   for (const auto& h : handlers) {
277     addOptionHandler(h);
278   }
279 }
280 
addOptionHandler(OptionHandler * handler)281 void OptionParser::addOptionHandler(OptionHandler* handler)
282 {
283   size_t optId = handler->getPref()->i;
284   assert(optId < handlers_.size());
285   handlers_[optId] = handler;
286   if (handler->getShortName()) {
287     shortOpts_[static_cast<unsigned char>(handler->getShortName())] = optId;
288   }
289 }
290 
parseDefaultValues(Option & option) const291 void OptionParser::parseDefaultValues(Option& option) const
292 {
293   for (const auto& h : handlers_) {
294     if (h && !h->getDefaultValue().empty()) {
295       h->parse(option, h->getDefaultValue());
296     }
297   }
298 }
299 
findByTag(uint32_t tag) const300 std::vector<const OptionHandler*> OptionParser::findByTag(uint32_t tag) const
301 {
302   std::vector<const OptionHandler*> result;
303   for (const auto& h : handlers_) {
304     if (h && !h->isHidden() && h->hasTag(tag)) {
305       result.push_back(h);
306     }
307   }
308   return result;
309 }
310 
311 std::vector<const OptionHandler*>
findByNameSubstring(const std::string & substring) const312 OptionParser::findByNameSubstring(const std::string& substring) const
313 {
314   std::vector<const OptionHandler*> result;
315   for (const auto& h : handlers_) {
316     if (h && !h->isHidden()) {
317       size_t nameLen = strlen(h->getName());
318       if (std::search(h->getName(), h->getName() + nameLen, substring.begin(),
319                       substring.end()) != h->getName() + nameLen) {
320         result.push_back(h);
321       }
322     }
323   }
324   return result;
325 }
326 
findAll() const327 std::vector<const OptionHandler*> OptionParser::findAll() const
328 {
329   std::vector<const OptionHandler*> result;
330   for (const auto& h : handlers_) {
331     if (h && !h->isHidden()) {
332       result.push_back(h);
333     }
334   }
335   return result;
336 }
337 
find(PrefPtr pref) const338 const OptionHandler* OptionParser::find(PrefPtr pref) const
339 {
340   return findById(pref->i);
341 }
342 
findById(size_t id) const343 const OptionHandler* OptionParser::findById(size_t id) const
344 {
345   if (id >= handlers_.size()) {
346     return handlers_[0];
347   }
348   const OptionHandler* h = handlers_[id];
349   if (!h || h->isHidden()) {
350     return handlers_[0];
351   }
352   else {
353     return h;
354   }
355 }
356 
findByShortName(char shortName) const357 const OptionHandler* OptionParser::findByShortName(char shortName) const
358 {
359   size_t idx = static_cast<unsigned char>(shortName);
360   return findById(shortOpts_[idx]);
361 }
362 
363 std::shared_ptr<OptionParser> OptionParser::optionParser_;
364 
getInstance()365 const std::shared_ptr<OptionParser>& OptionParser::getInstance()
366 {
367   if (!optionParser_) {
368     optionParser_ = std::make_shared<OptionParser>();
369     optionParser_->setOptionHandlers(
370         OptionHandlerFactory::createOptionHandlers());
371   }
372   return optionParser_;
373 }
374 
deleteInstance()375 void OptionParser::deleteInstance() { optionParser_.reset(); }
376 
377 } // namespace aria2
378