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