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