xref: /freebsd/contrib/kyua/utils/cmdline/options.cpp (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1 // Copyright 2010 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "utils/cmdline/options.hpp"
30 
31 #include <stdexcept>
32 #include <vector>
33 
34 #include "utils/cmdline/exceptions.hpp"
35 #include "utils/defs.hpp"
36 #include "utils/format/macros.hpp"
37 #include "utils/fs/exceptions.hpp"
38 #include "utils/fs/path.hpp"
39 #include "utils/sanity.hpp"
40 #include "utils/text/operations.ipp"
41 
42 namespace cmdline = utils::cmdline;
43 namespace text = utils::text;
44 
45 
46 /// Constructs a generic option with both a short and a long name.
47 ///
48 /// \param short_name_ The short name for the option.
49 /// \param long_name_ The long name for the option.
50 /// \param description_ A user-friendly description for the option.
51 /// \param arg_name_ If not NULL, specifies that the option must receive an
52 ///     argument and specifies the name of such argument for documentation
53 ///     purposes.
54 /// \param default_value_ If not NULL, specifies that the option has a default
55 ///     value for the mandatory argument.
base_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)56 cmdline::base_option::base_option(const char short_name_,
57                                   const char* long_name_,
58                                   const char* description_,
59                                   const char* arg_name_,
60                                   const char* default_value_) :
61     _short_name(short_name_),
62     _long_name(long_name_),
63     _description(description_),
64     _arg_name(arg_name_ == NULL ? "" : arg_name_),
65     _has_default_value(default_value_ != NULL),
66     _default_value(default_value_ == NULL ? "" : default_value_)
67 {
68     INV(short_name_ != '\0');
69 }
70 
71 
72 /// Constructs a generic option with a long name only.
73 ///
74 /// \param long_name_ The long name for the option.
75 /// \param description_ A user-friendly description for the option.
76 /// \param arg_name_ If not NULL, specifies that the option must receive an
77 ///     argument and specifies the name of such argument for documentation
78 ///     purposes.
79 /// \param default_value_ If not NULL, specifies that the option has a default
80 ///     value for the mandatory argument.
base_option(const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)81 cmdline::base_option::base_option(const char* long_name_,
82                                   const char* description_,
83                                   const char* arg_name_,
84                                   const char* default_value_) :
85     _short_name('\0'),
86     _long_name(long_name_),
87     _description(description_),
88     _arg_name(arg_name_ == NULL ? "" : arg_name_),
89     _has_default_value(default_value_ != NULL),
90     _default_value(default_value_ == NULL ? "" : default_value_)
91 {
92 }
93 
94 
95 /// Destructor for the option.
~base_option(void)96 cmdline::base_option::~base_option(void)
97 {
98 }
99 
100 
101 /// Checks whether the option has a short name or not.
102 ///
103 /// \return True if the option has a short name, false otherwise.
104 bool
has_short_name(void) const105 cmdline::base_option::has_short_name(void) const
106 {
107     return _short_name != '\0';
108 }
109 
110 
111 /// Returns the short name of the option.
112 ///
113 /// \pre has_short_name() must be true.
114 ///
115 /// \return The short name.
116 char
short_name(void) const117 cmdline::base_option::short_name(void) const
118 {
119     PRE(has_short_name());
120     return _short_name;
121 }
122 
123 
124 /// Returns the long name of the option.
125 ///
126 /// \return The long name.
127 const std::string&
long_name(void) const128 cmdline::base_option::long_name(void) const
129 {
130     return _long_name;
131 }
132 
133 
134 /// Returns the description of the option.
135 ///
136 /// \return The description.
137 const std::string&
description(void) const138 cmdline::base_option::description(void) const
139 {
140     return _description;
141 }
142 
143 
144 /// Checks whether the option needs an argument or not.
145 ///
146 /// \return True if the option needs an argument, false otherwise.
147 bool
needs_arg(void) const148 cmdline::base_option::needs_arg(void) const
149 {
150     return !_arg_name.empty();
151 }
152 
153 
154 /// Returns the argument name of the option for documentation purposes.
155 ///
156 /// \pre needs_arg() must be true.
157 ///
158 /// \return The argument name.
159 const std::string&
arg_name(void) const160 cmdline::base_option::arg_name(void) const
161 {
162     INV(needs_arg());
163     return _arg_name;
164 }
165 
166 
167 /// Checks whether the option has a default value for its argument.
168 ///
169 /// \pre needs_arg() must be true.
170 ///
171 /// \return True if the option has a default value, false otherwise.
172 bool
has_default_value(void) const173 cmdline::base_option::has_default_value(void) const
174 {
175     PRE(needs_arg());
176     return _has_default_value;
177 }
178 
179 
180 /// Returns the default value for the argument to the option.
181 ///
182 /// \pre has_default_value() must be true.
183 ///
184 /// \return The default value.
185 const std::string&
default_value(void) const186 cmdline::base_option::default_value(void) const
187 {
188     INV(has_default_value());
189     return _default_value;;
190 }
191 
192 
193 /// Formats the short name of the option for documentation purposes.
194 ///
195 /// \return A string describing the option's short name.
196 std::string
format_short_name(void) const197 cmdline::base_option::format_short_name(void) const
198 {
199     PRE(has_short_name());
200 
201     if (needs_arg()) {
202         return F("-%s %s") % short_name() % arg_name();
203     } else {
204         return F("-%s") % short_name();
205     }
206 }
207 
208 
209 /// Formats the long name of the option for documentation purposes.
210 ///
211 /// \return A string describing the option's long name.
212 std::string
format_long_name(void) const213 cmdline::base_option::format_long_name(void) const
214 {
215     if (needs_arg()) {
216         return F("--%s=%s") % long_name() % arg_name();
217     } else {
218         return F("--%s") % long_name();
219     }
220 }
221 
222 
223 
224 /// Ensures that an argument passed to the option is valid.
225 ///
226 /// This must be reimplemented by subclasses that describe options with
227 /// arguments.
228 ///
229 /// \throw cmdline::option_argument_value_error Subclasses must raise this
230 ///     exception to indicate the cases in which str is invalid.
231 void
validate(const std::string &) const232 cmdline::base_option::validate(const std::string& /* str */) const
233 {
234     UNREACHABLE_MSG("Option does not support an argument");
235 }
236 
237 
238 /// Constructs a boolean option with both a short and a long name.
239 ///
240 /// \param short_name_ The short name for the option.
241 /// \param long_name_ The long name for the option.
242 /// \param description_ A user-friendly description for the option.
bool_option(const char short_name_,const char * long_name_,const char * description_)243 cmdline::bool_option::bool_option(const char short_name_,
244                                   const char* long_name_,
245                                   const char* description_) :
246     base_option(short_name_, long_name_, description_)
247 {
248 }
249 
250 
251 /// Constructs a boolean option with a long name only.
252 ///
253 /// \param long_name_ The long name for the option.
254 /// \param description_ A user-friendly description for the option.
bool_option(const char * long_name_,const char * description_)255 cmdline::bool_option::bool_option(const char* long_name_,
256                                   const char* description_) :
257     base_option(long_name_, description_)
258 {
259 }
260 
261 
262 /// Constructs an integer option with both a short and a long name.
263 ///
264 /// \param short_name_ The short name for the option.
265 /// \param long_name_ The long name for the option.
266 /// \param description_ A user-friendly description for the option.
267 /// \param arg_name_ The name of the mandatory argument, for documentation
268 ///     purposes.
269 /// \param default_value_ If not NULL, the default value for the mandatory
270 ///     argument.
int_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)271 cmdline::int_option::int_option(const char short_name_,
272                                 const char* long_name_,
273                                 const char* description_,
274                                 const char* arg_name_,
275                                 const char* default_value_) :
276     base_option(short_name_, long_name_, description_, arg_name_,
277                 default_value_)
278 {
279 }
280 
281 
282 /// Constructs an integer option with a long name only.
283 ///
284 /// \param long_name_ The long name for the option.
285 /// \param description_ A user-friendly description for the option.
286 /// \param arg_name_ The name of the mandatory argument, for documentation
287 ///     purposes.
288 /// \param default_value_ If not NULL, the default value for the mandatory
289 ///     argument.
int_option(const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)290 cmdline::int_option::int_option(const char* long_name_,
291                                 const char* description_,
292                                 const char* arg_name_,
293                                 const char* default_value_) :
294     base_option(long_name_, description_, arg_name_, default_value_)
295 {
296 }
297 
298 
299 /// Ensures that an integer argument passed to the int_option is valid.
300 ///
301 /// \param raw_value The argument representing an integer as provided by the
302 ///     user.
303 ///
304 /// \throw cmdline::option_argument_value_error If the integer provided in
305 ///     raw_value is invalid.
306 void
validate(const std::string & raw_value) const307 cmdline::int_option::validate(const std::string& raw_value) const
308 {
309     try {
310         (void)text::to_type< int >(raw_value);
311     } catch (const std::runtime_error& e) {
312         throw cmdline::option_argument_value_error(
313             F("--%s") % long_name(), raw_value, "Not a valid integer");
314     }
315 }
316 
317 
318 /// Converts an integer argument to a native integer.
319 ///
320 /// \param raw_value The argument representing an integer as provided by the
321 ///     user.
322 ///
323 /// \return The integer.
324 ///
325 /// \pre validate(raw_value) must be true.
326 int
convert(const std::string & raw_value)327 cmdline::int_option::convert(const std::string& raw_value)
328 {
329     try {
330         return text::to_type< int >(raw_value);
331     } catch (const std::runtime_error& e) {
332         PRE_MSG(false, F("Raw value '%s' for int option not properly "
333                          "validated: %s") % raw_value % e.what());
334     }
335 }
336 
337 
338 /// Constructs a list option with both a short and a long name.
339 ///
340 /// \param short_name_ The short name for the option.
341 /// \param long_name_ The long name for the option.
342 /// \param description_ A user-friendly description for the option.
343 /// \param arg_name_ The name of the mandatory argument, for documentation
344 ///     purposes.
345 /// \param default_value_ If not NULL, the default value for the mandatory
346 ///     argument.
list_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)347 cmdline::list_option::list_option(const char short_name_,
348                                   const char* long_name_,
349                                   const char* description_,
350                                   const char* arg_name_,
351                                   const char* default_value_) :
352     base_option(short_name_, long_name_, description_, arg_name_,
353                 default_value_)
354 {
355 }
356 
357 
358 /// Constructs a list option with a long name only.
359 ///
360 /// \param long_name_ The long name for the option.
361 /// \param description_ A user-friendly description for the option.
362 /// \param arg_name_ The name of the mandatory argument, for documentation
363 ///     purposes.
364 /// \param default_value_ If not NULL, the default value for the mandatory
365 ///     argument.
list_option(const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)366 cmdline::list_option::list_option(const char* long_name_,
367                                   const char* description_,
368                                   const char* arg_name_,
369                                   const char* default_value_) :
370     base_option(long_name_, description_, arg_name_, default_value_)
371 {
372 }
373 
374 
375 /// Ensures that a lisstring argument passed to the list_option is valid.
376 void
validate(const std::string &) const377 cmdline::list_option::validate(
378     const std::string& /* raw_value */) const
379 {
380     // Any list is potentially valid; the caller must check for semantics.
381 }
382 
383 
384 /// Converts a string argument to a vector.
385 ///
386 /// \param raw_value The argument representing a list as provided by the user.
387 ///
388 /// \return The list.
389 ///
390 /// \pre validate(raw_value) must be true.
391 cmdline::list_option::option_type
convert(const std::string & raw_value)392 cmdline::list_option::convert(const std::string& raw_value)
393 {
394     try {
395         return text::split(raw_value, ',');
396     } catch (const std::runtime_error& e) {
397         PRE_MSG(false, F("Raw value '%s' for list option not properly "
398                          "validated: %s") % raw_value % e.what());
399     }
400 }
401 
402 
403 /// Constructs a path option with both a short and a long name.
404 ///
405 /// \param short_name_ The short name for the option.
406 /// \param long_name_ The long name for the option.
407 /// \param description_ A user-friendly description for the option.
408 /// \param arg_name_ The name of the mandatory argument, for documentation
409 ///     purposes.
410 /// \param default_value_ If not NULL, the default value for the mandatory
411 ///     argument.
path_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)412 cmdline::path_option::path_option(const char short_name_,
413                                   const char* long_name_,
414                                   const char* description_,
415                                   const char* arg_name_,
416                                   const char* default_value_) :
417     base_option(short_name_, long_name_, description_, arg_name_,
418                 default_value_)
419 {
420 }
421 
422 
423 /// Constructs a path option with a long name only.
424 ///
425 /// \param long_name_ The long name for the option.
426 /// \param description_ A user-friendly description for the option.
427 /// \param arg_name_ The name of the mandatory argument, for documentation
428 ///     purposes.
429 /// \param default_value_ If not NULL, the default value for the mandatory
430 ///     argument.
path_option(const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)431 cmdline::path_option::path_option(const char* long_name_,
432                                   const char* description_,
433                                   const char* arg_name_,
434                                   const char* default_value_) :
435     base_option(long_name_, description_, arg_name_, default_value_)
436 {
437 }
438 
439 
440 /// Ensures that a path argument passed to the path_option is valid.
441 ///
442 /// \param raw_value The argument representing a path as provided by the user.
443 ///
444 /// \throw cmdline::option_argument_value_error If the path provided in
445 ///     raw_value is invalid.
446 void
validate(const std::string & raw_value) const447 cmdline::path_option::validate(const std::string& raw_value) const
448 {
449     try {
450         (void)utils::fs::path(raw_value);
451     } catch (const utils::fs::error& e) {
452         throw cmdline::option_argument_value_error(F("--%s") % long_name(),
453                                                    raw_value, e.what());
454     }
455 }
456 
457 
458 /// Converts a path argument to a utils::fs::path.
459 ///
460 /// \param raw_value The argument representing a path as provided by the user.
461 ///
462 /// \return The path.
463 ///
464 /// \pre validate(raw_value) must be true.
465 utils::fs::path
convert(const std::string & raw_value)466 cmdline::path_option::convert(const std::string& raw_value)
467 {
468     try {
469         return utils::fs::path(raw_value);
470     } catch (const std::runtime_error& e) {
471         PRE_MSG(false, F("Raw value '%s' for path option not properly "
472                          "validated: %s") % raw_value % e.what());
473     }
474 }
475 
476 
477 /// Constructs a property option with both a short and a long name.
478 ///
479 /// \param short_name_ The short name for the option.
480 /// \param long_name_ The long name for the option.
481 /// \param description_ A user-friendly description for the option.
482 /// \param arg_name_ The name of the mandatory argument, for documentation
483 ///     purposes.  Must include the '=' delimiter.
property_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_)484 cmdline::property_option::property_option(const char short_name_,
485                                           const char* long_name_,
486                                           const char* description_,
487                                           const char* arg_name_) :
488     base_option(short_name_, long_name_, description_, arg_name_)
489 {
490     PRE(arg_name().find('=') != std::string::npos);
491 }
492 
493 
494 /// Constructs a property option with a long name only.
495 ///
496 /// \param long_name_ The long name for the option.
497 /// \param description_ A user-friendly description for the option.
498 /// \param arg_name_ The name of the mandatory argument, for documentation
499 ///     purposes.  Must include the '=' delimiter.
property_option(const char * long_name_,const char * description_,const char * arg_name_)500 cmdline::property_option::property_option(const char* long_name_,
501                                           const char* description_,
502                                           const char* arg_name_) :
503     base_option(long_name_, description_, arg_name_)
504 {
505     PRE(arg_name().find('=') != std::string::npos);
506 }
507 
508 
509 /// Validates the argument to a property option.
510 ///
511 /// \param raw_value The argument provided by the user.
512 void
validate(const std::string & raw_value) const513 cmdline::property_option::validate(const std::string& raw_value) const
514 {
515     const std::string::size_type pos = raw_value.find('=');
516     if (pos == std::string::npos)
517         throw cmdline::option_argument_value_error(
518             F("--%s") % long_name(), raw_value,
519             F("Argument does not have the form '%s'") % arg_name());
520 
521     const std::string key = raw_value.substr(0, pos);
522     if (key.empty())
523         throw cmdline::option_argument_value_error(
524             F("--%s") % long_name(), raw_value, "Empty property name");
525 
526     const std::string value = raw_value.substr(pos + 1);
527     if (value.empty())
528         throw cmdline::option_argument_value_error(
529             F("--%s") % long_name(), raw_value, "Empty value");
530 }
531 
532 
533 /// Returns the property option in a key/value pair form.
534 ///
535 /// \param raw_value The argument provided by the user.
536 ///
537 /// \return raw_value The key/value pair representation of the property.
538 ///
539 /// \pre validate(raw_value) must be true.
540 cmdline::property_option::option_type
convert(const std::string & raw_value)541 cmdline::property_option::convert(const std::string& raw_value)
542 {
543     const std::string::size_type pos = raw_value.find('=');
544     return std::make_pair(raw_value.substr(0, pos), raw_value.substr(pos + 1));
545 }
546 
547 
548 /// Constructs a string option with both a short and a long name.
549 ///
550 /// \param short_name_ The short name for the option.
551 /// \param long_name_ The long name for the option.
552 /// \param description_ A user-friendly description for the option.
553 /// \param arg_name_ The name of the mandatory argument, for documentation
554 ///     purposes.
555 /// \param default_value_ If not NULL, the default value for the mandatory
556 ///     argument.
string_option(const char short_name_,const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)557 cmdline::string_option::string_option(const char short_name_,
558                                       const char* long_name_,
559                                       const char* description_,
560                                       const char* arg_name_,
561                                       const char* default_value_) :
562     base_option(short_name_, long_name_, description_, arg_name_,
563                 default_value_)
564 {
565 }
566 
567 
568 /// Constructs a string option with a long name only.
569 ///
570 /// \param long_name_ The long name for the option.
571 /// \param description_ A user-friendly description for the option.
572 /// \param arg_name_ The name of the mandatory argument, for documentation
573 ///     purposes.
574 /// \param default_value_ If not NULL, the default value for the mandatory
575 ///     argument.
string_option(const char * long_name_,const char * description_,const char * arg_name_,const char * default_value_)576 cmdline::string_option::string_option(const char* long_name_,
577                                       const char* description_,
578                                       const char* arg_name_,
579                                       const char* default_value_) :
580     base_option(long_name_, description_, arg_name_, default_value_)
581 {
582 }
583 
584 
585 /// Does nothing; all string values are valid arguments to a string_option.
586 void
validate(const std::string &) const587 cmdline::string_option::validate(
588     const std::string& /* raw_value */) const
589 {
590     // Nothing to do.
591 }
592 
593 
594 /// Returns the string unmodified.
595 ///
596 /// \param raw_value The argument provided by the user.
597 ///
598 /// \return raw_value
599 ///
600 /// \pre validate(raw_value) must be true.
601 std::string
convert(const std::string & raw_value)602 cmdline::string_option::convert(const std::string& raw_value)
603 {
604     return raw_value;
605 }
606