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