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