Compare commits

..

No commits in common. "master" and "fix_iter_access" have entirely different histories.

11 changed files with 8753 additions and 16636 deletions

View File

@ -1,24 +0,0 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'cxxopts'
language: c++
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'cxxopts'
language: c++
fuzz-seconds: 300
- name: Upload Crash
uses: actions/upload-artifact@v3
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts

View File

@ -20,9 +20,11 @@ jobs:
build-ubuntu: build-ubuntu:
strategy: strategy:
matrix: matrix:
os: [ ubuntu-20.04, ubuntu-22.04 ] os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ]
compiler: [ g++-9, g++-10, clang++ ] compiler: [ g++-9, g++-10, clang++ ]
include:
- os: ubuntu-18.04
compiler: g++-7
name: Build and Test on Ubuntu name: Build and Test on Ubuntu
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:

View File

@ -6,11 +6,3 @@ cc_library(
strip_include_prefix = "include", strip_include_prefix = "include",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
cc_fuzz_test(
name = "cxxopts_fuzz_test",
srcs = ["test/fuzz.cpp"],
deps = [":cxxopts"],
)

View File

@ -3,51 +3,17 @@
This is the changelog for `cxxopts`, a C++11 library for parsing command line This is the changelog for `cxxopts`, a C++11 library for parsing command line
options. The project adheres to semantic versioning. options. The project adheres to semantic versioning.
## 3.2.1 ## Unreleased
### Bug fixes
* Fix compilation with optional on C++20.
## 3.2
### Bug fixes
* Fix unannotated fallthrough.
* Fix sign conversion with Unicode output.
* Don't initialize regex in static initialiser.
* Fix incorrect integer overflow checks.
### Added
* Add fuzzing to CI
### Changed
* Change quote output to '' to match Windows.
* Don't split positional arguments by the list delimiter.
* Order help groups by the order they were added.
## 3.1.1
### Bug Fixes
* Fixed version number in header.
* Fixed cast warning in Unicode function.
## 3.1
### Added ### Added
* Support for multiple long names for the same option (= multiple long aliases) * Support for multiple long names for the same option (= multiple long aliases)
* Add a `program()` function to retrieve the program name. * Add a `program()` function to retrieve the program name.
* Added a .clang-format file. * Added a .clang-format file.
* Added iterator and printing for a ParseResult.
### Changed ### Changed
* Cleanup exception code, add cxxopts::exceptions namespace. * Cleanup exception code, add cxxopts::exceptions namespace.
* Renamed several exceptions to be more descriptive, and added to a nested namespace.
### Bug Fixes ### Bug Fixes

View File

@ -85,6 +85,9 @@ result["opt"].as<type>()
to get its value. If "opt" doesn't exist, or isn't of the right type, then an to get its value. If "opt" doesn't exist, or isn't of the right type, then an
exception will be thrown. exception will be thrown.
Note that the result of `options.parse` should only be used as long as the
`options` object that created it is in scope.
## Unrecognised arguments ## Unrecognised arguments
You can allow unrecognised arguments to be skipped. This applies to both You can allow unrecognised arguments to be skipped. This applies to both
@ -193,8 +196,8 @@ therefore, `-o false` does not work.
## `std::vector<T>` values ## `std::vector<T>` values
Parsing a list of values into a `std::vector<T>` is also supported, as long as `T` 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 define symbol `CXXOPTS_VECTOR_DELIMITER` 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 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 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>`: that can be parsed as a `std::vector<double>`:
@ -275,3 +278,7 @@ GCC >= 4.9 or clang >= 3.1 with libc++ are known to work.
The following compilers are known not to work: The following compilers are known not to work:
* MSVC 2013 * MSVC 2013
# TODO list
* Allow unrecognised options.

View File

@ -1,16 +0,0 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_fuzzing",
sha256 = "d9002dd3cd6437017f08593124fdd1b13b3473c7b929ceb0e60d317cb9346118",
strip_prefix = "rules_fuzzing-0.3.2",
urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"],
)
load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
rules_fuzzing_dependencies()
load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
rules_fuzzing_init()

View File

@ -27,7 +27,6 @@ THE SOFTWARE.
#ifndef CXXOPTS_HPP_INCLUDED #ifndef CXXOPTS_HPP_INCLUDED
#define CXXOPTS_HPP_INCLUDED #define CXXOPTS_HPP_INCLUDED
#include <cstdlib>
#include <cstring> #include <cstring>
#include <exception> #include <exception>
#include <limits> #include <limits>
@ -41,7 +40,6 @@ THE SOFTWARE.
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <locale>
#ifdef CXXOPTS_NO_EXCEPTIONS #ifdef CXXOPTS_NO_EXCEPTIONS
#include <iostream> #include <iostream>
@ -56,8 +54,8 @@ THE SOFTWARE.
#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern #define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
#define CXXOPTS_LINKONCE __declspec(selectany) extern #define CXXOPTS_LINKONCE __declspec(selectany) extern
#else #else
#define CXXOPTS_LINKONCE_CONST #define CXXOPTS_LINKONCE_CONST
#define CXXOPTS_LINKONCE #define CXXOPTS_LINKONCE
#endif #endif
#ifndef CXXOPTS_NO_REGEX #ifndef CXXOPTS_NO_REGEX
@ -74,14 +72,6 @@ THE SOFTWARE.
# endif # endif
#endif #endif
#define CXXOPTS_FALLTHROUGH
#ifdef __has_cpp_attribute
#if __has_cpp_attribute(fallthrough)
#undef CXXOPTS_FALLTHROUGH
#define CXXOPTS_FALLTHROUGH [[fallthrough]]
#endif
#endif
#if __cplusplus >= 201603L #if __cplusplus >= 201603L
#define CXXOPTS_NODISCARD [[nodiscard]] #define CXXOPTS_NODISCARD [[nodiscard]]
#else #else
@ -93,8 +83,8 @@ THE SOFTWARE.
#endif #endif
#define CXXOPTS__VERSION_MAJOR 3 #define CXXOPTS__VERSION_MAJOR 3
#define CXXOPTS__VERSION_MINOR 2 #define CXXOPTS__VERSION_MINOR 0
#define CXXOPTS__VERSION_PATCH 1 #define CXXOPTS__VERSION_PATCH 0
#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6 #if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
#define CXXOPTS_NULL_DEREF_IGNORE #define CXXOPTS_NULL_DEREF_IGNORE
@ -239,10 +229,10 @@ stringAppend(String& s, Iterator begin, Iterator end)
} }
inline inline
size_t std::size_t
stringLength(const String& s) stringLength(const String& s)
{ {
return static_cast<size_t>(s.length()); return s.length();
} }
inline inline
@ -346,8 +336,13 @@ empty(const std::string& s)
namespace cxxopts { namespace cxxopts {
namespace { namespace {
#ifdef _WIN32
CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'"); CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'"); CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
#else
CXXOPTS_LINKONCE_CONST std::string LQUOTE("");
CXXOPTS_LINKONCE_CONST std::string RQUOTE("");
#endif
} // namespace } // namespace
// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
@ -369,9 +364,6 @@ class Value : public std::enable_shared_from_this<Value>
std::shared_ptr<Value> std::shared_ptr<Value>
clone() const = 0; clone() const = 0;
virtual void
add(const std::string& text) const = 0;
virtual void virtual void
parse(const std::string& text) const = 0; parse(const std::string& text) const = 0;
@ -765,31 +757,29 @@ inline ArguDesc ParseArgument(const char *arg, bool &matched)
namespace { namespace {
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const integer_pattern = std::basic_regex<char> integer_pattern
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"; ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const truthy_pattern = std::basic_regex<char> truthy_pattern
"(t|T)(rue)?|1"; ("(t|T)(rue)?|1");
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const falsy_pattern = std::basic_regex<char> falsy_pattern
"(f|F)(alse)?|0"; ("(f|F)(alse)?|0");
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const option_pattern = std::basic_regex<char> option_matcher
"--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)"; ("--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)");
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const option_specifier_pattern = std::basic_regex<char> option_specifier
"([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*"; ("([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*");
CXXOPTS_LINKONCE CXXOPTS_LINKONCE
const char* const option_specifier_separator_pattern = ", *"; std::basic_regex<char> option_specifier_separator(", *");
} // namespace } // namespace
inline IntegerDesc SplitInteger(const std::string &text) inline IntegerDesc SplitInteger(const std::string &text)
{ {
static const std::basic_regex<char> integer_matcher(integer_pattern);
std::smatch match; std::smatch match;
std::regex_match(text, match, integer_matcher); std::regex_match(text, match, integer_pattern);
if (match.length() == 0) if (match.length() == 0)
{ {
@ -813,17 +803,15 @@ inline IntegerDesc SplitInteger(const std::string &text)
inline bool IsTrueText(const std::string &text) inline bool IsTrueText(const std::string &text)
{ {
static const std::basic_regex<char> truthy_matcher(truthy_pattern);
std::smatch result; std::smatch result;
std::regex_match(text, result, truthy_matcher); std::regex_match(text, result, truthy_pattern);
return !result.empty(); return !result.empty();
} }
inline bool IsFalseText(const std::string &text) inline bool IsFalseText(const std::string &text)
{ {
static const std::basic_regex<char> falsy_matcher(falsy_pattern);
std::smatch result; std::smatch result;
std::regex_match(text, result, falsy_matcher); std::regex_match(text, result, falsy_pattern);
return !result.empty(); return !result.empty();
} }
@ -832,25 +820,22 @@ inline bool IsFalseText(const std::string &text)
// (without considering which or how many are single-character) // (without considering which or how many are single-character)
inline OptionNames split_option_names(const std::string &text) inline OptionNames split_option_names(const std::string &text)
{ {
static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern); if (!std::regex_match(text.c_str(), option_specifier))
if (!std::regex_match(text.c_str(), option_specifier_matcher))
{ {
throw_or_mimic<exceptions::invalid_option_format>(text); throw_or_mimic<exceptions::invalid_option_format>(text);
} }
OptionNames split_names; OptionNames split_names;
static const std::basic_regex<char> option_specifier_separator_matcher(option_specifier_separator_pattern);
constexpr int use_non_matches { -1 }; constexpr int use_non_matches { -1 };
auto token_iterator = std::sregex_token_iterator( auto token_iterator = std::sregex_token_iterator(
text.begin(), text.end(), option_specifier_separator_matcher, use_non_matches); text.begin(), text.end(), option_specifier_separator, use_non_matches);
std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names)); std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
return split_names; return split_names;
} }
inline ArguDesc ParseArgument(const char *arg, bool &matched) inline ArguDesc ParseArgument(const char *arg, bool &matched)
{ {
static const std::basic_regex<char> option_matcher(option_pattern);
std::match_results<const char*> result; std::match_results<const char*> result;
std::regex_match(arg, result, option_matcher); std::regex_match(arg, result, option_matcher);
matched = !result.empty(); matched = !result.empty();
@ -973,26 +958,13 @@ integer_parser(const std::string& text, T& value)
throw_or_mimic<exceptions::incorrect_argument_type>(text); throw_or_mimic<exceptions::incorrect_argument_type>(text);
} }
US limit = 0; const US next = static_cast<US>(result * base + digit);
if (negative) if (result > next)
{
limit = static_cast<US>(std::abs(static_cast<intmax_t>(std::numeric_limits<T>::min())));
}
else
{
limit = std::numeric_limits<T>::max();
}
if (base != 0 && result > limit / base)
{
throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
if (result * base > limit - digit)
{ {
throw_or_mimic<exceptions::incorrect_argument_type>(text); throw_or_mimic<exceptions::incorrect_argument_type>(text);
} }
result = static_cast<US>(result * base + digit); result = next;
} }
detail::check_signed_range<T>(negative, result, text); detail::check_signed_range<T>(negative, result, text);
@ -1062,6 +1034,25 @@ parse_value(const std::string& text, T& value) {
stringstream_parser(text, value); stringstream_parser(text, value);
} }
template <typename T>
void
parse_value(const std::string& text, std::vector<T>& value)
{
if (text.empty()) {
T v;
parse_value(text, v);
value.emplace_back(std::move(v));
return;
}
std::stringstream in(text);
std::string token;
while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
T v;
parse_value(token, v);
value.emplace_back(std::move(v));
}
}
#ifdef CXXOPTS_HAS_OPTIONAL #ifdef CXXOPTS_HAS_OPTIONAL
template <typename T> template <typename T>
void void
@ -1084,41 +1075,6 @@ void parse_value(const std::string& text, char& c)
c = text[0]; c = text[0];
} }
template <typename T>
void
parse_value(const std::string& text, std::vector<T>& value)
{
if (text.empty()) {
T v;
parse_value(text, v);
value.emplace_back(std::move(v));
return;
}
std::stringstream in(text);
std::string token;
while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
T v;
parse_value(token, v);
value.emplace_back(std::move(v));
}
}
template <typename T>
void
add_value(const std::string& text, T& value)
{
parse_value(text, value);
}
template <typename T>
void
add_value(const std::string& text, std::vector<T>& value)
{
T v;
add_value(text, v);
value.emplace_back(std::move(v));
}
template <typename T> template <typename T>
struct type_is_container struct type_is_container
{ {
@ -1170,12 +1126,6 @@ class abstract_value : public Value
m_implicit_value = rhs.m_implicit_value; m_implicit_value = rhs.m_implicit_value;
} }
void
add(const std::string& text) const override
{
add_value(text, *m_store);
}
void void
parse(const std::string& text) const override parse(const std::string& text) const override
{ {
@ -1461,19 +1411,6 @@ struct HelpGroupDetails
class OptionValue class OptionValue
{ {
public: public:
void
add
(
const std::shared_ptr<const OptionDetails>& details,
const std::string& text
)
{
ensure_value(details);
++m_count;
m_value->add(text);
m_long_names = &details->long_names();
}
void void
parse parse
( (
@ -1538,15 +1475,6 @@ CXXOPTS_DIAGNOSTIC_POP
return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get(); return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
} }
#ifdef CXXOPTS_HAS_OPTIONAL
template <typename T>
std::optional<T>
as_optional() const
{
return as<T>();
}
#endif
private: private:
void void
ensure_value(const std::shared_ptr<const OptionDetails>& details) ensure_value(const std::shared_ptr<const OptionDetails>& details)
@ -1569,7 +1497,7 @@ CXXOPTS_DIAGNOSTIC_POP
class KeyValue class KeyValue
{ {
public: public:
KeyValue(std::string key_, std::string value_) noexcept KeyValue(std::string key_, std::string value_)
: m_key(std::move(key_)) : m_key(std::move(key_))
, m_value(std::move(value_)) , m_value(std::move(value_))
{ {
@ -1622,8 +1550,7 @@ class ParseResult
Iterator(const Iterator&) = default; Iterator(const Iterator&) = default;
// GCC complains about m_iter not being initialised in the member // GCC complains about m_iter not being initialised in the member
// initializer list // initializer list
CXXOPTS_DIAGNOSTIC_PUSH
CXXOPTS_IGNORE_WARNING("-Weffc++") CXXOPTS_IGNORE_WARNING("-Weffc++")
Iterator(const ParseResult *pr, bool end=false) Iterator(const ParseResult *pr, bool end=false)
: m_pr(pr) : m_pr(pr)
@ -1759,24 +1686,6 @@ CXXOPTS_DIAGNOSTIC_POP
return viter->second; return viter->second;
} }
#ifdef CXXOPTS_HAS_OPTIONAL
template <typename T>
std::optional<T>
as_optional(const std::string& option) const
{
auto iter = m_keys.find(option);
if (iter != m_keys.end())
{
auto viter = m_values.find(iter->second);
if (viter != m_values.end())
{
return viter->second.as_optional<T>();
}
}
return std::nullopt;
}
#endif
const std::vector<KeyValue>& const std::vector<KeyValue>&
arguments() const arguments() const
{ {
@ -1871,7 +1780,7 @@ class OptionParser
); );
void void
add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg); add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
void void
parse_option parse_option
@ -2074,7 +1983,6 @@ class Options
std::unordered_set<std::string> m_positional_set{}; std::unordered_set<std::string> m_positional_set{};
//mapping from groups to help options //mapping from groups to help options
std::vector<std::string> m_group{};
std::map<std::string, HelpGroupDetails> m_help{}; std::map<std::string, HelpGroupDetails> m_help{};
}; };
@ -2327,7 +2235,6 @@ OptionAdder::operator()
case 1: case 1:
short_name = *first_short_name_iter; short_name = *first_short_name_iter;
option_names.erase(first_short_name_iter); option_names.erase(first_short_name_iter);
CXXOPTS_FALLTHROUGH;
case 0: case 0:
break; break;
default: default:
@ -2419,13 +2326,9 @@ OptionParser::checked_parse_arg
inline inline
void void
OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg) OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
{ {
auto hash = value->hash(); parse_option(iter->second, option, arg);
auto& result = m_parsed[hash];
result.add(value, arg);
m_sequential.emplace_back(value->essential_name(), arg);
} }
inline inline
@ -2442,14 +2345,14 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n
auto& result = m_parsed[iter->second->hash()]; auto& result = m_parsed[iter->second->hash()];
if (result.count() == 0) if (result.count() == 0)
{ {
add_to_option(iter->second, a); add_to_option(iter, *next, a);
++next; ++next;
return true; return true;
} }
++next; ++next;
continue; continue;
} }
add_to_option(iter->second, a); add_to_option(iter, *next, a);
return true; return true;
} }
throw_or_mimic<exceptions::no_such_option>(*next); throw_or_mimic<exceptions::no_such_option>(*next);
@ -2713,12 +2616,6 @@ Options::add_option
} }
//add the help details //add the help details
if (m_help.find(group) == m_help.end())
{
m_group.push_back(group);
}
auto& options = m_help[group]; auto& options = m_help[group];
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
@ -2850,7 +2747,19 @@ inline
void void
Options::generate_all_groups_help(String& result) const Options::generate_all_groups_help(String& result) const
{ {
generate_group_help(result, m_group); std::vector<std::string> all_groups;
std::transform(
m_help.begin(),
m_help.end(),
std::back_inserter(all_groups),
[] (const std::map<std::string, HelpGroupDetails>::value_type& group)
{
return group.first;
}
);
generate_group_help(result, all_groups);
} }
inline inline
@ -2890,7 +2799,19 @@ inline
std::vector<std::string> std::vector<std::string>
Options::groups() const Options::groups() const
{ {
return m_group; 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;
} }
inline inline

View File

@ -51,10 +51,3 @@ add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
add_executable(link_test link_a.cpp link_b.cpp) add_executable(link_test link_a.cpp link_b.cpp)
target_link_libraries(link_test cxxopts) target_link_libraries(link_test cxxopts)
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND ("${CMAKE_SYSTEM}" MATCHES "Linux"))
add_executable(fuzzer fuzz.cpp)
target_link_libraries(fuzzer PRIVATE cxxopts)
target_compile_options(fuzzer PRIVATE -fsanitize=fuzzer)
target_link_options(fuzzer PRIVATE -fsanitize=fuzzer)
endif()

File diff suppressed because it is too large Load Diff

View File

@ -1,107 +0,0 @@
#include <cassert>
#include <cxxopts.hpp>
#include <fuzzer/FuzzedDataProvider.h>
constexpr int kMaxOptions = 1024;
constexpr int kMaxArgSize = 1024;
enum class ParseableTypes
{
kInt,
kString,
kVectorString,
kFloat,
kDouble,
// Marker for fuzzer.
kMaxValue,
};
template <typename T>
void
add_fuzzed_option(cxxopts::Options* options, FuzzedDataProvider* provider)
{
assert(options);
assert(provider);
options->add_options()(provider->ConsumeRandomLengthString(kMaxArgSize),
provider->ConsumeRandomLengthString(kMaxArgSize),
cxxopts::value<T>());
}
extern "C" int
LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
try
{
FuzzedDataProvider provider(data, size);
// Randomly generate a usage string.
cxxopts::Options options(provider.ConsumeRandomLengthString(kMaxArgSize),
provider.ConsumeRandomLengthString(kMaxArgSize));
// Randomly generate a set of flags configurations.
for (int i = 0; i < provider.ConsumeIntegralInRange<int>(0, kMaxOptions);
i++)
{
switch (provider.ConsumeEnum<ParseableTypes>())
{
case ParseableTypes::kInt:
add_fuzzed_option<int>(&options, &provider);
break;
case ParseableTypes::kString:
add_fuzzed_option<std::string>(&options, &provider);
break;
case ParseableTypes::kVectorString:
add_fuzzed_option<std::vector<std::string>>(&options, &provider);
break;
case ParseableTypes::kFloat:
add_fuzzed_option<float>(&options, &provider);
break;
case ParseableTypes::kDouble:
add_fuzzed_option<double>(&options, &provider);
break;
default:
break;
}
}
// Sometimes allow unrecognised options.
if (provider.ConsumeBool())
{
options.allow_unrecognised_options();
}
// Sometimes allow trailing positional arguments.
if (provider.ConsumeBool())
{
std::string positional_option_name =
provider.ConsumeRandomLengthString(kMaxArgSize);
options.add_options()(positional_option_name,
provider.ConsumeRandomLengthString(kMaxArgSize),
cxxopts::value<std::vector<std::string>>());
options.parse_positional({positional_option_name});
}
// Build command line input.
const int argc = provider.ConsumeIntegralInRange<int>(1, kMaxOptions);
std::vector<std::string> command_line_container;
command_line_container.reserve(argc);
std::vector<const char*> argv;
argv.reserve(argc);
for (int i = 0; i < argc; i++)
{
command_line_container.push_back(
provider.ConsumeRandomLengthString(kMaxArgSize));
argv.push_back(command_line_container[i].c_str());
}
// Parse command line;
auto result = options.parse(argc, argv.data());
} catch (...)
{
}
return 0;
}

View File

@ -112,7 +112,7 @@ TEST_CASE("Basic options", "[options]")
CHECK(arguments[2].key() == "value"); CHECK(arguments[2].key() == "value");
CHECK(arguments[3].key() == "av"); CHECK(arguments[3].key() == "av");
CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::exceptions::option_has_no_value); CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::exceptions::option_has_no_value&);
CHECK(options.program() == "tester"); CHECK(options.program() == "tester");
} }
@ -147,7 +147,7 @@ TEST_CASE("Short options", "[options]")
CHECK(result["c"].as<std::string>() == "foo=something"); CHECK(result["c"].as<std::string>() == "foo=something");
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
cxxopts::exceptions::invalid_option_format); cxxopts::exceptions::invalid_option_format&);
} }
TEST_CASE("No positional", "[positional]") TEST_CASE("No positional", "[positional]")
@ -238,6 +238,9 @@ TEST_CASE("No positional with extras", "[positional]")
auto** argv = av.argv(); auto** argv = av.argv();
auto argc = av.argc(); auto argc = av.argc();
auto old_argv = argv;
auto old_argc = argc;
auto result = options.parse(argc, argv); auto result = options.parse(argc, argv);
auto& unmatched = result.unmatched(); auto& unmatched = result.unmatched();
@ -257,7 +260,7 @@ TEST_CASE("Positional not valid", "[positional]") {
auto** argv = av.argv(); auto** argv = av.argv();
auto argc = av.argc(); auto argc = av.argc();
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
} }
TEST_CASE("Positional with empty arguments", "[positional]") { TEST_CASE("Positional with empty arguments", "[positional]") {
@ -285,37 +288,6 @@ TEST_CASE("Positional with empty arguments", "[positional]") {
REQUIRE(actual == expected); REQUIRE(actual == expected);
} }
TEST_CASE("Positional with list delimiter", "[positional]") {
std::string single;
std::vector<std::string> positional;
cxxopts::Options options("test_all_positional_list_delimiter", " - test all positional with list delimiters");
options.add_options()
("single", "Single positional param",
cxxopts::value<std::string>(single))
("positional", "Positional parameters vector",
cxxopts::value<std::vector<std::string>>(positional))
;
Argv av({"tester", "a,b", "c,d", "e"});
auto argc = av.argc();
auto argv = av.argv();
std::vector<std::string> pos_names = {"single", "positional"};
options.parse_positional(pos_names.begin(), pos_names.end());
auto result = options.parse(argc, argv);
CHECK(result.unmatched().size() == 0);
REQUIRE(positional.size() == 2);
CHECK(single == "a,b");
CHECK(positional[0] == "c,d");
CHECK(positional[1] == "e");
}
TEST_CASE("Empty with implicit value", "[implicit]") TEST_CASE("Empty with implicit value", "[implicit]")
{ {
cxxopts::Options options("empty_implicit", "doesn't handle empty"); cxxopts::Options options("empty_implicit", "doesn't handle empty");
@ -347,7 +319,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]")
auto** argv = av.argv(); auto** argv = av.argv();
auto argc = av.argc(); auto argc = av.argc();
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::missing_argument); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::missing_argument&);
} }
SECTION("With equal-separated true") { SECTION("With equal-separated true") {
@ -517,7 +489,7 @@ TEST_CASE("Unsigned integers", "[options]")
auto argc = av.argc(); auto argc = av.argc();
options.parse_positional("positional"); options.parse_positional("positional");
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
} }
TEST_CASE("Integer bounds", "[integer]") TEST_CASE("Integer bounds", "[integer]")
@ -552,32 +524,14 @@ TEST_CASE("Overflow on boundary", "[integer]")
using namespace cxxopts::values; using namespace cxxopts::values;
int8_t si; int8_t si;
int16_t si16;
int64_t si64;
uint8_t ui; uint8_t ui;
uint16_t ui16;
uint64_t ui64;
CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("65536", ui16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("75536", ui16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("32768", si16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-32769", si16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-42769", si16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-75536", si16)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("18446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("28446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("9223372036854775808", si64)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-9223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-10223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
CHECK_THROWS_AS((integer_parser("-28446744073709551616", si64)), cxxopts::exceptions::incorrect_argument_type);
} }
TEST_CASE("Integer overflow", "[options]") TEST_CASE("Integer overflow", "[options]")
@ -594,11 +548,11 @@ TEST_CASE("Integer overflow", "[options]")
auto argc = av.argc(); auto argc = av.argc();
options.parse_positional("positional"); options.parse_positional("positional");
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
int integer = 0; int integer = 0;
CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type&);
CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type&);
} }
TEST_CASE("Floats", "[options]") TEST_CASE("Floats", "[options]")
@ -639,7 +593,7 @@ TEST_CASE("Invalid integers", "[integer]") {
auto argc = av.argc(); auto argc = av.argc();
options.parse_positional("positional"); options.parse_positional("positional");
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type&);
} }
TEST_CASE("Booleans", "[boolean]") { TEST_CASE("Booleans", "[boolean]") {
@ -709,13 +663,11 @@ TEST_CASE("std::vector", "[vector]") {
#ifdef CXXOPTS_HAS_OPTIONAL #ifdef CXXOPTS_HAS_OPTIONAL
TEST_CASE("std::optional", "[optional]") { TEST_CASE("std::optional", "[optional]") {
std::optional<std::string> optional; std::optional<std::string> optional;
std::optional<bool> opt_bool;
cxxopts::Options options("optional", " - tests optional"); cxxopts::Options options("optional", " - tests optional");
options.add_options() options.add_options()
("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional)) ("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional));
("optional_bool", "an boolean optional", cxxopts::value<std::optional<bool>>(opt_bool)->default_value("false"));
Argv av({"optional", "--optional", "foo", "--optional_bool", "true"}); Argv av({"optional", "--optional", "foo"});
auto** argv = av.argv(); auto** argv = av.argv();
auto argc = av.argc(); auto argc = av.argc();
@ -724,8 +676,6 @@ TEST_CASE("std::optional", "[optional]") {
REQUIRE(optional.has_value()); REQUIRE(optional.has_value());
CHECK(*optional == "foo"); CHECK(*optional == "foo");
CHECK(opt_bool.has_value());
CHECK(*opt_bool);
} }
#endif #endif
@ -749,7 +699,7 @@ TEST_CASE("Unrecognised options", "[options]") {
auto argc = av.argc(); auto argc = av.argc();
SECTION("Default behaviour") { SECTION("Default behaviour") {
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
} }
SECTION("After allowing unrecognised options") { SECTION("After allowing unrecognised options") {
@ -776,7 +726,7 @@ TEST_CASE("Allow bad short syntax", "[options]") {
auto argc = av.argc(); auto argc = av.argc();
SECTION("Default behaviour") { SECTION("Default behaviour") {
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax&);
} }
SECTION("After allowing unrecognised options") { SECTION("After allowing unrecognised options") {
@ -799,7 +749,7 @@ TEST_CASE("Invalid option syntax", "[options]") {
auto argc = av.argc(); auto argc = av.argc();
SECTION("Default behaviour") { SECTION("Default behaviour") {
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax&);
} }
} }
@ -818,60 +768,9 @@ TEST_CASE("Options empty", "[options]") {
auto** argv = argv_.argv(); auto** argv = argv_.argv();
CHECK(options.groups().empty()); CHECK(options.groups().empty());
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option&);
} }
#ifdef CXXOPTS_HAS_OPTIONAL
TEST_CASE("Optional value", "[optional]")
{
cxxopts::Options options("options", "query as std::optional");
options.add_options()
("int", "Integer", cxxopts::value<int>())
("float", "Float", cxxopts::value<float>())
("string", "String", cxxopts::value<std::string>())
;
SECTION("Available") {
Argv av({
"--int",
"42",
"--float",
"3.141",
"--string",
"Hello"
});
auto** argv = av.argv();
auto argc = av.argc();
auto result = options.parse(argc, argv);
CHECK(result.as_optional<int>("int"));
CHECK(result.as_optional<float>("float"));
CHECK(result.as_optional<string>("string"));
CHECK(*result.as_optional<int>("int") == 42);
CHECK(*result.as_optional<float>("float") == 3.141);
CHECK(*result.as_optional<string>("string") == "Hello");
}
SECTION("Unavailable") {
Argv av({
});
auto** argv = av.argv();
auto argc = av.argc();
auto result = options.parse(argc, argv);
CHECK(!result.as_optional<int>("int"));
CHECK(!result.as_optional<float>("float"));
CHECK(!result.as_optional<string>("string"));
}
}
#endif
TEST_CASE("Initializer list with group", "[options]") { TEST_CASE("Initializer list with group", "[options]") {
cxxopts::Options options("Initializer list group", " - test initializer list with group"); cxxopts::Options options("Initializer list group", " - test initializer list with group");