Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gebaar-libinput for openSUSE:Factory checked in at 2021-06-19 23:03:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gebaar-libinput (Old) and /work/SRC/openSUSE:Factory/.gebaar-libinput.new.2625 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gebaar-libinput" Sat Jun 19 23:03:14 2021 rev:2 rq:900729 version:0.0.5 Changes: -------- --- /work/SRC/openSUSE:Factory/gebaar-libinput/gebaar-libinput.changes 2019-04-30 12:58:29.234132690 +0200 +++ /work/SRC/openSUSE:Factory/.gebaar-libinput.new.2625/gebaar-libinput.changes 2021-06-19 23:03:44.291735884 +0200 @@ -1,0 +2,7 @@ +Mon May 31 20:17:53 UTC 2021 - Christophe Giboudeaux <christo...@krop.fr> + +- Add Patch to fix build with GCC 11: + * 0001-g-11-requires-limits-header.patch +- Update cxxopts to 2.1.2 + +------------------------------------------------------------------- Old: ---- cxxopts-2.1.2.tar.gz New: ---- 0001-g-11-requires-limits-header.patch cxxopts-2.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gebaar-libinput.spec ++++++ --- /var/tmp/diff_new_pack.2tpM5j/_old 2021-06-19 23:03:44.703736519 +0200 +++ /var/tmp/diff_new_pack.2tpM5j/_new 2021-06-19 23:03:44.707736526 +0200 @@ -17,7 +17,7 @@ %define cpptoml 0.1.1 -%define cxxopts 2.1.2 +%define cxxopts 2.2.1 Name: gebaar-libinput Version: 0.0.5 Release: 0 @@ -29,6 +29,8 @@ Source1: https://github.com/skystrife/cpptoml/archive/v%{cpptoml}.tar.gz#/cpptoml-%{cpptoml}.tar.gz Source2: https://github.com/jarro2783/cxxopts/archive/v%{cxxopts}.tar.gz#/cxxopts-%{cxxopts}.tar.gz Patch0: cmake-version.patch +# PATCH-FIX-UPSTREAM https://github.com/skystrife/cpptoml/pull/123 +Patch1: 0001-g-11-requires-limits-header.patch BuildRequires: cmake %if 0%{?suse_version} == 1500 BuildRequires: gcc8 @@ -53,6 +55,7 @@ %patch0 tar -xzf %{SOURCE1} -C libs/cpptoml --strip-components=1 tar -xzf %{SOURCE2} -C libs/cxxopts --strip-components=1 +%patch1 -p1 %build %if 0%{?suse_version} == 1500 ++++++ 0001-g-11-requires-limits-header.patch ++++++ >From 02e34e3a7ae81b0209f131623d978cce9a49e6a7 Mon Sep 17 00:00:00 2001 From: Dirk Eddelbuettel <e...@debian.org> Date: Mon, 30 Nov 2020 09:41:49 -0600 Subject: [PATCH] g++-11 requires limits header --- libs/cpptoml/include/cpptoml.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/cpptoml/include/cpptoml.h b/libs/cpptoml/include/cpptoml.h index 5a00da3..1dc9fd1 100644 --- a/libs/cpptoml/include/cpptoml.h +++ b/libs/cpptoml/include/cpptoml.h @@ -14,6 +14,7 @@ #include <cstring> #include <fstream> #include <iomanip> +#include <limits> #include <map> #include <memory> #include <sstream> -- 2.31.1 ++++++ cxxopts-2.1.2.tar.gz -> cxxopts-2.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/CHANGELOG.md new/cxxopts-2.2.1/CHANGELOG.md --- old/cxxopts-2.1.2/CHANGELOG.md 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/CHANGELOG.md 2020-08-04 00:59:54.000000000 +0200 @@ -3,11 +3,42 @@ This is the changelog for `cxxopts`, a C++11 library for parsing command line options. The project adheres to semantic versioning. -## 2.1.2 +## 2.2.1 + +### Changed + +* Only search for a C++ compiler in CMakeLists.txt. +* Allow installing to be disabled. + +### Bug Fixes + +* Fix error printing long help lines. +* Fix duplicate default options when there is a short and long option. + +## 2.2 + +### Changed + +* Allow integers to have leading zeroes. +* Build the tests by default. +* Don't check for container when showing positional help. + +### Added + +* Iterator inputs to `parse_positional`. +* Throw an exception if the option in `parse_positional` doesn't exist. +* Parse a delimited list in a single argument for vector options. +* Add an option to disable implicit value on booleans. ### Bug Fixes -* Use `std::forward` instead of returning a copy in `toLocalString`. +* Fix a warning about possible loss of data. +* Fix version numbering in CMakeLists.txt +* Remove unused declaration of the undefined `ParseResult::get_option`. +* Throw on invalid option syntax when beginning with a `-`. +* Throw in `as` when option wasn't present. +* Fix catching exceptions by reference. +* Fix out of bounds errors parsing integers. ## 2.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/CMakeLists.txt new/cxxopts-2.2.1/CMakeLists.txt --- old/cxxopts-2.1.2/CMakeLists.txt 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/CMakeLists.txt 2020-08-04 00:59:54.000000000 +0200 @@ -18,14 +18,25 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. cmake_minimum_required(VERSION 3.1) -project(cxxopts) -enable_testing() +# parse the current version from the cxxopts header +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/cxxopts.hpp" cxxopts_version_defines + REGEX "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH)") +foreach(ver ${cxxopts_version_defines}) + if(ver MATCHES "#define CXXOPTS__VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") + set(CXXOPTS__VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") + endif() +endforeach() +set(VERSION ${CXXOPTS__VERSION_MAJOR}.${CXXOPTS__VERSION_MINOR}.${CXXOPTS__VERSION_PATCH}) +message(STATUS "cxxopts version ${VERSION}") + +project(cxxopts VERSION "${VERSION}" LANGUAGES CXX) -set(VERSION "2.1.2") +enable_testing() option(CXXOPTS_BUILD_EXAMPLES "Set to ON to build examples" ON) -option(CXXOPTS_BUILD_TESTS "Set to ON to build tests" OFF) +option(CXXOPTS_BUILD_TESTS "Set to ON to build tests" ON) +option(CXXOPTS_ENABLE_INSTALL "Generate the install target" ON) # request c++11 without gnu extension for the whole project and enable more warnings if (CXXOPTS_CXX_STANDARD) @@ -60,35 +71,37 @@ $<INSTALL_INTERFACE:include> ) -include(CMakePackageConfigHelpers) -set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING - "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") -set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") -set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") -set(targets_export_name cxxopts-targets) - -# Generate the version, config and target files into the build directory. -write_basic_package_version_file( - ${version_config} - VERSION ${VERSION} - COMPATIBILITY AnyNewerVersion) -configure_package_config_file( - ${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in - ${project_config} - INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) -export(TARGETS cxxopts NAMESPACE cxxopts:: - FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) - -# Install version, config and target files. -install( - FILES ${project_config} ${version_config} - DESTINATION ${CXXOPTS_CMAKE_DIR}) -install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} - NAMESPACE cxxopts::) - -# Install the header file and export the target -install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) -install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) +if(CXXOPTS_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING + "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") + set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") + set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") + set(targets_export_name cxxopts-targets) + + # Generate the version, config and target files into the build directory. + write_basic_package_version_file( + ${version_config} + VERSION ${VERSION} + COMPATIBILITY AnyNewerVersion) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in + ${project_config} + INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) + export(TARGETS cxxopts NAMESPACE cxxopts:: + FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) + + # Install version, config and target files. + install( + FILES ${project_config} ${version_config} + DESTINATION ${CXXOPTS_CMAKE_DIR}) + install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} + NAMESPACE cxxopts::) + + # Install the header file and export the target + install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) + install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) +endif() add_subdirectory(src) add_subdirectory(test) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/INSTALL new/cxxopts-2.2.1/INSTALL --- old/cxxopts-2.1.2/INSTALL 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/INSTALL 2020-08-04 00:59:54.000000000 +0200 @@ -1,10 +1,23 @@ -It is preferable to build out of source. +== System installation == -cmake ${CXXOPTS_DIR} -make +This library is header only. So you can either copy `include/cxxopts.hpp` to `/usr/include` or `/usr/local/include`, or add `include` to your search path. +== Building the examples and tests == + +It is preferable to build out of source. Make a build directory somewhere, and then +do the following, where `${CXXOPTS_DIR}` is the path that you checked out `cxxopts` +to: + + cmake ${CXXOPTS_DIR} + make You can use another build tool, such as ninja. -cmake -G Ninja ${CXXOPTS_DIR} -ninja + cmake -G Ninja ${CXXOPTS_DIR} + ninja + + +To run the tests, you have to configure `cxxopts` with another flag: + cmake -D CXXOPTS_BUILD_TESTS=On ${CXXOPTS_DIR} + make + make test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/README.md new/cxxopts-2.2.1/README.md --- old/cxxopts-2.1.2/README.md 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/README.md 2020-08-04 00:59:54.000000000 +0200 @@ -111,8 +111,22 @@ Boolean options have a default implicit value of `"true"`, which can be overridden. The effect is that writing `-o` by itself will set option `o` to -`true`. However, they can also be written with various strings using either -`=value` or the next argument. +`true`. However, they can also be written with various strings using `=value`. +There is no way to disambiguate positional arguments from the value following +a boolean, so we have chosen that they will be positional arguments, and +therefore, `-o false` does not work. + +## `std::vector<T>` values + +Parsing of list of values in form of an `std::vector<T>` is also supported, as long as `T` +can be parsed. To separate single values in a list the definition `CXXOPTS_VECTOR_DELIMITER` +is used, which is ',' by default. Ensure that you use no whitespaces between values because +those would be interpreted as the next command line option. Example for a command line option +that can be parsed as a `std::vector<double>`: + +~~~ +--my_list=1,-2.1,3,4.5 +~~~ ## Custom help diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/include/cxxopts.hpp new/cxxopts-2.2.1/include/cxxopts.hpp --- old/cxxopts-2.1.2/include/cxxopts.hpp 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/include/cxxopts.hpp 2020-08-04 00:59:54.000000000 +0200 @@ -29,6 +29,7 @@ #include <cctype> #include <exception> #include <iostream> +#include <limits> #include <map> #include <memory> #include <regex> @@ -43,11 +44,23 @@ #define CXXOPTS_HAS_OPTIONAL #endif +#ifndef CXXOPTS_VECTOR_DELIMITER +#define CXXOPTS_VECTOR_DELIMITER ',' +#endif + +#define CXXOPTS__VERSION_MAJOR 2 +#define CXXOPTS__VERSION_MINOR 2 +#define CXXOPTS__VERSION_PATCH 0 + namespace cxxopts { static constexpr struct { uint8_t major, minor, patch; - } version = {2, 1, 2}; + } version = { + CXXOPTS__VERSION_MAJOR, + CXXOPTS__VERSION_MINOR, + CXXOPTS__VERSION_PATCH + }; } //when we ask cxxopts to use Unicode, help strings are processed using ICU, @@ -301,6 +314,9 @@ virtual std::shared_ptr<Value> implicit_value(const std::string& value) = 0; + virtual std::shared_ptr<Value> + no_implicit_value() = 0; + virtual bool is_boolean() const = 0; }; @@ -346,7 +362,7 @@ { public: option_exists_error(const std::string& option) - : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + u8" already exists") + : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") { } }; @@ -355,7 +371,16 @@ { public: invalid_option_format_error(const std::string& format) - : OptionSpecException(u8"Invalid option format " + LQUOTE + format + RQUOTE) + : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) + { + } + }; + + class option_syntax_exception : public OptionParseException { + public: + option_syntax_exception(const std::string& text) + : OptionParseException("Argument " + LQUOTE + text + RQUOTE + + " starts with a - but has incorrect syntax") { } }; @@ -364,7 +389,7 @@ { public: option_not_exists_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" does not exist") + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") { } }; @@ -374,7 +399,7 @@ public: missing_argument_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is missing an argument" + "Option " + LQUOTE + option + RQUOTE + " is missing an argument" ) { } @@ -385,7 +410,7 @@ public: option_requires_argument_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" requires an argument" + "Option " + LQUOTE + option + RQUOTE + " requires an argument" ) { } @@ -400,8 +425,8 @@ const std::string& arg ) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + - u8" does not take an argument, but argument " + + "Option " + LQUOTE + option + RQUOTE + + " does not take an argument, but argument " + LQUOTE + arg + RQUOTE + " given" ) { @@ -412,7 +437,7 @@ { public: option_not_present_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" not present") + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") { } }; @@ -425,7 +450,7 @@ const std::string& arg ) : OptionParseException( - u8"Argument " + LQUOTE + arg + RQUOTE + u8" failed to parse" + "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" ) { } @@ -436,7 +461,7 @@ public: option_required_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is required but not present" + "Option " + LQUOTE + option + RQUOTE + " is required but not present" ) { } @@ -447,11 +472,11 @@ namespace { std::basic_regex<char> integer_pattern - ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|((0x)?0)"); + ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); std::basic_regex<char> truthy_pattern - ("(t|T)(rue)?"); + ("(t|T)(rue)?|1"); std::basic_regex<char> falsy_pattern - ("((f|F)(alse)?)?"); + ("(f|F)(alse)?|0"); } namespace detail @@ -468,14 +493,14 @@ { if (negative) { - if (u > static_cast<U>(-std::numeric_limits<T>::min())) + if (u > static_cast<U>((std::numeric_limits<T>::min)())) { throw argument_incorrect_type(text); } } else { - if (u > static_cast<U>(std::numeric_limits<T>::max())) + if (u > static_cast<U>((std::numeric_limits<T>::max)())) { throw argument_incorrect_type(text); } @@ -506,7 +531,7 @@ // if we got to here, then `t` is a positive number that fits into // `R`. So to avoid MSVC C4146, we first cast it to `R`. // See https://github.com/jarro2783/cxxopts/issues/62 for more details. - return -static_cast<R>(t); + return -static_cast<R>(t-1)-1; } template <typename R, typename T> @@ -536,7 +561,6 @@ using US = typename std::make_unsigned<T>::type; - constexpr auto umax = std::numeric_limits<US>::max(); constexpr bool is_signed = std::numeric_limits<T>::is_signed; const bool negative = match.length(1) > 0; const uint8_t base = match.length(2) > 0 ? 16 : 10; @@ -547,31 +571,32 @@ for (auto iter = value_match.first; iter != value_match.second; ++iter) { - size_t digit = 0; + US digit = 0; if (*iter >= '0' && *iter <= '9') { - digit = *iter - '0'; + digit = static_cast<US>(*iter - '0'); } else if (base == 16 && *iter >= 'a' && *iter <= 'f') { - digit = *iter - 'a' + 10; + digit = static_cast<US>(*iter - 'a' + 10); } else if (base == 16 && *iter >= 'A' && *iter <= 'F') { - digit = *iter - 'A' + 10; + digit = static_cast<US>(*iter - 'A' + 10); } else { throw argument_incorrect_type(text); } - if (umax - digit < result * base) + US next = result * base + digit; + if (result > next) { throw argument_incorrect_type(text); } - result = result * base + digit; + result = next; } detail::check_signed_range<T>(negative, result, text); @@ -584,7 +609,7 @@ } else { - value = result; + value = static_cast<T>(result); } } @@ -697,9 +722,13 @@ void parse_value(const std::string& text, std::vector<T>& value) { - T v; - parse_value(text, v); - value.push_back(v); + std::stringstream in(text); + std::string token; + while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { + T v; + parse_value(token, v); + value.emplace_back(std::move(v)); + } } #ifdef CXXOPTS_HAS_OPTIONAL @@ -808,6 +837,13 @@ return shared_from_this(); } + std::shared_ptr<Value> + no_implicit_value() + { + m_implicit = false; + return shared_from_this(); + } + std::string get_default_value() const { @@ -1018,19 +1054,31 @@ parse_default(std::shared_ptr<const OptionDetails> details) { ensure_value(details); + m_default = true; m_value->parse(); } size_t - count() const + count() const noexcept { return m_count; } + // TODO: maybe default options should count towards the number of arguments + bool + has_default() const noexcept + { + return m_default; + } + template <typename T> const T& as() const { + if (m_value == nullptr) { + throw std::domain_error("No value"); + } + #ifdef CXXOPTS_NO_RTTI return static_cast<const values::standard_value<T>&>(*m_value).get(); #else @@ -1050,6 +1098,7 @@ std::shared_ptr<Value> m_value; size_t m_count = 0; + bool m_default = false; }; class KeyValue @@ -1068,7 +1117,8 @@ return m_key; } - const std::string + const + std::string& value() const { return m_value; @@ -1093,15 +1143,18 @@ public: ParseResult( - const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>&, + const std::shared_ptr< + std::unordered_map<std::string, std::shared_ptr<OptionDetails>> + >, std::vector<std::string>, + bool allow_unrecognised, int&, char**&); size_t count(const std::string& o) const { - auto iter = m_options.find(o); - if (iter == m_options.end()) + auto iter = m_options->find(o); + if (iter == m_options->end()) { return 0; } @@ -1114,9 +1167,9 @@ const OptionValue& operator[](const std::string& option) const { - auto iter = m_options.find(option); + auto iter = m_options->find(option); - if (iter == m_options.end()) + if (iter == m_options->end()) { throw option_not_present_exception(option); } @@ -1134,9 +1187,6 @@ private: - OptionValue& - get_option(std::shared_ptr<OptionDetails>); - void parse(int& argc, char**& argv); @@ -1167,18 +1217,23 @@ const std::string& name ); - const std::unordered_map<std::string, std::shared_ptr<OptionDetails>> - &m_options; + const std::shared_ptr< + std::unordered_map<std::string, std::shared_ptr<OptionDetails>> + > m_options; std::vector<std::string> m_positional; std::vector<std::string>::iterator m_next_positional; std::unordered_set<std::string> m_positional_set; std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; + bool m_allow_unrecognised; + std::vector<KeyValue> m_sequential; }; class Options { + typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> + OptionMap; public: Options(std::string program, std::string help_string = "") @@ -1187,6 +1242,8 @@ , m_custom_help("[OPTION...]") , m_positional_help("positional parameters") , m_show_positional(false) + , m_allow_unrecognised(false) + , m_options(std::make_shared<OptionMap>()) , m_next_positional(m_positional.end()) { } @@ -1212,6 +1269,13 @@ return *this; } + Options& + allow_unrecognised_options() + { + m_allow_unrecognised = true; + return *this; + } + ParseResult parse(int& argc, char**& argv); @@ -1239,8 +1303,14 @@ void parse_positional(std::initializer_list<std::string> options); + template <typename Iterator> + void + parse_positional(Iterator begin, Iterator end) { + parse_positional(std::vector<std::string>{begin, end}); + } + std::string - help(const std::vector<std::string>& groups = {""}) const; + help(const std::vector<std::string>& groups = {}) const; const std::vector<std::string> groups() const; @@ -1275,8 +1345,9 @@ std::string m_custom_help; std::string m_positional_help; bool m_show_positional; + bool m_allow_unrecognised; - std::unordered_map<std::string, std::shared_ptr<OptionDetails>> m_options; + std::shared_ptr<OptionMap> m_options; std::vector<std::string> m_positional; std::vector<std::string>::iterator m_next_positional; std::unordered_set<std::string> m_positional_set; @@ -1392,7 +1463,12 @@ lastSpace = current; } - if (size > width) + if (*current == '\n') + { + startLine = current + 1; + lastSpace = startLine; + } + else if (size > width) { if (lastSpace == startLine) { @@ -1408,6 +1484,7 @@ stringAppend(result, "\n"); stringAppend(result, start, ' '); startLine = lastSpace + 1; + lastSpace = startLine; } size = 0; } @@ -1429,13 +1506,17 @@ inline ParseResult::ParseResult ( - const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>& options, + const std::shared_ptr< + std::unordered_map<std::string, std::shared_ptr<OptionDetails>> + > options, std::vector<std::string> positional, + bool allow_unrecognised, int& argc, char**& argv ) : m_options(options) , m_positional(std::move(positional)) , m_next_positional(m_positional.begin()) +, m_allow_unrecognised(allow_unrecognised) { parse(argc, argv); } @@ -1567,9 +1648,9 @@ void ParseResult::add_to_option(const std::string& option, const std::string& arg) { - auto iter = m_options.find(option); + auto iter = m_options->find(option); - if (iter == m_options.end()) + if (iter == m_options->end()) { throw option_not_exists_exception(option); } @@ -1583,8 +1664,8 @@ { while (m_next_positional != m_positional.end()) { - auto iter = m_options.find(*m_next_positional); - if (iter != m_options.end()) + auto iter = m_options->find(*m_next_positional); + if (iter != m_options->end()) { auto& result = m_results[iter->second]; if (!iter->second->value().is_container()) @@ -1607,7 +1688,10 @@ return true; } } - ++m_next_positional; + else + { + throw option_not_exists_exception(*m_next_positional); + } } return false; @@ -1641,7 +1725,7 @@ ParseResult Options::parse(int& argc, char**& argv) { - ParseResult result(m_options, m_positional, argc, argv); + ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); return result; } @@ -1671,6 +1755,13 @@ { //not a flag + // but if it starts with a `-`, then it's an error + if (argv[current][0] == '-' && argv[current][1] != '\0') { + if (!m_allow_unrecognised) { + throw option_syntax_exception(argv[current]); + } + } + //if true is returned here then it was consumed, otherwise it is //ignored if (consume_positional(argv[current])) @@ -1693,11 +1784,19 @@ for (std::size_t i = 0; i != s.size(); ++i) { std::string name(1, s[i]); - auto iter = m_options.find(name); + auto iter = m_options->find(name); - if (iter == m_options.end()) + if (iter == m_options->end()) { - throw option_not_exists_exception(name); + if (m_allow_unrecognised) + { + continue; + } + else + { + //error + throw option_not_exists_exception(name); + } } auto value = iter->second; @@ -1722,11 +1821,23 @@ { const std::string& name = result[1]; - auto iter = m_options.find(name); + auto iter = m_options->find(name); - if (iter == m_options.end()) + if (iter == m_options->end()) { - throw option_not_exists_exception(name); + if (m_allow_unrecognised) + { + // keep unrecognised options in argument list, skip to next argument + argv[nextKeep] = argv[current]; + ++nextKeep; + ++current; + continue; + } + else + { + //error + throw option_not_exists_exception(name); + } } auto opt = iter->second; @@ -1750,14 +1861,14 @@ ++current; } - for (auto& opt : m_options) + for (auto& opt : *m_options) { auto& detail = opt.second; auto& value = detail->value(); auto& store = m_results[detail]; - if(!store.count() && value.has_default()){ + if(value.has_default() && !store.count() && !store.has_default()){ parse_default(detail); } } @@ -1828,7 +1939,7 @@ std::shared_ptr<OptionDetails> details ) { - auto in = m_options.emplace(option, details); + auto in = m_options->emplace(option, details); if (!in.second) { @@ -1861,19 +1972,18 @@ for (const auto& o : group->second.options) { - if (o.is_container && - m_positional_set.find(o.l) != m_positional_set.end() && + if (m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) { continue; } auto s = format_option(o); - longest = std::max(longest, stringLength(s)); + longest = (std::max)(longest, stringLength(s)); format.push_back(std::make_pair(s, String())); } - longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST)); + longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST)); //widest allowed description auto allowed = size_t{76} - longest - OPTION_DESC_GAP; @@ -1881,8 +1991,7 @@ auto fiter = format.begin(); for (const auto& o : group->second.options) { - if (o.is_container && - m_positional_set.find(o.l) != m_positional_set.end() && + if (m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) { continue; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/src/example.cpp new/cxxopts-2.2.1/src/example.cpp --- old/cxxopts-2.1.2/src/example.cpp 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/src/example.cpp 2020-08-04 00:59:54.000000000 +0200 @@ -26,7 +26,8 @@ #include "cxxopts.hpp" -int main(int argc, char* argv[]) +cxxopts::ParseResult +parse(int argc, char* argv[]) { try { @@ -37,7 +38,9 @@ bool apple = false; - options.add_options() + options + .allow_unrecognised_options() + .add_options() ("a,apple", "an apple", cxxopts::value<bool>(apple)) ("b,bob", "Bob") ("t,true", "True", cxxopts::value<bool>()->default_value("true")) @@ -53,6 +56,7 @@ ("help", "Print help") ("int", "An integer", cxxopts::value<int>(), "N") ("float", "A floating point number", cxxopts::value<float>()) + ("vector", "A list of doubles", cxxopts::value<std::vector<double>>()) ("option_that_is_too_long_for_the_help", "A very long option") #ifdef CXXOPTS_USE_UNICODE ("unicode", u8"A help option with non-ascii: ??. Here the size of the" @@ -127,13 +131,32 @@ std::cout << "float = " << result["float"].as<float>() << std::endl; } + if (result.count("vector")) + { + std::cout << "vector = "; + const auto values = result["vector"].as<std::vector<double>>(); + for (const auto& v : values) { + std::cout << v << ", "; + } + std::cout << std::endl; + } + std::cout << "Arguments remain = " << argc << std::endl; + return result; + } catch (const cxxopts::OptionException& e) { std::cout << "error parsing options: " << e.what() << std::endl; exit(1); } +} + +int main(int argc, char* argv[]) +{ + auto result = parse(argc, argv); + auto arguments = result.arguments(); + std::cout << "Saw " << arguments.size() << " arguments" << std::endl; return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cxxopts-2.1.2/test/options.cpp new/cxxopts-2.2.1/test/options.cpp --- old/cxxopts-2.1.2/test/options.cpp 2019-01-13 22:12:07.000000000 +0100 +++ new/cxxopts-2.2.1/test/options.cpp 2020-08-04 00:59:54.000000000 +0200 @@ -53,6 +53,7 @@ ("a,av", "a short option with a value", cxxopts::value<std::string>()) ("6,six", "a short number option") ("p, space", "an option with space between short and long") + ("nothing", "won't exist", cxxopts::value<std::string>()) ; Argv argv({ @@ -92,6 +93,8 @@ CHECK(arguments[1].key() == "short"); CHECK(arguments[2].key() == "value"); CHECK(arguments[3].key() == "av"); + + CHECK_THROWS_AS(result["nothing"].as<std::string>(), std::domain_error&); } TEST_CASE("Short options", "[options]") @@ -111,8 +114,8 @@ CHECK(result.count("a") == 1); CHECK(result["a"].as<std::string>() == "value"); - REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), - cxxopts::invalid_option_format_error); + REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), + cxxopts::invalid_option_format_error&); } TEST_CASE("No positional", "[positional]") @@ -145,7 +148,9 @@ auto argc = av.argc(); auto argv = av.argv(); - options.parse_positional({"positional"}); + std::vector<std::string> pos_names = {"positional"}; + + options.parse_positional(pos_names.begin(), pos_names.end()); auto result = options.parse(argc, argv); @@ -211,6 +216,22 @@ CHECK(argv[1] == std::string("a")); } +TEST_CASE("Positional not valid", "[positional]") { + cxxopts::Options options("positional_invalid", "invalid positional argument"); + options.add_options() + ("long", "a long option", cxxopts::value<std::string>()) + ; + + options.parse_positional("something"); + + Argv av({"foobar", "bar", "baz"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); +} + TEST_CASE("Empty with implicit value", "[implicit]") { cxxopts::Options options("empty_implicit", "doesn't handle empty"); @@ -229,12 +250,75 @@ REQUIRE(result["implicit"].as<std::string>() == ""); } +TEST_CASE("Boolean without implicit value", "[implicit]") +{ + cxxopts::Options options("no_implicit", "bool without an implicit value"); + options.add_options() + ("bool", "Boolean without implicit", cxxopts::value<bool>() + ->no_implicit_value()); + + SECTION("When no value provided") { + Argv av({"no_implicit", "--bool"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::missing_argument_exception&); + } + + SECTION("With equal-separated true") { + Argv av({"no_implicit", "--bool=true"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + auto result = options.parse(argc, argv); + CHECK(result.count("bool") == 1); + CHECK(result["bool"].as<bool>() == true); + } + + SECTION("With equal-separated false") { + Argv av({"no_implicit", "--bool=false"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + auto result = options.parse(argc, argv); + CHECK(result.count("bool") == 1); + CHECK(result["bool"].as<bool>() == false); + } + + SECTION("With space-separated true") { + Argv av({"no_implicit", "--bool", "true"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + auto result = options.parse(argc, argv); + CHECK(result.count("bool") == 1); + CHECK(result["bool"].as<bool>() == true); + } + + SECTION("With space-separated false") { + Argv av({"no_implicit", "--bool", "false"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + auto result = options.parse(argc, argv); + CHECK(result.count("bool") == 1); + CHECK(result["bool"].as<bool>() == false); + } +} + TEST_CASE("Default values", "[default]") { cxxopts::Options options("defaults", "has defaults"); options.add_options() - ("default", "Has implicit", cxxopts::value<int>() - ->default_value("42")); + ("default", "Has implicit", cxxopts::value<int>()->default_value("42")) + ("v,vector", "Default vector", cxxopts::value<std::vector<int>>() + ->default_value("1,4")) + ; SECTION("Sets defaults") { Argv av({"implicit"}); @@ -245,6 +329,11 @@ auto result = options.parse(argc, argv); CHECK(result.count("default") == 0); CHECK(result["default"].as<int>() == 42); + + auto& v = result["vector"].as<std::vector<int>>(); + REQUIRE(v.size() == 2); + CHECK(v[0] == 1); + CHECK(v[1] == 4); } SECTION("When values provided") { @@ -304,6 +393,30 @@ CHECK(positional[6] == 0x0); } +TEST_CASE("Leading zero integers", "[options]") +{ + cxxopts::Options options("parses_integers", "parses integers correctly"); + options.add_options() + ("positional", "Integers", cxxopts::value<std::vector<int>>()); + + Argv av({"ints", "--", "05", "06", "0x0ab", "0x0001"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + options.parse_positional("positional"); + auto result = options.parse(argc, argv); + + REQUIRE(result.count("positional") == 4); + + auto& positional = result["positional"].as<std::vector<int>>(); + REQUIRE(positional.size() == 4); + CHECK(positional[0] == 5); + CHECK(positional[1] == 6); + CHECK(positional[2] == 0xab); + CHECK(positional[3] == 0x1); +} + TEST_CASE("Unsigned integers", "[options]") { cxxopts::Options options("parses_unsigned", "detects unsigned errors"); @@ -316,7 +429,7 @@ auto argc = av.argc(); options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type); + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); } TEST_CASE("Integer bounds", "[integer]") @@ -353,16 +466,18 @@ int8_t si; uint8_t ui; - CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::argument_incorrect_type); - CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::argument_incorrect_type); - CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::argument_incorrect_type); - CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::argument_incorrect_type); - CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::argument_incorrect_type); - CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::argument_incorrect_type); + CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::argument_incorrect_type&); } TEST_CASE("Integer overflow", "[options]") { + using namespace cxxopts::values; + cxxopts::Options options("reject_overflow", "rejects overflowing integers"); options.add_options() ("positional", "Integers", cxxopts::value<std::vector<int8_t>>()); @@ -373,7 +488,11 @@ auto argc = av.argc(); options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type); + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); + + int integer = 0; + CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::argument_incorrect_type&); } TEST_CASE("Floats", "[options]") @@ -414,7 +533,7 @@ auto argc = av.argc(); options.parse_positional("positional"); - CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type); + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); } TEST_CASE("Booleans", "[boolean]") { @@ -423,6 +542,8 @@ ("bool", "A Boolean", cxxopts::value<bool>()) ("debug", "Debugging", cxxopts::value<bool>()) ("timing", "Timing", cxxopts::value<bool>()) + ("verbose", "Verbose", cxxopts::value<bool>()) + ("dry-run", "Dry Run", cxxopts::value<bool>()) ("noExplicitDefault", "No Explicit Default", cxxopts::value<bool>()) ("defaultTrue", "Timing", cxxopts::value<bool>()->default_value("true")) ("defaultFalse", "Timing", cxxopts::value<bool>()->default_value("false")) @@ -431,7 +552,7 @@ options.parse_positional("others"); - Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "extra"}); + Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "--verbose=1", "--dry-run=0", "extra"}); char** argv = av.argv(); auto argc = av.argc(); @@ -441,6 +562,8 @@ REQUIRE(result.count("bool") == 1); REQUIRE(result.count("debug") == 1); REQUIRE(result.count("timing") == 1); + REQUIRE(result.count("verbose") == 1); + REQUIRE(result.count("dry-run") == 1); REQUIRE(result.count("noExplicitDefault") == 0); REQUIRE(result.count("defaultTrue") == 0); REQUIRE(result.count("defaultFalse") == 0); @@ -448,6 +571,8 @@ CHECK(result["bool"].as<bool>() == false); CHECK(result["debug"].as<bool>() == true); CHECK(result["timing"].as<bool>() == true); + CHECK(result["verbose"].as<bool>() == true); + CHECK(result["dry-run"].as<bool>() == false); CHECK(result["noExplicitDefault"].as<bool>() == false); CHECK(result["defaultTrue"].as<bool>() == true); CHECK(result["defaultFalse"].as<bool>() == false); @@ -455,6 +580,26 @@ REQUIRE(result.count("others") == 1); } +TEST_CASE("std::vector", "[vector]") { + std::vector<double> vector; + cxxopts::Options options("vector", " - tests vector"); + options.add_options() + ("vector", "an vector option", cxxopts::value<std::vector<double>>(vector)); + + Argv av({"vector", "--vector", "1,-2.1,3,4.5"}); + + char** argv = av.argv(); + auto argc = av.argc(); + + options.parse(argc, argv); + + REQUIRE(vector.size() == 4); + CHECK(vector[0] == 1); + CHECK(vector[1] == -2.1); + CHECK(vector[2] == 3); + CHECK(vector[3] == 4.5); +} + #ifdef CXXOPTS_HAS_OPTIONAL TEST_CASE("std::optional", "[optional]") { std::optional<std::string> optional; @@ -473,3 +618,76 @@ CHECK(*optional == "foo"); } #endif + +TEST_CASE("Unrecognised options", "[options]") { + cxxopts::Options options("unknown_options", " - test unknown options"); + + options.add_options() + ("long", "a long option") + ("s,short", "a short option"); + + Argv av({ + "unknown_options", + "--unknown", + "--long", + "-su", + "--another_unknown", + }); + + char** argv = av.argv(); + auto argc = av.argc(); + + SECTION("Default behaviour") { + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); + } + + SECTION("After allowing unrecognised options") { + options.allow_unrecognised_options(); + CHECK_NOTHROW(options.parse(argc, argv)); + REQUIRE(argc == 3); + CHECK_THAT(argv[1], Catch::Equals("--unknown")); + } +} + +TEST_CASE("Allow bad short syntax", "[options]") { + cxxopts::Options options("unknown_options", " - test unknown options"); + + options.add_options() + ("long", "a long option") + ("s,short", "a short option"); + + Argv av({ + "unknown_options", + "-some_bad_short", + }); + + char** argv = av.argv(); + auto argc = av.argc(); + + SECTION("Default behaviour") { + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); + } + + SECTION("After allowing unrecognised options") { + options.allow_unrecognised_options(); + CHECK_NOTHROW(options.parse(argc, argv)); + REQUIRE(argc == 2); + CHECK_THAT(argv[1], Catch::Equals("-some_bad_short")); + } +} + +TEST_CASE("Invalid option syntax", "[options]") { + cxxopts::Options options("invalid_syntax", " - test invalid syntax"); + + Argv av({ + "invalid_syntax", + "--a", + }); + + char** argv = av.argv(); + auto argc = av.argc(); + + SECTION("Default behaviour") { + CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); + } +}