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