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