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