1 /*
2
3 Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22
23 */
24
25 #ifndef CXXOPTS_HPP_INCLUDED
26 #define CXXOPTS_HPP_INCLUDED
27
28 #include <cctype>
29 #include <cstring>
30 #include <exception>
31 #include <iostream>
32 #include <limits>
33 #include <list>
34 #include <map>
35 #include <memory>
36 #include <regex>
37 #include <sstream>
38 #include <string>
39 #include <unordered_map>
40 #include <unordered_set>
41 #include <utility>
42 #include <vector>
43
44 #ifdef __cpp_lib_optional
45 #include <optional>
46 #define CXXOPTS_HAS_OPTIONAL
47 #endif
48
49 #if __cplusplus >= 201603L
50 #define CXXOPTS_NODISCARD [[nodiscard]]
51 #else
52 #define CXXOPTS_NODISCARD
53 #endif
54
55 #ifndef CXXOPTS_VECTOR_DELIMITER
56 #define CXXOPTS_VECTOR_DELIMITER ','
57 #endif
58
59 #define CXXOPTS__VERSION_MAJOR 3
60 #define CXXOPTS__VERSION_MINOR 0
61 #define CXXOPTS__VERSION_PATCH 0
62
63 namespace cxxopts
64 {
65 static constexpr struct {
66 uint8_t major, minor, patch;
67 } version = {
68 CXXOPTS__VERSION_MAJOR,
69 CXXOPTS__VERSION_MINOR,
70 CXXOPTS__VERSION_PATCH
71 };
72 } // namespace cxxopts
73
74 //when we ask cxxopts to use Unicode, help strings are processed using ICU,
75 //which results in the correct lengths being computed for strings when they
76 //are formatted for the help output
77 //it is necessary to make sure that <unicode/unistr.h> can be found by the
78 //compiler, and that icu-uc is linked in to the binary.
79
80 #ifdef CXXOPTS_USE_UNICODE
81 #include <unicode/unistr.h>
82
83 namespace cxxopts
84 {
85 typedef icu::UnicodeString String;
86
87 inline
88 String
toLocalString(std::string s)89 toLocalString(std::string s)
90 {
91 return icu::UnicodeString::fromUTF8(std::move(s));
92 }
93
94 class UnicodeStringIterator : public
95 std::iterator<std::forward_iterator_tag, int32_t>
96 {
97 public:
98
UnicodeStringIterator(const icu::UnicodeString * string,int32_t pos)99 UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
100 : s(string)
101 , i(pos)
102 {
103 }
104
105 value_type
operator *() const106 operator*() const
107 {
108 return s->char32At(i);
109 }
110
111 bool
operator ==(const UnicodeStringIterator & rhs) const112 operator==(const UnicodeStringIterator& rhs) const
113 {
114 return s == rhs.s && i == rhs.i;
115 }
116
117 bool
operator !=(const UnicodeStringIterator & rhs) const118 operator!=(const UnicodeStringIterator& rhs) const
119 {
120 return !(*this == rhs);
121 }
122
123 UnicodeStringIterator&
operator ++()124 operator++()
125 {
126 ++i;
127 return *this;
128 }
129
130 UnicodeStringIterator
operator +(int32_t v)131 operator+(int32_t v)
132 {
133 return UnicodeStringIterator(s, i + v);
134 }
135
136 private:
137 const icu::UnicodeString* s;
138 int32_t i;
139 };
140
141 inline
142 String&
stringAppend(String & s,String a)143 stringAppend(String&s, String a)
144 {
145 return s.append(std::move(a));
146 }
147
148 inline
149 String&
stringAppend(String & s,size_t n,UChar32 c)150 stringAppend(String& s, size_t n, UChar32 c)
151 {
152 for (size_t i = 0; i != n; ++i)
153 {
154 s.append(c);
155 }
156
157 return s;
158 }
159
160 template <typename Iterator>
161 String&
stringAppend(String & s,Iterator begin,Iterator end)162 stringAppend(String& s, Iterator begin, Iterator end)
163 {
164 while (begin != end)
165 {
166 s.append(*begin);
167 ++begin;
168 }
169
170 return s;
171 }
172
173 inline
174 size_t
stringLength(const String & s)175 stringLength(const String& s)
176 {
177 return s.length();
178 }
179
180 inline
181 std::string
toUTF8String(const String & s)182 toUTF8String(const String& s)
183 {
184 std::string result;
185 s.toUTF8String(result);
186
187 return result;
188 }
189
190 inline
191 bool
empty(const String & s)192 empty(const String& s)
193 {
194 return s.isEmpty();
195 }
196 }
197
198 namespace std
199 {
200 inline
201 cxxopts::UnicodeStringIterator
begin(const icu::UnicodeString & s)202 begin(const icu::UnicodeString& s)
203 {
204 return cxxopts::UnicodeStringIterator(&s, 0);
205 }
206
207 inline
208 cxxopts::UnicodeStringIterator
end(const icu::UnicodeString & s)209 end(const icu::UnicodeString& s)
210 {
211 return cxxopts::UnicodeStringIterator(&s, s.length());
212 }
213 }
214
215 //ifdef CXXOPTS_USE_UNICODE
216 #else
217
218 namespace cxxopts
219 {
220 typedef std::string String;
221
222 template <typename T>
223 T
toLocalString(T && t)224 toLocalString(T&& t)
225 {
226 return std::forward<T>(t);
227 }
228
229 inline
230 size_t
stringLength(const String & s)231 stringLength(const String& s)
232 {
233 return s.length();
234 }
235
236 inline
237 String&
stringAppend(String & s,const String & a)238 stringAppend(String&s, const String& a)
239 {
240 return s.append(a);
241 }
242
243 inline
244 String&
stringAppend(String & s,size_t n,char c)245 stringAppend(String& s, size_t n, char c)
246 {
247 return s.append(n, c);
248 }
249
250 template <typename Iterator>
251 String&
stringAppend(String & s,Iterator begin,Iterator end)252 stringAppend(String& s, Iterator begin, Iterator end)
253 {
254 return s.append(begin, end);
255 }
256
257 template <typename T>
258 std::string
toUTF8String(T && t)259 toUTF8String(T&& t)
260 {
261 return std::forward<T>(t);
262 }
263
264 inline
265 bool
empty(const std::string & s)266 empty(const std::string& s)
267 {
268 return s.empty();
269 }
270 } // namespace cxxopts
271
272 //ifdef CXXOPTS_USE_UNICODE
273 #endif
274
275 namespace cxxopts
276 {
277 namespace
278 {
279 #ifdef _WIN32
280 const std::string LQUOTE("\'");
281 const std::string RQUOTE("\'");
282 #else
283 const std::string LQUOTE("‘");
284 const std::string RQUOTE("’");
285 #endif
286 } // namespace
287
288 #if defined(__GNUC__)
289 // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
290 // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
291 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
292 #pragma GCC diagnostic push
293 // This will be ignored under other compilers like LLVM clang.
294 #endif
295 class Value : public std::enable_shared_from_this<Value>
296 {
297 public:
298
299 virtual ~Value() = default;
300
301 virtual
302 std::shared_ptr<Value>
303 clone() const = 0;
304
305 virtual void
306 parse(const std::string& text) const = 0;
307
308 virtual void
309 parse() const = 0;
310
311 virtual bool
312 has_default() const = 0;
313
314 virtual bool
315 is_container() const = 0;
316
317 virtual bool
318 has_implicit() const = 0;
319
320 virtual std::string
321 get_default_value() const = 0;
322
323 virtual std::string
324 get_implicit_value() const = 0;
325
326 virtual std::shared_ptr<Value>
327 default_value(const std::string& value) = 0;
328
329 virtual std::shared_ptr<Value>
330 implicit_value(const std::string& value) = 0;
331
332 virtual std::shared_ptr<Value>
333 no_implicit_value() = 0;
334
335 virtual bool
336 is_boolean() const = 0;
337 };
338 #if defined(__GNUC__)
339 #pragma GCC diagnostic pop
340 #endif
341 class OptionException : public std::exception
342 {
343 public:
OptionException(std::string message)344 explicit OptionException(std::string message)
345 : m_message(std::move(message))
346 {
347 }
348
349 CXXOPTS_NODISCARD
350 const char*
what() const351 what() const noexcept override
352 {
353 return m_message.c_str();
354 }
355
356 private:
357 std::string m_message;
358 };
359
360 class OptionSpecException : public OptionException
361 {
362 public:
363
OptionSpecException(const std::string & message)364 explicit OptionSpecException(const std::string& message)
365 : OptionException(message)
366 {
367 }
368 };
369
370 class OptionParseException : public OptionException
371 {
372 public:
OptionParseException(const std::string & message)373 explicit OptionParseException(const std::string& message)
374 : OptionException(message)
375 {
376 }
377 };
378
379 class option_exists_error : public OptionSpecException
380 {
381 public:
option_exists_error(const std::string & option)382 explicit option_exists_error(const std::string& option)
383 : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
384 {
385 }
386 };
387
388 class invalid_option_format_error : public OptionSpecException
389 {
390 public:
invalid_option_format_error(const std::string & format)391 explicit invalid_option_format_error(const std::string& format)
392 : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
393 {
394 }
395 };
396
397 class option_syntax_exception : public OptionParseException {
398 public:
option_syntax_exception(const std::string & text)399 explicit option_syntax_exception(const std::string& text)
400 : OptionParseException("Argument " + LQUOTE + text + RQUOTE +
401 " starts with a - but has incorrect syntax")
402 {
403 }
404 };
405
406 class option_not_exists_exception : public OptionParseException
407 {
408 public:
option_not_exists_exception(const std::string & option)409 explicit option_not_exists_exception(const std::string& option)
410 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
411 {
412 }
413 };
414
415 class missing_argument_exception : public OptionParseException
416 {
417 public:
missing_argument_exception(const std::string & option)418 explicit missing_argument_exception(const std::string& option)
419 : OptionParseException(
420 "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
421 )
422 {
423 }
424 };
425
426 class option_requires_argument_exception : public OptionParseException
427 {
428 public:
option_requires_argument_exception(const std::string & option)429 explicit option_requires_argument_exception(const std::string& option)
430 : OptionParseException(
431 "Option " + LQUOTE + option + RQUOTE + " requires an argument"
432 )
433 {
434 }
435 };
436
437 class option_not_has_argument_exception : public OptionParseException
438 {
439 public:
option_not_has_argument_exception(const std::string & option,const std::string & arg)440 option_not_has_argument_exception
441 (
442 const std::string& option,
443 const std::string& arg
444 )
445 : OptionParseException(
446 "Option " + LQUOTE + option + RQUOTE +
447 " does not take an argument, but argument " +
448 LQUOTE + arg + RQUOTE + " given"
449 )
450 {
451 }
452 };
453
454 class option_not_present_exception : public OptionParseException
455 {
456 public:
option_not_present_exception(const std::string & option)457 explicit option_not_present_exception(const std::string& option)
458 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
459 {
460 }
461 };
462
463 class option_has_no_value_exception : public OptionException
464 {
465 public:
option_has_no_value_exception(const std::string & option)466 explicit option_has_no_value_exception(const std::string& option)
467 : OptionException(
468 option.empty() ?
469 ("Option " + LQUOTE + option + RQUOTE + " has no value") :
470 "Option has no value")
471 {
472 }
473 };
474
475 class argument_incorrect_type : public OptionParseException
476 {
477 public:
argument_incorrect_type(const std::string & arg)478 explicit argument_incorrect_type
479 (
480 const std::string& arg
481 )
482 : OptionParseException(
483 "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
484 )
485 {
486 }
487 };
488
489 class option_required_exception : public OptionParseException
490 {
491 public:
option_required_exception(const std::string & option)492 explicit option_required_exception(const std::string& option)
493 : OptionParseException(
494 "Option " + LQUOTE + option + RQUOTE + " is required but not present"
495 )
496 {
497 }
498 };
499
500 template <typename T>
throw_or_mimic(const std::string & text)501 void throw_or_mimic(const std::string& text)
502 {
503 static_assert(std::is_base_of<std::exception, T>::value,
504 "throw_or_mimic only works on std::exception and "
505 "deriving classes");
506
507 #ifndef CXXOPTS_NO_EXCEPTIONS
508 // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
509 throw T{text};
510 #else
511 // Otherwise manually instantiate the exception, print what() to stderr,
512 // and exit
513 T exception{text};
514 std::cerr << exception.what() << std::endl;
515 std::exit(EXIT_FAILURE);
516 #endif
517 }
518
519 namespace values
520 {
521 namespace
522 {
523 std::basic_regex<char> integer_pattern
524 ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
525 std::basic_regex<char> truthy_pattern
526 ("(t|T)(rue)?|1");
527 std::basic_regex<char> falsy_pattern
528 ("(f|F)(alse)?|0");
529 } // namespace
530
531 namespace detail
532 {
533 template <typename T, bool B>
534 struct SignedCheck;
535
536 template <typename T>
537 struct SignedCheck<T, true>
538 {
539 template <typename U>
540 void
operator ()cxxopts::values::detail::SignedCheck541 operator()(bool negative, U u, const std::string& text)
542 {
543 if (negative)
544 {
545 if (u > static_cast<U>((std::numeric_limits<T>::min)()))
546 {
547 throw_or_mimic<argument_incorrect_type>(text);
548 }
549 }
550 else
551 {
552 if (u > static_cast<U>((std::numeric_limits<T>::max)()))
553 {
554 throw_or_mimic<argument_incorrect_type>(text);
555 }
556 }
557 }
558 };
559
560 template <typename T>
561 struct SignedCheck<T, false>
562 {
563 template <typename U>
564 void
operator ()cxxopts::values::detail::SignedCheck565 operator()(bool, U, const std::string&) {}
566 };
567
568 template <typename T, typename U>
569 void
check_signed_range(bool negative,U value,const std::string & text)570 check_signed_range(bool negative, U value, const std::string& text)
571 {
572 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
573 }
574 } // namespace detail
575
576 template <typename R, typename T>
577 void
checked_negate(R & r,T && t,const std::string &,std::true_type)578 checked_negate(R& r, T&& t, const std::string&, std::true_type)
579 {
580 // if we got to here, then `t` is a positive number that fits into
581 // `R`. So to avoid MSVC C4146, we first cast it to `R`.
582 // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
583 r = static_cast<R>(-static_cast<R>(t-1)-1);
584 }
585
586 template <typename R, typename T>
587 void
checked_negate(R &,T &&,const std::string & text,std::false_type)588 checked_negate(R&, T&&, const std::string& text, std::false_type)
589 {
590 throw_or_mimic<argument_incorrect_type>(text);
591 }
592
593 template <typename T>
594 void
integer_parser(const std::string & text,T & value)595 integer_parser(const std::string& text, T& value)
596 {
597 std::smatch match;
598 std::regex_match(text, match, integer_pattern);
599
600 if (match.length() == 0)
601 {
602 throw_or_mimic<argument_incorrect_type>(text);
603 }
604
605 if (match.length(4) > 0)
606 {
607 value = 0;
608 return;
609 }
610
611 using US = typename std::make_unsigned<T>::type;
612
613 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
614 const bool negative = match.length(1) > 0;
615 const uint8_t base = match.length(2) > 0 ? 16 : 10;
616
617 auto value_match = match[3];
618
619 US result = 0;
620
621 for (auto iter = value_match.first; iter != value_match.second; ++iter)
622 {
623 US digit = 0;
624
625 if (*iter >= '0' && *iter <= '9')
626 {
627 digit = static_cast<US>(*iter - '0');
628 }
629 else if (base == 16 && *iter >= 'a' && *iter <= 'f')
630 {
631 digit = static_cast<US>(*iter - 'a' + 10);
632 }
633 else if (base == 16 && *iter >= 'A' && *iter <= 'F')
634 {
635 digit = static_cast<US>(*iter - 'A' + 10);
636 }
637 else
638 {
639 throw_or_mimic<argument_incorrect_type>(text);
640 }
641
642 const US next = static_cast<US>(result * base + digit);
643 if (result > next)
644 {
645 throw_or_mimic<argument_incorrect_type>(text);
646 }
647
648 result = next;
649 }
650
651 detail::check_signed_range<T>(negative, result, text);
652
653 if (negative)
654 {
655 checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
656 }
657 else
658 {
659 value = static_cast<T>(result);
660 }
661 }
662
663 template <typename T>
stringstream_parser(const std::string & text,T & value)664 void stringstream_parser(const std::string& text, T& value)
665 {
666 std::stringstream in(text);
667 in >> value;
668 if (!in) {
669 throw_or_mimic<argument_incorrect_type>(text);
670 }
671 }
672
673 inline
674 void
parse_value(const std::string & text,uint8_t & value)675 parse_value(const std::string& text, uint8_t& value)
676 {
677 integer_parser(text, value);
678 }
679
680 inline
681 void
parse_value(const std::string & text,int8_t & value)682 parse_value(const std::string& text, int8_t& value)
683 {
684 integer_parser(text, value);
685 }
686
687 inline
688 void
parse_value(const std::string & text,uint16_t & value)689 parse_value(const std::string& text, uint16_t& value)
690 {
691 integer_parser(text, value);
692 }
693
694 inline
695 void
parse_value(const std::string & text,int16_t & value)696 parse_value(const std::string& text, int16_t& value)
697 {
698 integer_parser(text, value);
699 }
700
701 inline
702 void
parse_value(const std::string & text,uint32_t & value)703 parse_value(const std::string& text, uint32_t& value)
704 {
705 integer_parser(text, value);
706 }
707
708 inline
709 void
parse_value(const std::string & text,int32_t & value)710 parse_value(const std::string& text, int32_t& value)
711 {
712 integer_parser(text, value);
713 }
714
715 inline
716 void
parse_value(const std::string & text,uint64_t & value)717 parse_value(const std::string& text, uint64_t& value)
718 {
719 integer_parser(text, value);
720 }
721
722 inline
723 void
parse_value(const std::string & text,int64_t & value)724 parse_value(const std::string& text, int64_t& value)
725 {
726 integer_parser(text, value);
727 }
728
729 inline
730 void
parse_value(const std::string & text,bool & value)731 parse_value(const std::string& text, bool& value)
732 {
733 std::smatch result;
734 std::regex_match(text, result, truthy_pattern);
735
736 if (!result.empty())
737 {
738 value = true;
739 return;
740 }
741
742 std::regex_match(text, result, falsy_pattern);
743 if (!result.empty())
744 {
745 value = false;
746 return;
747 }
748
749 throw_or_mimic<argument_incorrect_type>(text);
750 }
751
752 inline
753 void
parse_value(const std::string & text,std::string & value)754 parse_value(const std::string& text, std::string& value)
755 {
756 value = text;
757 }
758
759 // The fallback parser. It uses the stringstream parser to parse all types
760 // that have not been overloaded explicitly. It has to be placed in the
761 // source code before all other more specialized templates.
762 template <typename T>
763 void
parse_value(const std::string & text,T & value)764 parse_value(const std::string& text, T& value) {
765 stringstream_parser(text, value);
766 }
767
768 template <typename T>
769 void
parse_value(const std::string & text,std::vector<T> & value)770 parse_value(const std::string& text, std::vector<T>& value)
771 {
772 std::stringstream in(text);
773 std::string token;
774 while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
775 T v;
776 parse_value(token, v);
777 value.emplace_back(std::move(v));
778 }
779 }
780
781 #ifdef CXXOPTS_HAS_OPTIONAL
782 template <typename T>
783 void
parse_value(const std::string & text,std::optional<T> & value)784 parse_value(const std::string& text, std::optional<T>& value)
785 {
786 T result;
787 parse_value(text, result);
788 value = std::move(result);
789 }
790 #endif
791
792 inline
parse_value(const std::string & text,char & c)793 void parse_value(const std::string& text, char& c)
794 {
795 if (text.length() != 1)
796 {
797 throw_or_mimic<argument_incorrect_type>(text);
798 }
799
800 c = text[0];
801 }
802
803 template <typename T>
804 struct type_is_container
805 {
806 static constexpr bool value = false;
807 };
808
809 template <typename T>
810 struct type_is_container<std::vector<T>>
811 {
812 static constexpr bool value = true;
813 };
814
815 template <typename T>
816 class abstract_value : public Value
817 {
818 using Self = abstract_value<T>;
819
820 public:
abstract_value()821 abstract_value()
822 : m_result(std::make_shared<T>())
823 , m_store(m_result.get())
824 {
825 }
826
abstract_value(T * t)827 explicit abstract_value(T* t)
828 : m_store(t)
829 {
830 }
831
832 ~abstract_value() override = default;
833
834 abstract_value& operator=(const abstract_value&) = default;
835
abstract_value(const abstract_value & rhs)836 abstract_value(const abstract_value& rhs)
837 {
838 if (rhs.m_result)
839 {
840 m_result = std::make_shared<T>();
841 m_store = m_result.get();
842 }
843 else
844 {
845 m_store = rhs.m_store;
846 }
847
848 m_default = rhs.m_default;
849 m_implicit = rhs.m_implicit;
850 m_default_value = rhs.m_default_value;
851 m_implicit_value = rhs.m_implicit_value;
852 }
853
854 void
parse(const std::string & text) const855 parse(const std::string& text) const override
856 {
857 parse_value(text, *m_store);
858 }
859
860 bool
is_container() const861 is_container() const override
862 {
863 return type_is_container<T>::value;
864 }
865
866 void
parse() const867 parse() const override
868 {
869 parse_value(m_default_value, *m_store);
870 }
871
872 bool
has_default() const873 has_default() const override
874 {
875 return m_default;
876 }
877
878 bool
has_implicit() const879 has_implicit() const override
880 {
881 return m_implicit;
882 }
883
884 std::shared_ptr<Value>
default_value(const std::string & value)885 default_value(const std::string& value) override
886 {
887 m_default = true;
888 m_default_value = value;
889 return shared_from_this();
890 }
891
892 std::shared_ptr<Value>
implicit_value(const std::string & value)893 implicit_value(const std::string& value) override
894 {
895 m_implicit = true;
896 m_implicit_value = value;
897 return shared_from_this();
898 }
899
900 std::shared_ptr<Value>
no_implicit_value()901 no_implicit_value() override
902 {
903 m_implicit = false;
904 return shared_from_this();
905 }
906
907 std::string
get_default_value() const908 get_default_value() const override
909 {
910 return m_default_value;
911 }
912
913 std::string
get_implicit_value() const914 get_implicit_value() const override
915 {
916 return m_implicit_value;
917 }
918
919 bool
is_boolean() const920 is_boolean() const override
921 {
922 return std::is_same<T, bool>::value;
923 }
924
925 const T&
get() const926 get() const
927 {
928 if (m_store == nullptr)
929 {
930 return *m_result;
931 }
932 return *m_store;
933 }
934
935 protected:
936 std::shared_ptr<T> m_result{};
937 T* m_store{};
938
939 bool m_default = false;
940 bool m_implicit = false;
941
942 std::string m_default_value{};
943 std::string m_implicit_value{};
944 };
945
946 template <typename T>
947 class standard_value : public abstract_value<T>
948 {
949 public:
950 using abstract_value<T>::abstract_value;
951
952 CXXOPTS_NODISCARD
953 std::shared_ptr<Value>
clone() const954 clone() const
955 {
956 return std::make_shared<standard_value<T>>(*this);
957 }
958 };
959
960 template <>
961 class standard_value<bool> : public abstract_value<bool>
962 {
963 public:
964 ~standard_value() override = default;
965
standard_value()966 standard_value()
967 {
968 set_default_and_implicit();
969 }
970
standard_value(bool * b)971 explicit standard_value(bool* b)
972 : abstract_value(b)
973 {
974 set_default_and_implicit();
975 }
976
977 std::shared_ptr<Value>
clone() const978 clone() const override
979 {
980 return std::make_shared<standard_value<bool>>(*this);
981 }
982
983 private:
984
985 void
set_default_and_implicit()986 set_default_and_implicit()
987 {
988 m_default = true;
989 m_default_value = "false";
990 m_implicit = true;
991 m_implicit_value = "true";
992 }
993 };
994 } // namespace values
995
996 template <typename T>
997 std::shared_ptr<Value>
value()998 value()
999 {
1000 return std::make_shared<values::standard_value<T>>();
1001 }
1002
1003 template <typename T>
1004 std::shared_ptr<Value>
value(T & t)1005 value(T& t)
1006 {
1007 return std::make_shared<values::standard_value<T>>(&t);
1008 }
1009
1010 class OptionAdder;
1011
1012 class OptionDetails
1013 {
1014 public:
OptionDetails(std::string short_,std::string long_,String desc,std::shared_ptr<const Value> val)1015 OptionDetails
1016 (
1017 std::string short_,
1018 std::string long_,
1019 String desc,
1020 std::shared_ptr<const Value> val
1021 )
1022 : m_short(std::move(short_))
1023 , m_long(std::move(long_))
1024 , m_desc(std::move(desc))
1025 , m_value(std::move(val))
1026 , m_count(0)
1027 {
1028 m_hash = std::hash<std::string>{}(m_long + m_short);
1029 }
1030
OptionDetails(const OptionDetails & rhs)1031 OptionDetails(const OptionDetails& rhs)
1032 : m_desc(rhs.m_desc)
1033 , m_count(rhs.m_count)
1034 {
1035 m_value = rhs.m_value->clone();
1036 }
1037
1038 OptionDetails(OptionDetails&& rhs) = default;
1039
1040 CXXOPTS_NODISCARD
1041 const String&
description() const1042 description() const
1043 {
1044 return m_desc;
1045 }
1046
1047 CXXOPTS_NODISCARD
1048 const Value&
value() const1049 value() const {
1050 return *m_value;
1051 }
1052
1053 CXXOPTS_NODISCARD
1054 std::shared_ptr<Value>
make_storage() const1055 make_storage() const
1056 {
1057 return m_value->clone();
1058 }
1059
1060 CXXOPTS_NODISCARD
1061 const std::string&
short_name() const1062 short_name() const
1063 {
1064 return m_short;
1065 }
1066
1067 CXXOPTS_NODISCARD
1068 const std::string&
long_name() const1069 long_name() const
1070 {
1071 return m_long;
1072 }
1073
1074 size_t
hash() const1075 hash() const
1076 {
1077 return m_hash;
1078 }
1079
1080 private:
1081 std::string m_short{};
1082 std::string m_long{};
1083 String m_desc{};
1084 std::shared_ptr<const Value> m_value{};
1085 int m_count;
1086
1087 size_t m_hash{};
1088 };
1089
1090 struct HelpOptionDetails
1091 {
1092 std::string s;
1093 std::string l;
1094 String desc;
1095 bool has_default;
1096 std::string default_value;
1097 bool has_implicit;
1098 std::string implicit_value;
1099 std::string arg_help;
1100 bool is_container;
1101 bool is_boolean;
1102 };
1103
1104 struct HelpGroupDetails
1105 {
1106 std::string name{};
1107 std::string description{};
1108 std::vector<HelpOptionDetails> options{};
1109 };
1110
1111 class OptionValue
1112 {
1113 public:
1114 void
parse(const std::shared_ptr<const OptionDetails> & details,const std::string & text)1115 parse
1116 (
1117 const std::shared_ptr<const OptionDetails>& details,
1118 const std::string& text
1119 )
1120 {
1121 ensure_value(details);
1122 ++m_count;
1123 m_value->parse(text);
1124 m_long_name = &details->long_name();
1125 }
1126
1127 void
parse_default(const std::shared_ptr<const OptionDetails> & details)1128 parse_default(const std::shared_ptr<const OptionDetails>& details)
1129 {
1130 ensure_value(details);
1131 m_default = true;
1132 m_long_name = &details->long_name();
1133 m_value->parse();
1134 }
1135
1136 CXXOPTS_NODISCARD
1137 size_t
count() const1138 count() const noexcept
1139 {
1140 return m_count;
1141 }
1142
1143 // TODO: maybe default options should count towards the number of arguments
1144 CXXOPTS_NODISCARD
1145 bool
has_default() const1146 has_default() const noexcept
1147 {
1148 return m_default;
1149 }
1150
1151 template <typename T>
1152 const T&
as() const1153 as() const
1154 {
1155 if (m_value == nullptr) {
1156 throw_or_mimic<option_has_no_value_exception>(
1157 m_long_name == nullptr ? "" : *m_long_name);
1158 }
1159
1160 #ifdef CXXOPTS_NO_RTTI
1161 return static_cast<const values::standard_value<T>&>(*m_value).get();
1162 #else
1163 return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
1164 #endif
1165 }
1166
1167 private:
1168 void
ensure_value(const std::shared_ptr<const OptionDetails> & details)1169 ensure_value(const std::shared_ptr<const OptionDetails>& details)
1170 {
1171 if (m_value == nullptr)
1172 {
1173 m_value = details->make_storage();
1174 }
1175 }
1176
1177
1178 const std::string* m_long_name = nullptr;
1179 // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1180 // where the key has the string we point to.
1181 std::shared_ptr<Value> m_value{};
1182 size_t m_count = 0;
1183 bool m_default = false;
1184 };
1185
1186 class KeyValue
1187 {
1188 public:
KeyValue(std::string key_,std::string value_)1189 KeyValue(std::string key_, std::string value_)
1190 : m_key(std::move(key_))
1191 , m_value(std::move(value_))
1192 {
1193 }
1194
1195 CXXOPTS_NODISCARD
1196 const std::string&
key() const1197 key() const
1198 {
1199 return m_key;
1200 }
1201
1202 CXXOPTS_NODISCARD
1203 const std::string&
value() const1204 value() const
1205 {
1206 return m_value;
1207 }
1208
1209 template <typename T>
1210 T
as() const1211 as() const
1212 {
1213 T result;
1214 values::parse_value(m_value, result);
1215 return result;
1216 }
1217
1218 private:
1219 std::string m_key;
1220 std::string m_value;
1221 };
1222
1223 using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
1224 using NameHashMap = std::unordered_map<std::string, size_t>;
1225
1226 class ParseResult
1227 {
1228 public:
1229
ParseResult()1230 ParseResult() {}
1231
1232 ParseResult(const ParseResult&) = default;
1233
ParseResult(NameHashMap && keys,ParsedHashMap && values,std::vector<KeyValue> sequential,std::vector<std::string> && unmatched_args)1234 ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
1235 : m_keys(std::move(keys))
1236 , m_values(std::move(values))
1237 , m_sequential(std::move(sequential))
1238 , m_unmatched(std::move(unmatched_args))
1239 {
1240 }
1241
1242 ParseResult& operator=(ParseResult&&) = default;
1243 ParseResult& operator=(const ParseResult&) = default;
1244
1245 size_t
count(const std::string & o) const1246 count(const std::string& o) const
1247 {
1248 auto iter = m_keys.find(o);
1249 if (iter == m_keys.end())
1250 {
1251 return 0;
1252 }
1253
1254 auto viter = m_values.find(iter->second);
1255
1256 if (viter == m_values.end())
1257 {
1258 return 0;
1259 }
1260
1261 return viter->second.count();
1262 }
1263
1264 const OptionValue&
operator [](const std::string & option) const1265 operator[](const std::string& option) const
1266 {
1267 auto iter = m_keys.find(option);
1268
1269 if (iter == m_keys.end())
1270 {
1271 throw_or_mimic<option_not_present_exception>(option);
1272 }
1273
1274 auto viter = m_values.find(iter->second);
1275
1276 if (viter == m_values.end())
1277 {
1278 throw_or_mimic<option_not_present_exception>(option);
1279 }
1280
1281 return viter->second;
1282 }
1283
1284 const std::vector<KeyValue>&
arguments() const1285 arguments() const
1286 {
1287 return m_sequential;
1288 }
1289
1290 const std::vector<std::string>&
unmatched() const1291 unmatched() const
1292 {
1293 return m_unmatched;
1294 }
1295
1296 private:
1297 NameHashMap m_keys{};
1298 ParsedHashMap m_values{};
1299 std::vector<KeyValue> m_sequential{};
1300 std::vector<std::string> m_unmatched{};
1301 };
1302
1303 struct Option
1304 {
Optioncxxopts::Option1305 Option
1306 (
1307 std::string opts,
1308 std::string desc,
1309 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
1310 std::string arg_help = ""
1311 )
1312 : opts_(std::move(opts))
1313 , desc_(std::move(desc))
1314 , value_(std::move(value))
1315 , arg_help_(std::move(arg_help))
1316 {
1317 }
1318
1319 std::string opts_;
1320 std::string desc_;
1321 std::shared_ptr<const Value> value_;
1322 std::string arg_help_;
1323 };
1324
1325 using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1326 using PositionalList = std::vector<std::string>;
1327 using PositionalListIterator = PositionalList::const_iterator;
1328
1329 class OptionParser
1330 {
1331 public:
OptionParser(const OptionMap & options,const PositionalList & positional,bool allow_unrecognised)1332 OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
1333 : m_options(options)
1334 , m_positional(positional)
1335 , m_allow_unrecognised(allow_unrecognised)
1336 {
1337 }
1338
1339 ParseResult
1340 parse(int argc, const char* const* argv);
1341
1342 bool
1343 consume_positional(const std::string& a, PositionalListIterator& next);
1344
1345 void
1346 checked_parse_arg
1347 (
1348 int argc,
1349 const char* const* argv,
1350 int& current,
1351 const std::shared_ptr<OptionDetails>& value,
1352 const std::string& name
1353 );
1354
1355 void
1356 add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
1357
1358 void
1359 parse_option
1360 (
1361 const std::shared_ptr<OptionDetails>& value,
1362 const std::string& name,
1363 const std::string& arg = ""
1364 );
1365
1366 void
1367 parse_default(const std::shared_ptr<OptionDetails>& details);
1368
1369 private:
1370
1371 void finalise_aliases();
1372
1373 const OptionMap& m_options;
1374 const PositionalList& m_positional;
1375
1376 std::vector<KeyValue> m_sequential{};
1377 bool m_allow_unrecognised;
1378
1379 ParsedHashMap m_parsed{};
1380 NameHashMap m_keys{};
1381 };
1382
1383 class Options
1384 {
1385 public:
1386
Options(std::string program,std::string help_string="")1387 explicit Options(std::string program, std::string help_string = "")
1388 : m_program(std::move(program))
1389 , m_help_string(toLocalString(std::move(help_string)))
1390 , m_custom_help("[OPTION...]")
1391 , m_positional_help("positional parameters")
1392 , m_show_positional(false)
1393 , m_allow_unrecognised(false)
1394 , m_options(std::make_shared<OptionMap>())
1395 {
1396 }
1397
1398 Options&
positional_help(std::string help_text)1399 positional_help(std::string help_text)
1400 {
1401 m_positional_help = std::move(help_text);
1402 return *this;
1403 }
1404
1405 Options&
custom_help(std::string help_text)1406 custom_help(std::string help_text)
1407 {
1408 m_custom_help = std::move(help_text);
1409 return *this;
1410 }
1411
1412 Options&
show_positional_help()1413 show_positional_help()
1414 {
1415 m_show_positional = true;
1416 return *this;
1417 }
1418
1419 Options&
allow_unrecognised_options()1420 allow_unrecognised_options()
1421 {
1422 m_allow_unrecognised = true;
1423 return *this;
1424 }
1425
1426 ParseResult
1427 parse(int argc, const char* const* argv);
1428
1429 OptionAdder
1430 add_options(std::string group = "");
1431
1432 void
1433 add_options
1434 (
1435 const std::string& group,
1436 std::initializer_list<Option> options
1437 );
1438
1439 void
1440 add_option
1441 (
1442 const std::string& group,
1443 const Option& option
1444 );
1445
1446 void
1447 add_option
1448 (
1449 const std::string& group,
1450 const std::string& s,
1451 const std::string& l,
1452 std::string desc,
1453 const std::shared_ptr<const Value>& value,
1454 std::string arg_help
1455 );
1456
1457 //parse positional arguments into the given option
1458 void
1459 parse_positional(std::string option);
1460
1461 void
1462 parse_positional(std::vector<std::string> options);
1463
1464 void
1465 parse_positional(std::initializer_list<std::string> options);
1466
1467 template <typename Iterator>
1468 void
parse_positional(Iterator begin,Iterator end)1469 parse_positional(Iterator begin, Iterator end) {
1470 parse_positional(std::vector<std::string>{begin, end});
1471 }
1472
1473 std::string
1474 help(const std::vector<std::string>& groups = {}) const;
1475
1476 std::vector<std::string>
1477 groups() const;
1478
1479 const HelpGroupDetails&
1480 group_help(const std::string& group) const;
1481
1482 private:
1483
1484 void
1485 add_one_option
1486 (
1487 const std::string& option,
1488 const std::shared_ptr<OptionDetails>& details
1489 );
1490
1491 String
1492 help_one_group(const std::string& group) const;
1493
1494 void
1495 generate_group_help
1496 (
1497 String& result,
1498 const std::vector<std::string>& groups
1499 ) const;
1500
1501 void
1502 generate_all_groups_help(String& result) const;
1503
1504 std::string m_program{};
1505 String m_help_string{};
1506 std::string m_custom_help{};
1507 std::string m_positional_help{};
1508 bool m_show_positional;
1509 bool m_allow_unrecognised;
1510
1511 std::shared_ptr<OptionMap> m_options;
1512 std::vector<std::string> m_positional{};
1513 std::unordered_set<std::string> m_positional_set{};
1514
1515 //mapping from groups to help options
1516 std::map<std::string, HelpGroupDetails> m_help{};
1517
1518 std::list<OptionDetails> m_option_list{};
1519 std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
1520 };
1521
1522 class OptionAdder
1523 {
1524 public:
1525
OptionAdder(Options & options,std::string group)1526 OptionAdder(Options& options, std::string group)
1527 : m_options(options), m_group(std::move(group))
1528 {
1529 }
1530
1531 OptionAdder&
1532 operator()
1533 (
1534 const std::string& opts,
1535 const std::string& desc,
1536 const std::shared_ptr<const Value>& value
1537 = ::cxxopts::value<bool>(),
1538 std::string arg_help = ""
1539 );
1540
1541 private:
1542 Options& m_options;
1543 std::string m_group;
1544 };
1545
1546 namespace
1547 {
1548 constexpr int OPTION_LONGEST = 30;
1549 constexpr int OPTION_DESC_GAP = 2;
1550
1551 std::basic_regex<char> option_matcher
1552 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
1553
1554 std::basic_regex<char> option_specifier
1555 ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
1556
1557 String
format_option(const HelpOptionDetails & o)1558 format_option
1559 (
1560 const HelpOptionDetails& o
1561 )
1562 {
1563 const auto& s = o.s;
1564 const auto& l = o.l;
1565
1566 String result = " ";
1567
1568 if (!s.empty())
1569 {
1570 result += "-" + toLocalString(s);
1571 if (!l.empty())
1572 {
1573 result += ",";
1574 }
1575 }
1576 else
1577 {
1578 result += " ";
1579 }
1580
1581 if (!l.empty())
1582 {
1583 result += " --" + toLocalString(l);
1584 }
1585
1586 auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
1587
1588 if (!o.is_boolean)
1589 {
1590 if (o.has_implicit)
1591 {
1592 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
1593 }
1594 else
1595 {
1596 result += " " + arg;
1597 }
1598 }
1599
1600 return result;
1601 }
1602
1603 String
format_description(const HelpOptionDetails & o,size_t start,size_t width)1604 format_description
1605 (
1606 const HelpOptionDetails& o,
1607 size_t start,
1608 size_t width
1609 )
1610 {
1611 auto desc = o.desc;
1612
1613 if (o.has_default && (!o.is_boolean || o.default_value != "false"))
1614 {
1615 if(!o.default_value.empty())
1616 {
1617 desc += toLocalString(" (default: " + o.default_value + ")");
1618 }
1619 else
1620 {
1621 desc += toLocalString(" (default: \"\")");
1622 }
1623 }
1624
1625 String result;
1626
1627 auto current = std::begin(desc);
1628 auto startLine = current;
1629 auto lastSpace = current;
1630
1631 auto size = size_t{};
1632
1633 while (current != std::end(desc))
1634 {
1635 if (*current == ' ')
1636 {
1637 lastSpace = current;
1638 }
1639
1640 if (*current == '\n')
1641 {
1642 startLine = current + 1;
1643 lastSpace = startLine;
1644 }
1645 else if (size > width)
1646 {
1647 if (lastSpace == startLine)
1648 {
1649 stringAppend(result, startLine, current + 1);
1650 stringAppend(result, "\n");
1651 stringAppend(result, start, ' ');
1652 startLine = current + 1;
1653 lastSpace = startLine;
1654 }
1655 else
1656 {
1657 stringAppend(result, startLine, lastSpace);
1658 stringAppend(result, "\n");
1659 stringAppend(result, start, ' ');
1660 startLine = lastSpace + 1;
1661 lastSpace = startLine;
1662 }
1663 size = 0;
1664 }
1665 else
1666 {
1667 ++size;
1668 }
1669
1670 ++current;
1671 }
1672
1673 //append whatever is left
1674 stringAppend(result, startLine, current);
1675
1676 return result;
1677 }
1678 } // namespace
1679
1680 inline
1681 void
add_options(const std::string & group,std::initializer_list<Option> options)1682 Options::add_options
1683 (
1684 const std::string &group,
1685 std::initializer_list<Option> options
1686 )
1687 {
1688 OptionAdder option_adder(*this, group);
1689 for (const auto &option: options)
1690 {
1691 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
1692 }
1693 }
1694
1695 inline
1696 OptionAdder
add_options(std::string group)1697 Options::add_options(std::string group)
1698 {
1699 return OptionAdder(*this, std::move(group));
1700 }
1701
1702 inline
1703 OptionAdder&
operator ()(const std::string & opts,const std::string & desc,const std::shared_ptr<const Value> & value,std::string arg_help)1704 OptionAdder::operator()
1705 (
1706 const std::string& opts,
1707 const std::string& desc,
1708 const std::shared_ptr<const Value>& value,
1709 std::string arg_help
1710 )
1711 {
1712 std::match_results<const char*> result;
1713 std::regex_match(opts.c_str(), result, option_specifier);
1714
1715 if (result.empty())
1716 {
1717 throw_or_mimic<invalid_option_format_error>(opts);
1718 }
1719
1720 const auto& short_match = result[2];
1721 const auto& long_match = result[3];
1722
1723 if (!short_match.length() && !long_match.length())
1724 {
1725 throw_or_mimic<invalid_option_format_error>(opts);
1726 } else if (long_match.length() == 1 && short_match.length())
1727 {
1728 throw_or_mimic<invalid_option_format_error>(opts);
1729 }
1730
1731 auto option_names = []
1732 (
1733 const std::sub_match<const char*>& short_,
1734 const std::sub_match<const char*>& long_
1735 )
1736 {
1737 if (long_.length() == 1)
1738 {
1739 return std::make_tuple(long_.str(), short_.str());
1740 }
1741 return std::make_tuple(short_.str(), long_.str());
1742 }(short_match, long_match);
1743
1744 m_options.add_option
1745 (
1746 m_group,
1747 std::get<0>(option_names),
1748 std::get<1>(option_names),
1749 desc,
1750 value,
1751 std::move(arg_help)
1752 );
1753
1754 return *this;
1755 }
1756
1757 inline
1758 void
parse_default(const std::shared_ptr<OptionDetails> & details)1759 OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
1760 {
1761 // TODO: remove the duplicate code here
1762 auto& store = m_parsed[details->hash()];
1763 store.parse_default(details);
1764 }
1765
1766 inline
1767 void
parse_option(const std::shared_ptr<OptionDetails> & value,const std::string &,const std::string & arg)1768 OptionParser::parse_option
1769 (
1770 const std::shared_ptr<OptionDetails>& value,
1771 const std::string& /*name*/,
1772 const std::string& arg
1773 )
1774 {
1775 auto hash = value->hash();
1776 auto& result = m_parsed[hash];
1777 result.parse(value, arg);
1778
1779 m_sequential.emplace_back(value->long_name(), arg);
1780 }
1781
1782 inline
1783 void
checked_parse_arg(int argc,const char * const * argv,int & current,const std::shared_ptr<OptionDetails> & value,const std::string & name)1784 OptionParser::checked_parse_arg
1785 (
1786 int argc,
1787 const char* const* argv,
1788 int& current,
1789 const std::shared_ptr<OptionDetails>& value,
1790 const std::string& name
1791 )
1792 {
1793 if (current + 1 >= argc)
1794 {
1795 if (value->value().has_implicit())
1796 {
1797 parse_option(value, name, value->value().get_implicit_value());
1798 }
1799 else
1800 {
1801 throw_or_mimic<missing_argument_exception>(name);
1802 }
1803 }
1804 else
1805 {
1806 if (value->value().has_implicit())
1807 {
1808 parse_option(value, name, value->value().get_implicit_value());
1809 }
1810 else
1811 {
1812 parse_option(value, name, argv[current + 1]);
1813 ++current;
1814 }
1815 }
1816 }
1817
1818 inline
1819 void
add_to_option(OptionMap::const_iterator iter,const std::string & option,const std::string & arg)1820 OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
1821 {
1822 parse_option(iter->second, option, arg);
1823 }
1824
1825 inline
1826 bool
consume_positional(const std::string & a,PositionalListIterator & next)1827 OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
1828 {
1829 while (next != m_positional.end())
1830 {
1831 auto iter = m_options.find(*next);
1832 if (iter != m_options.end())
1833 {
1834 auto& result = m_parsed[iter->second->hash()];
1835 if (!iter->second->value().is_container())
1836 {
1837 if (result.count() == 0)
1838 {
1839 add_to_option(iter, *next, a);
1840 ++next;
1841 return true;
1842 }
1843 ++next;
1844 continue;
1845 }
1846 add_to_option(iter, *next, a);
1847 return true;
1848 }
1849 throw_or_mimic<option_not_exists_exception>(*next);
1850 }
1851
1852 return false;
1853 }
1854
1855 inline
1856 void
parse_positional(std::string option)1857 Options::parse_positional(std::string option)
1858 {
1859 parse_positional(std::vector<std::string>{std::move(option)});
1860 }
1861
1862 inline
1863 void
parse_positional(std::vector<std::string> options)1864 Options::parse_positional(std::vector<std::string> options)
1865 {
1866 m_positional = std::move(options);
1867
1868 m_positional_set.insert(m_positional.begin(), m_positional.end());
1869 }
1870
1871 inline
1872 void
parse_positional(std::initializer_list<std::string> options)1873 Options::parse_positional(std::initializer_list<std::string> options)
1874 {
1875 parse_positional(std::vector<std::string>(options));
1876 }
1877
1878 inline
1879 ParseResult
parse(int argc,const char * const * argv)1880 Options::parse(int argc, const char* const* argv)
1881 {
1882 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
1883
1884 return parser.parse(argc, argv);
1885 }
1886
1887 inline ParseResult
parse(int argc,const char * const * argv)1888 OptionParser::parse(int argc, const char* const* argv)
1889 {
1890 int current = 1;
1891 bool consume_remaining = false;
1892 PositionalListIterator next_positional = m_positional.begin();
1893
1894 std::vector<std::string> unmatched;
1895
1896 while (current != argc)
1897 {
1898 if (strcmp(argv[current], "--") == 0)
1899 {
1900 consume_remaining = true;
1901 ++current;
1902 break;
1903 }
1904
1905 std::match_results<const char*> result;
1906 std::regex_match(argv[current], result, option_matcher);
1907
1908 if (result.empty())
1909 {
1910 //not a flag
1911
1912 // but if it starts with a `-`, then it's an error
1913 if (argv[current][0] == '-' && argv[current][1] != '\0') {
1914 if (!m_allow_unrecognised) {
1915 throw_or_mimic<option_syntax_exception>(argv[current]);
1916 }
1917 }
1918
1919 //if true is returned here then it was consumed, otherwise it is
1920 //ignored
1921 if (consume_positional(argv[current], next_positional))
1922 {
1923 }
1924 else
1925 {
1926 unmatched.push_back(argv[current]);
1927 }
1928 //if we return from here then it was parsed successfully, so continue
1929 }
1930 else
1931 {
1932 //short or long option?
1933 if (result[4].length() != 0)
1934 {
1935 const std::string& s = result[4];
1936
1937 for (std::size_t i = 0; i != s.size(); ++i)
1938 {
1939 std::string name(1, s[i]);
1940 auto iter = m_options.find(name);
1941
1942 if (iter == m_options.end())
1943 {
1944 if (m_allow_unrecognised)
1945 {
1946 continue;
1947 }
1948 //error
1949 throw_or_mimic<option_not_exists_exception>(name);
1950 }
1951
1952 auto value = iter->second;
1953
1954 if (i + 1 == s.size())
1955 {
1956 //it must be the last argument
1957 checked_parse_arg(argc, argv, current, value, name);
1958 }
1959 else if (value->value().has_implicit())
1960 {
1961 parse_option(value, name, value->value().get_implicit_value());
1962 }
1963 else
1964 {
1965 //error
1966 throw_or_mimic<option_requires_argument_exception>(name);
1967 }
1968 }
1969 }
1970 else if (result[1].length() != 0)
1971 {
1972 const std::string& name = result[1];
1973
1974 auto iter = m_options.find(name);
1975
1976 if (iter == m_options.end())
1977 {
1978 if (m_allow_unrecognised)
1979 {
1980 // keep unrecognised options in argument list, skip to next argument
1981 unmatched.push_back(argv[current]);
1982 ++current;
1983 continue;
1984 }
1985 //error
1986 throw_or_mimic<option_not_exists_exception>(name);
1987 }
1988
1989 auto opt = iter->second;
1990
1991 //equals provided for long option?
1992 if (result[2].length() != 0)
1993 {
1994 //parse the option given
1995
1996 parse_option(opt, name, result[3]);
1997 }
1998 else
1999 {
2000 //parse the next argument
2001 checked_parse_arg(argc, argv, current, opt, name);
2002 }
2003 }
2004
2005 }
2006
2007 ++current;
2008 }
2009
2010 for (auto& opt : m_options)
2011 {
2012 auto& detail = opt.second;
2013 const auto& value = detail->value();
2014
2015 auto& store = m_parsed[detail->hash()];
2016
2017 if(value.has_default() && !store.count() && !store.has_default()){
2018 parse_default(detail);
2019 }
2020 }
2021
2022 if (consume_remaining)
2023 {
2024 while (current < argc)
2025 {
2026 if (!consume_positional(argv[current], next_positional)) {
2027 break;
2028 }
2029 ++current;
2030 }
2031
2032 //adjust argv for any that couldn't be swallowed
2033 while (current != argc) {
2034 unmatched.push_back(argv[current]);
2035 ++current;
2036 }
2037 }
2038
2039 finalise_aliases();
2040
2041 ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
2042 return parsed;
2043 }
2044
2045 inline
2046 void
finalise_aliases()2047 OptionParser::finalise_aliases()
2048 {
2049 for (auto& option: m_options)
2050 {
2051 auto& detail = *option.second;
2052 auto hash = detail.hash();
2053 m_keys[detail.short_name()] = hash;
2054 m_keys[detail.long_name()] = hash;
2055
2056 m_parsed.emplace(hash, OptionValue());
2057 }
2058 }
2059
2060 inline
2061 void
add_option(const std::string & group,const Option & option)2062 Options::add_option
2063 (
2064 const std::string& group,
2065 const Option& option
2066 )
2067 {
2068 add_options(group, {option});
2069 }
2070
2071 inline
2072 void
add_option(const std::string & group,const std::string & s,const std::string & l,std::string desc,const std::shared_ptr<const Value> & value,std::string arg_help)2073 Options::add_option
2074 (
2075 const std::string& group,
2076 const std::string& s,
2077 const std::string& l,
2078 std::string desc,
2079 const std::shared_ptr<const Value>& value,
2080 std::string arg_help
2081 )
2082 {
2083 auto stringDesc = toLocalString(std::move(desc));
2084 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2085
2086 if (!s.empty())
2087 {
2088 add_one_option(s, option);
2089 }
2090
2091 if (!l.empty())
2092 {
2093 add_one_option(l, option);
2094 }
2095
2096 m_option_list.push_front(*option.get());
2097 auto iter = m_option_list.begin();
2098 m_option_map[s] = iter;
2099 m_option_map[l] = iter;
2100
2101 //add the help details
2102 auto& options = m_help[group];
2103
2104 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
2105 value->has_default(), value->get_default_value(),
2106 value->has_implicit(), value->get_implicit_value(),
2107 std::move(arg_help),
2108 value->is_container(),
2109 value->is_boolean()});
2110 }
2111
2112 inline
2113 void
add_one_option(const std::string & option,const std::shared_ptr<OptionDetails> & details)2114 Options::add_one_option
2115 (
2116 const std::string& option,
2117 const std::shared_ptr<OptionDetails>& details
2118 )
2119 {
2120 auto in = m_options->emplace(option, details);
2121
2122 if (!in.second)
2123 {
2124 throw_or_mimic<option_exists_error>(option);
2125 }
2126 }
2127
2128 inline
2129 String
help_one_group(const std::string & g) const2130 Options::help_one_group(const std::string& g) const
2131 {
2132 using OptionHelp = std::vector<std::pair<String, String>>;
2133
2134 auto group = m_help.find(g);
2135 if (group == m_help.end())
2136 {
2137 return "";
2138 }
2139
2140 OptionHelp format;
2141
2142 size_t longest = 0;
2143
2144 String result;
2145
2146 if (!g.empty())
2147 {
2148 result += toLocalString(" " + g + " options:\n");
2149 }
2150
2151 for (const auto& o : group->second.options)
2152 {
2153 if (m_positional_set.find(o.l) != m_positional_set.end() &&
2154 !m_show_positional)
2155 {
2156 continue;
2157 }
2158
2159 auto s = format_option(o);
2160 longest = (std::max)(longest, stringLength(s));
2161 format.push_back(std::make_pair(s, String()));
2162 }
2163
2164 longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
2165
2166 //widest allowed description
2167 auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
2168
2169 auto fiter = format.begin();
2170 for (const auto& o : group->second.options)
2171 {
2172 if (m_positional_set.find(o.l) != m_positional_set.end() &&
2173 !m_show_positional)
2174 {
2175 continue;
2176 }
2177
2178 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
2179
2180 result += fiter->first;
2181 if (stringLength(fiter->first) > longest)
2182 {
2183 result += '\n';
2184 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2185 }
2186 else
2187 {
2188 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2189 stringLength(fiter->first),
2190 ' '));
2191 }
2192 result += d;
2193 result += '\n';
2194
2195 ++fiter;
2196 }
2197
2198 return result;
2199 }
2200
2201 inline
2202 void
generate_group_help(String & result,const std::vector<std::string> & print_groups) const2203 Options::generate_group_help
2204 (
2205 String& result,
2206 const std::vector<std::string>& print_groups
2207 ) const
2208 {
2209 for (size_t i = 0; i != print_groups.size(); ++i)
2210 {
2211 const String& group_help_text = help_one_group(print_groups[i]);
2212 if (empty(group_help_text))
2213 {
2214 continue;
2215 }
2216 result += group_help_text;
2217 if (i < print_groups.size() - 1)
2218 {
2219 result += '\n';
2220 }
2221 }
2222 }
2223
2224 inline
2225 void
generate_all_groups_help(String & result) const2226 Options::generate_all_groups_help(String& result) const
2227 {
2228 std::vector<std::string> all_groups;
2229 all_groups.reserve(m_help.size());
2230
2231 for (const auto& group : m_help)
2232 {
2233 all_groups.push_back(group.first);
2234 }
2235
2236 generate_group_help(result, all_groups);
2237 }
2238
2239 inline
2240 std::string
help(const std::vector<std::string> & help_groups) const2241 Options::help(const std::vector<std::string>& help_groups) const
2242 {
2243 String result = m_help_string + "\nUsage:\n " +
2244 toLocalString(m_program) + " " + toLocalString(m_custom_help);
2245
2246 if (!m_positional.empty() && !m_positional_help.empty()) {
2247 result += " " + toLocalString(m_positional_help);
2248 }
2249
2250 result += "\n\n";
2251
2252 if (help_groups.empty())
2253 {
2254 generate_all_groups_help(result);
2255 }
2256 else
2257 {
2258 generate_group_help(result, help_groups);
2259 }
2260
2261 return toUTF8String(result);
2262 }
2263
2264 inline
2265 std::vector<std::string>
groups() const2266 Options::groups() const
2267 {
2268 std::vector<std::string> g;
2269
2270 std::transform(
2271 m_help.begin(),
2272 m_help.end(),
2273 std::back_inserter(g),
2274 [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
2275 {
2276 return pair.first;
2277 }
2278 );
2279
2280 return g;
2281 }
2282
2283 inline
2284 const HelpGroupDetails&
group_help(const std::string & group) const2285 Options::group_help(const std::string& group) const
2286 {
2287 return m_help.at(group);
2288 }
2289
2290 } // namespace cxxopts
2291
2292 #endif //CXXOPTS_HPP_INCLUDED
2293