@@ -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);
 	}
 }
 
@@ -11,6 +11,9 @@ 
 #include <list>
 #include <map>
 
+class KeyValueParser;
+class OptionValue;
+
 enum OptionArgument {
 	ArgumentNone,
 	ArgumentRequired,
@@ -21,6 +24,7 @@  enum OptionType {
 	OptionNone,
 	OptionInteger,
 	OptionString,
+	OptionKeyValue,
 };
 
 struct Option {
@@ -30,14 +34,13 @@  struct Option {
 	OptionArgument argument;
 	const char *argumentName;
 	const char *help;
+	KeyValueParser *keyValueParser;
 
 	bool hasShortOption() const { return isalnum(opt); }
 	bool hasLongOption() const { return name != nullptr; }
 	const char *typeName() const;
 };
 
-class OptionValue;
-
 template <typename T>
 class OptionsBase
 {
@@ -47,6 +50,7 @@  public:
 	const OptionValue &operator[](const T &opt) const;
 
 private:
+	friend class KeyValueParser;
 	friend class OptionsParser;
 
 	bool parseValue(const T &opt, const Option &option, const char *value);
@@ -55,6 +59,23 @@  private:
 	std::map<T, OptionValue> values_;
 };
 
+class KeyValueParser
+{
+public:
+	class Options : public OptionsBase<std::string>
+	{
+	};
+
+	bool addOption(const char *name, OptionType type, const char *help,
+		       OptionArgument argument = ArgumentNone);
+
+	Options parse(const char *arguments);
+	void usage(int indent);
+
+private:
+	std::map<std::string, Option> optionsMap_;
+};
+
 class OptionValue
 {
 public:
@@ -62,16 +83,19 @@  public:
 	OptionValue(int value);
 	OptionValue(const char *value);
 	OptionValue(const std::string &value);
+	OptionValue(const KeyValueParser::Options &value);
 
 	OptionType type() const { return type_; }
 
 	operator int() const;
 	operator std::string() const;
+	operator KeyValueParser::Options() const;
 
 private:
 	OptionType type_;
 	int integer_;
 	std::string string_;
+	KeyValueParser::Options keyValues_;
 };
 
 class OptionsParser
@@ -85,6 +109,8 @@  public:
 		       const char *name = nullptr,
 		       OptionArgument argument = ArgumentNone,
 		       const char *argumentName = nullptr);
+	bool addOption(int opt, KeyValueParser *parser, const char *help,
+		       const char *name = nullptr);
 
 	Options parse(int argc, char *argv[]);
 	void usage();