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