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