1 /*
2
3 Copyright (c) 2014, 2015, 2016 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 CXX_OPTS_HPP
26 #define CXX_OPTS_HPP
27
28 #if defined(__GNUC__)
29 #pragma GCC diagnostic push
30 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
31 #endif
32
33 #include <cstring>
34 #include <exception>
35 #include <iostream>
36 #include <map>
37 #include <memory>
38 #include <regex>
39 #include <sstream>
40 #include <string>
41 #include <unordered_set>
42 #include <vector>
43
44 //when we ask cxxopts to use Unicode, help strings are processed using ICU,
45 //which results in the correct lengths being computed for strings when they
46 //are formatted for the help output
47 //it is necessary to make sure that <unicode/unistr.h> can be found by the
48 //compiler, and that icu-uc is linked in to the binary.
49
50 #ifdef CXXOPTS_USE_UNICODE
51 #include <unicode/unistr.h>
52
53 namespace cxxopts
54 {
55 typedef icu::UnicodeString String;
56
57 inline
58 String
toLocalString(std::string s)59 toLocalString(std::string s)
60 {
61 return icu::UnicodeString::fromUTF8(s);
62 }
63
64 class UnicodeStringIterator : public
65 std::iterator<std::forward_iterator_tag, int32_t>
66 {
67 public:
68
UnicodeStringIterator(const icu::UnicodeString * s,int32_t pos)69 UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos)
70 : s(s)
71 , i(pos)
72 {
73 }
74
75 value_type
operator *() const76 operator*() const
77 {
78 return s->char32At(i);
79 }
80
81 bool
operator ==(const UnicodeStringIterator & rhs) const82 operator==(const UnicodeStringIterator& rhs) const
83 {
84 return s == rhs.s && i == rhs.i;
85 }
86
87 bool
operator !=(const UnicodeStringIterator & rhs) const88 operator!=(const UnicodeStringIterator& rhs) const
89 {
90 return !(*this == rhs);
91 }
92
93 UnicodeStringIterator&
operator ++()94 operator++()
95 {
96 ++i;
97 return *this;
98 }
99
100 UnicodeStringIterator
operator +(int32_t v)101 operator+(int32_t v)
102 {
103 return UnicodeStringIterator(s, i + v);
104 }
105
106 private:
107 const icu::UnicodeString* s;
108 int32_t i;
109 };
110
111 inline
112 String&
stringAppend(String & s,String a)113 stringAppend(String&s, String a)
114 {
115 return s.append(std::move(a));
116 }
117
118 inline
119 String&
stringAppend(String & s,int n,UChar32 c)120 stringAppend(String& s, int n, UChar32 c)
121 {
122 for (int i = 0; i != n; ++i)
123 {
124 s.append(c);
125 }
126
127 return s;
128 }
129
130 template <typename Iterator>
131 String&
stringAppend(String & s,Iterator begin,Iterator end)132 stringAppend(String& s, Iterator begin, Iterator end)
133 {
134 while (begin != end)
135 {
136 s.append(*begin);
137 ++begin;
138 }
139
140 return s;
141 }
142
143 inline
144 size_t
stringLength(const String & s)145 stringLength(const String& s)
146 {
147 return s.length();
148 }
149
150 inline
151 std::string
toUTF8String(const String & s)152 toUTF8String(const String& s)
153 {
154 std::string result;
155 s.toUTF8String(result);
156
157 return result;
158 }
159
160 inline
161 bool
empty(const String & s)162 empty(const String& s)
163 {
164 return s.isEmpty();
165 }
166 }
167
168 namespace std
169 {
170 cxxopts::UnicodeStringIterator
begin(const icu::UnicodeString & s)171 begin(const icu::UnicodeString& s)
172 {
173 return cxxopts::UnicodeStringIterator(&s, 0);
174 }
175
176 cxxopts::UnicodeStringIterator
end(const icu::UnicodeString & s)177 end(const icu::UnicodeString& s)
178 {
179 return cxxopts::UnicodeStringIterator(&s, s.length());
180 }
181 }
182
183 //ifdef CXXOPTS_USE_UNICODE
184 #else
185
186 namespace cxxopts
187 {
188 typedef std::string String;
189
190 template <typename T>
191 T
toLocalString(T && t)192 toLocalString(T&& t)
193 {
194 return t;
195 }
196
197 inline
198 size_t
stringLength(const String & s)199 stringLength(const String& s)
200 {
201 return s.length();
202 }
203
204 inline
205 String&
stringAppend(String & s,String a)206 stringAppend(String&s, String a)
207 {
208 return s.append(std::move(a));
209 }
210
211 inline
212 String&
stringAppend(String & s,size_t n,char c)213 stringAppend(String& s, size_t n, char c)
214 {
215 return s.append(n, c);
216 }
217
218 template <typename Iterator>
219 String&
stringAppend(String & s,Iterator begin,Iterator end)220 stringAppend(String& s, Iterator begin, Iterator end)
221 {
222 return s.append(begin, end);
223 }
224
225 template <typename T>
226 std::string
toUTF8String(T && t)227 toUTF8String(T&& t)
228 {
229 return std::forward<T>(t);
230 }
231
232 inline
233 bool
empty(const std::string & s)234 empty(const std::string& s)
235 {
236 return s.empty();
237 }
238 }
239
240 //ifdef CXXOPTS_USE_UNICODE
241 #endif
242
243 namespace cxxopts
244 {
245 class Value : public std::enable_shared_from_this<Value>
246 {
247 public:
248
249 virtual void
250 parse(const std::string& text) const = 0;
251
252 virtual void
253 parse() const = 0;
254
255 virtual bool
256 has_arg() const = 0;
257
258 virtual bool
259 has_default() const = 0;
260
261 virtual bool
262 is_container() const = 0;
263
264 virtual bool
265 has_implicit() const = 0;
266
267 virtual std::string
268 get_default_value() const = 0;
269
270 virtual std::string
271 get_implicit_value() const = 0;
272
273 virtual std::shared_ptr<Value>
274 default_value(const std::string& value) = 0;
275
276 virtual std::shared_ptr<Value>
277 implicit_value(const std::string& value) = 0;
278 };
279
280 class OptionException : public std::exception
281 {
282 public:
OptionException(const std::string & message)283 OptionException(const std::string& message)
284 : m_message(message)
285 {
286 }
287
288 virtual const char*
what() const289 what() const noexcept
290 {
291 return m_message.c_str();
292 }
293
294 private:
295 std::string m_message;
296 };
297
298 class OptionSpecException : public OptionException
299 {
300 public:
301
OptionSpecException(const std::string & message)302 OptionSpecException(const std::string& message)
303 : OptionException(message)
304 {
305 }
306 };
307
308 class OptionParseException : public OptionException
309 {
310 public:
OptionParseException(const std::string & message)311 OptionParseException(const std::string& message)
312 : OptionException(message)
313 {
314 }
315 };
316
317 class option_exists_error : public OptionSpecException
318 {
319 public:
option_exists_error(const std::string & option)320 option_exists_error(const std::string& option)
321 : OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
322 {
323 }
324 };
325
326 class invalid_option_format_error : public OptionSpecException
327 {
328 public:
invalid_option_format_error(const std::string & format)329 invalid_option_format_error(const std::string& format)
330 : OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
331 {
332 }
333 };
334
335 class option_not_exists_exception : public OptionParseException
336 {
337 public:
option_not_exists_exception(const std::string & option)338 option_not_exists_exception(const std::string& option)
339 : OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
340 {
341 }
342 };
343
344 class missing_argument_exception : public OptionParseException
345 {
346 public:
missing_argument_exception(const std::string & option)347 missing_argument_exception(const std::string& option)
348 : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
349 {
350 }
351 };
352
353 class option_requires_argument_exception : public OptionParseException
354 {
355 public:
option_requires_argument_exception(const std::string & option)356 option_requires_argument_exception(const std::string& option)
357 : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
358 {
359 }
360 };
361
362 class option_not_has_argument_exception : public OptionParseException
363 {
364 public:
option_not_has_argument_exception(const std::string & option,const std::string & arg)365 option_not_has_argument_exception
366 (
367 const std::string& option,
368 const std::string& arg
369 )
370 : OptionParseException(
371 u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
372 + arg + "’ given")
373 {
374 }
375 };
376
377 class option_not_present_exception : public OptionParseException
378 {
379 public:
option_not_present_exception(const std::string & option)380 option_not_present_exception(const std::string& option)
381 : OptionParseException(u8"Option ‘" + option + u8"’ not present")
382 {
383 }
384 };
385
386 class argument_incorrect_type : public OptionParseException
387 {
388 public:
argument_incorrect_type(const std::string & arg)389 argument_incorrect_type
390 (
391 const std::string& arg
392 )
393 : OptionParseException(
394 u8"Argument ‘" + arg + u8"’ failed to parse"
395 )
396 {
397 }
398 };
399
400 namespace values
401 {
402 template <typename T>
403 void
parse_value(const std::string & text,T & value)404 parse_value(const std::string& text, T& value)
405 {
406 std::istringstream is(text);
407 if (!(is >> value))
408 {
409 throw argument_incorrect_type(text);
410 }
411
412 if (is.rdbuf()->in_avail() != 0)
413 {
414 throw argument_incorrect_type(text);
415 }
416 }
417
418 inline
419 void
parse_value(const std::string &,bool & value)420 parse_value(const std::string& /*text*/, bool& value)
421 {
422 //TODO recognise on, off, yes, no, enable, disable
423 //so that we can write --long=yes explicitly
424 value = true;
425 }
426
427 inline
428 void
parse_value(const std::string & text,std::string & value)429 parse_value(const std::string& text, std::string& value)
430 {
431 value = text;
432 }
433
434 template <typename T>
435 void
parse_value(const std::string & text,std::vector<T> & value)436 parse_value(const std::string& text, std::vector<T>& value)
437 {
438 T v;
439 parse_value(text, v);
440 value.push_back(v);
441 }
442
443 template <typename T>
444 struct value_has_arg
445 {
446 static constexpr bool value = true;
447 };
448
449 template <>
450 struct value_has_arg<bool>
451 {
452 static constexpr bool value = false;
453 };
454
455 template <typename T>
456 struct type_is_container
457 {
458 static constexpr bool value = false;
459 };
460
461 template <typename T>
462 struct type_is_container<std::vector<T>>
463 {
464 static constexpr bool value = true;
465 };
466
467 template <typename T>
468 class standard_value : public Value
469 {
470 public:
standard_value()471 standard_value()
472 : m_result(std::make_shared<T>())
473 , m_store(m_result.get())
474 {
475 }
476
standard_value(T * t)477 standard_value(T* t)
478 : m_store(t)
479 {
480 }
481
482 void
parse(const std::string & text) const483 parse(const std::string& text) const
484 {
485 parse_value(text, *m_store);
486 }
487
488 bool
is_container() const489 is_container() const
490 {
491 return type_is_container<T>::value;
492 }
493
494 void
parse() const495 parse() const
496 {
497 parse_value(m_default_value, *m_store);
498 }
499
500 bool
has_arg() const501 has_arg() const
502 {
503 return value_has_arg<T>::value;
504 }
505
506 bool
has_default() const507 has_default() const
508 {
509 return m_default;
510 }
511
512 bool
has_implicit() const513 has_implicit() const
514 {
515 return m_implicit;
516 }
517
518 virtual std::shared_ptr<Value>
default_value(const std::string & value)519 default_value(const std::string& value){
520 m_default = true;
521 m_default_value = value;
522 return shared_from_this();
523 }
524
525 virtual std::shared_ptr<Value>
implicit_value(const std::string & value)526 implicit_value(const std::string& value){
527 m_implicit = true;
528 m_implicit_value = value;
529 return shared_from_this();
530 }
531
532 std::string
get_default_value() const533 get_default_value() const
534 {
535 return m_default_value;
536 }
537
538 std::string
get_implicit_value() const539 get_implicit_value() const
540 {
541 return m_implicit_value;
542 }
543
544 const T&
get() const545 get() const
546 {
547 if (m_store == nullptr)
548 {
549 return *m_result;
550 }
551 else
552 {
553 return *m_store;
554 }
555 }
556
557 protected:
558 std::shared_ptr<T> m_result;
559 T* m_store;
560 bool m_default = false;
561 std::string m_default_value;
562 bool m_implicit = false;
563 std::string m_implicit_value;
564 };
565 }
566
567 template <typename T>
568 std::shared_ptr<Value>
value()569 value()
570 {
571 return std::make_shared<values::standard_value<T>>();
572 }
573
574 template <typename T>
575 std::shared_ptr<Value>
value(T & t)576 value(T& t)
577 {
578 return std::make_shared<values::standard_value<T>>(&t);
579 }
580
581 class OptionAdder;
582
583 class OptionDetails
584 {
585 public:
OptionDetails(const String & description,std::shared_ptr<const Value> value)586 OptionDetails
587 (
588 const String& description,
589 std::shared_ptr<const Value> value
590 )
591 : m_desc(description)
592 , m_value(value)
593 , m_count(0)
594 {
595 }
596
597 const String&
description() const598 description() const
599 {
600 return m_desc;
601 }
602
603 bool
has_arg() const604 has_arg() const
605 {
606 return m_value->has_arg();
607 }
608
609 void
parse(const std::string & text)610 parse(const std::string& text)
611 {
612 m_value->parse(text);
613 ++m_count;
614 }
615
616 void
parse_default()617 parse_default()
618 {
619 m_value->parse();
620 }
621
622 int
count() const623 count() const
624 {
625 return m_count;
626 }
627
value() const628 const Value& value() const {
629 return *m_value;
630 }
631
632 template <typename T>
633 const T&
as() const634 as() const
635 {
636 #ifdef CXXOPTS_NO_RTTI
637 return static_cast<const values::standard_value<T>&>(*m_value).get();
638 #else
639 return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
640 #endif
641 }
642
643 private:
644 String m_desc;
645 std::shared_ptr<const Value> m_value;
646 int m_count;
647 };
648
649 struct HelpOptionDetails
650 {
651 std::string s;
652 std::string l;
653 String desc;
654 bool has_arg;
655 bool has_default;
656 std::string default_value;
657 bool has_implicit;
658 std::string implicit_value;
659 std::string arg_help;
660 bool is_container;
661 };
662
663 struct HelpGroupDetails
664 {
665 std::string name;
666 std::string description;
667 std::vector<HelpOptionDetails> options;
668 };
669
670 class Options
671 {
672 public:
673
Options(std::string program,std::string help_string="")674 Options(std::string program, std::string help_string = "")
675 : m_program(std::move(program))
676 , m_help_string(toLocalString(std::move(help_string)))
677 , m_positional_help("positional parameters")
678 , m_next_positional(m_positional.end())
679 {
680 }
681
682 inline
683 Options&
positional_help(const std::string & help_text)684 positional_help(const std::string& help_text)
685 {
686 m_positional_help = std::move(help_text);
687 return *this;
688 }
689
690 inline
691 void
692 parse(int& argc, char**& argv);
693
694 inline
695 OptionAdder
696 add_options(std::string group = "");
697
698 inline
699 void
700 add_option
701 (
702 const std::string& group,
703 const std::string& s,
704 const std::string& l,
705 std::string desc,
706 std::shared_ptr<const Value> value,
707 std::string arg_help
708 );
709
710 int
count(const std::string & o) const711 count(const std::string& o) const
712 {
713 auto iter = m_options.find(o);
714 if (iter == m_options.end())
715 {
716 return 0;
717 }
718
719 return iter->second->count();
720 }
721
722 const OptionDetails&
operator [](const std::string & option) const723 operator[](const std::string& option) const
724 {
725 auto iter = m_options.find(option);
726
727 if (iter == m_options.end())
728 {
729 throw option_not_present_exception(option);
730 }
731
732 return *iter->second;
733 }
734
735 //parse positional arguments into the given option
736 inline
737 void
738 parse_positional(std::string option);
739
740 inline
741 void
742 parse_positional(std::vector<std::string> options);
743
744 inline
745 std::string
746 help(const std::vector<std::string>& groups = {""}) const;
747
748 inline
749 const std::vector<std::string>
750 groups() const;
751
752 inline
753 const HelpGroupDetails&
754 group_help(const std::string& group) const;
755
756 private:
757
758 inline
759 void
760 add_one_option
761 (
762 const std::string& option,
763 std::shared_ptr<OptionDetails> details
764 );
765
766 inline
767 bool
768 consume_positional(std::string a);
769
770 inline
771 void
772 add_to_option(const std::string& option, const std::string& arg);
773
774 inline
775 void
776 parse_option
777 (
778 std::shared_ptr<OptionDetails> value,
779 const std::string& name,
780 const std::string& arg = ""
781 );
782
783 inline
784 void
785 checked_parse_arg
786 (
787 int argc,
788 char* argv[],
789 int& current,
790 std::shared_ptr<OptionDetails> value,
791 const std::string& name
792 );
793
794 inline
795 String
796 help_one_group(const std::string& group) const;
797
798 inline
799 void
800 generate_group_help(String& result, const std::vector<std::string>& groups) const;
801
802 inline
803 void
804 generate_all_groups_help(String& result) const;
805
806 std::string m_program;
807 String m_help_string;
808 std::string m_positional_help;
809
810 std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
811 std::vector<std::string> m_positional;
812 std::vector<std::string>::iterator m_next_positional;
813 std::unordered_set<std::string> m_positional_set;
814
815 //mapping from groups to help options
816 std::map<std::string, HelpGroupDetails> m_help;
817 };
818
819 class OptionAdder
820 {
821 public:
822
OptionAdder(Options & options,std::string group)823 OptionAdder(Options& options, std::string group)
824 : m_options(options), m_group(std::move(group))
825 {
826 }
827
828 inline
829 OptionAdder&
830 operator()
831 (
832 const std::string& opts,
833 const std::string& desc,
834 std::shared_ptr<const Value> value
835 = ::cxxopts::value<bool>(),
836 std::string arg_help = ""
837 );
838
839 private:
840 Options& m_options;
841 std::string m_group;
842 };
843
844 }
845
846 namespace cxxopts
847 {
848
849 namespace
850 {
851
852 constexpr int OPTION_LONGEST = 30;
853 constexpr int OPTION_DESC_GAP = 2;
854
855 std::basic_regex<char> option_matcher
856 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
857
858 std::basic_regex<char> option_specifier
859 ("(([[:alnum:]]),)?([[:alnum:]][-_[:alnum:]]+)");
860
861 String
format_option(const HelpOptionDetails & o)862 format_option
863 (
864 const HelpOptionDetails& o
865 )
866 {
867 auto& s = o.s;
868 auto& l = o.l;
869
870 String result = " ";
871
872 if (s.size() > 0)
873 {
874 result += "-" + toLocalString(s) + ",";
875 }
876 else
877 {
878 result += " ";
879 }
880
881 if (l.size() > 0)
882 {
883 result += " --" + toLocalString(l);
884 }
885
886 if (o.has_arg)
887 {
888 auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
889
890 if (o.has_implicit)
891 {
892 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
893 }
894 else
895 {
896 result += " " + arg;
897 }
898 }
899
900 return result;
901 }
902
903 String
format_description(const HelpOptionDetails & o,size_t start,size_t width)904 format_description
905 (
906 const HelpOptionDetails& o,
907 size_t start,
908 size_t width
909 )
910 {
911 auto desc = o.desc;
912
913 if (o.has_default)
914 {
915 desc += toLocalString(" (default: " + o.default_value + ")");
916 }
917
918 String result;
919
920 auto current = std::begin(desc);
921 auto startLine = current;
922 auto lastSpace = current;
923
924 auto size = size_t{};
925
926 while (current != std::end(desc))
927 {
928 if (*current == ' ')
929 {
930 lastSpace = current;
931 }
932
933 if (size > width)
934 {
935 if (lastSpace == startLine)
936 {
937 stringAppend(result, startLine, current + 1);
938 stringAppend(result, "\n");
939 stringAppend(result, start, ' ');
940 startLine = current + 1;
941 lastSpace = startLine;
942 }
943 else
944 {
945 stringAppend(result, startLine, lastSpace);
946 stringAppend(result, "\n");
947 stringAppend(result, start, ' ');
948 startLine = lastSpace + 1;
949 }
950 size = 0;
951 }
952 else
953 {
954 ++size;
955 }
956
957 ++current;
958 }
959
960 //append whatever is left
961 stringAppend(result, startLine, current);
962
963 return result;
964 }
965 }
966
967 OptionAdder
add_options(std::string group)968 Options::add_options(std::string group)
969 {
970 return OptionAdder(*this, std::move(group));
971 }
972
973 OptionAdder&
operator ()(const std::string & opts,const std::string & desc,std::shared_ptr<const Value> value,std::string arg_help)974 OptionAdder::operator()
975 (
976 const std::string& opts,
977 const std::string& desc,
978 std::shared_ptr<const Value> value,
979 std::string arg_help
980 )
981 {
982 std::match_results<const char*> result;
983 std::regex_match(opts.c_str(), result, option_specifier);
984
985 if (result.empty())
986 {
987 throw invalid_option_format_error(opts);
988 }
989
990 const auto& s = result[2];
991 const auto& l = result[3];
992
993 m_options.add_option(m_group, s.str(), l.str(), desc, value,
994 std::move(arg_help));
995
996 return *this;
997 }
998
999 void
parse_option(std::shared_ptr<OptionDetails> value,const std::string &,const std::string & arg)1000 Options::parse_option
1001 (
1002 std::shared_ptr<OptionDetails> value,
1003 const std::string& /*name*/,
1004 const std::string& arg
1005 )
1006 {
1007 value->parse(arg);
1008 }
1009
1010 void
checked_parse_arg(int argc,char * argv[],int & current,std::shared_ptr<OptionDetails> value,const std::string & name)1011 Options::checked_parse_arg
1012 (
1013 int argc,
1014 char* argv[],
1015 int& current,
1016 std::shared_ptr<OptionDetails> value,
1017 const std::string& name
1018 )
1019 {
1020 if (current + 1 >= argc)
1021 {
1022 if (value->value().has_implicit())
1023 {
1024 parse_option(value, name, value->value().get_implicit_value());
1025 }
1026 else
1027 {
1028 throw missing_argument_exception(name);
1029 }
1030 }
1031 else
1032 {
1033 if (argv[current + 1][0] == '-' && value->value().has_implicit())
1034 {
1035 parse_option(value, name, value->value().get_implicit_value());
1036 }
1037 else
1038 {
1039 parse_option(value, name, argv[current + 1]);
1040 ++current;
1041 }
1042 }
1043 }
1044
1045 void
add_to_option(const std::string & option,const std::string & arg)1046 Options::add_to_option(const std::string& option, const std::string& arg)
1047 {
1048 auto iter = m_options.find(option);
1049
1050 if (iter == m_options.end())
1051 {
1052 throw option_not_exists_exception(option);
1053 }
1054
1055 parse_option(iter->second, option, arg);
1056 }
1057
1058 bool
consume_positional(std::string a)1059 Options::consume_positional(std::string a)
1060 {
1061 while (m_next_positional != m_positional.end())
1062 {
1063 auto iter = m_options.find(*m_next_positional);
1064 if (iter != m_options.end())
1065 {
1066 if (!iter->second->value().is_container())
1067 {
1068 if (iter->second->count() == 0)
1069 {
1070 add_to_option(*m_next_positional, a);
1071 ++m_next_positional;
1072 return true;
1073 }
1074 else
1075 {
1076 ++m_next_positional;
1077 continue;
1078 }
1079 }
1080 else
1081 {
1082 add_to_option(*m_next_positional, a);
1083 return true;
1084 }
1085 }
1086 ++m_next_positional;
1087 }
1088
1089 return false;
1090 }
1091
1092 void
parse_positional(std::string option)1093 Options::parse_positional(std::string option)
1094 {
1095 parse_positional(std::vector<std::string>{option});
1096 }
1097
1098 void
parse_positional(std::vector<std::string> options)1099 Options::parse_positional(std::vector<std::string> options)
1100 {
1101 m_positional = std::move(options);
1102 m_next_positional = m_positional.begin();
1103
1104 m_positional_set.insert(m_positional.begin(), m_positional.end());
1105 }
1106
1107 void
parse(int & argc,char ** & argv)1108 Options::parse(int& argc, char**& argv)
1109 {
1110 int current = 1;
1111
1112 int nextKeep = 1;
1113
1114 bool consume_remaining = false;
1115
1116 while (current != argc)
1117 {
1118 if (strcmp(argv[current], "--") == 0)
1119 {
1120 consume_remaining = true;
1121 ++current;
1122 break;
1123 }
1124
1125 std::match_results<const char*> result;
1126 std::regex_match(argv[current], result, option_matcher);
1127
1128 if (result.empty())
1129 {
1130 //not a flag
1131
1132 //if true is returned here then it was consumed, otherwise it is
1133 //ignored
1134 if (consume_positional(argv[current]))
1135 {
1136 }
1137 else
1138 {
1139 argv[nextKeep] = argv[current];
1140 ++nextKeep;
1141 }
1142 //if we return from here then it was parsed successfully, so continue
1143 }
1144 else
1145 {
1146 //short or long option?
1147 if (result[4].length() != 0)
1148 {
1149 const std::string& s = result[4];
1150
1151 for (std::size_t i = 0; i != s.size(); ++i)
1152 {
1153 std::string name(1, s[i]);
1154 auto iter = m_options.find(name);
1155
1156 if (iter == m_options.end())
1157 {
1158 throw option_not_exists_exception(name);
1159 }
1160
1161 auto value = iter->second;
1162
1163 //if no argument then just add it
1164 if (!value->has_arg())
1165 {
1166 parse_option(value, name);
1167 }
1168 else
1169 {
1170 //it must be the last argument
1171 if (i + 1 == s.size())
1172 {
1173 checked_parse_arg(argc, argv, current, value, name);
1174 }
1175 else if (value->value().has_implicit())
1176 {
1177 parse_option(value, name, value->value().get_implicit_value());
1178 }
1179 else
1180 {
1181 //error
1182 throw option_requires_argument_exception(name);
1183 }
1184 }
1185 }
1186 }
1187 else if (result[1].length() != 0)
1188 {
1189 const std::string& name = result[1];
1190
1191 auto iter = m_options.find(name);
1192
1193 if (iter == m_options.end())
1194 {
1195 throw option_not_exists_exception(name);
1196 }
1197
1198 auto opt = iter->second;
1199
1200 //equals provided for long option?
1201 if (result[3].length() != 0)
1202 {
1203 //parse the option given
1204
1205 //but if it doesn't take an argument, this is an error
1206 if (!opt->has_arg())
1207 {
1208 throw option_not_has_argument_exception(name, result[3]);
1209 }
1210
1211 parse_option(opt, name, result[3]);
1212 }
1213 else
1214 {
1215 if (opt->has_arg())
1216 {
1217 //parse the next argument
1218 checked_parse_arg(argc, argv, current, opt, name);
1219 }
1220 else
1221 {
1222 //parse with empty argument
1223 parse_option(opt, name);
1224 }
1225 }
1226 }
1227
1228 }
1229
1230 ++current;
1231 }
1232
1233 for (auto& opt : m_options)
1234 {
1235 auto& detail = opt.second;
1236 auto& value = detail->value();
1237
1238 if(!detail->count() && value.has_default()){
1239 detail->parse_default();
1240 }
1241 }
1242
1243 if (consume_remaining)
1244 {
1245 while (current < argc)
1246 {
1247 if (!consume_positional(argv[current])) {
1248 break;
1249 }
1250 ++current;
1251 }
1252
1253 //adjust argv for any that couldn't be swallowed
1254 while (current != argc) {
1255 argv[nextKeep] = argv[current];
1256 ++nextKeep;
1257 ++current;
1258 }
1259 }
1260
1261 argc = nextKeep;
1262
1263 }
1264
1265 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)1266 Options::add_option
1267 (
1268 const std::string& group,
1269 const std::string& s,
1270 const std::string& l,
1271 std::string desc,
1272 std::shared_ptr<const Value> value,
1273 std::string arg_help
1274 )
1275 {
1276 auto stringDesc = toLocalString(std::move(desc));
1277 auto option = std::make_shared<OptionDetails>(stringDesc, value);
1278
1279 if (s.size() > 0)
1280 {
1281 add_one_option(s, option);
1282 }
1283
1284 if (l.size() > 0)
1285 {
1286 add_one_option(l, option);
1287 }
1288
1289 //add the help details
1290 auto& options = m_help[group];
1291
1292 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
1293 value->has_arg(),
1294 value->has_default(), value->get_default_value(),
1295 value->has_implicit(), value->get_implicit_value(),
1296 std::move(arg_help),
1297 value->is_container()});
1298 }
1299
1300 void
add_one_option(const std::string & option,std::shared_ptr<OptionDetails> details)1301 Options::add_one_option
1302 (
1303 const std::string& option,
1304 std::shared_ptr<OptionDetails> details
1305 )
1306 {
1307 auto in = m_options.emplace(option, details);
1308
1309 if (!in.second)
1310 {
1311 throw option_exists_error(option);
1312 }
1313 }
1314
1315 String
help_one_group(const std::string & g) const1316 Options::help_one_group(const std::string& g) const
1317 {
1318 typedef std::vector<std::pair<String, String>> OptionHelp;
1319
1320 auto group = m_help.find(g);
1321 if (group == m_help.end())
1322 {
1323 return "";
1324 }
1325
1326 OptionHelp format;
1327
1328 size_t longest = 0;
1329
1330 String result;
1331
1332 if (!g.empty())
1333 {
1334 result += toLocalString(" " + g + " options:\n");
1335 }
1336
1337 for (const auto& o : group->second.options)
1338 {
1339 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
1340 {
1341 continue;
1342 }
1343
1344 auto s = format_option(o);
1345 longest = std::max(longest, stringLength(s));
1346 format.push_back(std::make_pair(s, String()));
1347 }
1348
1349 longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
1350
1351 //widest allowed description
1352 auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
1353
1354 auto fiter = format.begin();
1355 for (const auto& o : group->second.options)
1356 {
1357 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
1358 {
1359 continue;
1360 }
1361
1362 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
1363
1364 result += fiter->first;
1365 if (stringLength(fiter->first) > longest)
1366 {
1367 result += '\n';
1368 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
1369 }
1370 else
1371 {
1372 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
1373 stringLength(fiter->first),
1374 ' '));
1375 }
1376 result += d;
1377 result += '\n';
1378
1379 ++fiter;
1380 }
1381
1382 return result;
1383 }
1384
1385 void
generate_group_help(String & result,const std::vector<std::string> & groups) const1386 Options::generate_group_help(String& result, const std::vector<std::string>& groups) const
1387 {
1388 for (std::size_t i = 0; i < groups.size(); ++i)
1389 {
1390 String const& group_help = help_one_group(groups[i]);
1391 if (empty(group_help)) continue;
1392 result += group_help;
1393 if (i < groups.size() - 1)
1394 {
1395 result += '\n';
1396 }
1397 }
1398 }
1399
1400 void
generate_all_groups_help(String & result) const1401 Options::generate_all_groups_help(String& result) const
1402 {
1403 std::vector<std::string> groups;
1404 groups.reserve(m_help.size());
1405
1406 for (auto& group : m_help)
1407 {
1408 groups.push_back(group.first);
1409 }
1410
1411 generate_group_help(result, groups);
1412 }
1413
1414 std::string
help(const std::vector<std::string> & groups) const1415 Options::help(const std::vector<std::string>& groups) const
1416 {
1417 String result = m_help_string + "\nUsage:\n " +
1418 toLocalString(m_program) + " [OPTION...]";
1419
1420 if (m_positional.size() > 0) {
1421 result += " " + toLocalString(m_positional_help);
1422 }
1423
1424 result += "\n\n";
1425
1426 if (groups.size() == 0)
1427 {
1428 generate_all_groups_help(result);
1429 }
1430 else
1431 {
1432 generate_group_help(result, groups);
1433 }
1434
1435 return toUTF8String(result);
1436 }
1437
1438 const std::vector<std::string>
groups() const1439 Options::groups() const
1440 {
1441 std::vector<std::string> g;
1442
1443 std::transform(
1444 m_help.begin(),
1445 m_help.end(),
1446 std::back_inserter(g),
1447 [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
1448 {
1449 return pair.first;
1450 }
1451 );
1452
1453 return g;
1454 }
1455
1456 const HelpGroupDetails&
group_help(const std::string & group) const1457 Options::group_help(const std::string& group) const
1458 {
1459 return m_help.at(group);
1460 }
1461
1462 }
1463
1464 #if defined(__GNU__)
1465 #pragma GCC diagnostic pop
1466 #endif
1467
1468 #endif //CXX_OPTS_HPP
1469