summaryrefslogtreecommitdiff
path: root/src/cam/options.cpp
diff options
context:
space:
mode:
authorNiklas Söderlund <niklas.soderlund@ragnatech.se>2019-01-28 01:02:18 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-02-01 11:42:03 +0200
commit270eb0acc79346ac0b641f405c40bf3b959a4c6f (patch)
treefca6218b4fb3e0765e2c9e6fb3f366caea03e32e /src/cam/options.cpp
parentc8c546fe99a343c13c7c0b8f2e5e180c19831b43 (diff)
cam: options: Add a key=value parser
Some options passed to the cam utility need to be complex and specify a list of key=value pairs. Add a new parser to deal with these options, usable on its own to parse key=value pairs from any string. Integrate the KeyValueParser into the existing OptionsParser. The cam application can fully describe all its options in one location and perform full parsing of all arguments in one go. The KeyValueParser also integrates itself with the usage() printing of the OptionsParser. Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/cam/options.cpp')
-rw-r--r--src/cam/options.cpp176
1 files changed, 175 insertions, 1 deletions
diff --git a/src/cam/options.cpp b/src/cam/options.cpp
index 204081f3..4c9f3a36 100644
--- a/src/cam/options.cpp
+++ b/src/cam/options.cpp
@@ -27,6 +27,9 @@ const char *Option::typeName() const
case OptionString:
return "string";
+
+ case OptionKeyValue:
+ return "key=value";
}
return "unknown";
@@ -82,6 +85,15 @@ bool OptionsBase<T>::parseValue(const T &opt, const Option &option,
case OptionString:
value = OptionValue(optarg ? optarg : "");
break;
+
+ case OptionKeyValue:
+ KeyValueParser *kvParser = option.keyValueParser;
+ KeyValueParser::Options keyValues = kvParser->parse(optarg);
+ if (!keyValues.valid())
+ return false;
+
+ value = OptionValue(keyValues);
+ break;
}
values_[opt] = value;
@@ -95,6 +107,141 @@ void OptionsBase<T>::clear()
}
template class OptionsBase<int>;
+template class OptionsBase<std::string>;
+
+/* -----------------------------------------------------------------------------
+ * KeyValueParser
+ */
+
+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 });
+ return true;
+}
+
+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;
+ options.clear();
+ break;
+ }
+
+ OptionArgument arg = optionsMap_[key].argument;
+ if (value.empty() && arg == ArgumentRequired) {
+ std::cerr << "Option " << key << " requires an argument"
+ << std::endl;
+ options.clear();
+ break;
+ } else if (!value.empty() && arg == ArgumentNone) {
+ std::cerr << "Option " << key << " takes no argument"
+ << std::endl;
+ options.clear();
+ break;
+ }
+
+ 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;
+ options.clear();
+ break;
+ }
+ }
+
+ return options;
+}
+
+void KeyValueParser::usage(int indent)
+{
+ unsigned int space = 0;
+
+ for (auto const &iter : optionsMap_) {
+ const Option &option = iter.second;
+ unsigned int length = 14;
+ if (option.argument != ArgumentNone)
+ length += 1 + strlen(option.typeName());
+ if (option.argument == ArgumentOptional)
+ length += 2;
+
+ if (length > space)
+ space = length;
+ }
+
+ space = (space + 7) / 8 * 8;
+
+ for (auto const &iter : optionsMap_) {
+ const Option &option = iter.second;
+ std::string argument = 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) << std::right << " "
+ << std::setw(space) << std::left << 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 + space) << " ";
+ help = end + 1;
+ } else {
+ std::cerr << help << std::endl;
+ }
+ }
+ }
+}
/* -----------------------------------------------------------------------------
* OptionValue
@@ -120,6 +267,11 @@ OptionValue::OptionValue(const std::string &value)
{
}
+OptionValue::OptionValue(const KeyValueParser::Options &value)
+ : type_(OptionKeyValue), keyValues_(value)
+{
+}
+
OptionValue::operator int() const
{
if (type_ != OptionInteger)
@@ -136,6 +288,14 @@ OptionValue::operator std::string() const
return string_;
}
+OptionValue::operator KeyValueParser::Options() const
+{
+ if (type_ != OptionKeyValue)
+ return KeyValueParser::Options();
+
+ return keyValues_;
+}
+
/* -----------------------------------------------------------------------------
* OptionsParser
*/
@@ -160,11 +320,22 @@ bool OptionsParser::addOption(int opt, OptionType type, const char *help,
return false;
options_.push_back(Option({ opt, type, name, argument, argumentName,
- help }));
+ help, nullptr }));
optionsMap_[opt] = &options_.back();
return true;
}
+bool OptionsParser::addOption(int opt, KeyValueParser *parser, const char *help,
+ const char *name)
+{
+ if (!addOption(opt, OptionKeyValue, help, name, ArgumentRequired,
+ "key=value[,key=value,...]"))
+ return false;
+
+ options_.back().keyValueParser = parser;
+ return true;
+}
+
OptionsParser::Options OptionsParser::parse(int argc, char **argv)
{
OptionsParser::Options options;
@@ -301,6 +472,9 @@ void OptionsParser::usage()
std::cerr << help << std::endl;
}
}
+
+ if (option.keyValueParser)
+ option.keyValueParser->usage(indent);
}
}