summaryrefslogtreecommitdiff
path: root/src/cam/options.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cam/options.cpp')
-rw-r--r--src/cam/options.cpp1141
1 files changed, 0 insertions, 1141 deletions
diff --git a/src/cam/options.cpp b/src/cam/options.cpp
deleted file mode 100644
index 4f7e8691..00000000
--- a/src/cam/options.cpp
+++ /dev/null
@@ -1,1141 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * options.cpp - cam - Options parsing
- */
-
-#include <assert.h>
-#include <getopt.h>
-#include <iomanip>
-#include <iostream>
-#include <string.h>
-
-#include "options.h"
-
-/**
- * \enum OptionArgument
- * \brief Indicate if an option takes an argument
- *
- * \var OptionArgument::ArgumentNone
- * \brief The option doesn't accept any argument
- *
- * \var OptionArgument::ArgumentRequired
- * \brief The option requires an argument
- *
- * \var OptionArgument::ArgumentOptional
- * \brief The option accepts an optional argument
- */
-
-/**
- * \enum OptionType
- * \brief The type of argument for an option
- *
- * \var OptionType::OptionNone
- * \brief No argument type, used for options that take no argument
- *
- * \var OptionType::OptionInteger
- * \brief Integer argument type, with an optional base prefix (`0` for base 8,
- * `0x` for base 16, none for base 10)
- *
- * \var OptionType::OptionString
- * \brief String argument
- *
- * \var OptionType::OptionKeyValue
- * \brief key=value list argument
- */
-
-/* -----------------------------------------------------------------------------
- * Option
- */
-
-/**
- * \struct Option
- * \brief Store metadata about an option
- *
- * \var Option::opt
- * \brief The option identifier
- *
- * \var Option::type
- * \brief The type of the option argument
- *
- * \var Option::name
- * \brief The option name
- *
- * \var Option::argument
- * \brief Whether the option accepts an optional argument, a mandatory
- * argument, or no argument at all
- *
- * \var Option::argumentName
- * \brief The argument name used in the help text
- *
- * \var Option::help
- * \brief The help text (may be a multi-line string)
- *
- * \var Option::keyValueParser
- * \brief For options of type OptionType::OptionKeyValue, the key-value parser
- * to parse the argument
- *
- * \var Option::isArray
- * \brief Whether the option can appear once or multiple times
- *
- * \var Option::parent
- * \brief The parent option
- *
- * \var Option::children
- * \brief List of child options, storing all options whose parent is this option
- *
- * \fn Option::hasShortOption()
- * \brief Tell if the option has a short option specifier (e.g. `-f`)
- * \return True if the option has a short option specifier, false otherwise
- *
- * \fn Option::hasLongOption()
- * \brief Tell if the option has a long option specifier (e.g. `--foo`)
- * \return True if the option has a long option specifier, false otherwise
- */
-struct Option {
- int opt;
- OptionType type;
- const char *name;
- OptionArgument argument;
- const char *argumentName;
- const char *help;
- KeyValueParser *keyValueParser;
- bool isArray;
- Option *parent;
- std::list<Option> children;
-
- bool hasShortOption() const { return isalnum(opt); }
- bool hasLongOption() const { return name != nullptr; }
- const char *typeName() const;
- std::string optionName() const;
-};
-
-/**
- * \brief Retrieve a string describing the option type
- * \return A string describing the option type
- */
-const char *Option::typeName() const
-{
- switch (type) {
- case OptionNone:
- return "none";
-
- case OptionInteger:
- return "integer";
-
- case OptionString:
- return "string";
-
- case OptionKeyValue:
- return "key=value";
- }
-
- return "unknown";
-}
-
-/**
- * \brief Retrieve a string describing the option name, with leading dashes
- * \return A string describing the option name, as a long option identifier
- * (double dash) if the option has a name, or a short option identifier (single
- * dash) otherwise
- */
-std::string Option::optionName() const
-{
- if (name)
- return "--" + std::string(name);
- else
- return "-" + std::string(1, opt);
-}
-
-/* -----------------------------------------------------------------------------
- * OptionBase<T>
- */
-
-/**
- * \class template<typename T> OptionBase
- * \brief Container to store the values of parsed options
- * \tparam T The type through which options are identified
- *
- * The OptionsBase class is generated by a parser (either OptionsParser or
- * KeyValueParser) when parsing options. It stores values for all the options
- * found, and exposes accessor functions to retrieve them. The options are
- * accessed through an identifier to type \a T, which is an int referencing an
- * Option::opt for OptionsParser, or a std::string referencing an Option::name
- * for KeyValueParser.
- */
-
-/**
- * \fn OptionsBase::OptionsBase()
- * \brief Construct an OptionsBase instance
- *
- * The constructed instance is initially invalid, and will be populated by the
- * options parser.
- */
-
-/**
- * \brief Tell if the stored options list is empty
- * \return True if the container is empty, false otherwise
- */
-template<typename T>
-bool OptionsBase<T>::empty() const
-{
- return values_.empty();
-}
-
-/**
- * \brief Tell if the options parsing completed successfully
- * \return True if the container is returned after successfully parsing
- * options, false if it is returned after an error was detected during parsing
- */
-template<typename T>
-bool OptionsBase<T>::valid() const
-{
- return valid_;
-}
-
-/**
- * \brief Tell if the option \a opt is specified
- * \param[in] opt The option to search for
- * \return True if the \a opt option is set, false otherwise
- */
-template<typename T>
-bool OptionsBase<T>::isSet(const T &opt) const
-{
- return values_.find(opt) != values_.end();
-}
-
-/**
- * \brief Retrieve the value of option \a opt
- * \param[in] opt The option to retrieve
- * \return The value of option \a opt if found, an empty OptionValue otherwise
- */
-template<typename T>
-const OptionValue &OptionsBase<T>::operator[](const T &opt) const
-{
- static const OptionValue empty;
-
- auto it = values_.find(opt);
- if (it != values_.end())
- return it->second;
- return empty;
-}
-
-/**
- * \brief Mark the container as invalid
- *
- * This function can be used in a key-value parser's override of the
- * KeyValueParser::parse() function to mark the returned options as invalid if
- * a validation error occurs.
- */
-template<typename T>
-void OptionsBase<T>::invalidate()
-{
- valid_ = false;
-}
-
-template<typename T>
-bool OptionsBase<T>::parseValue(const T &opt, const Option &option,
- const char *arg)
-{
- OptionValue value;
-
- switch (option.type) {
- case OptionNone:
- break;
-
- case OptionInteger:
- unsigned int integer;
-
- if (arg) {
- char *endptr;
- integer = strtoul(arg, &endptr, 0);
- if (*endptr != '\0')
- return false;
- } else {
- integer = 0;
- }
-
- value = OptionValue(integer);
- break;
-
- case OptionString:
- value = OptionValue(arg ? arg : "");
- break;
-
- case OptionKeyValue:
- KeyValueParser *kvParser = option.keyValueParser;
- KeyValueParser::Options keyValues = kvParser->parse(arg);
- if (!keyValues.valid())
- return false;
-
- value = OptionValue(keyValues);
- break;
- }
-
- if (option.isArray)
- values_[opt].addValue(value);
- else
- values_[opt] = value;
-
- return true;
-}
-
-template class OptionsBase<int>;
-template class OptionsBase<std::string>;
-
-/* -----------------------------------------------------------------------------
- * KeyValueParser
- */
-
-/**
- * \class KeyValueParser
- * \brief A specialized parser for list of key-value pairs
- *
- * The KeyValueParser is an options parser for comma-separated lists of
- * `key=value` pairs. The supported keys are added to the parser with
- * addOption(). A given key can only appear once in the parsed list.
- *
- * Instances of this class can be passed to the OptionsParser::addOption()
- * function to create options that take key-value pairs as an option argument.
- * Specialized versions of the key-value parser can be created by inheriting
- * from this class, to pre-build the options list in the constructor, and to add
- * custom validation by overriding the parse() function.
- */
-
-/**
- * \class KeyValueParser::Options
- * \brief An option list generated by the key-value parser
- *
- * This is a specialization of OptionsBase with the option reference type set to
- * std::string.
- */
-
-KeyValueParser::KeyValueParser() = default;
-KeyValueParser::~KeyValueParser() = default;
-
-/**
- * \brief Add a supported option to the parser
- * \param[in] name The option name, corresponding to the key name in the
- * key=value pair. The name shall be unique.
- * \param[in] type The type of the value in the key=value pair
- * \param[in] help The help text
- * \param[in] argument Whether the value is optional, mandatory or not allowed.
- * Shall be ArgumentNone if \a type is OptionNone.
- *
- * \sa OptionsParser
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool KeyValueParser::addOption(const char *name, OptionType type,
- const char *help, OptionArgument argument)
-{
- if (!name)
- return false;
- if (!help || help[0] == '\0')
- return false;
- if (argument != ArgumentNone && type == OptionNone)
- return false;
-
- /* Reject duplicate options. */
- if (optionsMap_.find(name) != optionsMap_.end())
- return false;
-
- optionsMap_[name] = Option({ 0, type, name, argument, nullptr,
- help, nullptr, false, nullptr, {} });
- return true;
-}
-
-/**
- * \brief Parse a string containing a list of key-value pairs
- * \param[in] arguments The key-value pairs string to parse
- *
- * If a parsing error occurs, the parsing stops and the function returns an
- * invalid container. The container is populated with the options successfully
- * parsed so far.
- *
- * \return A valid container with the list of parsed options on success, or an
- * invalid container otherwise
- */
-KeyValueParser::Options KeyValueParser::parse(const char *arguments)
-{
- Options options;
-
- for (const char *pair = arguments; *arguments != '\0'; pair = arguments) {
- const char *comma = strchrnul(arguments, ',');
- size_t len = comma - pair;
-
- /* Skip over the comma. */
- arguments = *comma == ',' ? comma + 1 : comma;
-
- /* Skip to the next pair if the pair is empty. */
- if (!len)
- continue;
-
- std::string key;
- std::string value;
-
- const char *separator = static_cast<const char *>(memchr(pair, '=', len));
- if (!separator) {
- key = std::string(pair, len);
- value = "";
- } else {
- key = std::string(pair, separator - pair);
- value = std::string(separator + 1, comma - separator - 1);
- }
-
- /* The key is mandatory, the value might be optional. */
- if (key.empty())
- continue;
-
- if (optionsMap_.find(key) == optionsMap_.end()) {
- std::cerr << "Invalid option " << key << std::endl;
- return options;
- }
-
- OptionArgument arg = optionsMap_[key].argument;
- if (value.empty() && arg == ArgumentRequired) {
- std::cerr << "Option " << key << " requires an argument"
- << std::endl;
- return options;
- } else if (!value.empty() && arg == ArgumentNone) {
- std::cerr << "Option " << key << " takes no argument"
- << std::endl;
- return options;
- }
-
- const Option &option = optionsMap_[key];
- if (!options.parseValue(key, option, value.c_str())) {
- std::cerr << "Failed to parse '" << value << "' as "
- << option.typeName() << " for option " << key
- << std::endl;
- return options;
- }
- }
-
- options.valid_ = true;
- return options;
-}
-
-unsigned int KeyValueParser::maxOptionLength() const
-{
- unsigned int maxLength = 0;
-
- for (auto const &iter : optionsMap_) {
- const Option &option = iter.second;
- unsigned int length = 10 + strlen(option.name);
- if (option.argument != ArgumentNone)
- length += 1 + strlen(option.typeName());
- if (option.argument == ArgumentOptional)
- length += 2;
-
- if (length > maxLength)
- maxLength = length;
- }
-
- return maxLength;
-}
-
-void KeyValueParser::usage(int indent)
-{
- for (auto const &iter : optionsMap_) {
- const Option &option = iter.second;
- std::string argument = std::string(" ") + option.name;
-
- if (option.argument != ArgumentNone) {
- if (option.argument == ArgumentOptional)
- argument += "[=";
- else
- argument += "=";
- argument += option.typeName();
- if (option.argument == ArgumentOptional)
- argument += "]";
- }
-
- std::cerr << std::setw(indent) << argument;
-
- for (const char *help = option.help, *end = help; end;) {
- end = strchr(help, '\n');
- if (end) {
- std::cerr << std::string(help, end - help + 1);
- std::cerr << std::setw(indent) << " ";
- help = end + 1;
- } else {
- std::cerr << help << std::endl;
- }
- }
- }
-}
-
-/* -----------------------------------------------------------------------------
- * OptionValue
- */
-
-/**
- * \class OptionValue
- * \brief Container to store the value of an option
- *
- * The OptionValue class is a variant-type container to store the value of an
- * option. It supports empty values, integers, strings, key-value lists, as well
- * as arrays of those types. For array values, all array elements shall have the
- * same type.
- *
- * OptionValue instances are organized in a tree-based structure that matches
- * the parent-child relationship of the options added to the parser. Children
- * are retrieved with the children() function, and are stored as an
- * OptionsBase<int>.
- */
-
-/**
- * \enum OptionValue::ValueType
- * \brief The option value type
- *
- * \var OptionValue::ValueType::ValueNone
- * \brief Empty value
- *
- * \var OptionValue::ValueType::ValueInteger
- * \brief Integer value (int)
- *
- * \var OptionValue::ValueType::ValueString
- * \brief String value (std::string)
- *
- * \var OptionValue::ValueType::ValueKeyValue
- * \brief Key-value list value (KeyValueParser::Options)
- *
- * \var OptionValue::ValueType::ValueArray
- * \brief Array value
- */
-
-/**
- * \brief Construct an empty OptionValue instance
- *
- * The value type is set to ValueType::ValueNone.
- */
-OptionValue::OptionValue()
- : type_(ValueNone), integer_(0)
-{
-}
-
-/**
- * \brief Construct an integer OptionValue instance
- * \param[in] value The integer value
- *
- * The value type is set to ValueType::ValueInteger.
- */
-OptionValue::OptionValue(int value)
- : type_(ValueInteger), integer_(value)
-{
-}
-
-/**
- * \brief Construct a string OptionValue instance
- * \param[in] value The string value
- *
- * The value type is set to ValueType::ValueString.
- */
-OptionValue::OptionValue(const char *value)
- : type_(ValueString), integer_(0), string_(value)
-{
-}
-
-/**
- * \brief Construct a string OptionValue instance
- * \param[in] value The string value
- *
- * The value type is set to ValueType::ValueString.
- */
-OptionValue::OptionValue(const std::string &value)
- : type_(ValueString), integer_(0), string_(value)
-{
-}
-
-/**
- * \brief Construct a key-value OptionValue instance
- * \param[in] value The key-value list
- *
- * The value type is set to ValueType::ValueKeyValue.
- */
-OptionValue::OptionValue(const KeyValueParser::Options &value)
- : type_(ValueKeyValue), integer_(0), keyValues_(value)
-{
-}
-
-/**
- * \brief Add an entry to an array value
- * \param[in] value The entry value
- *
- * This function can only be called if the OptionValue type is
- * ValueType::ValueNone or ValueType::ValueArray. Upon return, the type will be
- * set to ValueType::ValueArray.
- */
-void OptionValue::addValue(const OptionValue &value)
-{
- assert(type_ == ValueNone || type_ == ValueArray);
-
- type_ = ValueArray;
- array_.push_back(value);
-}
-
-/**
- * \fn OptionValue::type()
- * \brief Retrieve the value type
- * \return The value type
- */
-
-/**
- * \fn OptionValue::empty()
- * \brief Check if the value is empty
- * \return True if the value is empty (type set to ValueType::ValueNone), or
- * false otherwise
- */
-
-/**
- * \brief Cast the value to an int
- * \return The option value as an int, or 0 if the value type isn't
- * ValueType::ValueInteger
- */
-OptionValue::operator int() const
-{
- return toInteger();
-}
-
-/**
- * \brief Cast the value to a std::string
- * \return The option value as an std::string, or an empty string if the value
- * type isn't ValueType::ValueString
- */
-OptionValue::operator std::string() const
-{
- return toString();
-}
-
-/**
- * \brief Retrieve the value as an int
- * \return The option value as an int, or 0 if the value type isn't
- * ValueType::ValueInteger
- */
-int OptionValue::toInteger() const
-{
- if (type_ != ValueInteger)
- return 0;
-
- return integer_;
-}
-
-/**
- * \brief Retrieve the value as a std::string
- * \return The option value as a std::string, or an empty string if the value
- * type isn't ValueType::ValueString
- */
-std::string OptionValue::toString() const
-{
- if (type_ != ValueString)
- return std::string();
-
- return string_;
-}
-
-/**
- * \brief Retrieve the value as a key-value list
- *
- * The behaviour is undefined if the value type isn't ValueType::ValueKeyValue.
- *
- * \return The option value as a KeyValueParser::Options
- */
-const KeyValueParser::Options &OptionValue::toKeyValues() const
-{
- assert(type_ == ValueKeyValue);
- return keyValues_;
-}
-
-/**
- * \brief Retrieve the value as an array
- *
- * The behaviour is undefined if the value type isn't ValueType::ValueArray.
- *
- * \return The option value as a std::vector of OptionValue
- */
-const std::vector<OptionValue> &OptionValue::toArray() const
-{
- assert(type_ == ValueArray);
- return array_;
-}
-
-/**
- * \brief Retrieve the list of child values
- * \return The list of child values
- */
-const OptionsParser::Options &OptionValue::children() const
-{
- return children_;
-}
-
-/* -----------------------------------------------------------------------------
- * OptionsParser
- */
-
-/**
- * \class OptionsParser
- * \brief A command line options parser
- *
- * The OptionsParser class is an easy to use options parser for POSIX-style
- * command line options. Supports short (e.g. `-f`) and long (e.g. `--foo`)
- * options, optional and mandatory arguments, automatic parsing arguments for
- * integer types and comma-separated list of key=value pairs, and multi-value
- * arguments. It handles help text generation automatically.
- *
- * An OptionsParser instance is initialized by adding supported options with
- * addOption(). Options are specified by an identifier and a name. If the
- * identifier is an alphanumeric character, it will be used by the parser as a
- * short option identifier (e.g. `-f`). The name, if specified, will be used as
- * a long option identifier (e.g. `--foo`). It should not include the double
- * dashes. The name is optional if the option identifier is an alphanumeric
- * character and mandatory otherwise.
- *
- * An option has a mandatory help text, which is used to print the full options
- * list with the usage() function. The help text may be a multi-line string.
- * Correct indentation of the help text is handled automatically.
- *
- * Options accept arguments when created with OptionArgument::ArgumentRequired
- * or OptionArgument::ArgumentOptional. If the argument is required, it can be
- * specified as a positional argument after the option (e.g. `-f bar`,
- * `--foo bar`), collated with the short option (e.g. `-fbar`) or separated from
- * the long option by an equal sign (e.g. `--foo=bar`'). When the argument is
- * optional, it must be collated with the short option or separated from the
- * long option by an equal sign.
- *
- * If an option has a required or optional argument, an argument name must be
- * set when adding the option. The argument name is used in the help text as a
- * place holder for an argument value. For instance, a `--write` option that
- * takes a file name as an argument could set the argument name to `filename`,
- * and the help text would display `--write filename`. This is only used to
- * clarify the help text and has no effect on option parsing.
- *
- * The option type tells the parser how to process the argument. Arguments for
- * string options (OptionType::OptionString) are stored as-is without any
- * processing. Arguments for integer options (OptionType::OptionInteger) are
- * converted to an integer value, using an optional base prefix (`0` for base 8,
- * `0x` for base 16, none for base 10). Arguments for key-value options are
- * parsed by a KeyValueParser given to addOption().
- *
- * By default, a given option can appear once only in the parsed command line.
- * If the option is created as an array option, the parser will accept multiple
- * instances of the option. The order in which identical options are specified
- * is preserved in the values of an array option.
- *
- * After preparing the parser, it can be used any number of times to parse
- * command line options with the parse() function. The function returns an
- * Options instance that stores the values for the parsed options. The
- * Options::isSet() function can be used to test if an option has been found,
- * and is the only way to access options that take no argument (specified by
- * OptionType::OptionNone and OptionArgument::ArgumentNone). For options that
- * accept an argument, the option value can be access by Options::operator[]()
- * using the option identifier as the key. The order in which different options
- * are specified on the command line isn't preserved.
- *
- * Options can be created with parent-child relationships to organize them as a
- * tree instead of a flat list. When parsing a command line, the child options
- * are considered related to the parent option that precedes them. This is
- * useful when the parent is an array option. The Options values list generated
- * by the parser then turns into a tree, which each parent value storing the
- * values of child options that follow that instance of the parent option.
- * For instance, with a `capture` option specified as a child of a `camera`
- * array option, parsing the command line
- *
- * `--camera 1 --capture=10 --camera 2 --capture=20`
- *
- * will return an Options instance containing a single OptionValue instance of
- * array type, for the `camera` option. The OptionValue will contain two
- * entries, with the first entry containing the integer value 1 and the second
- * entry the integer value 2. Each of those entries will in turn store an
- * Options instance that contains the respective children. The first entry will
- * store in its children a `capture` option of value 10, and the second entry a
- * `capture` option of value 20.
- *
- * The command line
- *
- * `--capture=10 --camera 1`
- *
- * would result in a parsing error, as the `capture` option has no preceding
- * `camera` option on the command line.
- */
-
-/**
- * \class OptionsParser::Options
- * \brief An option list generated by the options parser
- *
- * This is a specialization of OptionsBase with the option reference type set to
- * int.
- */
-
-OptionsParser::OptionsParser() = default;
-OptionsParser::~OptionsParser() = default;
-
-/**
- * \brief Add an option to the parser
- * \param[in] opt The option identifier
- * \param[in] type The type of the option argument
- * \param[in] help The help text (may be a multi-line string)
- * \param[in] name The option name
- * \param[in] argument Whether the option accepts an optional argument, a
- * mandatory argument, or no argument at all
- * \param[in] argumentName The argument name used in the help text
- * \param[in] array Whether the option can appear once or multiple times
- * \param[in] parent The identifier of the parent option (optional)
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool OptionsParser::addOption(int opt, OptionType type, const char *help,
- const char *name, OptionArgument argument,
- const char *argumentName, bool array, int parent)
-{
- /*
- * Options must have at least a short or long name, and a text message.
- * If an argument is accepted, it must be described by argumentName.
- */
- if (!isalnum(opt) && !name)
- return false;
- if (!help || help[0] == '\0')
- return false;
- if (argument != ArgumentNone && !argumentName)
- return false;
-
- /* Reject duplicate options. */
- if (optionsMap_.find(opt) != optionsMap_.end())
- return false;
-
- /*
- * If a parent is specified, create the option as a child of its parent.
- * Otherwise, create it in the parser's options list.
- */
- Option *option;
-
- if (parent) {
- auto iter = optionsMap_.find(parent);
- if (iter == optionsMap_.end())
- return false;
-
- Option *parentOpt = iter->second;
- parentOpt->children.push_back({
- opt, type, name, argument, argumentName, help, nullptr,
- array, parentOpt, {}
- });
- option = &parentOpt->children.back();
- } else {
- options_.push_back({ opt, type, name, argument, argumentName,
- help, nullptr, array, nullptr, {} });
- option = &options_.back();
- }
-
- optionsMap_[opt] = option;
-
- return true;
-}
-
-/**
- * \brief Add a key-value pair option to the parser
- * \param[in] opt The option identifier
- * \param[in] parser The KeyValueParser for the option value
- * \param[in] help The help text (may be a multi-line string)
- * \param[in] name The option name
- * \param[in] array Whether the option can appear once or multiple times
- *
- * \sa Option
- *
- * \return True if the option was added successfully, false if an error
- * occurred.
- */
-bool OptionsParser::addOption(int opt, KeyValueParser *parser, const char *help,
- const char *name, bool array, int parent)
-{
- if (!addOption(opt, OptionKeyValue, help, name, ArgumentRequired,
- "key=value[,key=value,...]", array, parent))
- return false;
-
- optionsMap_[opt]->keyValueParser = parser;
- return true;
-}
-
-/**
- * \brief Parse command line arguments
- * \param[in] argc The number of arguments in the \a argv array
- * \param[in] argv The array of arguments
- *
- * If a parsing error occurs, the parsing stops, the function prints an error
- * message that identifies the invalid argument, prints usage information with
- * usage(), and returns an invalid container. The container is populated with
- * the options successfully parsed so far.
- *
- * \return A valid container with the list of parsed options on success, or an
- * invalid container otherwise
- */
-OptionsParser::Options OptionsParser::parse(int argc, char **argv)
-{
- OptionsParser::Options options;
-
- /*
- * Allocate short and long options arrays large enough to contain all
- * options.
- */
- char shortOptions[optionsMap_.size() * 3 + 2];
- struct option longOptions[optionsMap_.size() + 1];
- unsigned int ids = 0;
- unsigned int idl = 0;
-
- shortOptions[ids++] = ':';
-
- for (const auto [opt, option] : optionsMap_) {
- if (option->hasShortOption()) {
- shortOptions[ids++] = opt;
- if (option->argument != ArgumentNone)
- shortOptions[ids++] = ':';
- if (option->argument == ArgumentOptional)
- shortOptions[ids++] = ':';
- }
-
- if (option->hasLongOption()) {
- longOptions[idl].name = option->name;
-
- switch (option->argument) {
- case ArgumentNone:
- longOptions[idl].has_arg = no_argument;
- break;
- case ArgumentRequired:
- longOptions[idl].has_arg = required_argument;
- break;
- case ArgumentOptional:
- longOptions[idl].has_arg = optional_argument;
- break;
- }
-
- longOptions[idl].flag = 0;
- longOptions[idl].val = option->opt;
- idl++;
- }
- }
-
- shortOptions[ids] = '\0';
- memset(&longOptions[idl], 0, sizeof(longOptions[idl]));
-
- opterr = 0;
-
- while (true) {
- int c = getopt_long(argc, argv, shortOptions, longOptions, nullptr);
-
- if (c == -1)
- break;
-
- if (c == '?' || c == ':') {
- if (c == '?')
- std::cerr << "Invalid option ";
- else
- std::cerr << "Missing argument for option ";
- std::cerr << argv[optind - 1] << std::endl;
-
- usage();
- return options;
- }
-
- const Option &option = *optionsMap_[c];
- if (!parseValue(option, optarg, &options)) {
- usage();
- return options;
- }
- }
-
- if (optind < argc) {
- std::cerr << "Invalid non-option argument '" << argv[optind]
- << "'" << std::endl;
- usage();
- return options;
- }
-
- options.valid_ = true;
- return options;
-}
-
-/**
- * \brief Print usage text to std::cerr
- *
- * The usage text list all the supported option with their arguments. It is
- * generated automatically from the options added to the parser. Caller of this
- * function may print additional usage information for the application before
- * the list of options.
- */
-void OptionsParser::usage()
-{
- unsigned int indent = 0;
-
- for (const auto &opt : optionsMap_) {
- const Option *option = opt.second;
- unsigned int length = 14;
- if (option->hasLongOption())
- length += 2 + strlen(option->name);
- if (option->argument != ArgumentNone)
- length += 1 + strlen(option->argumentName);
- if (option->argument == ArgumentOptional)
- length += 2;
- if (option->isArray)
- length += 4;
-
- if (length > indent)
- indent = length;
-
- if (option->keyValueParser) {
- length = option->keyValueParser->maxOptionLength();
- if (length > indent)
- indent = length;
- }
- }
-
- indent = (indent + 7) / 8 * 8;
-
- std::cerr << "Options:" << std::endl;
-
- std::ios_base::fmtflags f(std::cerr.flags());
- std::cerr << std::left;
-
- usageOptions(options_, indent);
-
- std::cerr.flags(f);
-}
-
-void OptionsParser::usageOptions(const std::list<Option> &options,
- unsigned int indent)
-{
- std::vector<const Option *> parentOptions;
-
- for (const Option &option : options) {
- std::string argument;
- if (option.hasShortOption())
- argument = std::string(" -")
- + static_cast<char>(option.opt);
- else
- argument = " ";
-
- if (option.hasLongOption()) {
- if (option.hasShortOption())
- argument += ", ";
- else
- argument += " ";
- argument += std::string("--") + option.name;
- }
-
- if (option.argument != ArgumentNone) {
- if (option.argument == ArgumentOptional)
- argument += "[=";
- else
- argument += " ";
- argument += option.argumentName;
- if (option.argument == ArgumentOptional)
- argument += "]";
- }
-
- if (option.isArray)
- argument += " ...";
-
- std::cerr << std::setw(indent) << argument;
-
- for (const char *help = option.help, *end = help; end; ) {
- end = strchr(help, '\n');
- if (end) {
- std::cerr << std::string(help, end - help + 1);
- std::cerr << std::setw(indent) << " ";
- help = end + 1;
- } else {
- std::cerr << help << std::endl;
- }
- }
-
- if (option.keyValueParser)
- option.keyValueParser->usage(indent);
-
- if (!option.children.empty())
- parentOptions.push_back(&option);
- }
-
- if (parentOptions.empty())
- return;
-
- for (const Option *option : parentOptions) {
- std::cerr << std::endl << "Options valid in the context of "
- << option->optionName() << ":" << std::endl;
- usageOptions(option->children, indent);
- }
-}
-
-std::tuple<OptionsParser::Options *, const Option *>
-OptionsParser::childOption(const Option *parent, Options *options)
-{
- /*
- * The parent argument points to the parent of the leaf node Option,
- * and the options argument to the root node of the Options tree. Use
- * recursive calls to traverse the Option tree up to the root node while
- * traversing the Options tree down to the leaf node:
- */
-
- /*
- * - If we have no parent, we've reached the root node of the Option
- * tree, the options argument is what we need.
- */
- if (!parent)
- return { options, nullptr };
-
- /*
- * - If the parent has a parent, use recursion to move one level up the
- * Option tree. This returns the Options corresponding to parent, or
- * nullptr if a suitable Options child isn't found.
- */
- if (parent->parent) {
- const Option *error;
- std::tie(options, error) = childOption(parent->parent, options);
-
- /* Propagate the error all the way back up the call stack. */
- if (!error)
- return { options, error };
- }
-
- /*
- * - The parent has no parent, we're now one level down the root.
- * Return the Options child corresponding to the parent. The child may
- * not exist if options are specified in an incorrect order.
- */
- if (!options->isSet(parent->opt))
- return { nullptr, parent };
-
- /*
- * If the child value is of array type, children are not stored in the
- * value .children() list, but in the .children() of the value's array
- * elements. Use the last array element in that case, as a child option
- * relates to the last instance of its parent option.
- */
- const OptionValue *value = &(*options)[parent->opt];
- if (value->type() == OptionValue::ValueArray)
- value = &value->toArray().back();
-
- return { const_cast<Options *>(&value->children()), nullptr };
-}
-
-bool OptionsParser::parseValue(const Option &option, const char *arg,
- Options *options)
-{
- const Option *error;
-
- std::tie(options, error) = childOption(option.parent, options);
- if (error) {
- std::cerr << "Option " << option.optionName() << " requires a "
- << error->optionName() << " context" << std::endl;
- return false;
- }
-
- if (!options->parseValue(option.opt, option, arg)) {
- std::cerr << "Can't parse " << option.typeName()
- << " argument for option " << option.optionName()
- << std::endl;
- return false;
- }
-
- return true;
-}