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 <cstring>
29 #include <cctype>
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 1
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 +
383 " starts with a - but has incorrect syntax")
384 {
385 }
386 };
387
388 class option_not_exists_exception : public OptionParseException
389 {
390 public:
option_not_exists_exception(const std::string & option)391 option_not_exists_exception(const std::string& option)
392 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
393 {
394 }
395 };
396
397 class missing_argument_exception : public OptionParseException
398 {
399 public:
missing_argument_exception(const std::string & option)400 missing_argument_exception(const std::string& option)
401 : OptionParseException(
402 "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
403 )
404 {
405 }
406 };
407
408 class option_requires_argument_exception : public OptionParseException
409 {
410 public:
option_requires_argument_exception(const std::string & option)411 option_requires_argument_exception(const std::string& option)
412 : OptionParseException(
413 "Option " + LQUOTE + option + RQUOTE + " requires an argument"
414 )
415 {
416 }
417 };
418
419 class option_not_has_argument_exception : public OptionParseException
420 {
421 public:
option_not_has_argument_exception(const std::string & option,const std::string & arg)422 option_not_has_argument_exception
423 (
424 const std::string& option,
425 const std::string& arg
426 )
427 : OptionParseException(
428 "Option " + LQUOTE + option + RQUOTE +
429 " does not take an argument, but argument " +
430 LQUOTE + arg + RQUOTE + " given"
431 )
432 {
433 }
434 };
435
436 class option_not_present_exception : public OptionParseException
437 {
438 public:
option_not_present_exception(const std::string & option)439 option_not_present_exception(const std::string& option)
440 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
441 {
442 }
443 };
444
445 class argument_incorrect_type : public OptionParseException
446 {
447 public:
argument_incorrect_type(const std::string & arg)448 argument_incorrect_type
449 (
450 const std::string& arg
451 )
452 : OptionParseException(
453 "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
454 )
455 {
456 }
457 };
458
459 class option_required_exception : public OptionParseException
460 {
461 public:
option_required_exception(const std::string & option)462 option_required_exception(const std::string& option)
463 : OptionParseException(
464 "Option " + LQUOTE + option + RQUOTE + " is required but not present"
465 )
466 {
467 }
468 };
469
470 namespace values
471 {
472 namespace
473 {
474 std::basic_regex<char> integer_pattern
475 ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
476 std::basic_regex<char> truthy_pattern
477 ("(t|T)(rue)?|1");
478 std::basic_regex<char> falsy_pattern
479 ("(f|F)(alse)?|0");
480 }
481
482 namespace detail
483 {
484 template <typename T, bool B>
485 struct SignedCheck;
486
487 template <typename T>
488 struct SignedCheck<T, true>
489 {
490 template <typename U>
491 void
operator ()cxxopts::values::detail::SignedCheck492 operator()(bool negative, U u, const std::string& text)
493 {
494 if (negative)
495 {
496 if (u > static_cast<U>((std::numeric_limits<T>::min)()))
497 {
498 throw argument_incorrect_type(text);
499 }
500 }
501 else
502 {
503 if (u > static_cast<U>((std::numeric_limits<T>::max)()))
504 {
505 throw argument_incorrect_type(text);
506 }
507 }
508 }
509 };
510
511 template <typename T>
512 struct SignedCheck<T, false>
513 {
514 template <typename U>
515 void
operator ()cxxopts::values::detail::SignedCheck516 operator()(bool, U, const std::string&) {}
517 };
518
519 template <typename T, typename U>
520 void
check_signed_range(bool negative,U value,const std::string & text)521 check_signed_range(bool negative, U value, const std::string& text)
522 {
523 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
524 }
525 }
526
527 template <typename R, typename T>
528 R
checked_negate(T && t,const std::string &,std::true_type)529 checked_negate(T&& t, const std::string&, std::true_type)
530 {
531 // if we got to here, then `t` is a positive number that fits into
532 // `R`. So to avoid MSVC C4146, we first cast it to `R`.
533 // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
534 return -static_cast<R>(t-1)-1;
535 }
536
537 template <typename R, typename T>
538 T
checked_negate(T &&,const std::string & text,std::false_type)539 checked_negate(T&&, const std::string& text, std::false_type)
540 {
541 throw argument_incorrect_type(text);
542 }
543
544 template <typename T>
545 void
integer_parser(const std::string & text,T & value)546 integer_parser(const std::string& text, T& value)
547 {
548 std::smatch match;
549 std::regex_match(text, match, integer_pattern);
550
551 if (match.length() == 0)
552 {
553 throw argument_incorrect_type(text);
554 }
555
556 if (match.length(4) > 0)
557 {
558 value = 0;
559 return;
560 }
561
562 using US = typename std::make_unsigned<T>::type;
563
564 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
565 const bool negative = match.length(1) > 0;
566 const uint8_t base = match.length(2) > 0 ? 16 : 10;
567
568 auto value_match = match[3];
569
570 US result = 0;
571
572 for (auto iter = value_match.first; iter != value_match.second; ++iter)
573 {
574 US digit = 0;
575
576 if (*iter >= '0' && *iter <= '9')
577 {
578 digit = static_cast<US>(*iter - '0');
579 }
580 else if (base == 16 && *iter >= 'a' && *iter <= 'f')
581 {
582 digit = static_cast<US>(*iter - 'a' + 10);
583 }
584 else if (base == 16 && *iter >= 'A' && *iter <= 'F')
585 {
586 digit = static_cast<US>(*iter - 'A' + 10);
587 }
588 else
589 {
590 throw argument_incorrect_type(text);
591 }
592
593 US next = result * base + digit;
594 if (result > next)
595 {
596 throw argument_incorrect_type(text);
597 }
598
599 result = next;
600 }
601
602 detail::check_signed_range<T>(negative, result, text);
603
604 if (negative)
605 {
606 value = checked_negate<T>(result,
607 text,
608 std::integral_constant<bool, is_signed>());
609 }
610 else
611 {
612 value = static_cast<T>(result);
613 }
614 }
615
616 template <typename T>
stringstream_parser(const std::string & text,T & value)617 void stringstream_parser(const std::string& text, T& value)
618 {
619 std::stringstream in(text);
620 in >> value;
621 if (!in) {
622 throw argument_incorrect_type(text);
623 }
624 }
625
626 inline
627 void
parse_value(const std::string & text,uint8_t & value)628 parse_value(const std::string& text, uint8_t& value)
629 {
630 integer_parser(text, value);
631 }
632
633 inline
634 void
parse_value(const std::string & text,int8_t & value)635 parse_value(const std::string& text, int8_t& value)
636 {
637 integer_parser(text, value);
638 }
639
640 inline
641 void
parse_value(const std::string & text,uint16_t & value)642 parse_value(const std::string& text, uint16_t& value)
643 {
644 integer_parser(text, value);
645 }
646
647 inline
648 void
parse_value(const std::string & text,int16_t & value)649 parse_value(const std::string& text, int16_t& value)
650 {
651 integer_parser(text, value);
652 }
653
654 inline
655 void
parse_value(const std::string & text,uint32_t & value)656 parse_value(const std::string& text, uint32_t& value)
657 {
658 integer_parser(text, value);
659 }
660
661 inline
662 void
parse_value(const std::string & text,int32_t & value)663 parse_value(const std::string& text, int32_t& value)
664 {
665 integer_parser(text, value);
666 }
667
668 inline
669 void
parse_value(const std::string & text,uint64_t & value)670 parse_value(const std::string& text, uint64_t& value)
671 {
672 integer_parser(text, value);
673 }
674
675 inline
676 void
parse_value(const std::string & text,int64_t & value)677 parse_value(const std::string& text, int64_t& value)
678 {
679 integer_parser(text, value);
680 }
681
682 inline
683 void
parse_value(const std::string & text,bool & value)684 parse_value(const std::string& text, bool& value)
685 {
686 std::smatch result;
687 std::regex_match(text, result, truthy_pattern);
688
689 if (!result.empty())
690 {
691 value = true;
692 return;
693 }
694
695 std::regex_match(text, result, falsy_pattern);
696 if (!result.empty())
697 {
698 value = false;
699 return;
700 }
701
702 throw argument_incorrect_type(text);
703 }
704
705 inline
706 void
parse_value(const std::string & text,std::string & value)707 parse_value(const std::string& text, std::string& value)
708 {
709 value = text;
710 }
711
712 // The fallback parser. It uses the stringstream parser to parse all types
713 // that have not been overloaded explicitly. It has to be placed in the
714 // source code before all other more specialized templates.
715 template <typename T>
716 void
parse_value(const std::string & text,T & value)717 parse_value(const std::string& text, T& value) {
718 stringstream_parser(text, value);
719 }
720
721 template <typename T>
722 void
parse_value(const std::string & text,std::vector<T> & value)723 parse_value(const std::string& text, std::vector<T>& value)
724 {
725 std::stringstream in(text);
726 std::string token;
727 while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
728 T v;
729 parse_value(token, v);
730 value.emplace_back(std::move(v));
731 }
732 }
733
734 #ifdef CXXOPTS_HAS_OPTIONAL
735 template <typename T>
736 void
parse_value(const std::string & text,std::optional<T> & value)737 parse_value(const std::string& text, std::optional<T>& value)
738 {
739 T result;
740 parse_value(text, result);
741 value = std::move(result);
742 }
743 #endif
744
745 template <typename T>
746 struct type_is_container
747 {
748 static constexpr bool value = false;
749 };
750
751 template <typename T>
752 struct type_is_container<std::vector<T>>
753 {
754 static constexpr bool value = true;
755 };
756
757 template <typename T>
758 class abstract_value : public Value
759 {
760 using Self = abstract_value<T>;
761
762 public:
abstract_value()763 abstract_value()
764 : m_result(std::make_shared<T>())
765 , m_store(m_result.get())
766 {
767 }
768
abstract_value(T * t)769 abstract_value(T* t)
770 : m_store(t)
771 {
772 }
773
774 virtual ~abstract_value() = default;
775
abstract_value(const abstract_value & rhs)776 abstract_value(const abstract_value& rhs)
777 {
778 if (rhs.m_result)
779 {
780 m_result = std::make_shared<T>();
781 m_store = m_result.get();
782 }
783 else
784 {
785 m_store = rhs.m_store;
786 }
787
788 m_default = rhs.m_default;
789 m_implicit = rhs.m_implicit;
790 m_default_value = rhs.m_default_value;
791 m_implicit_value = rhs.m_implicit_value;
792 }
793
794 void
parse(const std::string & text) const795 parse(const std::string& text) const
796 {
797 parse_value(text, *m_store);
798 }
799
800 bool
is_container() const801 is_container() const
802 {
803 return type_is_container<T>::value;
804 }
805
806 void
parse() const807 parse() const
808 {
809 parse_value(m_default_value, *m_store);
810 }
811
812 bool
has_default() const813 has_default() const
814 {
815 return m_default;
816 }
817
818 bool
has_implicit() const819 has_implicit() const
820 {
821 return m_implicit;
822 }
823
824 std::shared_ptr<Value>
default_value(const std::string & value)825 default_value(const std::string& value)
826 {
827 m_default = true;
828 m_default_value = value;
829 return shared_from_this();
830 }
831
832 std::shared_ptr<Value>
implicit_value(const std::string & value)833 implicit_value(const std::string& value)
834 {
835 m_implicit = true;
836 m_implicit_value = value;
837 return shared_from_this();
838 }
839
840 std::shared_ptr<Value>
no_implicit_value()841 no_implicit_value()
842 {
843 m_implicit = false;
844 return shared_from_this();
845 }
846
847 std::string
get_default_value() const848 get_default_value() const
849 {
850 return m_default_value;
851 }
852
853 std::string
get_implicit_value() const854 get_implicit_value() const
855 {
856 return m_implicit_value;
857 }
858
859 bool
is_boolean() const860 is_boolean() const
861 {
862 return std::is_same<T, bool>::value;
863 }
864
865 const T&
get() const866 get() const
867 {
868 if (m_store == nullptr)
869 {
870 return *m_result;
871 }
872 else
873 {
874 return *m_store;
875 }
876 }
877
878 protected:
879 std::shared_ptr<T> m_result;
880 T* m_store;
881
882 bool m_default = false;
883 bool m_implicit = false;
884
885 std::string m_default_value;
886 std::string m_implicit_value;
887 };
888
889 template <typename T>
890 class standard_value : public abstract_value<T>
891 {
892 public:
893 using abstract_value<T>::abstract_value;
894
895 std::shared_ptr<Value>
clone() const896 clone() const
897 {
898 return std::make_shared<standard_value<T>>(*this);
899 }
900 };
901
902 template <>
903 class standard_value<bool> : public abstract_value<bool>
904 {
905 public:
906 ~standard_value() = default;
907
standard_value()908 standard_value()
909 {
910 set_default_and_implicit();
911 }
912
standard_value(bool * b)913 standard_value(bool* b)
914 : abstract_value(b)
915 {
916 set_default_and_implicit();
917 }
918
919 std::shared_ptr<Value>
clone() const920 clone() const
921 {
922 return std::make_shared<standard_value<bool>>(*this);
923 }
924
925 private:
926
927 void
set_default_and_implicit()928 set_default_and_implicit()
929 {
930 m_default = true;
931 m_default_value = "false";
932 m_implicit = true;
933 m_implicit_value = "true";
934 }
935 };
936 }
937
938 template <typename T>
939 std::shared_ptr<Value>
value()940 value()
941 {
942 return std::make_shared<values::standard_value<T>>();
943 }
944
945 template <typename T>
946 std::shared_ptr<Value>
value(T & t)947 value(T& t)
948 {
949 return std::make_shared<values::standard_value<T>>(&t);
950 }
951
952 class OptionAdder;
953
954 class OptionDetails
955 {
956 public:
OptionDetails(const std::string & short_,const std::string & long_,const String & desc,std::shared_ptr<const Value> val)957 OptionDetails
958 (
959 const std::string& short_,
960 const std::string& long_,
961 const String& desc,
962 std::shared_ptr<const Value> val
963 )
964 : m_short(short_)
965 , m_long(long_)
966 , m_desc(desc)
967 , m_value(val)
968 , m_count(0)
969 {
970 }
971
OptionDetails(const OptionDetails & rhs)972 OptionDetails(const OptionDetails& rhs)
973 : m_desc(rhs.m_desc)
974 , m_count(rhs.m_count)
975 {
976 m_value = rhs.m_value->clone();
977 }
978
979 OptionDetails(OptionDetails&& rhs) = default;
980
981 const String&
description() const982 description() const
983 {
984 return m_desc;
985 }
986
value() const987 const Value& value() const {
988 return *m_value;
989 }
990
991 std::shared_ptr<Value>
make_storage() const992 make_storage() const
993 {
994 return m_value->clone();
995 }
996
997 const std::string&
short_name() const998 short_name() const
999 {
1000 return m_short;
1001 }
1002
1003 const std::string&
long_name() const1004 long_name() const
1005 {
1006 return m_long;
1007 }
1008
1009 private:
1010 std::string m_short;
1011 std::string m_long;
1012 String m_desc;
1013 std::shared_ptr<const Value> m_value;
1014 int m_count;
1015 };
1016
1017 struct HelpOptionDetails
1018 {
1019 std::string s;
1020 std::string l;
1021 String desc;
1022 bool has_default;
1023 std::string default_value;
1024 bool has_implicit;
1025 std::string implicit_value;
1026 std::string arg_help;
1027 bool is_container;
1028 bool is_boolean;
1029 };
1030
1031 struct HelpGroupDetails
1032 {
1033 std::string name;
1034 std::string description;
1035 std::vector<HelpOptionDetails> options;
1036 };
1037
1038 class OptionValue
1039 {
1040 public:
1041 void
parse(std::shared_ptr<const OptionDetails> details,const std::string & text)1042 parse
1043 (
1044 std::shared_ptr<const OptionDetails> details,
1045 const std::string& text
1046 )
1047 {
1048 ensure_value(details);
1049 ++m_count;
1050 m_value->parse(text);
1051 }
1052
1053 void
parse_default(std::shared_ptr<const OptionDetails> details)1054 parse_default(std::shared_ptr<const OptionDetails> details)
1055 {
1056 ensure_value(details);
1057 m_default = true;
1058 m_value->parse();
1059 }
1060
1061 size_t
count() const1062 count() const noexcept
1063 {
1064 return m_count;
1065 }
1066
1067 // TODO: maybe default options should count towards the number of arguments
1068 bool
has_default() const1069 has_default() const noexcept
1070 {
1071 return m_default;
1072 }
1073
1074 template <typename T>
1075 const T&
as() const1076 as() const
1077 {
1078 if (m_value == nullptr) {
1079 throw std::domain_error("No value");
1080 }
1081
1082 #ifdef CXXOPTS_NO_RTTI
1083 return static_cast<const values::standard_value<T>&>(*m_value).get();
1084 #else
1085 return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
1086 #endif
1087 }
1088
1089 private:
1090 void
ensure_value(std::shared_ptr<const OptionDetails> details)1091 ensure_value(std::shared_ptr<const OptionDetails> details)
1092 {
1093 if (m_value == nullptr)
1094 {
1095 m_value = details->make_storage();
1096 }
1097 }
1098
1099 std::shared_ptr<Value> m_value;
1100 size_t m_count = 0;
1101 bool m_default = false;
1102 };
1103
1104 class KeyValue
1105 {
1106 public:
KeyValue(std::string key_,std::string value_)1107 KeyValue(std::string key_, std::string value_)
1108 : m_key(std::move(key_))
1109 , m_value(std::move(value_))
1110 {
1111 }
1112
1113 const
1114 std::string&
key() const1115 key() const
1116 {
1117 return m_key;
1118 }
1119
1120 const
1121 std::string&
value() const1122 value() const
1123 {
1124 return m_value;
1125 }
1126
1127 template <typename T>
1128 T
as() const1129 as() const
1130 {
1131 T result;
1132 values::parse_value(m_value, result);
1133 return result;
1134 }
1135
1136 private:
1137 std::string m_key;
1138 std::string m_value;
1139 };
1140
1141 class ParseResult
1142 {
1143 public:
1144
1145 ParseResult(
1146 const std::shared_ptr<
1147 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1148 >,
1149 std::vector<std::string>,
1150 bool allow_unrecognised,
1151 int&, char**&);
1152
1153 size_t
count(const std::string & o) const1154 count(const std::string& o) const
1155 {
1156 auto iter = m_options->find(o);
1157 if (iter == m_options->end())
1158 {
1159 return 0;
1160 }
1161
1162 auto riter = m_results.find(iter->second);
1163
1164 return riter->second.count();
1165 }
1166
1167 const OptionValue&
operator [](const std::string & option) const1168 operator[](const std::string& option) const
1169 {
1170 auto iter = m_options->find(option);
1171
1172 if (iter == m_options->end())
1173 {
1174 throw option_not_present_exception(option);
1175 }
1176
1177 auto riter = m_results.find(iter->second);
1178
1179 return riter->second;
1180 }
1181
1182 const std::vector<KeyValue>&
arguments() const1183 arguments() const
1184 {
1185 return m_sequential;
1186 }
1187
1188 private:
1189
1190 void
1191 parse(int& argc, char**& argv);
1192
1193 void
1194 add_to_option(const std::string& option, const std::string& arg);
1195
1196 bool
1197 consume_positional(std::string a);
1198
1199 void
1200 parse_option
1201 (
1202 std::shared_ptr<OptionDetails> value,
1203 const std::string& name,
1204 const std::string& arg = ""
1205 );
1206
1207 void
1208 parse_default(std::shared_ptr<OptionDetails> details);
1209
1210 void
1211 checked_parse_arg
1212 (
1213 int argc,
1214 char* argv[],
1215 int& current,
1216 std::shared_ptr<OptionDetails> value,
1217 const std::string& name
1218 );
1219
1220 const std::shared_ptr<
1221 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1222 > m_options;
1223 std::vector<std::string> m_positional;
1224 std::vector<std::string>::iterator m_next_positional;
1225 std::unordered_set<std::string> m_positional_set;
1226 std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
1227
1228 bool m_allow_unrecognised;
1229
1230 std::vector<KeyValue> m_sequential;
1231 };
1232
1233 class Options
1234 {
1235 typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1236 OptionMap;
1237 public:
1238
Options(std::string program,std::string help_string="")1239 Options(std::string program, std::string help_string = "")
1240 : m_program(std::move(program))
1241 , m_help_string(toLocalString(std::move(help_string)))
1242 , m_custom_help("[OPTION...]")
1243 , m_positional_help("positional parameters")
1244 , m_show_positional(false)
1245 , m_allow_unrecognised(false)
1246 , m_options(std::make_shared<OptionMap>())
1247 , m_next_positional(m_positional.end())
1248 {
1249 }
1250
1251 Options&
positional_help(std::string help_text)1252 positional_help(std::string help_text)
1253 {
1254 m_positional_help = std::move(help_text);
1255 return *this;
1256 }
1257
1258 Options&
custom_help(std::string help_text)1259 custom_help(std::string help_text)
1260 {
1261 m_custom_help = std::move(help_text);
1262 return *this;
1263 }
1264
1265 Options&
show_positional_help()1266 show_positional_help()
1267 {
1268 m_show_positional = true;
1269 return *this;
1270 }
1271
1272 Options&
allow_unrecognised_options()1273 allow_unrecognised_options()
1274 {
1275 m_allow_unrecognised = true;
1276 return *this;
1277 }
1278
1279 ParseResult
1280 parse(int& argc, char**& argv);
1281
1282 OptionAdder
1283 add_options(std::string group = "");
1284
1285 void
1286 add_option
1287 (
1288 const std::string& group,
1289 const std::string& s,
1290 const std::string& l,
1291 std::string desc,
1292 std::shared_ptr<const Value> value,
1293 std::string arg_help
1294 );
1295
1296 //parse positional arguments into the given option
1297 void
1298 parse_positional(std::string option);
1299
1300 void
1301 parse_positional(std::vector<std::string> options);
1302
1303 void
1304 parse_positional(std::initializer_list<std::string> options);
1305
1306 template <typename Iterator>
1307 void
parse_positional(Iterator begin,Iterator end)1308 parse_positional(Iterator begin, Iterator end) {
1309 parse_positional(std::vector<std::string>{begin, end});
1310 }
1311
1312 std::string
1313 help(const std::vector<std::string>& groups = {}) const;
1314
1315 const std::vector<std::string>
1316 groups() const;
1317
1318 const HelpGroupDetails&
1319 group_help(const std::string& group) const;
1320
1321 private:
1322
1323 void
1324 add_one_option
1325 (
1326 const std::string& option,
1327 std::shared_ptr<OptionDetails> details
1328 );
1329
1330 String
1331 help_one_group(const std::string& group) const;
1332
1333 void
1334 generate_group_help
1335 (
1336 String& result,
1337 const std::vector<std::string>& groups
1338 ) const;
1339
1340 void
1341 generate_all_groups_help(String& result) const;
1342
1343 std::string m_program;
1344 String m_help_string;
1345 std::string m_custom_help;
1346 std::string m_positional_help;
1347 bool m_show_positional;
1348 bool m_allow_unrecognised;
1349
1350 std::shared_ptr<OptionMap> m_options;
1351 std::vector<std::string> m_positional;
1352 std::vector<std::string>::iterator m_next_positional;
1353 std::unordered_set<std::string> m_positional_set;
1354
1355 //mapping from groups to help options
1356 std::map<std::string, HelpGroupDetails> m_help;
1357 };
1358
1359 class OptionAdder
1360 {
1361 public:
1362
OptionAdder(Options & options,std::string group)1363 OptionAdder(Options& options, std::string group)
1364 : m_options(options), m_group(std::move(group))
1365 {
1366 }
1367
1368 OptionAdder&
1369 operator()
1370 (
1371 const std::string& opts,
1372 const std::string& desc,
1373 std::shared_ptr<const Value> value
1374 = ::cxxopts::value<bool>(),
1375 std::string arg_help = ""
1376 );
1377
1378 private:
1379 Options& m_options;
1380 std::string m_group;
1381 };
1382
1383 namespace
1384 {
1385 constexpr int OPTION_LONGEST = 30;
1386 constexpr int OPTION_DESC_GAP = 2;
1387
1388 std::basic_regex<char> option_matcher
1389 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
1390
1391 std::basic_regex<char> option_specifier
1392 ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
1393
1394 String
format_option(const HelpOptionDetails & o)1395 format_option
1396 (
1397 const HelpOptionDetails& o
1398 )
1399 {
1400 auto& s = o.s;
1401 auto& l = o.l;
1402
1403 String result = " ";
1404
1405 if (s.size() > 0)
1406 {
1407 result += "-" + toLocalString(s) + ",";
1408 }
1409 else
1410 {
1411 result += " ";
1412 }
1413
1414 if (l.size() > 0)
1415 {
1416 result += " --" + toLocalString(l);
1417 }
1418
1419 auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
1420
1421 if (!o.is_boolean)
1422 {
1423 if (o.has_implicit)
1424 {
1425 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
1426 }
1427 else
1428 {
1429 result += " " + arg;
1430 }
1431 }
1432
1433 return result;
1434 }
1435
1436 String
format_description(const HelpOptionDetails & o,size_t start,size_t width)1437 format_description
1438 (
1439 const HelpOptionDetails& o,
1440 size_t start,
1441 size_t width
1442 )
1443 {
1444 auto desc = o.desc;
1445
1446 if (o.has_default && (!o.is_boolean || o.default_value != "false"))
1447 {
1448 desc += toLocalString(" (default: " + o.default_value + ")");
1449 }
1450
1451 String result;
1452
1453 auto current = std::begin(desc);
1454 auto startLine = current;
1455 auto lastSpace = current;
1456
1457 auto size = size_t{};
1458
1459 while (current != std::end(desc))
1460 {
1461 if (*current == ' ')
1462 {
1463 lastSpace = current;
1464 }
1465
1466 if (*current == '\n')
1467 {
1468 startLine = current + 1;
1469 lastSpace = startLine;
1470 }
1471 else if (size > width)
1472 {
1473 if (lastSpace == startLine)
1474 {
1475 stringAppend(result, startLine, current + 1);
1476 stringAppend(result, "\n");
1477 stringAppend(result, start, ' ');
1478 startLine = current + 1;
1479 lastSpace = startLine;
1480 }
1481 else
1482 {
1483 stringAppend(result, startLine, lastSpace);
1484 stringAppend(result, "\n");
1485 stringAppend(result, start, ' ');
1486 startLine = lastSpace + 1;
1487 lastSpace = startLine;
1488 }
1489 size = 0;
1490 }
1491 else
1492 {
1493 ++size;
1494 }
1495
1496 ++current;
1497 }
1498
1499 //append whatever is left
1500 stringAppend(result, startLine, current);
1501
1502 return result;
1503 }
1504 }
1505
1506 inline
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)1507 ParseResult::ParseResult
1508 (
1509 const std::shared_ptr<
1510 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1511 > options,
1512 std::vector<std::string> positional,
1513 bool allow_unrecognised,
1514 int& argc, char**& argv
1515 )
1516 : m_options(options)
1517 , m_positional(std::move(positional))
1518 , m_next_positional(m_positional.begin())
1519 , m_allow_unrecognised(allow_unrecognised)
1520 {
1521 parse(argc, argv);
1522 }
1523
1524 inline
1525 OptionAdder
add_options(std::string group)1526 Options::add_options(std::string group)
1527 {
1528 return OptionAdder(*this, std::move(group));
1529 }
1530
1531 inline
1532 OptionAdder&
operator ()(const std::string & opts,const std::string & desc,std::shared_ptr<const Value> value,std::string arg_help)1533 OptionAdder::operator()
1534 (
1535 const std::string& opts,
1536 const std::string& desc,
1537 std::shared_ptr<const Value> value,
1538 std::string arg_help
1539 )
1540 {
1541 std::match_results<const char*> result;
1542 std::regex_match(opts.c_str(), result, option_specifier);
1543
1544 if (result.empty())
1545 {
1546 throw invalid_option_format_error(opts);
1547 }
1548
1549 const auto& short_match = result[2];
1550 const auto& long_match = result[3];
1551
1552 if (!short_match.length() && !long_match.length())
1553 {
1554 throw invalid_option_format_error(opts);
1555 } else if (long_match.length() == 1 && short_match.length())
1556 {
1557 throw invalid_option_format_error(opts);
1558 }
1559
1560 auto option_names = []
1561 (
1562 const std::sub_match<const char*>& short_,
1563 const std::sub_match<const char*>& long_
1564 )
1565 {
1566 if (long_.length() == 1)
1567 {
1568 return std::make_tuple(long_.str(), short_.str());
1569 }
1570 else
1571 {
1572 return std::make_tuple(short_.str(), long_.str());
1573 }
1574 }(short_match, long_match);
1575
1576 m_options.add_option
1577 (
1578 m_group,
1579 std::get<0>(option_names),
1580 std::get<1>(option_names),
1581 desc,
1582 value,
1583 std::move(arg_help)
1584 );
1585
1586 return *this;
1587 }
1588
1589 inline
1590 void
parse_default(std::shared_ptr<OptionDetails> details)1591 ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
1592 {
1593 m_results[details].parse_default(details);
1594 }
1595
1596 inline
1597 void
parse_option(std::shared_ptr<OptionDetails> value,const std::string &,const std::string & arg)1598 ParseResult::parse_option
1599 (
1600 std::shared_ptr<OptionDetails> value,
1601 const std::string& /*name*/,
1602 const std::string& arg
1603 )
1604 {
1605 auto& result = m_results[value];
1606 result.parse(value, arg);
1607
1608 m_sequential.emplace_back(value->long_name(), arg);
1609 }
1610
1611 inline
1612 void
checked_parse_arg(int argc,char * argv[],int & current,std::shared_ptr<OptionDetails> value,const std::string & name)1613 ParseResult::checked_parse_arg
1614 (
1615 int argc,
1616 char* argv[],
1617 int& current,
1618 std::shared_ptr<OptionDetails> value,
1619 const std::string& name
1620 )
1621 {
1622 if (current + 1 >= argc)
1623 {
1624 if (value->value().has_implicit())
1625 {
1626 parse_option(value, name, value->value().get_implicit_value());
1627 }
1628 else
1629 {
1630 throw missing_argument_exception(name);
1631 }
1632 }
1633 else
1634 {
1635 if (value->value().has_implicit())
1636 {
1637 parse_option(value, name, value->value().get_implicit_value());
1638 }
1639 else
1640 {
1641 parse_option(value, name, argv[current + 1]);
1642 ++current;
1643 }
1644 }
1645 }
1646
1647 inline
1648 void
add_to_option(const std::string & option,const std::string & arg)1649 ParseResult::add_to_option(const std::string& option, const std::string& arg)
1650 {
1651 auto iter = m_options->find(option);
1652
1653 if (iter == m_options->end())
1654 {
1655 throw option_not_exists_exception(option);
1656 }
1657
1658 parse_option(iter->second, option, arg);
1659 }
1660
1661 inline
1662 bool
consume_positional(std::string a)1663 ParseResult::consume_positional(std::string a)
1664 {
1665 while (m_next_positional != m_positional.end())
1666 {
1667 auto iter = m_options->find(*m_next_positional);
1668 if (iter != m_options->end())
1669 {
1670 auto& result = m_results[iter->second];
1671 if (!iter->second->value().is_container())
1672 {
1673 if (result.count() == 0)
1674 {
1675 add_to_option(*m_next_positional, a);
1676 ++m_next_positional;
1677 return true;
1678 }
1679 else
1680 {
1681 ++m_next_positional;
1682 continue;
1683 }
1684 }
1685 else
1686 {
1687 add_to_option(*m_next_positional, a);
1688 return true;
1689 }
1690 }
1691 else
1692 {
1693 throw option_not_exists_exception(*m_next_positional);
1694 }
1695 }
1696
1697 return false;
1698 }
1699
1700 inline
1701 void
parse_positional(std::string option)1702 Options::parse_positional(std::string option)
1703 {
1704 parse_positional(std::vector<std::string>{std::move(option)});
1705 }
1706
1707 inline
1708 void
parse_positional(std::vector<std::string> options)1709 Options::parse_positional(std::vector<std::string> options)
1710 {
1711 m_positional = std::move(options);
1712 m_next_positional = m_positional.begin();
1713
1714 m_positional_set.insert(m_positional.begin(), m_positional.end());
1715 }
1716
1717 inline
1718 void
parse_positional(std::initializer_list<std::string> options)1719 Options::parse_positional(std::initializer_list<std::string> options)
1720 {
1721 parse_positional(std::vector<std::string>(std::move(options)));
1722 }
1723
1724 inline
1725 ParseResult
parse(int & argc,char ** & argv)1726 Options::parse(int& argc, char**& argv)
1727 {
1728 ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1729 return result;
1730 }
1731
1732 inline
1733 void
parse(int & argc,char ** & argv)1734 ParseResult::parse(int& argc, char**& argv)
1735 {
1736 int current = 1;
1737
1738 int nextKeep = 1;
1739
1740 bool consume_remaining = false;
1741
1742 while (current != argc)
1743 {
1744 if (strcmp(argv[current], "--") == 0)
1745 {
1746 consume_remaining = true;
1747 ++current;
1748 break;
1749 }
1750
1751 std::match_results<const char*> result;
1752 std::regex_match(argv[current], result, option_matcher);
1753
1754 if (result.empty())
1755 {
1756 //not a flag
1757
1758 // but if it starts with a `-`, then it's an error
1759 if (argv[current][0] == '-' && argv[current][1] != '\0') {
1760 if (!m_allow_unrecognised) {
1761 throw option_syntax_exception(argv[current]);
1762 }
1763 }
1764
1765 //if true is returned here then it was consumed, otherwise it is
1766 //ignored
1767 if (consume_positional(argv[current]))
1768 {
1769 }
1770 else
1771 {
1772 argv[nextKeep] = argv[current];
1773 ++nextKeep;
1774 }
1775 //if we return from here then it was parsed successfully, so continue
1776 }
1777 else
1778 {
1779 //short or long option?
1780 if (result[4].length() != 0)
1781 {
1782 const std::string& s = result[4];
1783
1784 for (std::size_t i = 0; i != s.size(); ++i)
1785 {
1786 std::string name(1, s[i]);
1787 auto iter = m_options->find(name);
1788
1789 if (iter == m_options->end())
1790 {
1791 if (m_allow_unrecognised)
1792 {
1793 continue;
1794 }
1795 else
1796 {
1797 //error
1798 throw option_not_exists_exception(name);
1799 }
1800 }
1801
1802 auto value = iter->second;
1803
1804 if (i + 1 == s.size())
1805 {
1806 //it must be the last argument
1807 checked_parse_arg(argc, argv, current, value, name);
1808 }
1809 else if (value->value().has_implicit())
1810 {
1811 parse_option(value, name, value->value().get_implicit_value());
1812 }
1813 else
1814 {
1815 //error
1816 throw option_requires_argument_exception(name);
1817 }
1818 }
1819 }
1820 else if (result[1].length() != 0)
1821 {
1822 const std::string& name = result[1];
1823
1824 auto iter = m_options->find(name);
1825
1826 if (iter == m_options->end())
1827 {
1828 if (m_allow_unrecognised)
1829 {
1830 // keep unrecognised options in argument list, skip to next argument
1831 argv[nextKeep] = argv[current];
1832 ++nextKeep;
1833 ++current;
1834 continue;
1835 }
1836 else
1837 {
1838 //error
1839 throw option_not_exists_exception(name);
1840 }
1841 }
1842
1843 auto opt = iter->second;
1844
1845 //equals provided for long option?
1846 if (result[2].length() != 0)
1847 {
1848 //parse the option given
1849
1850 parse_option(opt, name, result[3]);
1851 }
1852 else
1853 {
1854 //parse the next argument
1855 checked_parse_arg(argc, argv, current, opt, name);
1856 }
1857 }
1858
1859 }
1860
1861 ++current;
1862 }
1863
1864 for (auto& opt : *m_options)
1865 {
1866 auto& detail = opt.second;
1867 auto& value = detail->value();
1868
1869 auto& store = m_results[detail];
1870
1871 if(value.has_default() && !store.count() && !store.has_default()){
1872 parse_default(detail);
1873 }
1874 }
1875
1876 if (consume_remaining)
1877 {
1878 while (current < argc)
1879 {
1880 if (!consume_positional(argv[current])) {
1881 break;
1882 }
1883 ++current;
1884 }
1885
1886 //adjust argv for any that couldn't be swallowed
1887 while (current != argc) {
1888 argv[nextKeep] = argv[current];
1889 ++nextKeep;
1890 ++current;
1891 }
1892 }
1893
1894 argc = nextKeep;
1895
1896 }
1897
1898 inline
1899 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)1900 Options::add_option
1901 (
1902 const std::string& group,
1903 const std::string& s,
1904 const std::string& l,
1905 std::string desc,
1906 std::shared_ptr<const Value> value,
1907 std::string arg_help
1908 )
1909 {
1910 auto stringDesc = toLocalString(std::move(desc));
1911 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
1912
1913 if (s.size() > 0)
1914 {
1915 add_one_option(s, option);
1916 }
1917
1918 if (l.size() > 0)
1919 {
1920 add_one_option(l, option);
1921 }
1922
1923 //add the help details
1924 auto& options = m_help[group];
1925
1926 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
1927 value->has_default(), value->get_default_value(),
1928 value->has_implicit(), value->get_implicit_value(),
1929 std::move(arg_help),
1930 value->is_container(),
1931 value->is_boolean()});
1932 }
1933
1934 inline
1935 void
add_one_option(const std::string & option,std::shared_ptr<OptionDetails> details)1936 Options::add_one_option
1937 (
1938 const std::string& option,
1939 std::shared_ptr<OptionDetails> details
1940 )
1941 {
1942 auto in = m_options->emplace(option, details);
1943
1944 if (!in.second)
1945 {
1946 throw option_exists_error(option);
1947 }
1948 }
1949
1950 inline
1951 String
help_one_group(const std::string & g) const1952 Options::help_one_group(const std::string& g) const
1953 {
1954 typedef std::vector<std::pair<String, String>> OptionHelp;
1955
1956 auto group = m_help.find(g);
1957 if (group == m_help.end())
1958 {
1959 return "";
1960 }
1961
1962 OptionHelp format;
1963
1964 size_t longest = 0;
1965
1966 String result;
1967
1968 if (!g.empty())
1969 {
1970 result += toLocalString(" " + g + " options:\n");
1971 }
1972
1973 for (const auto& o : group->second.options)
1974 {
1975 if (m_positional_set.find(o.l) != m_positional_set.end() &&
1976 !m_show_positional)
1977 {
1978 continue;
1979 }
1980
1981 auto s = format_option(o);
1982 longest = (std::max)(longest, stringLength(s));
1983 format.push_back(std::make_pair(s, String()));
1984 }
1985
1986 longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
1987
1988 //widest allowed description
1989 auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
1990
1991 auto fiter = format.begin();
1992 for (const auto& o : group->second.options)
1993 {
1994 if (m_positional_set.find(o.l) != m_positional_set.end() &&
1995 !m_show_positional)
1996 {
1997 continue;
1998 }
1999
2000 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
2001
2002 result += fiter->first;
2003 if (stringLength(fiter->first) > longest)
2004 {
2005 result += '\n';
2006 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2007 }
2008 else
2009 {
2010 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2011 stringLength(fiter->first),
2012 ' '));
2013 }
2014 result += d;
2015 result += '\n';
2016
2017 ++fiter;
2018 }
2019
2020 return result;
2021 }
2022
2023 inline
2024 void
generate_group_help(String & result,const std::vector<std::string> & print_groups) const2025 Options::generate_group_help
2026 (
2027 String& result,
2028 const std::vector<std::string>& print_groups
2029 ) const
2030 {
2031 for (size_t i = 0; i != print_groups.size(); ++i)
2032 {
2033 const String& group_help_text = help_one_group(print_groups[i]);
2034 if (empty(group_help_text))
2035 {
2036 continue;
2037 }
2038 result += group_help_text;
2039 if (i < print_groups.size() - 1)
2040 {
2041 result += '\n';
2042 }
2043 }
2044 }
2045
2046 inline
2047 void
generate_all_groups_help(String & result) const2048 Options::generate_all_groups_help(String& result) const
2049 {
2050 std::vector<std::string> all_groups;
2051 all_groups.reserve(m_help.size());
2052
2053 for (auto& group : m_help)
2054 {
2055 all_groups.push_back(group.first);
2056 }
2057
2058 generate_group_help(result, all_groups);
2059 }
2060
2061 inline
2062 std::string
help(const std::vector<std::string> & help_groups) const2063 Options::help(const std::vector<std::string>& help_groups) const
2064 {
2065 String result = m_help_string + "\nUsage:\n " +
2066 toLocalString(m_program) + " " + toLocalString(m_custom_help);
2067
2068 if (m_positional.size() > 0 && m_positional_help.size() > 0) {
2069 result += " " + toLocalString(m_positional_help);
2070 }
2071
2072 result += "\n\n";
2073
2074 if (help_groups.size() == 0)
2075 {
2076 generate_all_groups_help(result);
2077 }
2078 else
2079 {
2080 generate_group_help(result, help_groups);
2081 }
2082
2083 return toUTF8String(result);
2084 }
2085
2086 inline
2087 const std::vector<std::string>
groups() const2088 Options::groups() const
2089 {
2090 std::vector<std::string> g;
2091
2092 std::transform(
2093 m_help.begin(),
2094 m_help.end(),
2095 std::back_inserter(g),
2096 [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
2097 {
2098 return pair.first;
2099 }
2100 );
2101
2102 return g;
2103 }
2104
2105 inline
2106 const HelpGroupDetails&
group_help(const std::string & group) const2107 Options::group_help(const std::string& group) const
2108 {
2109 return m_help.at(group);
2110 }
2111
2112 }
2113
2114 #endif //CXXOPTS_HPP_INCLUDED
2115