You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1312 lines
						
					
					
						
							25 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1312 lines
						
					
					
						
							25 KiB
						
					
					
				| /* | |
|  | |
| Copyright (c) 2014 Jarryd Beck | |
|  | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
|  | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
|  | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| THE SOFTWARE. | |
|  | |
| */ | |
| 
 | |
| #ifndef CXX_OPTS_HPP | |
| #define CXX_OPTS_HPP | |
|  | |
| #if defined(__GNUC__) | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" | |
| #endif | |
|  | |
| #include <exception> | |
| #include <iostream> | |
| #include <map> | |
| #include <memory> | |
| #include <regex> | |
| #include <sstream> | |
| #include <string> | |
| #include <vector> | |
|  | |
| //when we ask cxxopts to use Unicode, help strings are processed using ICU, | |
| //which results in the correct lengths being computed for strings when they | |
| //are formatted for the help output | |
| //it is necessary to make sure that <unicode/unistr.h> can be found by the | |
| //compiler, and that icu-uc is linked in to the binary. | |
|  | |
| #ifdef CXXOPTS_USE_UNICODE | |
| #include <unicode/unistr.h> | |
|  | |
| namespace cxxopts | |
| { | |
|   typedef icu::UnicodeString String; | |
| 
 | |
|   inline | |
|   String | |
|   toLocalString(std::string s) | |
|   { | |
|     return icu::UnicodeString::fromUTF8(s); | |
|   } | |
| 
 | |
|   class UnicodeStringIterator : public | |
|     std::iterator<std::forward_iterator_tag, int32_t> | |
|   { | |
|     public: | |
| 
 | |
|     UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos) | |
|     : s(s) | |
|     , i(pos) | |
|     { | |
|     } | |
| 
 | |
|     value_type | |
|     operator*() const | |
|     { | |
|       return s->char32At(i); | |
|     } | |
| 
 | |
|     bool | |
|     operator==(const UnicodeStringIterator& rhs) const | |
|     { | |
|       return s == rhs.s && i == rhs.i; | |
|     } | |
| 
 | |
|     bool | |
|     operator!=(const UnicodeStringIterator& rhs) const | |
|     { | |
|       return !(*this == rhs); | |
|     } | |
| 
 | |
|     UnicodeStringIterator& | |
|     operator++() | |
|     { | |
|       ++i; | |
|       return *this; | |
|     } | |
| 
 | |
|     UnicodeStringIterator | |
|     operator+(int32_t v) | |
|     { | |
|       return UnicodeStringIterator(s, i + v); | |
|     } | |
| 
 | |
|     private: | |
|     const icu::UnicodeString* s; | |
|     int32_t i; | |
|   }; | |
| 
 | |
|   inline | |
|   String& | |
|   stringAppend(String&s, String a) | |
|   { | |
|     return s.append(std::move(a)); | |
|   } | |
| 
 | |
|   inline | |
|   String& | |
|   stringAppend(String& s, int n, UChar32 c) | |
|   { | |
|     for (int i = 0; i != n; ++i) | |
|     { | |
|       s.append(c); | |
|     } | |
| 
 | |
|     return s; | |
|   } | |
| 
 | |
|   template <typename Iterator> | |
|   String& | |
|   stringAppend(String& s, Iterator begin, Iterator end) | |
|   { | |
|     while (begin != end) | |
|     { | |
|       s.append(*begin); | |
|       ++begin; | |
|     } | |
| 
 | |
|     return s; | |
|   } | |
| 
 | |
|   inline | |
|   size_t | |
|   stringLength(const String& s) | |
|   { | |
|     return s.length(); | |
|   } | |
| 
 | |
|   inline | |
|   std::string | |
|   toUTF8String(const String& s) | |
|   { | |
|     std::string result; | |
|     s.toUTF8String(result); | |
| 
 | |
|     return result; | |
|   } | |
| } | |
| 
 | |
| namespace std | |
| { | |
|   cxxopts::UnicodeStringIterator | |
|   begin(const icu::UnicodeString& s) | |
|   { | |
|     return cxxopts::UnicodeStringIterator(&s, 0); | |
|   } | |
| 
 | |
|   cxxopts::UnicodeStringIterator | |
|   end(const icu::UnicodeString& s) | |
|   { | |
|     return cxxopts::UnicodeStringIterator(&s, s.length()); | |
|   } | |
| } | |
| 
 | |
| //ifdef CXXOPTS_USE_UNICODE | |
| #else | |
|  | |
| namespace cxxopts | |
| { | |
|   typedef std::string String; | |
| 
 | |
|   template <typename T> | |
|   T | |
|   toLocalString(T&& t) | |
|   { | |
|     return t; | |
|   } | |
| 
 | |
|   inline | |
|   size_t | |
|   stringLength(const String& s) | |
|   { | |
|     return s.length(); | |
|   } | |
| 
 | |
|   inline | |
|   String& | |
|   stringAppend(String&s, String a) | |
|   { | |
|     return s.append(std::move(a)); | |
|   } | |
| 
 | |
|   inline | |
|   String& | |
|   stringAppend(String& s, size_t n, char c) | |
|   { | |
|     return s.append(n, c); | |
|   } | |
| 
 | |
|   template <typename Iterator> | |
|   String& | |
|   stringAppend(String& s, Iterator begin, Iterator end) | |
|   { | |
|     return s.append(begin, end); | |
|   } | |
| 
 | |
|   template <typename T> | |
|   std::string | |
|   toUTF8String(T&& t) | |
|   { | |
|     return std::forward<T>(t); | |
|   } | |
| 
 | |
| } | |
| 
 | |
| //ifdef CXXOPTS_USE_UNICODE | |
| #endif | |
|  | |
| namespace cxxopts | |
| { | |
|   class Value : public std::enable_shared_from_this<Value> | |
|   { | |
|     public: | |
| 
 | |
|     virtual void | |
|     parse(const std::string& text) const = 0; | |
| 
 | |
|     virtual void | |
|     parse() const = 0; | |
| 
 | |
|     virtual bool | |
|     has_arg() const = 0; | |
| 
 | |
|     virtual bool | |
|     has_default() const = 0; | |
| 
 | |
|     virtual bool | |
|     has_implicit() const = 0; | |
| 
 | |
|     virtual std::string | |
|     get_default_value() const = 0; | |
| 
 | |
|     virtual std::string | |
|     get_implicit_value() const = 0; | |
| 
 | |
|     virtual std::shared_ptr<Value> | |
|     default_value(const std::string& value) = 0; | |
| 
 | |
|     virtual std::shared_ptr<Value> | |
|     implicit_value(const std::string& value) = 0; | |
|   }; | |
| 
 | |
|   class OptionException : public std::exception | |
|   { | |
|     public: | |
|     OptionException(const std::string& message) | |
|     : m_message(message) | |
|     { | |
|     } | |
| 
 | |
|     virtual const char* | |
|     what() const noexcept | |
|     { | |
|       return m_message.c_str(); | |
|     } | |
| 
 | |
|     private: | |
|     std::string m_message; | |
|   }; | |
| 
 | |
|   class OptionSpecException : public OptionException | |
|   { | |
|     public: | |
| 
 | |
|     OptionSpecException(const std::string& message) | |
|     : OptionException(message) | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class OptionParseException : public OptionException | |
|   { | |
|     public: | |
|     OptionParseException(const std::string& message) | |
|     : OptionException(message) | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class option_exists_error : public OptionSpecException | |
|   { | |
|     public: | |
|     option_exists_error(const std::string& option) | |
|     : OptionSpecException(u8"Option ‘" + option + u8"’ already exists") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class invalid_option_format_error : public OptionSpecException | |
|   { | |
|     public: | |
|     invalid_option_format_error(const std::string& format) | |
|     : OptionSpecException(u8"Invalid option format ‘" + format + u8"’") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class option_not_exists_exception : public OptionParseException | |
|   { | |
|     public: | |
|     option_not_exists_exception(const std::string& option) | |
|     : OptionParseException(u8"Option ‘" + option + u8"’ does not exist") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class missing_argument_exception : public OptionParseException | |
|   { | |
|     public: | |
|     missing_argument_exception(const std::string& option) | |
|     : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class option_requires_argument_exception : public OptionParseException | |
|   { | |
|     public: | |
|     option_requires_argument_exception(const std::string& option) | |
|     : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class option_not_has_argument_exception : public OptionParseException | |
|   { | |
|     public: | |
|     option_not_has_argument_exception | |
|     ( | |
|       const std::string& option, | |
|       const std::string& arg | |
|     ) | |
|     : OptionParseException( | |
|         u8"Option ‘" + option + u8"’ does not take an argument, but argument‘" | |
|         + arg + "’ given") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class option_not_present_exception : public OptionParseException | |
|   { | |
|     public: | |
|     option_not_present_exception(const std::string& option) | |
|     : OptionParseException(u8"Option ‘" + option + u8"’ not present") | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   class argument_incorrect_type : public OptionParseException | |
|   { | |
|     public: | |
|     argument_incorrect_type | |
|     ( | |
|       const std::string& arg | |
|     ) | |
|     : OptionParseException( | |
|       u8"Argument ‘" + arg + u8"’ failed to parse" | |
|     ) | |
|     { | |
|     } | |
|   }; | |
| 
 | |
|   namespace values | |
|   { | |
|     template <typename T> | |
|     void | |
|     parse_value(const std::string& text, T& value) | |
|     { | |
|       std::istringstream is(text); | |
|       if (!(is >> value)) | |
|       { | |
|         std::cerr << "cannot parse empty value" << std::endl; | |
|         throw argument_incorrect_type(text); | |
|       } | |
| 
 | |
|       if (is.rdbuf()->in_avail() != 0) | |
|       { | |
|         throw argument_incorrect_type(text); | |
|       } | |
|     } | |
| 
 | |
|     template <typename T> | |
|     void | |
|     parse_value(const std::string& text, std::vector<T>& value) | |
|     { | |
|       T v; | |
|       parse_value(text, v); | |
|       value.push_back(v); | |
|     } | |
| 
 | |
|     inline | |
|     void | |
|     parse_value(const std::string& /*text*/, bool& value) | |
|     { | |
|       //TODO recognise on, off, yes, no, enable, disable | |
|       //so that we can write --long=yes explicitly | |
|       value = true; | |
|     } | |
| 
 | |
|     inline | |
|     void | |
|     parse_value(const std::string& text, std::string& value) | |
|     { | |
|       value = text; | |
|     } | |
| 
 | |
|     template <typename T> | |
|     struct value_has_arg | |
|     { | |
|       static constexpr bool value = true; | |
|     }; | |
| 
 | |
|     template <> | |
|     struct value_has_arg<bool> | |
|     { | |
|       static constexpr bool value = false; | |
|     }; | |
| 
 | |
|     template <typename T> | |
|     class standard_value : public Value | |
|     { | |
|       public: | |
|       standard_value() | |
|       : m_result(std::make_shared<T>()) | |
|       , m_store(m_result.get()) | |
|       { | |
|       } | |
| 
 | |
|       standard_value(T* t) | |
|       : m_store(t) | |
|       { | |
|       } | |
| 
 | |
|       void | |
|       parse(const std::string& text) const | |
|       { | |
|         if (m_implicit && text.empty()) | |
|         { | |
|           parse_value(m_implicit_value, *m_store); | |
|         } | |
|         else | |
|         { | |
|           parse_value(text, *m_store); | |
|         } | |
|       } | |
| 
 | |
|       void | |
|       parse() const | |
|       { | |
|         parse_value(m_default_value, *m_store); | |
|       } | |
| 
 | |
|       bool | |
|       has_arg() const | |
|       { | |
|         return value_has_arg<T>::value; | |
|       } | |
| 
 | |
|       bool | |
|       has_default() const | |
|       { | |
|         return m_default; | |
|       } | |
| 
 | |
|       bool | |
|       has_implicit() const | |
|       { | |
|         return m_implicit; | |
|       } | |
| 
 | |
|       virtual std::shared_ptr<Value> | |
|       default_value(const std::string& value){ | |
|         m_default = true; | |
|         m_default_value = value; | |
|         return shared_from_this(); | |
|       } | |
| 
 | |
|       virtual std::shared_ptr<Value> | |
|       implicit_value(const std::string& value){ | |
|         m_implicit = true; | |
|         m_implicit_value = value; | |
|         return shared_from_this(); | |
|       } | |
| 
 | |
|       std::string | |
|       get_default_value() const | |
|       { | |
|         return m_default_value; | |
|       } | |
| 
 | |
|       std::string | |
|       get_implicit_value() const | |
|       { | |
|         return m_implicit_value; | |
|       } | |
| 
 | |
|       const T& | |
|       get() const | |
|       { | |
|         if (m_store == nullptr) | |
|         { | |
|           return *m_result; | |
|         } | |
|         else | |
|         { | |
|           return *m_store; | |
|         } | |
|       } | |
| 
 | |
|       protected: | |
|       std::shared_ptr<T> m_result; | |
|       T* m_store; | |
|       bool m_default = false; | |
|       std::string m_default_value; | |
|       bool m_implicit = false; | |
|       std::string m_implicit_value; | |
|     }; | |
|   } | |
| 
 | |
|   template <typename T> | |
|   std::shared_ptr<Value> | |
|   value() | |
|   { | |
|     return std::make_shared<values::standard_value<T>>(); | |
|   } | |
| 
 | |
|   template <typename T> | |
|   std::shared_ptr<Value> | |
|   value(T& t) | |
|   { | |
|     return std::make_shared<values::standard_value<T>>(&t); | |
|   } | |
| 
 | |
|   class OptionAdder; | |
| 
 | |
|   class OptionDetails | |
|   { | |
|     public: | |
|     OptionDetails | |
|     ( | |
|       const String& description, | |
|       std::shared_ptr<const Value> value | |
|     ) | |
|     : m_desc(description) | |
|     , m_value(value) | |
|     , m_count(0) | |
|     { | |
|     } | |
| 
 | |
|     const String& | |
|     description() const | |
|     { | |
|       return m_desc; | |
|     } | |
| 
 | |
|     bool | |
|     has_arg() const | |
|     { | |
|       return m_value->has_arg(); | |
|     } | |
| 
 | |
|     void | |
|     parse(const std::string& text) | |
|     { | |
|       m_value->parse(text); | |
|       ++m_count; | |
|     } | |
| 
 | |
|     void | |
|     parse_default() | |
|     { | |
|       m_value->parse(); | |
|       ++m_count; | |
|     } | |
| 
 | |
|     int | |
|     count() const | |
|     { | |
|       return m_count; | |
|     } | |
| 
 | |
|     const Value& value() const { | |
|         return *m_value; | |
|     } | |
| 
 | |
|     template <typename T> | |
|     const T& | |
|     as() const | |
|     { | |
| #ifdef CXXOPTS_NO_RTTI | |
|       return static_cast<const values::standard_value<T>&>(*m_value).get(); | |
| #else | |
|       return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); | |
| #endif | |
|     } | |
| 
 | |
|     private: | |
|     String m_desc; | |
|     std::shared_ptr<const Value> m_value; | |
|     int m_count; | |
|   }; | |
| 
 | |
|   struct HelpOptionDetails | |
|   { | |
|     std::string s; | |
|     std::string l; | |
|     String desc; | |
|     bool has_arg; | |
|     bool has_default; | |
|     std::string default_value; | |
|     bool has_implicit; | |
|     std::string implicit_value; | |
|     std::string arg_help; | |
|   }; | |
| 
 | |
|   struct HelpGroupDetails | |
|   { | |
|     std::string name; | |
|     std::string description; | |
|     std::vector<HelpOptionDetails> options; | |
|   }; | |
| 
 | |
|   class Options | |
|   { | |
|     public: | |
| 
 | |
|     Options(std::string program, std::string help_string = "") | |
|     : m_program(std::move(program)) | |
|     , m_help_string(toLocalString(std::move(help_string))) | |
|     { | |
|     } | |
| 
 | |
|     inline | |
|     void | |
|     parse(int& argc, char**& argv); | |
| 
 | |
|     inline | |
|     OptionAdder | |
|     add_options(std::string group = ""); | |
| 
 | |
|     inline | |
|     void | |
|     add_option | |
|     ( | |
|       const std::string& group, | |
|       const std::string& s, | |
|       const std::string& l, | |
|       std::string desc, | |
|       std::shared_ptr<const Value> value, | |
|       std::string arg_help | |
|     ); | |
| 
 | |
|     int | |
|     count(const std::string& o) const | |
|     { | |
|       auto iter = m_options.find(o); | |
|       if (iter == m_options.end()) | |
|       { | |
|         return 0; | |
|       } | |
| 
 | |
|       return iter->second->count(); | |
|     } | |
| 
 | |
|     const OptionDetails& | |
|     operator[](const std::string& option) const | |
|     { | |
|       auto iter = m_options.find(option); | |
| 
 | |
|       if (iter == m_options.end()) | |
|       { | |
|         throw option_not_present_exception(option); | |
|       } | |
| 
 | |
|       return *iter->second; | |
|     } | |
| 
 | |
|     //parse positional arguments into the given option | |
|     inline | |
|     void | |
|     parse_positional(std::string option); | |
| 
 | |
|     inline | |
|     std::string | |
|     help(const std::vector<std::string>& groups = {""}) const; | |
| 
 | |
|     inline | |
|     const std::vector<std::string> | |
|     groups() const; | |
| 
 | |
|     inline | |
|     const HelpGroupDetails& | |
|     group_help(const std::string& group) const; | |
| 
 | |
|     private: | |
| 
 | |
|     inline | |
|     void | |
|     add_one_option | |
|     ( | |
|       const std::string& option, | |
|       std::shared_ptr<OptionDetails> details | |
|     ); | |
| 
 | |
|     inline | |
|     bool | |
|     consume_positional(std::string a); | |
| 
 | |
|     inline | |
|     void | |
|     add_to_option(const std::string& option, const std::string& arg); | |
| 
 | |
|     inline | |
|     void | |
|     parse_option | |
|     ( | |
|       std::shared_ptr<OptionDetails> value, | |
|       const std::string& name, | |
|       const std::string& arg = "" | |
|     ); | |
| 
 | |
|     inline | |
|     void | |
|     checked_parse_arg | |
|     ( | |
|       int argc, | |
|       char* argv[], | |
|       int& current, | |
|       std::shared_ptr<OptionDetails> value, | |
|       const std::string& name | |
|     ); | |
| 
 | |
|     inline | |
|     String | |
|     help_one_group(const std::string& group) const; | |
| 
 | |
|     std::string m_program; | |
|     String m_help_string; | |
| 
 | |
|     std::map<std::string, std::shared_ptr<OptionDetails>> m_options; | |
|     std::string m_positional; | |
| 
 | |
|     //mapping from groups to help options | |
|     std::map<std::string, HelpGroupDetails> m_help; | |
|   }; | |
| 
 | |
|   class OptionAdder | |
|   { | |
|     public: | |
| 
 | |
|     OptionAdder(Options& options, std::string group) | |
|     : m_options(options), m_group(std::move(group)) | |
|     { | |
|     } | |
| 
 | |
|     inline | |
|     OptionAdder& | |
|     operator() | |
|     ( | |
|       const std::string& opts, | |
|       const std::string& desc, | |
|       std::shared_ptr<const Value> value | |
|         = ::cxxopts::value<bool>(), | |
|       std::string arg_help = "" | |
|     ); | |
| 
 | |
|     private: | |
|     Options& m_options; | |
|     std::string m_group; | |
|   }; | |
| 
 | |
| } | |
| 
 | |
| namespace cxxopts | |
| { | |
| 
 | |
|   namespace | |
|   { | |
| 
 | |
|     constexpr int OPTION_LONGEST = 30; | |
|     constexpr int OPTION_DESC_GAP = 2; | |
| 
 | |
|     std::basic_regex<char> option_matcher | |
|       ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)"); | |
| 
 | |
|     std::basic_regex<char> option_specifier | |
|       ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)"); | |
| 
 | |
|     String | |
|     format_option | |
|     ( | |
|       const HelpOptionDetails& o | |
|     ) | |
|     { | |
|       auto& s = o.s; | |
|       auto& l = o.l; | |
| 
 | |
|       String result = "  "; | |
| 
 | |
|       if (s.size() > 0) | |
|       { | |
|         result += "-" + toLocalString(s) + ","; | |
|       } | |
|       else | |
|       { | |
|         result += "   "; | |
|       } | |
| 
 | |
|       if (l.size() > 0) | |
|       { | |
|         result += " --" + toLocalString(l); | |
|       } | |
| 
 | |
|       if (o.has_arg) | |
|       { | |
|         auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; | |
| 
 | |
|         if (o.has_implicit) | |
|         { | |
|           result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; | |
|         } | |
|         else | |
|         { | |
|           result += " " + arg; | |
|         } | |
|       } | |
| 
 | |
|       return result; | |
|     } | |
| 
 | |
|     String | |
|     format_description | |
|     ( | |
|       const HelpOptionDetails& o, | |
|       size_t start, | |
|       size_t width | |
|     ) | |
|     { | |
|       auto desc = o.desc; | |
| 
 | |
|       if (o.has_default) | |
|       { | |
|         desc += toLocalString(" (default:" + o.default_value + ")"); | |
|       } | |
| 
 | |
|       String result; | |
| 
 | |
|       auto current = std::begin(desc); | |
|       auto startLine = current; | |
|       auto lastSpace = current; | |
| 
 | |
|       auto size = size_t{}; | |
| 
 | |
|       while (current != std::end(desc)) | |
|       { | |
|         if (*current == ' ') | |
|         { | |
|           lastSpace = current; | |
|         } | |
| 
 | |
|         if (size > width) | |
|         { | |
|           if (lastSpace == startLine) | |
|           { | |
|             stringAppend(result, startLine, current + 1); | |
|             stringAppend(result, "\n"); | |
|             stringAppend(result, start, ' '); | |
|             startLine = current + 1; | |
|             lastSpace = startLine; | |
|           } | |
|           else | |
|           { | |
|             stringAppend(result, startLine, lastSpace); | |
|             stringAppend(result, "\n"); | |
|             stringAppend(result, start, ' '); | |
|             startLine = lastSpace + 1; | |
|           } | |
|           size = 0; | |
|         } | |
|         else | |
|         { | |
|           ++size; | |
|         } | |
| 
 | |
|         ++current; | |
|       } | |
| 
 | |
|       //append whatever is left | |
|       stringAppend(result, startLine, current); | |
| 
 | |
|       return result; | |
|     } | |
|   } | |
| 
 | |
| OptionAdder | |
| Options::add_options(std::string group) | |
| { | |
|   return OptionAdder(*this, std::move(group)); | |
| } | |
| 
 | |
| OptionAdder& | |
| OptionAdder::operator() | |
| ( | |
|   const std::string& opts, | |
|   const std::string& desc, | |
|   std::shared_ptr<const Value> value, | |
|   std::string arg_help | |
| ) | |
| { | |
|   std::match_results<const char*> result; | |
|   std::regex_match(opts.c_str(), result, option_specifier); | |
| 
 | |
|   if (result.empty()) | |
|   { | |
|     throw invalid_option_format_error(opts); | |
|   } | |
| 
 | |
|   const auto& s = result[2]; | |
|   const auto& l = result[3]; | |
| 
 | |
|   m_options.add_option(m_group, s.str(), l.str(), desc, value, | |
|     std::move(arg_help)); | |
| 
 | |
|   return *this; | |
| } | |
| 
 | |
| void | |
| Options::parse_option | |
| ( | |
|   std::shared_ptr<OptionDetails> value, | |
|   const std::string& /*name*/, | |
|   const std::string& arg | |
| ) | |
| { | |
|   value->parse(arg); | |
| } | |
| 
 | |
| void | |
| Options::checked_parse_arg | |
| ( | |
|   int argc, | |
|   char* argv[], | |
|   int& current, | |
|   std::shared_ptr<OptionDetails> value, | |
|   const std::string& name | |
| ) | |
| { | |
|   if (current + 1 >= argc) | |
|   { | |
|     if (value->value().has_implicit()) | |
|     { | |
|       parse_option(value, name, ""); | |
|     } | |
|     else | |
|     { | |
|       throw missing_argument_exception(name); | |
|     } | |
|   } | |
|   else | |
|   { | |
|     if (argv[current + 1][0] == '-' && value->value().has_implicit()) | |
|     { | |
|       parse_option(value, name, ""); | |
|     } | |
|     else | |
|     { | |
|       parse_option(value, name, argv[current + 1]); | |
|       ++current; | |
|     } | |
|   } | |
| } | |
| 
 | |
| void | |
| Options::add_to_option(const std::string& option, const std::string& arg) | |
| { | |
|   auto iter = m_options.find(option); | |
| 
 | |
|   if (iter == m_options.end()) | |
|   { | |
|     throw option_not_exists_exception(option); | |
|   } | |
| 
 | |
|   parse_option(iter->second, option, arg); | |
| } | |
| 
 | |
| bool | |
| Options::consume_positional(std::string a) | |
| { | |
|   if (m_positional.size() > 0) | |
|   { | |
|     add_to_option(m_positional, a); | |
|     return true; | |
|   } | |
|   else | |
|   { | |
|     return false; | |
|   } | |
| } | |
| 
 | |
| void | |
| Options::parse_positional(std::string option) | |
| { | |
|   m_positional = std::move(option); | |
| } | |
| 
 | |
| void | |
| Options::parse(int& argc, char**& argv) | |
| { | |
|   int current = 1; | |
| 
 | |
|   int nextKeep = 1; | |
| 
 | |
|   while (current != argc) | |
|   { | |
|     std::match_results<const char*> result; | |
|     std::regex_match(argv[current], result, option_matcher); | |
| 
 | |
|     if (result.empty()) | |
|     { | |
|       //not a flag | |
|  | |
|       //if true is returned here then it was consumed, otherwise it is | |
|       //ignored | |
|       if (consume_positional(argv[current])) | |
|       { | |
|       } | |
|       else | |
|       { | |
|         argv[nextKeep] = argv[current]; | |
|         ++nextKeep; | |
|       } | |
|       //if we return from here then it was parsed successfully, so continue | |
|     } | |
|     else | |
|     { | |
|       //short or long option? | |
|       if (result[4].length() != 0) | |
|       { | |
|         const std::string& s = result[4]; | |
| 
 | |
|         for (std::size_t i = 0; i != s.size(); ++i) | |
|         { | |
|           std::string name(1, s[i]); | |
|           auto iter = m_options.find(name); | |
| 
 | |
|           if (iter == m_options.end()) | |
|           { | |
|             throw option_not_exists_exception(name); | |
|           } | |
| 
 | |
|           auto value = iter->second; | |
| 
 | |
|           //if no argument then just add it | |
|           if (!value->has_arg()) | |
|           { | |
|             parse_option(value, name); | |
|           } | |
|           else | |
|           { | |
|             //it must be the last argument | |
|             if (i + 1 == s.size()) | |
|             { | |
|               checked_parse_arg(argc, argv, current, value, name); | |
|             } | |
|             else if (value->value().has_implicit()) | |
|             { | |
|               parse_option(value, name, ""); | |
|             } | |
|             else | |
|             { | |
|               //error | |
|               throw option_requires_argument_exception(name); | |
|             } | |
|           } | |
|         } | |
|       } | |
|       else if (result[1].length() != 0) | |
|       { | |
|         const std::string& name = result[1]; | |
| 
 | |
|         auto iter = m_options.find(name); | |
| 
 | |
|         if (iter == m_options.end()) | |
|         { | |
|           throw option_not_exists_exception(name); | |
|         } | |
| 
 | |
|         auto opt = iter->second; | |
| 
 | |
|         //equals provided for long option? | |
|         if (result[3].length() != 0) | |
|         { | |
|           //parse the option given | |
|  | |
|           //but if it doesn't take an argument, this is an error | |
|           if (!opt->has_arg()) | |
|           { | |
|             throw option_not_has_argument_exception(name, result[3]); | |
|           } | |
| 
 | |
|           parse_option(opt, name, result[3]); | |
|         } | |
|         else | |
|         { | |
|           if (opt->has_arg()) | |
|           { | |
|             //parse the next argument | |
|             checked_parse_arg(argc, argv, current, opt, name); | |
|           } | |
|           else | |
|           { | |
|             //parse with empty argument | |
|             parse_option(opt, name); | |
|           } | |
|         } | |
|       } | |
| 
 | |
|     } | |
| 
 | |
|     ++current; | |
|   } | |
| 
 | |
|   for (auto& opt : m_options) | |
|   { | |
|     auto& detail = opt.second; | |
|     auto& value = detail->value(); | |
| 
 | |
|     if(!detail->count() && value.has_default()){ | |
|       detail->parse_default(); | |
|     } | |
|   } | |
| 
 | |
|   argc = nextKeep; | |
| } | |
| 
 | |
| void | |
| Options::add_option | |
| ( | |
|   const std::string& group, | |
|   const std::string& s, | |
|   const std::string& l, | |
|   std::string desc, | |
|   std::shared_ptr<const Value> value, | |
|   std::string arg_help | |
| ) | |
| { | |
|   auto stringDesc = toLocalString(std::move(desc)); | |
|   auto option = std::make_shared<OptionDetails>(stringDesc, value); | |
| 
 | |
|   if (s.size() > 0) | |
|   { | |
|     add_one_option(s, option); | |
|   } | |
| 
 | |
|   if (l.size() > 0) | |
|   { | |
|     add_one_option(l, option); | |
|   } | |
| 
 | |
|   //add the help details | |
|   auto& options = m_help[group]; | |
| 
 | |
|   options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, | |
|       value->has_arg(), | |
|       value->has_default(), value->get_default_value(), | |
|       value->has_implicit(), value->get_implicit_value(), | |
|       std::move(arg_help)}); | |
| } | |
| 
 | |
| void | |
| Options::add_one_option | |
| ( | |
|   const std::string& option, | |
|   std::shared_ptr<OptionDetails> details | |
| ) | |
| { | |
|   auto in = m_options.emplace(option, details); | |
| 
 | |
|   if (!in.second) | |
|   { | |
|     throw option_exists_error(option); | |
|   } | |
| } | |
| 
 | |
| String | |
| Options::help_one_group(const std::string& g) const | |
| { | |
|   typedef std::vector<std::pair<String, String>> OptionHelp; | |
| 
 | |
|   auto group = m_help.find(g); | |
|   if (group == m_help.end()) | |
|   { | |
|     return ""; | |
|   } | |
| 
 | |
|   OptionHelp format; | |
| 
 | |
|   size_t longest = 0; | |
| 
 | |
|   String result; | |
| 
 | |
|   if (!g.empty()) | |
|   { | |
|     result += toLocalString(" " + g + " options:\n\n"); | |
|   } | |
| 
 | |
|   for (const auto& o : group->second.options) | |
|   { | |
|     auto s = format_option(o); | |
|     longest = std::max(longest, stringLength(s)); | |
|     format.push_back(std::make_pair(s, String())); | |
|   } | |
| 
 | |
|   longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST)); | |
| 
 | |
|   //widest allowed description | |
|   auto allowed = size_t{76} - longest - OPTION_DESC_GAP; | |
| 
 | |
|   auto fiter = format.begin(); | |
|   for (const auto& o : group->second.options) | |
|   { | |
|     auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); | |
| 
 | |
|     result += fiter->first; | |
|     if (stringLength(fiter->first) > longest) | |
|     { | |
|       result += "\n"; | |
|       result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); | |
|     } | |
|     else | |
|     { | |
|       result += toLocalString(std::string(longest + OPTION_DESC_GAP - | |
|         stringLength(fiter->first), | |
|         ' ')); | |
|     } | |
|     result += d; | |
|     result += "\n"; | |
| 
 | |
|     ++fiter; | |
|   } | |
| 
 | |
|   return result; | |
| } | |
| 
 | |
| std::string | |
| Options::help(const std::vector<std::string>& groups) const | |
| { | |
|   String result = "Usage:\n  " + toLocalString(m_program) + " [OPTION...]" | |
|     + m_help_string + "\n\n"; | |
| 
 | |
|   for (std::size_t i = 0; i < groups.size(); ++i) | |
|   { | |
|     result += help_one_group(groups[i]); | |
|     if (i < groups.size() - 1) | |
|     { | |
|       result += "\n"; | |
|     } | |
|   } | |
| 
 | |
|   return toUTF8String(result); | |
| } | |
| 
 | |
| const std::vector<std::string> | |
| Options::groups() const | |
| { | |
|   std::vector<std::string> g; | |
| 
 | |
|   std::transform( | |
|     m_help.begin(), | |
|     m_help.end(), | |
|     std::back_inserter(g), | |
|     [] (const std::map<std::string, HelpGroupDetails>::value_type& pair) | |
|     { | |
|       return pair.first; | |
|     } | |
|   ); | |
| 
 | |
|   return g; | |
| } | |
| 
 | |
| const HelpGroupDetails& | |
| Options::group_help(const std::string& group) const | |
| { | |
|   return m_help.at(group); | |
| } | |
| 
 | |
| } | |
| 
 | |
| #if defined(__GNU__) | |
| #pragma GCC diagnostic pop | |
| #endif | |
|  | |
| #endif //CXX_OPTS_HPP
 |