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