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 <list>
34 #include <map>
35 #include <memory>
36 #include <sstream>
37 #include <string>
38 #include <unordered_map>
39 #include <unordered_set>
40 #include <utility>
41 #include <vector>
42 #include <algorithm>
43 
44 #if defined(__GNUC__) && !defined(__clang__)
45 #  if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
46 #    define CXXOPTS_NO_REGEX true
47 #  endif
48 #endif
49 
50 #ifndef CXXOPTS_NO_REGEX
51 #  include <regex>
52 #endif  // CXXOPTS_NO_REGEX
53 
54 #ifdef __cpp_lib_optional
55 #include <optional>
56 #define CXXOPTS_HAS_OPTIONAL
57 #endif
58 
59 #if __cplusplus >= 201603L
60 #define CXXOPTS_NODISCARD [[nodiscard]]
61 #else
62 #define CXXOPTS_NODISCARD
63 #endif
64 
65 #ifndef CXXOPTS_VECTOR_DELIMITER
66 #define CXXOPTS_VECTOR_DELIMITER ','
67 #endif
68 
69 #define CXXOPTS__VERSION_MAJOR 3
70 #define CXXOPTS__VERSION_MINOR 0
71 #define CXXOPTS__VERSION_PATCH 0
72 
73 #if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
74   #define CXXOPTS_NULL_DEREF_IGNORE
75 #endif
76 
77 namespace cxxopts
78 {
79   static constexpr struct {
80     uint8_t major, minor, patch;
81   } version = {
82     CXXOPTS__VERSION_MAJOR,
83     CXXOPTS__VERSION_MINOR,
84     CXXOPTS__VERSION_PATCH
85   };
86 } // namespace cxxopts
87 
88 //when we ask cxxopts to use Unicode, help strings are processed using ICU,
89 //which results in the correct lengths being computed for strings when they
90 //are formatted for the help output
91 //it is necessary to make sure that <unicode/unistr.h> can be found by the
92 //compiler, and that icu-uc is linked in to the binary.
93 
94 #ifdef CXXOPTS_USE_UNICODE
95 #include <unicode/unistr.h>
96 
97 namespace cxxopts
98 {
99   using String = icu::UnicodeString;
100 
101   inline
102   String
toLocalString(std::string s)103   toLocalString(std::string s)
104   {
105     return icu::UnicodeString::fromUTF8(std::move(s));
106   }
107 
108 #if defined(__GNUC__)
109 // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
110 // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
111 #pragma GCC diagnostic push
112 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
113 #pragma GCC diagnostic ignored "-Weffc++"
114 // This will be ignored under other compilers like LLVM clang.
115 #endif
116   class UnicodeStringIterator : public
117     std::iterator<std::forward_iterator_tag, int32_t>
118   {
119     public:
120 
UnicodeStringIterator(const icu::UnicodeString * string,int32_t pos)121     UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
122     : s(string)
123     , i(pos)
124     {
125     }
126 
127     value_type
operator *() const128     operator*() const
129     {
130       return s->char32At(i);
131     }
132 
133     bool
operator ==(const UnicodeStringIterator & rhs) const134     operator==(const UnicodeStringIterator& rhs) const
135     {
136       return s == rhs.s && i == rhs.i;
137     }
138 
139     bool
operator !=(const UnicodeStringIterator & rhs) const140     operator!=(const UnicodeStringIterator& rhs) const
141     {
142       return !(*this == rhs);
143     }
144 
145     UnicodeStringIterator&
operator ++()146     operator++()
147     {
148       ++i;
149       return *this;
150     }
151 
152     UnicodeStringIterator
operator +(int32_t v)153     operator+(int32_t v)
154     {
155       return UnicodeStringIterator(s, i + v);
156     }
157 
158     private:
159     const icu::UnicodeString* s;
160     int32_t i;
161   };
162 #if defined(__GNUC__)
163 #pragma GCC diagnostic pop
164 #endif
165 
166   inline
167   String&
stringAppend(String & s,String a)168   stringAppend(String&s, String a)
169   {
170     return s.append(std::move(a));
171   }
172 
173   inline
174   String&
stringAppend(String & s,size_t n,UChar32 c)175   stringAppend(String& s, size_t n, UChar32 c)
176   {
177     for (size_t i = 0; i != n; ++i)
178     {
179       s.append(c);
180     }
181 
182     return s;
183   }
184 
185   template <typename Iterator>
186   String&
stringAppend(String & s,Iterator begin,Iterator end)187   stringAppend(String& s, Iterator begin, Iterator end)
188   {
189     while (begin != end)
190     {
191       s.append(*begin);
192       ++begin;
193     }
194 
195     return s;
196   }
197 
198   inline
199   size_t
stringLength(const String & s)200   stringLength(const String& s)
201   {
202     return s.length();
203   }
204 
205   inline
206   std::string
toUTF8String(const String & s)207   toUTF8String(const String& s)
208   {
209     std::string result;
210     s.toUTF8String(result);
211 
212     return result;
213   }
214 
215   inline
216   bool
empty(const String & s)217   empty(const String& s)
218   {
219     return s.isEmpty();
220   }
221 }
222 
223 namespace std
224 {
225   inline
226   cxxopts::UnicodeStringIterator
begin(const icu::UnicodeString & s)227   begin(const icu::UnicodeString& s)
228   {
229     return cxxopts::UnicodeStringIterator(&s, 0);
230   }
231 
232   inline
233   cxxopts::UnicodeStringIterator
end(const icu::UnicodeString & s)234   end(const icu::UnicodeString& s)
235   {
236     return cxxopts::UnicodeStringIterator(&s, s.length());
237   }
238 }
239 
240 //ifdef CXXOPTS_USE_UNICODE
241 #else
242 
243 namespace cxxopts
244 {
245   using String = std::string;
246 
247   template <typename T>
248   T
toLocalString(T && t)249   toLocalString(T&& t)
250   {
251     return std::forward<T>(t);
252   }
253 
254   inline
255   size_t
stringLength(const String & s)256   stringLength(const String& s)
257   {
258     return s.length();
259   }
260 
261   inline
262   String&
stringAppend(String & s,const String & a)263   stringAppend(String&s, const String& a)
264   {
265     return s.append(a);
266   }
267 
268   inline
269   String&
stringAppend(String & s,size_t n,char c)270   stringAppend(String& s, size_t n, char c)
271   {
272     return s.append(n, c);
273   }
274 
275   template <typename Iterator>
276   String&
stringAppend(String & s,Iterator begin,Iterator end)277   stringAppend(String& s, Iterator begin, Iterator end)
278   {
279     return s.append(begin, end);
280   }
281 
282   template <typename T>
283   std::string
toUTF8String(T && t)284   toUTF8String(T&& t)
285   {
286     return std::forward<T>(t);
287   }
288 
289   inline
290   bool
empty(const std::string & s)291   empty(const std::string& s)
292   {
293     return s.empty();
294   }
295 } // namespace cxxopts
296 
297 //ifdef CXXOPTS_USE_UNICODE
298 #endif
299 
300 namespace cxxopts
301 {
302   namespace
303   {
304 #ifdef _WIN32
305     const std::string LQUOTE("\'");
306     const std::string RQUOTE("\'");
307 #else
308     const std::string LQUOTE("‘");
309     const std::string RQUOTE("’");
310 #endif
311   } // namespace
312 
313 #if defined(__GNUC__)
314 // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
315 // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
316 #pragma GCC diagnostic push
317 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
318 #pragma GCC diagnostic ignored "-Weffc++"
319 // This will be ignored under other compilers like LLVM clang.
320 #endif
321   class Value : public std::enable_shared_from_this<Value>
322   {
323     public:
324 
325     virtual ~Value() = default;
326 
327     virtual
328     std::shared_ptr<Value>
329     clone() const = 0;
330 
331     virtual void
332     parse(const std::string& text) const = 0;
333 
334     virtual void
335     parse() const = 0;
336 
337     virtual bool
338     has_default() const = 0;
339 
340     virtual bool
341     is_container() const = 0;
342 
343     virtual bool
344     has_implicit() const = 0;
345 
346     virtual std::string
347     get_default_value() const = 0;
348 
349     virtual std::string
350     get_implicit_value() const = 0;
351 
352     virtual std::shared_ptr<Value>
353     default_value(const std::string& value) = 0;
354 
355     virtual std::shared_ptr<Value>
356     implicit_value(const std::string& value) = 0;
357 
358     virtual std::shared_ptr<Value>
359     no_implicit_value() = 0;
360 
361     virtual bool
362     is_boolean() const = 0;
363   };
364 #if defined(__GNUC__)
365 #pragma GCC diagnostic pop
366 #endif
367   class OptionException : public std::exception
368   {
369     public:
OptionException(std::string message)370     explicit OptionException(std::string  message)
371     : m_message(std::move(message))
372     {
373     }
374 
375     CXXOPTS_NODISCARD
376     const char*
what() const377     what() const noexcept override
378     {
379       return m_message.c_str();
380     }
381 
382     private:
383     std::string m_message;
384   };
385 
386   class OptionSpecException : public OptionException
387   {
388     public:
389 
OptionSpecException(const std::string & message)390     explicit OptionSpecException(const std::string& message)
391     : OptionException(message)
392     {
393     }
394   };
395 
396   class OptionParseException : public OptionException
397   {
398     public:
OptionParseException(const std::string & message)399     explicit OptionParseException(const std::string& message)
400     : OptionException(message)
401     {
402     }
403   };
404 
405   class option_exists_error : public OptionSpecException
406   {
407     public:
option_exists_error(const std::string & option)408     explicit option_exists_error(const std::string& option)
409     : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
410     {
411     }
412   };
413 
414   class invalid_option_format_error : public OptionSpecException
415   {
416     public:
invalid_option_format_error(const std::string & format)417     explicit invalid_option_format_error(const std::string& format)
418     : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
419     {
420     }
421   };
422 
423   class option_syntax_exception : public OptionParseException {
424     public:
option_syntax_exception(const std::string & text)425     explicit option_syntax_exception(const std::string& text)
426     : OptionParseException("Argument " + LQUOTE + text + RQUOTE +
427         " starts with a - but has incorrect syntax")
428     {
429     }
430   };
431 
432   class option_not_exists_exception : public OptionParseException
433   {
434     public:
option_not_exists_exception(const std::string & option)435     explicit option_not_exists_exception(const std::string& option)
436     : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
437     {
438     }
439   };
440 
441   class missing_argument_exception : public OptionParseException
442   {
443     public:
missing_argument_exception(const std::string & option)444     explicit missing_argument_exception(const std::string& option)
445     : OptionParseException(
446         "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
447       )
448     {
449     }
450   };
451 
452   class option_requires_argument_exception : public OptionParseException
453   {
454     public:
option_requires_argument_exception(const std::string & option)455     explicit option_requires_argument_exception(const std::string& option)
456     : OptionParseException(
457         "Option " + LQUOTE + option + RQUOTE + " requires an argument"
458       )
459     {
460     }
461   };
462 
463   class option_not_has_argument_exception : public OptionParseException
464   {
465     public:
option_not_has_argument_exception(const std::string & option,const std::string & arg)466     option_not_has_argument_exception
467     (
468       const std::string& option,
469       const std::string& arg
470     )
471     : OptionParseException(
472         "Option " + LQUOTE + option + RQUOTE +
473         " does not take an argument, but argument " +
474         LQUOTE + arg + RQUOTE + " given"
475       )
476     {
477     }
478   };
479 
480   class option_not_present_exception : public OptionParseException
481   {
482     public:
option_not_present_exception(const std::string & option)483     explicit option_not_present_exception(const std::string& option)
484     : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
485     {
486     }
487   };
488 
489   class option_has_no_value_exception : public OptionException
490   {
491     public:
option_has_no_value_exception(const std::string & option)492     explicit option_has_no_value_exception(const std::string& option)
493     : OptionException(
494         !option.empty() ?
495         ("Option " + LQUOTE + option + RQUOTE + " has no value") :
496         "Option has no value")
497     {
498     }
499   };
500 
501   class argument_incorrect_type : public OptionParseException
502   {
503     public:
argument_incorrect_type(const std::string & arg)504     explicit argument_incorrect_type
505     (
506       const std::string& arg
507     )
508     : OptionParseException(
509         "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
510       )
511     {
512     }
513   };
514 
515   class option_required_exception : public OptionParseException
516   {
517     public:
option_required_exception(const std::string & option)518     explicit option_required_exception(const std::string& option)
519     : OptionParseException(
520         "Option " + LQUOTE + option + RQUOTE + " is required but not present"
521       )
522     {
523     }
524   };
525 
526   template <typename T>
throw_or_mimic(const std::string & text)527   void throw_or_mimic(const std::string& text)
528   {
529     static_assert(std::is_base_of<std::exception, T>::value,
530                   "throw_or_mimic only works on std::exception and "
531                   "deriving classes");
532 
533 #ifndef CXXOPTS_NO_EXCEPTIONS
534     // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
535     throw T{text};
536 #else
537     // Otherwise manually instantiate the exception, print what() to stderr,
538     // and exit
539     T exception{text};
540     std::cerr << exception.what() << std::endl;
541     std::exit(EXIT_FAILURE);
542 #endif
543   }
544 
545   namespace values
546   {
547     namespace parser_tool
548     {
549       struct IntegerDesc
550       {
551         std::string negative = "";
552         std::string base     = "";
553         std::string value    = "";
554       };
555       struct ArguDesc {
556         std::string arg_name  = "";
557         bool        grouping  = false;
558         bool        set_value = false;
559         std::string value     = "";
560       };
561 #ifdef CXXOPTS_NO_REGEX
SplitInteger(const std::string & text)562       inline IntegerDesc SplitInteger(const std::string &text)
563       {
564         if (text.empty())
565         {
566           throw_or_mimic<argument_incorrect_type>(text);
567         }
568         IntegerDesc desc;
569         const char *pdata = text.c_str();
570         if (*pdata == '-')
571         {
572           pdata += 1;
573           desc.negative = "-";
574         }
575         if (strncmp(pdata, "0x", 2) == 0)
576         {
577           pdata += 2;
578           desc.base = "0x";
579         }
580         if (*pdata != '\0')
581         {
582           desc.value = std::string(pdata);
583         }
584         else
585         {
586           throw_or_mimic<argument_incorrect_type>(text);
587         }
588         return desc;
589       }
590 
IsTrueText(const std::string & text)591       inline bool IsTrueText(const std::string &text)
592       {
593         const char *pdata = text.c_str();
594         if (*pdata == 't' || *pdata == 'T')
595         {
596           pdata += 1;
597           if (strncmp(pdata, "rue\0", 4) == 0)
598           {
599             return true;
600           }
601         }
602         else if (strncmp(pdata, "1\0", 2) == 0)
603         {
604           return true;
605         }
606         return false;
607       }
608 
IsFalseText(const std::string & text)609       inline bool IsFalseText(const std::string &text)
610       {
611         const char *pdata = text.c_str();
612         if (*pdata == 'f' || *pdata == 'F')
613         {
614           pdata += 1;
615           if (strncmp(pdata, "alse\0", 5) == 0)
616           {
617             return true;
618           }
619         }
620         else if (strncmp(pdata, "0\0", 2) == 0)
621         {
622           return true;
623         }
624         return false;
625       }
626 
SplitSwitchDef(const std::string & text)627       inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
628       {
629         std::string short_sw, long_sw;
630         const char *pdata = text.c_str();
631         if (isalnum(*pdata) && *(pdata + 1) == ',') {
632           short_sw = std::string(1, *pdata);
633           pdata += 2;
634         }
635         while (*pdata == ' ') { pdata += 1; }
636         if (isalnum(*pdata)) {
637           const char *store = pdata;
638           pdata += 1;
639           while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') {
640             pdata += 1;
641           }
642           if (*pdata == '\0') {
643             long_sw = std::string(store, pdata - store);
644           } else {
645             throw_or_mimic<invalid_option_format_error>(text);
646           }
647         }
648         return std::pair<std::string, std::string>(short_sw, long_sw);
649       }
650 
ParseArgument(const char * arg,bool & matched)651       inline ArguDesc ParseArgument(const char *arg, bool &matched)
652       {
653         ArguDesc argu_desc;
654         const char *pdata = arg;
655         matched = false;
656         if (strncmp(pdata, "--", 2) == 0)
657         {
658           pdata += 2;
659           if (isalnum(*pdata))
660           {
661             argu_desc.arg_name.push_back(*pdata);
662             pdata += 1;
663             while (isalnum(*pdata) || *pdata == '-' || *pdata == '_')
664             {
665               argu_desc.arg_name.push_back(*pdata);
666               pdata += 1;
667             }
668             if (argu_desc.arg_name.length() > 1)
669             {
670               if (*pdata == '=')
671               {
672                 argu_desc.set_value = true;
673                 pdata += 1;
674                 if (*pdata != '\0')
675                 {
676                   argu_desc.value = std::string(pdata);
677                 }
678                 matched = true;
679               }
680               else if (*pdata == '\0')
681               {
682                 matched = true;
683               }
684             }
685           }
686         }
687         else if (strncmp(pdata, "-", 1) == 0)
688         {
689           pdata += 1;
690           argu_desc.grouping = true;
691           while (isalnum(*pdata))
692           {
693             argu_desc.arg_name.push_back(*pdata);
694             pdata += 1;
695           }
696           matched = !argu_desc.arg_name.empty() && *pdata == '\0';
697         }
698         return argu_desc;
699       }
700 
701 #else  // CXXOPTS_NO_REGEX
702 
703       namespace
704       {
705 
706         std::basic_regex<char> integer_pattern
707           ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
708         std::basic_regex<char> truthy_pattern
709           ("(t|T)(rue)?|1");
710         std::basic_regex<char> falsy_pattern
711           ("(f|F)(alse)?|0");
712 
713         std::basic_regex<char> option_matcher
714           ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
715         std::basic_regex<char> option_specifier
716           ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
717 
718       } // namespace
719 
SplitInteger(const std::string & text)720       inline IntegerDesc SplitInteger(const std::string &text)
721       {
722         std::smatch match;
723         std::regex_match(text, match, integer_pattern);
724 
725         if (match.length() == 0)
726         {
727           throw_or_mimic<argument_incorrect_type>(text);
728         }
729 
730         IntegerDesc desc;
731         desc.negative = match[1];
732         desc.base = match[2];
733         desc.value = match[3];
734 
735         if (match.length(4) > 0)
736         {
737           desc.base = match[5];
738           desc.value = "0";
739           return desc;
740         }
741 
742         return desc;
743       }
744 
IsTrueText(const std::string & text)745       inline bool IsTrueText(const std::string &text)
746       {
747         std::smatch result;
748         std::regex_match(text, result, truthy_pattern);
749         return !result.empty();
750       }
751 
IsFalseText(const std::string & text)752       inline bool IsFalseText(const std::string &text)
753       {
754         std::smatch result;
755         std::regex_match(text, result, falsy_pattern);
756         return !result.empty();
757       }
758 
SplitSwitchDef(const std::string & text)759       inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
760       {
761         std::match_results<const char*> result;
762         std::regex_match(text.c_str(), result, option_specifier);
763         if (result.empty())
764         {
765           throw_or_mimic<invalid_option_format_error>(text);
766         }
767 
768         const std::string& short_sw = result[2];
769         const std::string& long_sw = result[3];
770 
771         return std::pair<std::string, std::string>(short_sw, long_sw);
772       }
773 
ParseArgument(const char * arg,bool & matched)774       inline ArguDesc ParseArgument(const char *arg, bool &matched)
775       {
776         std::match_results<const char*> result;
777         std::regex_match(arg, result, option_matcher);
778         matched = !result.empty();
779 
780         ArguDesc argu_desc;
781         if (matched) {
782           argu_desc.arg_name = result[1].str();
783           argu_desc.set_value = result[2].length() > 0;
784           argu_desc.value = result[3].str();
785           if (result[4].length() > 0)
786           {
787             argu_desc.grouping = true;
788             argu_desc.arg_name = result[4].str();
789           }
790         }
791 
792         return argu_desc;
793       }
794 
795 #endif  // CXXOPTS_NO_REGEX
796 #undef CXXOPTS_NO_REGEX
797   }
798 
799     namespace detail
800     {
801       template <typename T, bool B>
802       struct SignedCheck;
803 
804       template <typename T>
805       struct SignedCheck<T, true>
806       {
807         template <typename U>
808         void
operator ()cxxopts::values::detail::SignedCheck809         operator()(bool negative, U u, const std::string& text)
810         {
811           if (negative)
812           {
813             if (u > static_cast<U>((std::numeric_limits<T>::min)()))
814             {
815               throw_or_mimic<argument_incorrect_type>(text);
816             }
817           }
818           else
819           {
820             if (u > static_cast<U>((std::numeric_limits<T>::max)()))
821             {
822               throw_or_mimic<argument_incorrect_type>(text);
823             }
824           }
825         }
826       };
827 
828       template <typename T>
829       struct SignedCheck<T, false>
830       {
831         template <typename U>
832         void
operator ()cxxopts::values::detail::SignedCheck833         operator()(bool, U, const std::string&) const {}
834       };
835 
836       template <typename T, typename U>
837       void
check_signed_range(bool negative,U value,const std::string & text)838       check_signed_range(bool negative, U value, const std::string& text)
839       {
840         SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
841       }
842     } // namespace detail
843 
844     template <typename R, typename T>
845     void
checked_negate(R & r,T && t,const std::string &,std::true_type)846     checked_negate(R& r, T&& t, const std::string&, std::true_type)
847     {
848       // if we got to here, then `t` is a positive number that fits into
849       // `R`. So to avoid MSVC C4146, we first cast it to `R`.
850       // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
851       r = static_cast<R>(-static_cast<R>(t-1)-1);
852     }
853 
854     template <typename R, typename T>
855     void
checked_negate(R &,T &&,const std::string & text,std::false_type)856     checked_negate(R&, T&&, const std::string& text, std::false_type)
857     {
858       throw_or_mimic<argument_incorrect_type>(text);
859     }
860 
861     template <typename T>
862     void
integer_parser(const std::string & text,T & value)863     integer_parser(const std::string& text, T& value)
864     {
865       parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
866 
867       using US = typename std::make_unsigned<T>::type;
868       constexpr bool is_signed = std::numeric_limits<T>::is_signed;
869 
870       const bool          negative    = int_desc.negative.length() > 0;
871       const uint8_t       base        = int_desc.base.length() > 0 ? 16 : 10;
872       const std::string & value_match = int_desc.value;
873 
874       US result = 0;
875 
876       for (char ch : value_match)
877       {
878         US digit = 0;
879 
880         if (ch >= '0' && ch <= '9')
881         {
882           digit = static_cast<US>(ch - '0');
883         }
884         else if (base == 16 && ch >= 'a' && ch <= 'f')
885         {
886           digit = static_cast<US>(ch - 'a' + 10);
887         }
888         else if (base == 16 && ch >= 'A' && ch <= 'F')
889         {
890           digit = static_cast<US>(ch - 'A' + 10);
891         }
892         else
893         {
894           throw_or_mimic<argument_incorrect_type>(text);
895         }
896 
897         const US next = static_cast<US>(result * base + digit);
898         if (result > next)
899         {
900           throw_or_mimic<argument_incorrect_type>(text);
901         }
902 
903         result = next;
904       }
905 
906       detail::check_signed_range<T>(negative, result, text);
907 
908       if (negative)
909       {
910         checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
911       }
912       else
913       {
914         value = static_cast<T>(result);
915       }
916     }
917 
918     template <typename T>
stringstream_parser(const std::string & text,T & value)919     void stringstream_parser(const std::string& text, T& value)
920     {
921       std::stringstream in(text);
922       in >> value;
923       if (!in) {
924         throw_or_mimic<argument_incorrect_type>(text);
925       }
926     }
927 
928     template <typename T,
929              typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
930              >
parse_value(const std::string & text,T & value)931     void parse_value(const std::string& text, T& value)
932     {
933         integer_parser(text, value);
934     }
935 
936     inline
937     void
parse_value(const std::string & text,bool & value)938     parse_value(const std::string& text, bool& value)
939     {
940       if (parser_tool::IsTrueText(text))
941       {
942         value = true;
943         return;
944       }
945 
946       if (parser_tool::IsFalseText(text))
947       {
948         value = false;
949         return;
950       }
951 
952       throw_or_mimic<argument_incorrect_type>(text);
953     }
954 
955     inline
956     void
parse_value(const std::string & text,std::string & value)957     parse_value(const std::string& text, std::string& value)
958     {
959       value = text;
960     }
961 
962     // The fallback parser. It uses the stringstream parser to parse all types
963     // that have not been overloaded explicitly.  It has to be placed in the
964     // source code before all other more specialized templates.
965     template <typename T,
966              typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
967              >
968     void
parse_value(const std::string & text,T & value)969     parse_value(const std::string& text, T& value) {
970       stringstream_parser(text, value);
971     }
972 
973     template <typename T>
974     void
parse_value(const std::string & text,std::vector<T> & value)975     parse_value(const std::string& text, std::vector<T>& value)
976     {
977       std::stringstream in(text);
978       std::string token;
979       while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
980         T v;
981         parse_value(token, v);
982         value.emplace_back(std::move(v));
983       }
984     }
985 
986 #ifdef CXXOPTS_HAS_OPTIONAL
987     template <typename T>
988     void
parse_value(const std::string & text,std::optional<T> & value)989     parse_value(const std::string& text, std::optional<T>& value)
990     {
991       T result;
992       parse_value(text, result);
993       value = std::move(result);
994     }
995 #endif
996 
997     inline
parse_value(const std::string & text,char & c)998     void parse_value(const std::string& text, char& c)
999     {
1000       if (text.length() != 1)
1001       {
1002         throw_or_mimic<argument_incorrect_type>(text);
1003       }
1004 
1005       c = text[0];
1006     }
1007 
1008     template <typename T>
1009     struct type_is_container
1010     {
1011       static constexpr bool value = false;
1012     };
1013 
1014     template <typename T>
1015     struct type_is_container<std::vector<T>>
1016     {
1017       static constexpr bool value = true;
1018     };
1019 
1020     template <typename T>
1021     class abstract_value : public Value
1022     {
1023       using Self = abstract_value<T>;
1024 
1025       public:
abstract_value()1026       abstract_value()
1027       : m_result(std::make_shared<T>())
1028       , m_store(m_result.get())
1029       {
1030       }
1031 
abstract_value(T * t)1032       explicit abstract_value(T* t)
1033       : m_store(t)
1034       {
1035       }
1036 
1037       ~abstract_value() override = default;
1038 
1039       abstract_value& operator=(const abstract_value&) = default;
1040 
abstract_value(const abstract_value & rhs)1041       abstract_value(const abstract_value& rhs)
1042       {
1043         if (rhs.m_result)
1044         {
1045           m_result = std::make_shared<T>();
1046           m_store = m_result.get();
1047         }
1048         else
1049         {
1050           m_store = rhs.m_store;
1051         }
1052 
1053         m_default = rhs.m_default;
1054         m_implicit = rhs.m_implicit;
1055         m_default_value = rhs.m_default_value;
1056         m_implicit_value = rhs.m_implicit_value;
1057       }
1058 
1059       void
parse(const std::string & text) const1060       parse(const std::string& text) const override
1061       {
1062         parse_value(text, *m_store);
1063       }
1064 
1065       bool
is_container() const1066       is_container() const override
1067       {
1068         return type_is_container<T>::value;
1069       }
1070 
1071       void
parse() const1072       parse() const override
1073       {
1074         parse_value(m_default_value, *m_store);
1075       }
1076 
1077       bool
has_default() const1078       has_default() const override
1079       {
1080         return m_default;
1081       }
1082 
1083       bool
has_implicit() const1084       has_implicit() const override
1085       {
1086         return m_implicit;
1087       }
1088 
1089       std::shared_ptr<Value>
default_value(const std::string & value)1090       default_value(const std::string& value) override
1091       {
1092         m_default = true;
1093         m_default_value = value;
1094         return shared_from_this();
1095       }
1096 
1097       std::shared_ptr<Value>
implicit_value(const std::string & value)1098       implicit_value(const std::string& value) override
1099       {
1100         m_implicit = true;
1101         m_implicit_value = value;
1102         return shared_from_this();
1103       }
1104 
1105       std::shared_ptr<Value>
no_implicit_value()1106       no_implicit_value() override
1107       {
1108         m_implicit = false;
1109         return shared_from_this();
1110       }
1111 
1112       std::string
get_default_value() const1113       get_default_value() const override
1114       {
1115         return m_default_value;
1116       }
1117 
1118       std::string
get_implicit_value() const1119       get_implicit_value() const override
1120       {
1121         return m_implicit_value;
1122       }
1123 
1124       bool
is_boolean() const1125       is_boolean() const override
1126       {
1127         return std::is_same<T, bool>::value;
1128       }
1129 
1130       const T&
get() const1131       get() const
1132       {
1133         if (m_store == nullptr)
1134         {
1135           return *m_result;
1136         }
1137         return *m_store;
1138       }
1139 
1140       protected:
1141       std::shared_ptr<T> m_result{};
1142       T* m_store{};
1143 
1144       bool m_default = false;
1145       bool m_implicit = false;
1146 
1147       std::string m_default_value{};
1148       std::string m_implicit_value{};
1149     };
1150 
1151     template <typename T>
1152     class standard_value : public abstract_value<T>
1153     {
1154       public:
1155       using abstract_value<T>::abstract_value;
1156 
1157       CXXOPTS_NODISCARD
1158       std::shared_ptr<Value>
clone() const1159       clone() const override
1160       {
1161         return std::make_shared<standard_value<T>>(*this);
1162       }
1163     };
1164 
1165     template <>
1166     class standard_value<bool> : public abstract_value<bool>
1167     {
1168       public:
1169       ~standard_value() override = default;
1170 
standard_value()1171       standard_value()
1172       {
1173         set_default_and_implicit();
1174       }
1175 
standard_value(bool * b)1176       explicit standard_value(bool* b)
1177       : abstract_value(b)
1178       {
1179         set_default_and_implicit();
1180       }
1181 
1182       std::shared_ptr<Value>
clone() const1183       clone() const override
1184       {
1185         return std::make_shared<standard_value<bool>>(*this);
1186       }
1187 
1188       private:
1189 
1190       void
set_default_and_implicit()1191       set_default_and_implicit()
1192       {
1193         m_default = true;
1194         m_default_value = "false";
1195         m_implicit = true;
1196         m_implicit_value = "true";
1197       }
1198     };
1199   } // namespace values
1200 
1201   template <typename T>
1202   std::shared_ptr<Value>
value()1203   value()
1204   {
1205     return std::make_shared<values::standard_value<T>>();
1206   }
1207 
1208   template <typename T>
1209   std::shared_ptr<Value>
value(T & t)1210   value(T& t)
1211   {
1212     return std::make_shared<values::standard_value<T>>(&t);
1213   }
1214 
1215   class OptionAdder;
1216 
1217   class OptionDetails
1218   {
1219     public:
OptionDetails(std::string short_,std::string long_,String desc,std::shared_ptr<const Value> val)1220     OptionDetails
1221     (
1222       std::string short_,
1223       std::string long_,
1224       String desc,
1225       std::shared_ptr<const Value> val
1226     )
1227     : m_short(std::move(short_))
1228     , m_long(std::move(long_))
1229     , m_desc(std::move(desc))
1230     , m_value(std::move(val))
1231     , m_count(0)
1232     {
1233       m_hash = std::hash<std::string>{}(m_long + m_short);
1234     }
1235 
OptionDetails(const OptionDetails & rhs)1236     OptionDetails(const OptionDetails& rhs)
1237     : m_desc(rhs.m_desc)
1238     , m_value(rhs.m_value->clone())
1239     , m_count(rhs.m_count)
1240     {
1241     }
1242 
1243     OptionDetails(OptionDetails&& rhs) = default;
1244 
1245     CXXOPTS_NODISCARD
1246     const String&
description() const1247     description() const
1248     {
1249       return m_desc;
1250     }
1251 
1252     CXXOPTS_NODISCARD
1253     const Value&
value() const1254     value() const {
1255         return *m_value;
1256     }
1257 
1258     CXXOPTS_NODISCARD
1259     std::shared_ptr<Value>
make_storage() const1260     make_storage() const
1261     {
1262       return m_value->clone();
1263     }
1264 
1265     CXXOPTS_NODISCARD
1266     const std::string&
short_name() const1267     short_name() const
1268     {
1269       return m_short;
1270     }
1271 
1272     CXXOPTS_NODISCARD
1273     const std::string&
long_name() const1274     long_name() const
1275     {
1276       return m_long;
1277     }
1278 
1279     size_t
hash() const1280     hash() const
1281     {
1282       return m_hash;
1283     }
1284 
1285     private:
1286     std::string m_short{};
1287     std::string m_long{};
1288     String m_desc{};
1289     std::shared_ptr<const Value> m_value{};
1290     int m_count;
1291 
1292     size_t m_hash{};
1293   };
1294 
1295   struct HelpOptionDetails
1296   {
1297     std::string s;
1298     std::string l;
1299     String desc;
1300     bool has_default;
1301     std::string default_value;
1302     bool has_implicit;
1303     std::string implicit_value;
1304     std::string arg_help;
1305     bool is_container;
1306     bool is_boolean;
1307   };
1308 
1309   struct HelpGroupDetails
1310   {
1311     std::string name{};
1312     std::string description{};
1313     std::vector<HelpOptionDetails> options{};
1314   };
1315 
1316   class OptionValue
1317   {
1318     public:
1319     void
parse(const std::shared_ptr<const OptionDetails> & details,const std::string & text)1320     parse
1321     (
1322       const std::shared_ptr<const OptionDetails>& details,
1323       const std::string& text
1324     )
1325     {
1326       ensure_value(details);
1327       ++m_count;
1328       m_value->parse(text);
1329       m_long_name = &details->long_name();
1330     }
1331 
1332     void
parse_default(const std::shared_ptr<const OptionDetails> & details)1333     parse_default(const std::shared_ptr<const OptionDetails>& details)
1334     {
1335       ensure_value(details);
1336       m_default = true;
1337       m_long_name = &details->long_name();
1338       m_value->parse();
1339     }
1340 
1341     void
parse_no_value(const std::shared_ptr<const OptionDetails> & details)1342     parse_no_value(const std::shared_ptr<const OptionDetails>& details)
1343     {
1344       m_long_name = &details->long_name();
1345     }
1346 
1347 #if defined(CXXOPTS_NULL_DEREF_IGNORE)
1348 #pragma GCC diagnostic push
1349 #pragma GCC diagnostic ignored "-Wnull-dereference"
1350 #endif
1351 
1352     CXXOPTS_NODISCARD
1353     size_t
count() const1354     count() const noexcept
1355     {
1356       return m_count;
1357     }
1358 
1359 #if defined(CXXOPTS_NULL_DEREF_IGNORE)
1360 #pragma GCC diagnostic pop
1361 #endif
1362 
1363     // TODO: maybe default options should count towards the number of arguments
1364     CXXOPTS_NODISCARD
1365     bool
has_default() const1366     has_default() const noexcept
1367     {
1368       return m_default;
1369     }
1370 
1371     template <typename T>
1372     const T&
as() const1373     as() const
1374     {
1375       if (m_value == nullptr) {
1376           throw_or_mimic<option_has_no_value_exception>(
1377               m_long_name == nullptr ? "" : *m_long_name);
1378       }
1379 
1380 #ifdef CXXOPTS_NO_RTTI
1381       return static_cast<const values::standard_value<T>&>(*m_value).get();
1382 #else
1383       return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
1384 #endif
1385     }
1386 
1387     private:
1388     void
ensure_value(const std::shared_ptr<const OptionDetails> & details)1389     ensure_value(const std::shared_ptr<const OptionDetails>& details)
1390     {
1391       if (m_value == nullptr)
1392       {
1393         m_value = details->make_storage();
1394       }
1395     }
1396 
1397 
1398     const std::string* m_long_name = nullptr;
1399     // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1400     // where the key has the string we point to.
1401     std::shared_ptr<Value> m_value{};
1402     size_t m_count = 0;
1403     bool m_default = false;
1404   };
1405 
1406   class KeyValue
1407   {
1408     public:
KeyValue(std::string key_,std::string value_)1409     KeyValue(std::string key_, std::string value_)
1410     : m_key(std::move(key_))
1411     , m_value(std::move(value_))
1412     {
1413     }
1414 
1415     CXXOPTS_NODISCARD
1416     const std::string&
key() const1417     key() const
1418     {
1419       return m_key;
1420     }
1421 
1422     CXXOPTS_NODISCARD
1423     const std::string&
value() const1424     value() const
1425     {
1426       return m_value;
1427     }
1428 
1429     template <typename T>
1430     T
as() const1431     as() const
1432     {
1433       T result;
1434       values::parse_value(m_value, result);
1435       return result;
1436     }
1437 
1438     private:
1439     std::string m_key;
1440     std::string m_value;
1441   };
1442 
1443   using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
1444   using NameHashMap = std::unordered_map<std::string, size_t>;
1445 
1446   class ParseResult
1447   {
1448     public:
1449 
1450     ParseResult() = default;
1451     ParseResult(const ParseResult&) = default;
1452 
ParseResult(NameHashMap && keys,ParsedHashMap && values,std::vector<KeyValue> sequential,std::vector<std::string> && unmatched_args)1453     ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
1454     : m_keys(std::move(keys))
1455     , m_values(std::move(values))
1456     , m_sequential(std::move(sequential))
1457     , m_unmatched(std::move(unmatched_args))
1458     {
1459     }
1460 
1461     ParseResult& operator=(ParseResult&&) = default;
1462     ParseResult& operator=(const ParseResult&) = default;
1463 
1464     size_t
count(const std::string & o) const1465     count(const std::string& o) const
1466     {
1467       auto iter = m_keys.find(o);
1468       if (iter == m_keys.end())
1469       {
1470         return 0;
1471       }
1472 
1473       auto viter = m_values.find(iter->second);
1474 
1475       if (viter == m_values.end())
1476       {
1477         return 0;
1478       }
1479 
1480       return viter->second.count();
1481     }
1482 
1483     const OptionValue&
operator [](const std::string & option) const1484     operator[](const std::string& option) const
1485     {
1486       auto iter = m_keys.find(option);
1487 
1488       if (iter == m_keys.end())
1489       {
1490         throw_or_mimic<option_not_present_exception>(option);
1491       }
1492 
1493       auto viter = m_values.find(iter->second);
1494 
1495       if (viter == m_values.end())
1496       {
1497         throw_or_mimic<option_not_present_exception>(option);
1498       }
1499 
1500       return viter->second;
1501     }
1502 
1503     const std::vector<KeyValue>&
arguments() const1504     arguments() const
1505     {
1506       return m_sequential;
1507     }
1508 
1509     const std::vector<std::string>&
unmatched() const1510     unmatched() const
1511     {
1512       return m_unmatched;
1513     }
1514 
1515     private:
1516     NameHashMap m_keys{};
1517     ParsedHashMap m_values{};
1518     std::vector<KeyValue> m_sequential{};
1519     std::vector<std::string> m_unmatched{};
1520   };
1521 
1522   struct Option
1523   {
Optioncxxopts::Option1524     Option
1525     (
1526       std::string opts,
1527       std::string desc,
1528       std::shared_ptr<const Value>  value = ::cxxopts::value<bool>(),
1529       std::string arg_help = ""
1530     )
1531     : opts_(std::move(opts))
1532     , desc_(std::move(desc))
1533     , value_(std::move(value))
1534     , arg_help_(std::move(arg_help))
1535     {
1536     }
1537 
1538     std::string opts_;
1539     std::string desc_;
1540     std::shared_ptr<const Value> value_;
1541     std::string arg_help_;
1542   };
1543 
1544   using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1545   using PositionalList = std::vector<std::string>;
1546   using PositionalListIterator = PositionalList::const_iterator;
1547 
1548   class OptionParser
1549   {
1550     public:
OptionParser(const OptionMap & options,const PositionalList & positional,bool allow_unrecognised)1551     OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
1552     : m_options(options)
1553     , m_positional(positional)
1554     , m_allow_unrecognised(allow_unrecognised)
1555     {
1556     }
1557 
1558     ParseResult
1559     parse(int argc, const char* const* argv);
1560 
1561     bool
1562     consume_positional(const std::string& a, PositionalListIterator& next);
1563 
1564     void
1565     checked_parse_arg
1566     (
1567       int argc,
1568       const char* const* argv,
1569       int& current,
1570       const std::shared_ptr<OptionDetails>& value,
1571       const std::string& name
1572     );
1573 
1574     void
1575     add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
1576 
1577     void
1578     parse_option
1579     (
1580       const std::shared_ptr<OptionDetails>& value,
1581       const std::string& name,
1582       const std::string& arg = ""
1583     );
1584 
1585     void
1586     parse_default(const std::shared_ptr<OptionDetails>& details);
1587 
1588     void
1589     parse_no_value(const std::shared_ptr<OptionDetails>& details);
1590 
1591     private:
1592 
1593     void finalise_aliases();
1594 
1595     const OptionMap& m_options;
1596     const PositionalList& m_positional;
1597 
1598     std::vector<KeyValue> m_sequential{};
1599     bool m_allow_unrecognised;
1600 
1601     ParsedHashMap m_parsed{};
1602     NameHashMap m_keys{};
1603   };
1604 
1605   class Options
1606   {
1607     public:
1608 
Options(std::string program,std::string help_string="")1609     explicit Options(std::string program, std::string help_string = "")
1610     : m_program(std::move(program))
1611     , m_help_string(toLocalString(std::move(help_string)))
1612     , m_custom_help("[OPTION...]")
1613     , m_positional_help("positional parameters")
1614     , m_show_positional(false)
1615     , m_allow_unrecognised(false)
1616     , m_width(76)
1617     , m_tab_expansion(false)
1618     , m_options(std::make_shared<OptionMap>())
1619     {
1620     }
1621 
1622     Options&
positional_help(std::string help_text)1623     positional_help(std::string help_text)
1624     {
1625       m_positional_help = std::move(help_text);
1626       return *this;
1627     }
1628 
1629     Options&
custom_help(std::string help_text)1630     custom_help(std::string help_text)
1631     {
1632       m_custom_help = std::move(help_text);
1633       return *this;
1634     }
1635 
1636     Options&
show_positional_help()1637     show_positional_help()
1638     {
1639       m_show_positional = true;
1640       return *this;
1641     }
1642 
1643     Options&
allow_unrecognised_options()1644     allow_unrecognised_options()
1645     {
1646       m_allow_unrecognised = true;
1647       return *this;
1648     }
1649 
1650     Options&
set_width(size_t width)1651     set_width(size_t width)
1652     {
1653       m_width = width;
1654       return *this;
1655     }
1656 
1657     Options&
set_tab_expansion(bool expansion=true)1658     set_tab_expansion(bool expansion=true)
1659     {
1660       m_tab_expansion = expansion;
1661       return *this;
1662     }
1663 
1664     ParseResult
1665     parse(int argc, const char* const* argv);
1666 
1667     OptionAdder
1668     add_options(std::string group = "");
1669 
1670     void
1671     add_options
1672     (
1673       const std::string& group,
1674       std::initializer_list<Option> options
1675     );
1676 
1677     void
1678     add_option
1679     (
1680       const std::string& group,
1681       const Option& option
1682     );
1683 
1684     void
1685     add_option
1686     (
1687       const std::string& group,
1688       const std::string& s,
1689       const std::string& l,
1690       std::string desc,
1691       const std::shared_ptr<const Value>& value,
1692       std::string arg_help
1693     );
1694 
1695     //parse positional arguments into the given option
1696     void
1697     parse_positional(std::string option);
1698 
1699     void
1700     parse_positional(std::vector<std::string> options);
1701 
1702     void
1703     parse_positional(std::initializer_list<std::string> options);
1704 
1705     template <typename Iterator>
1706     void
parse_positional(Iterator begin,Iterator end)1707     parse_positional(Iterator begin, Iterator end) {
1708       parse_positional(std::vector<std::string>{begin, end});
1709     }
1710 
1711     std::string
1712     help(const std::vector<std::string>& groups = {}) const;
1713 
1714     std::vector<std::string>
1715     groups() const;
1716 
1717     const HelpGroupDetails&
1718     group_help(const std::string& group) const;
1719 
1720     private:
1721 
1722     void
1723     add_one_option
1724     (
1725       const std::string& option,
1726       const std::shared_ptr<OptionDetails>& details
1727     );
1728 
1729     String
1730     help_one_group(const std::string& group) const;
1731 
1732     void
1733     generate_group_help
1734     (
1735       String& result,
1736       const std::vector<std::string>& groups
1737     ) const;
1738 
1739     void
1740     generate_all_groups_help(String& result) const;
1741 
1742     std::string m_program{};
1743     String m_help_string{};
1744     std::string m_custom_help{};
1745     std::string m_positional_help{};
1746     bool m_show_positional;
1747     bool m_allow_unrecognised;
1748     size_t m_width;
1749     bool m_tab_expansion;
1750 
1751     std::shared_ptr<OptionMap> m_options;
1752     std::vector<std::string> m_positional{};
1753     std::unordered_set<std::string> m_positional_set{};
1754 
1755     //mapping from groups to help options
1756     std::map<std::string, HelpGroupDetails> m_help{};
1757 
1758     std::list<OptionDetails> m_option_list{};
1759     std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
1760   };
1761 
1762   class OptionAdder
1763   {
1764     public:
1765 
OptionAdder(Options & options,std::string group)1766     OptionAdder(Options& options, std::string group)
1767     : m_options(options), m_group(std::move(group))
1768     {
1769     }
1770 
1771     OptionAdder&
1772     operator()
1773     (
1774       const std::string& opts,
1775       const std::string& desc,
1776       const std::shared_ptr<const Value>& value
1777         = ::cxxopts::value<bool>(),
1778       std::string arg_help = ""
1779     );
1780 
1781     private:
1782     Options& m_options;
1783     std::string m_group;
1784   };
1785 
1786   namespace
1787   {
1788     constexpr size_t OPTION_LONGEST = 30;
1789     constexpr size_t OPTION_DESC_GAP = 2;
1790 
1791     String
format_option(const HelpOptionDetails & o)1792     format_option
1793     (
1794       const HelpOptionDetails& o
1795     )
1796     {
1797       const auto& s = o.s;
1798       const auto& l = o.l;
1799 
1800       String result = "  ";
1801 
1802       if (!s.empty())
1803       {
1804         result += "-" + toLocalString(s);
1805         if (!l.empty())
1806         {
1807           result += ",";
1808         }
1809       }
1810       else
1811       {
1812         result += "   ";
1813       }
1814 
1815       if (!l.empty())
1816       {
1817         result += " --" + toLocalString(l);
1818       }
1819 
1820       auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
1821 
1822       if (!o.is_boolean)
1823       {
1824         if (o.has_implicit)
1825         {
1826           result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
1827         }
1828         else
1829         {
1830           result += " " + arg;
1831         }
1832       }
1833 
1834       return result;
1835     }
1836 
1837     String
format_description(const HelpOptionDetails & o,size_t start,size_t allowed,bool tab_expansion)1838     format_description
1839     (
1840       const HelpOptionDetails& o,
1841       size_t start,
1842       size_t allowed,
1843       bool tab_expansion
1844     )
1845     {
1846       auto desc = o.desc;
1847 
1848       if (o.has_default && (!o.is_boolean || o.default_value != "false"))
1849       {
1850         if(!o.default_value.empty())
1851         {
1852           desc += toLocalString(" (default: " + o.default_value + ")");
1853         }
1854         else
1855         {
1856           desc += toLocalString(" (default: \"\")");
1857         }
1858       }
1859 
1860       String result;
1861 
1862       if (tab_expansion)
1863       {
1864         String desc2;
1865         auto size = size_t{ 0 };
1866         for (auto c = std::begin(desc); c != std::end(desc); ++c)
1867         {
1868           if (*c == '\n')
1869           {
1870             desc2 += *c;
1871             size = 0;
1872           }
1873           else if (*c == '\t')
1874           {
1875             auto skip = 8 - size % 8;
1876             stringAppend(desc2, skip, ' ');
1877             size += skip;
1878           }
1879           else
1880           {
1881             desc2 += *c;
1882             ++size;
1883           }
1884         }
1885         desc = desc2;
1886       }
1887 
1888       desc += " ";
1889 
1890       auto current = std::begin(desc);
1891       auto previous = current;
1892       auto startLine = current;
1893       auto lastSpace = current;
1894 
1895       auto size = size_t{};
1896 
1897       bool appendNewLine;
1898       bool onlyWhiteSpace = true;
1899 
1900       while (current != std::end(desc))
1901       {
1902         appendNewLine = false;
1903 
1904         if (std::isblank(*previous))
1905         {
1906           lastSpace = current;
1907         }
1908 
1909         if (!std::isblank(*current))
1910         {
1911           onlyWhiteSpace = false;
1912         }
1913 
1914         while (*current == '\n')
1915         {
1916           previous = current;
1917           ++current;
1918           appendNewLine = true;
1919         }
1920 
1921         if (!appendNewLine && size >= allowed)
1922         {
1923           if (lastSpace != startLine)
1924           {
1925             current = lastSpace;
1926             previous = current;
1927           }
1928           appendNewLine = true;
1929         }
1930 
1931         if (appendNewLine)
1932         {
1933           stringAppend(result, startLine, current);
1934           startLine = current;
1935           lastSpace = current;
1936 
1937           if (*previous != '\n')
1938           {
1939             stringAppend(result, "\n");
1940           }
1941 
1942           stringAppend(result, start, ' ');
1943 
1944           if (*previous != '\n')
1945           {
1946             stringAppend(result, lastSpace, current);
1947           }
1948 
1949           onlyWhiteSpace = true;
1950           size = 0;
1951         }
1952 
1953         previous = current;
1954         ++current;
1955         ++size;
1956       }
1957 
1958       //append whatever is left but ignore whitespace
1959       if (!onlyWhiteSpace)
1960       {
1961         stringAppend(result, startLine, previous);
1962       }
1963 
1964       return result;
1965     }
1966   } // namespace
1967 
1968 inline
1969 void
add_options(const std::string & group,std::initializer_list<Option> options)1970 Options::add_options
1971 (
1972   const std::string &group,
1973   std::initializer_list<Option> options
1974 )
1975 {
1976  OptionAdder option_adder(*this, group);
1977  for (const auto &option: options)
1978  {
1979    option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
1980  }
1981 }
1982 
1983 inline
1984 OptionAdder
add_options(std::string group)1985 Options::add_options(std::string group)
1986 {
1987   return OptionAdder(*this, std::move(group));
1988 }
1989 
1990 inline
1991 OptionAdder&
operator ()(const std::string & opts,const std::string & desc,const std::shared_ptr<const Value> & value,std::string arg_help)1992 OptionAdder::operator()
1993 (
1994   const std::string& opts,
1995   const std::string& desc,
1996   const std::shared_ptr<const Value>& value,
1997   std::string arg_help
1998 )
1999 {
2000   std::string short_sw, long_sw;
2001   std::tie(short_sw, long_sw) = values::parser_tool::SplitSwitchDef(opts);
2002 
2003   if (!short_sw.length() && !long_sw.length())
2004   {
2005     throw_or_mimic<invalid_option_format_error>(opts);
2006   }
2007   else if (long_sw.length() == 1 && short_sw.length())
2008   {
2009     throw_or_mimic<invalid_option_format_error>(opts);
2010   }
2011 
2012   auto option_names = []
2013   (
2014     const std::string &short_,
2015     const std::string &long_
2016   )
2017   {
2018     if (long_.length() == 1)
2019     {
2020       return std::make_tuple(long_, short_);
2021     }
2022     return std::make_tuple(short_, long_);
2023   }(short_sw, long_sw);
2024 
2025   m_options.add_option
2026   (
2027     m_group,
2028     std::get<0>(option_names),
2029     std::get<1>(option_names),
2030     desc,
2031     value,
2032     std::move(arg_help)
2033   );
2034 
2035   return *this;
2036 }
2037 
2038 inline
2039 void
parse_default(const std::shared_ptr<OptionDetails> & details)2040 OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
2041 {
2042   // TODO: remove the duplicate code here
2043   auto& store = m_parsed[details->hash()];
2044   store.parse_default(details);
2045 }
2046 
2047 inline
2048 void
parse_no_value(const std::shared_ptr<OptionDetails> & details)2049 OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
2050 {
2051   auto& store = m_parsed[details->hash()];
2052   store.parse_no_value(details);
2053 }
2054 
2055 inline
2056 void
parse_option(const std::shared_ptr<OptionDetails> & value,const std::string &,const std::string & arg)2057 OptionParser::parse_option
2058 (
2059   const std::shared_ptr<OptionDetails>& value,
2060   const std::string& /*name*/,
2061   const std::string& arg
2062 )
2063 {
2064   auto hash = value->hash();
2065   auto& result = m_parsed[hash];
2066   result.parse(value, arg);
2067 
2068   m_sequential.emplace_back(value->long_name(), arg);
2069 }
2070 
2071 inline
2072 void
checked_parse_arg(int argc,const char * const * argv,int & current,const std::shared_ptr<OptionDetails> & value,const std::string & name)2073 OptionParser::checked_parse_arg
2074 (
2075   int argc,
2076   const char* const* argv,
2077   int& current,
2078   const std::shared_ptr<OptionDetails>& value,
2079   const std::string& name
2080 )
2081 {
2082   if (current + 1 >= argc)
2083   {
2084     if (value->value().has_implicit())
2085     {
2086       parse_option(value, name, value->value().get_implicit_value());
2087     }
2088     else
2089     {
2090       throw_or_mimic<missing_argument_exception>(name);
2091     }
2092   }
2093   else
2094   {
2095     if (value->value().has_implicit())
2096     {
2097       parse_option(value, name, value->value().get_implicit_value());
2098     }
2099     else
2100     {
2101       parse_option(value, name, argv[current + 1]);
2102       ++current;
2103     }
2104   }
2105 }
2106 
2107 inline
2108 void
add_to_option(OptionMap::const_iterator iter,const std::string & option,const std::string & arg)2109 OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
2110 {
2111   parse_option(iter->second, option, arg);
2112 }
2113 
2114 inline
2115 bool
consume_positional(const std::string & a,PositionalListIterator & next)2116 OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
2117 {
2118   while (next != m_positional.end())
2119   {
2120     auto iter = m_options.find(*next);
2121     if (iter != m_options.end())
2122     {
2123       if (!iter->second->value().is_container())
2124       {
2125         auto& result = m_parsed[iter->second->hash()];
2126         if (result.count() == 0)
2127         {
2128           add_to_option(iter, *next, a);
2129           ++next;
2130           return true;
2131         }
2132         ++next;
2133         continue;
2134       }
2135       add_to_option(iter, *next, a);
2136       return true;
2137     }
2138     throw_or_mimic<option_not_exists_exception>(*next);
2139   }
2140 
2141   return false;
2142 }
2143 
2144 inline
2145 void
parse_positional(std::string option)2146 Options::parse_positional(std::string option)
2147 {
2148   parse_positional(std::vector<std::string>{std::move(option)});
2149 }
2150 
2151 inline
2152 void
parse_positional(std::vector<std::string> options)2153 Options::parse_positional(std::vector<std::string> options)
2154 {
2155   m_positional = std::move(options);
2156 
2157   m_positional_set.insert(m_positional.begin(), m_positional.end());
2158 }
2159 
2160 inline
2161 void
parse_positional(std::initializer_list<std::string> options)2162 Options::parse_positional(std::initializer_list<std::string> options)
2163 {
2164   parse_positional(std::vector<std::string>(options));
2165 }
2166 
2167 inline
2168 ParseResult
parse(int argc,const char * const * argv)2169 Options::parse(int argc, const char* const* argv)
2170 {
2171   OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
2172 
2173   return parser.parse(argc, argv);
2174 }
2175 
2176 inline ParseResult
parse(int argc,const char * const * argv)2177 OptionParser::parse(int argc, const char* const* argv)
2178 {
2179   int current = 1;
2180   bool consume_remaining = false;
2181   auto next_positional = m_positional.begin();
2182 
2183   std::vector<std::string> unmatched;
2184 
2185   while (current != argc)
2186   {
2187     if (strcmp(argv[current], "--") == 0)
2188     {
2189       consume_remaining = true;
2190       ++current;
2191       break;
2192     }
2193     bool matched = false;
2194     values::parser_tool::ArguDesc argu_desc =
2195         values::parser_tool::ParseArgument(argv[current], matched);
2196 
2197     if (!matched)
2198     {
2199       //not a flag
2200 
2201       // but if it starts with a `-`, then it's an error
2202       if (argv[current][0] == '-' && argv[current][1] != '\0') {
2203         if (!m_allow_unrecognised) {
2204           throw_or_mimic<option_syntax_exception>(argv[current]);
2205         }
2206       }
2207 
2208       //if true is returned here then it was consumed, otherwise it is
2209       //ignored
2210       if (consume_positional(argv[current], next_positional))
2211       {
2212       }
2213       else
2214       {
2215         unmatched.emplace_back(argv[current]);
2216       }
2217       //if we return from here then it was parsed successfully, so continue
2218     }
2219     else
2220     {
2221       //short or long option?
2222       if (argu_desc.grouping)
2223       {
2224         const std::string& s = argu_desc.arg_name;
2225 
2226         for (std::size_t i = 0; i != s.size(); ++i)
2227         {
2228           std::string name(1, s[i]);
2229           auto iter = m_options.find(name);
2230 
2231           if (iter == m_options.end())
2232           {
2233             if (m_allow_unrecognised)
2234             {
2235               continue;
2236             }
2237             //error
2238             throw_or_mimic<option_not_exists_exception>(name);
2239           }
2240 
2241           auto value = iter->second;
2242 
2243           if (i + 1 == s.size())
2244           {
2245             //it must be the last argument
2246             checked_parse_arg(argc, argv, current, value, name);
2247           }
2248           else if (value->value().has_implicit())
2249           {
2250             parse_option(value, name, value->value().get_implicit_value());
2251           }
2252           else if (i + 1 < s.size())
2253           {
2254             std::string arg_value = s.substr(i + 1);
2255             parse_option(value, name, arg_value);
2256             break;
2257           }
2258           else
2259           {
2260             //error
2261             throw_or_mimic<option_requires_argument_exception>(name);
2262           }
2263         }
2264       }
2265       else if (argu_desc.arg_name.length() != 0)
2266       {
2267         const std::string& name = argu_desc.arg_name;
2268 
2269         auto iter = m_options.find(name);
2270 
2271         if (iter == m_options.end())
2272         {
2273           if (m_allow_unrecognised)
2274           {
2275             // keep unrecognised options in argument list, skip to next argument
2276             unmatched.emplace_back(argv[current]);
2277             ++current;
2278             continue;
2279           }
2280           //error
2281           throw_or_mimic<option_not_exists_exception>(name);
2282         }
2283 
2284         auto opt = iter->second;
2285 
2286         //equals provided for long option?
2287         if (argu_desc.set_value)
2288         {
2289           //parse the option given
2290 
2291           parse_option(opt, name, argu_desc.value);
2292         }
2293         else
2294         {
2295           //parse the next argument
2296           checked_parse_arg(argc, argv, current, opt, name);
2297         }
2298       }
2299 
2300     }
2301 
2302     ++current;
2303   }
2304 
2305   for (auto& opt : m_options)
2306   {
2307     auto& detail = opt.second;
2308     const auto& value = detail->value();
2309 
2310     auto& store = m_parsed[detail->hash()];
2311 
2312     if (value.has_default()) {
2313       if (!store.count() && !store.has_default()) {
2314         parse_default(detail);
2315       }
2316     }
2317     else {
2318       parse_no_value(detail);
2319     }
2320   }
2321 
2322   if (consume_remaining)
2323   {
2324     while (current < argc)
2325     {
2326       if (!consume_positional(argv[current], next_positional)) {
2327         break;
2328       }
2329       ++current;
2330     }
2331 
2332     //adjust argv for any that couldn't be swallowed
2333     while (current != argc) {
2334       unmatched.emplace_back(argv[current]);
2335       ++current;
2336     }
2337   }
2338 
2339   finalise_aliases();
2340 
2341   ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
2342   return parsed;
2343 }
2344 
2345 inline
2346 void
finalise_aliases()2347 OptionParser::finalise_aliases()
2348 {
2349   for (auto& option: m_options)
2350   {
2351     auto& detail = *option.second;
2352     auto hash = detail.hash();
2353     m_keys[detail.short_name()] = hash;
2354     m_keys[detail.long_name()] = hash;
2355 
2356     m_parsed.emplace(hash, OptionValue());
2357   }
2358 }
2359 
2360 inline
2361 void
add_option(const std::string & group,const Option & option)2362 Options::add_option
2363 (
2364   const std::string& group,
2365   const Option& option
2366 )
2367 {
2368     add_options(group, {option});
2369 }
2370 
2371 inline
2372 void
add_option(const std::string & group,const std::string & s,const std::string & l,std::string desc,const std::shared_ptr<const Value> & value,std::string arg_help)2373 Options::add_option
2374 (
2375   const std::string& group,
2376   const std::string& s,
2377   const std::string& l,
2378   std::string desc,
2379   const std::shared_ptr<const Value>& value,
2380   std::string arg_help
2381 )
2382 {
2383   auto stringDesc = toLocalString(std::move(desc));
2384   auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2385 
2386   if (!s.empty())
2387   {
2388     add_one_option(s, option);
2389   }
2390 
2391   if (!l.empty())
2392   {
2393     add_one_option(l, option);
2394   }
2395 
2396   m_option_list.push_front(*option.get());
2397   auto iter = m_option_list.begin();
2398   m_option_map[s] = iter;
2399   m_option_map[l] = iter;
2400 
2401   //add the help details
2402   auto& options = m_help[group];
2403 
2404   options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
2405       value->has_default(), value->get_default_value(),
2406       value->has_implicit(), value->get_implicit_value(),
2407       std::move(arg_help),
2408       value->is_container(),
2409       value->is_boolean()});
2410 }
2411 
2412 inline
2413 void
add_one_option(const std::string & option,const std::shared_ptr<OptionDetails> & details)2414 Options::add_one_option
2415 (
2416   const std::string& option,
2417   const std::shared_ptr<OptionDetails>& details
2418 )
2419 {
2420   auto in = m_options->emplace(option, details);
2421 
2422   if (!in.second)
2423   {
2424     throw_or_mimic<option_exists_error>(option);
2425   }
2426 }
2427 
2428 inline
2429 String
help_one_group(const std::string & g) const2430 Options::help_one_group(const std::string& g) const
2431 {
2432   using OptionHelp = std::vector<std::pair<String, String>>;
2433 
2434   auto group = m_help.find(g);
2435   if (group == m_help.end())
2436   {
2437     return "";
2438   }
2439 
2440   OptionHelp format;
2441 
2442   size_t longest = 0;
2443 
2444   String result;
2445 
2446   if (!g.empty())
2447   {
2448     result += toLocalString(" " + g + " options:\n");
2449   }
2450 
2451   for (const auto& o : group->second.options)
2452   {
2453     if (m_positional_set.find(o.l) != m_positional_set.end() &&
2454         !m_show_positional)
2455     {
2456       continue;
2457     }
2458 
2459     auto s = format_option(o);
2460     longest = (std::max)(longest, stringLength(s));
2461     format.push_back(std::make_pair(s, String()));
2462   }
2463   longest = (std::min)(longest, OPTION_LONGEST);
2464 
2465   //widest allowed description -- min 10 chars for helptext/line
2466   size_t allowed = 10;
2467   if (m_width > allowed + longest + OPTION_DESC_GAP)
2468   {
2469     allowed = m_width - longest - OPTION_DESC_GAP;
2470   }
2471 
2472   auto fiter = format.begin();
2473   for (const auto& o : group->second.options)
2474   {
2475     if (m_positional_set.find(o.l) != m_positional_set.end() &&
2476         !m_show_positional)
2477     {
2478       continue;
2479     }
2480 
2481     auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2482 
2483     result += fiter->first;
2484     if (stringLength(fiter->first) > longest)
2485     {
2486       result += '\n';
2487       result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2488     }
2489     else
2490     {
2491       result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2492         stringLength(fiter->first),
2493         ' '));
2494     }
2495     result += d;
2496     result += '\n';
2497 
2498     ++fiter;
2499   }
2500 
2501   return result;
2502 }
2503 
2504 inline
2505 void
generate_group_help(String & result,const std::vector<std::string> & print_groups) const2506 Options::generate_group_help
2507 (
2508   String& result,
2509   const std::vector<std::string>& print_groups
2510 ) const
2511 {
2512   for (size_t i = 0; i != print_groups.size(); ++i)
2513   {
2514     const String& group_help_text = help_one_group(print_groups[i]);
2515     if (empty(group_help_text))
2516     {
2517       continue;
2518     }
2519     result += group_help_text;
2520     if (i < print_groups.size() - 1)
2521     {
2522       result += '\n';
2523     }
2524   }
2525 }
2526 
2527 inline
2528 void
generate_all_groups_help(String & result) const2529 Options::generate_all_groups_help(String& result) const
2530 {
2531   std::vector<std::string> all_groups;
2532 
2533   std::transform(
2534     m_help.begin(),
2535     m_help.end(),
2536     std::back_inserter(all_groups),
2537     [] (const std::map<std::string, HelpGroupDetails>::value_type& group)
2538     {
2539       return group.first;
2540     }
2541   );
2542 
2543   generate_group_help(result, all_groups);
2544 }
2545 
2546 inline
2547 std::string
help(const std::vector<std::string> & help_groups) const2548 Options::help(const std::vector<std::string>& help_groups) const
2549 {
2550   String result = m_help_string + "\nUsage:\n  " +
2551     toLocalString(m_program) + " " + toLocalString(m_custom_help);
2552 
2553   if (!m_positional.empty() && !m_positional_help.empty()) {
2554     result += " " + toLocalString(m_positional_help);
2555   }
2556 
2557   result += "\n\n";
2558 
2559   if (help_groups.empty())
2560   {
2561     generate_all_groups_help(result);
2562   }
2563   else
2564   {
2565     generate_group_help(result, help_groups);
2566   }
2567 
2568   return toUTF8String(result);
2569 }
2570 
2571 inline
2572 std::vector<std::string>
groups() const2573 Options::groups() const
2574 {
2575   std::vector<std::string> g;
2576 
2577   std::transform(
2578     m_help.begin(),
2579     m_help.end(),
2580     std::back_inserter(g),
2581     [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
2582     {
2583       return pair.first;
2584     }
2585   );
2586 
2587   return g;
2588 }
2589 
2590 inline
2591 const HelpGroupDetails&
group_help(const std::string & group) const2592 Options::group_help(const std::string& group) const
2593 {
2594   return m_help.at(group);
2595 }
2596 
2597 } // namespace cxxopts
2598 
2599 #endif //CXXOPTS_HPP_INCLUDED
2600