Patch Detail
Show a patch.
GET /api/1.1/patches/467/?format=api
{ "id": 467, "url": "https://patchwork.libcamera.org/api/1.1/patches/467/?format=api", "web_url": "https://patchwork.libcamera.org/patch/467/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20190131234721.22606-8-laurent.pinchart@ideasonboard.com>", "date": "2019-01-31T23:47:20", "name": "[libcamera-devel,v2,7/8] cam: options: Add a key=value parser", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "65d714d841b377b3208f5355b6856a9d213e9d4f", "submitter": { "id": 2, "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api", "name": "Laurent Pinchart", "email": "laurent.pinchart@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/467/mbox/", "series": [ { "id": 158, "url": "https://patchwork.libcamera.org/api/1.1/series/158/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=158", "date": "2019-01-31T23:47:13", "name": "cam: add --format option to configure a stream", "version": 2, "mbox": "https://patchwork.libcamera.org/series/158/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/467/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/467/checks/", "tags": {}, "headers": { "Return-Path": "<laurent.pinchart@ideasonboard.com>", "Received": [ "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7FF6C60DB4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 1 Feb 2019 00:47:45 +0100 (CET)", "from pendragon.ideasonboard.com (85-76-34-136-nat.elisa-mobile.fi\n\t[85.76.34.136])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id E49DB41;\n\tFri, 1 Feb 2019 00:47:43 +0100 (CET)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1548978465;\n\tbh=2kwFiiV5dhbZPpqRDRQdFylZbKrvGmQD2D8xsk42WuM=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=GXEM+lH6n+ev5j6lbqAy/vaEZ8GfdjMNzp6MBnUjYCmjpTufwZ4f+OZ2yU9fEKS0b\n\tcb9bUgzWIP7av6b9jvyXBe6nQHVlp/3K5j1lb8OW23wFMS+AgFGZV3v2Wf7+dzy0KY\n\tASBOzByxG7ho9sTDozHxDU4hrbO5KOE3pp5BEF48=", "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 1 Feb 2019 01:47:20 +0200", "Message-Id": "<20190131234721.22606-8-laurent.pinchart@ideasonboard.com>", "X-Mailer": "git-send-email 2.19.2", "In-Reply-To": "<20190131234721.22606-1-laurent.pinchart@ideasonboard.com>", "References": "<20190131234721.22606-1-laurent.pinchart@ideasonboard.com>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 7/8] cam: options: Add a key=value\n\tparser", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.23", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "X-List-Received-Date": "Thu, 31 Jan 2019 23:47:45 -0000" }, "content": "From: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\nSome options passed to the cam utility need to be complex and specify a\nlist of key=value pairs. Add a new parser to deal with these options,\nusable on its own to parse key=value pairs from any string.\n\nIntegrate the KeyValueParser into the existing OptionsParser. The cam\napplication can fully describe all its options in one location and\nperform full parsing of all arguments in one go. The KeyValueParser also\nintegrates itself with the usage() printing of the OptionsParser.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\nChanges since v1:\n\n- Remove OptionsParser::Options::isKeyValue()\n- Return bool from addOption()\n- Refactor parse() method\n- Add option type handling\n- Remove argumentName from addOption()\n- Cosmetic changes\n---\n src/cam/options.cpp | 176 +++++++++++++++++++++++++++++++++++++++++++-\n src/cam/options.h | 30 +++++++-\n 2 files changed, 203 insertions(+), 3 deletions(-)", "diff": "diff --git a/src/cam/options.cpp b/src/cam/options.cpp\nindex 204081f3cd8e..4c9f3a36d435 100644\n--- a/src/cam/options.cpp\n+++ b/src/cam/options.cpp\n@@ -27,6 +27,9 @@ const char *Option::typeName() const\n \n \tcase OptionString:\n \t\treturn \"string\";\n+\n+\tcase OptionKeyValue:\n+\t\treturn \"key=value\";\n \t}\n \n \treturn \"unknown\";\n@@ -82,6 +85,15 @@ bool OptionsBase<T>::parseValue(const T &opt, const Option &option,\n \tcase OptionString:\n \t\tvalue = OptionValue(optarg ? optarg : \"\");\n \t\tbreak;\n+\n+\tcase OptionKeyValue:\n+\t\tKeyValueParser *kvParser = option.keyValueParser;\n+\t\tKeyValueParser::Options keyValues = kvParser->parse(optarg);\n+\t\tif (!keyValues.valid())\n+\t\t\treturn false;\n+\n+\t\tvalue = OptionValue(keyValues);\n+\t\tbreak;\n \t}\n \n \tvalues_[opt] = value;\n@@ -95,6 +107,141 @@ void OptionsBase<T>::clear()\n }\n \n template class OptionsBase<int>;\n+template class OptionsBase<std::string>;\n+\n+/* -----------------------------------------------------------------------------\n+ * KeyValueParser\n+ */\n+\n+bool KeyValueParser::addOption(const char *name, OptionType type,\n+\t\t\t const char *help, OptionArgument argument)\n+{\n+\tif (!name)\n+\t\treturn false;\n+\tif (!help || help[0] == '\\0')\n+\t\treturn false;\n+\tif (argument != ArgumentNone && type == OptionNone)\n+\t\treturn false;\n+\n+\t/* Reject duplicate options. */\n+\tif (optionsMap_.find(name) != optionsMap_.end())\n+\t\treturn false;\n+\n+\toptionsMap_[name] = Option({ 0, type, name, argument, nullptr,\n+\t\t\t\t help, nullptr });\n+\treturn true;\n+}\n+\n+KeyValueParser::Options KeyValueParser::parse(const char *arguments)\n+{\n+\tOptions options;\n+\n+\tfor (const char *pair = arguments; *arguments != '\\0'; pair = arguments) {\n+\t\tconst char *comma = strchrnul(arguments, ',');\n+\t\tsize_t len = comma - pair;\n+\n+\t\t/* Skip over the comma. */\n+\t\targuments = *comma == ',' ? comma + 1 : comma;\n+\n+\t\t/* Skip to the next pair if the pair is empty. */\n+\t\tif (!len)\n+\t\t\tcontinue;\n+\n+\t\tstd::string key;\n+\t\tstd::string value;\n+\n+\t\tconst char *separator = static_cast<const char *>(memchr(pair, '=', len));\n+\t\tif (!separator) {\n+\t\t\tkey = std::string(pair, len);\n+\t\t\tvalue = \"\";\n+\t\t} else {\n+\t\t\tkey = std::string(pair, separator - pair);\n+\t\t\tvalue = std::string(separator + 1, comma - separator - 1);\n+\t\t}\n+\n+\t\t/* The key is mandatory, the value might be optional. */\n+\t\tif (key.empty())\n+\t\t\tcontinue;\n+\n+\t\tif (optionsMap_.find(key) == optionsMap_.end()) {\n+\t\t\tstd::cerr << \"Invalid option \" << key << std::endl;\n+\t\t\toptions.clear();\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tOptionArgument arg = optionsMap_[key].argument;\n+\t\tif (value.empty() && arg == ArgumentRequired) {\n+\t\t\tstd::cerr << \"Option \" << key << \" requires an argument\"\n+\t\t\t\t << std::endl;\n+\t\t\toptions.clear();\n+\t\t\tbreak;\n+\t\t} else if (!value.empty() && arg == ArgumentNone) {\n+\t\t\tstd::cerr << \"Option \" << key << \" takes no argument\"\n+\t\t\t\t << std::endl;\n+\t\t\toptions.clear();\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tconst Option &option = optionsMap_[key];\n+\t\tif (!options.parseValue(key, option, value.c_str())) {\n+\t\t\tstd::cerr << \"Failed to parse '\" << value << \"' as \"\n+\t\t\t\t << option.typeName() << \" for option \" << key\n+\t\t\t\t << std::endl;\n+\t\t\toptions.clear();\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn options;\n+}\n+\n+void KeyValueParser::usage(int indent)\n+{\n+\tunsigned int space = 0;\n+\n+\tfor (auto const &iter : optionsMap_) {\n+\t\tconst Option &option = iter.second;\n+\t\tunsigned int length = 14;\n+\t\tif (option.argument != ArgumentNone)\n+\t\t\tlength += 1 + strlen(option.typeName());\n+\t\tif (option.argument == ArgumentOptional)\n+\t\t\tlength += 2;\n+\n+\t\tif (length > space)\n+\t\t\tspace = length;\n+\t}\n+\n+\tspace = (space + 7) / 8 * 8;\n+\n+\tfor (auto const &iter : optionsMap_) {\n+\t\tconst Option &option = iter.second;\n+\t\tstd::string argument = option.name;\n+\n+\t\tif (option.argument != ArgumentNone) {\n+\t\t\tif (option.argument == ArgumentOptional)\n+\t\t\t\targument += \"[=\";\n+\t\t\telse\n+\t\t\t\targument += \"=\";\n+\t\t\targument += option.typeName();\n+\t\t\tif (option.argument == ArgumentOptional)\n+\t\t\t\targument += \"]\";\n+\t\t}\n+\n+\t\tstd::cerr << std::setw(indent) << std::right << \" \"\n+\t\t\t << std::setw(space) << std::left << argument;\n+\n+\t\tfor (const char *help = option.help, *end = help; end;) {\n+\t\t\tend = strchr(help, '\\n');\n+\t\t\tif (end) {\n+\t\t\t\tstd::cerr << std::string(help, end - help + 1);\n+\t\t\t\tstd::cerr << std::setw(indent + space) << \" \";\n+\t\t\t\thelp = end + 1;\n+\t\t\t} else {\n+\t\t\t\tstd::cerr << help << std::endl;\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n \n /* -----------------------------------------------------------------------------\n * OptionValue\n@@ -120,6 +267,11 @@ OptionValue::OptionValue(const std::string &value)\n {\n }\n \n+OptionValue::OptionValue(const KeyValueParser::Options &value)\n+\t: type_(OptionKeyValue), keyValues_(value)\n+{\n+}\n+\n OptionValue::operator int() const\n {\n \tif (type_ != OptionInteger)\n@@ -136,6 +288,14 @@ OptionValue::operator std::string() const\n \treturn string_;\n }\n \n+OptionValue::operator KeyValueParser::Options() const\n+{\n+\tif (type_ != OptionKeyValue)\n+\t\treturn KeyValueParser::Options();\n+\n+\treturn keyValues_;\n+}\n+\n /* -----------------------------------------------------------------------------\n * OptionsParser\n */\n@@ -160,11 +320,22 @@ bool OptionsParser::addOption(int opt, OptionType type, const char *help,\n \t\treturn false;\n \n \toptions_.push_back(Option({ opt, type, name, argument, argumentName,\n-\t\t\t\t help }));\n+\t\t\t\t help, nullptr }));\n \toptionsMap_[opt] = &options_.back();\n \treturn true;\n }\n \n+bool OptionsParser::addOption(int opt, KeyValueParser *parser, const char *help,\n+\t\t\t const char *name)\n+{\n+\tif (!addOption(opt, OptionKeyValue, help, name, ArgumentRequired,\n+\t\t \"key=value[,key=value,...]\"))\n+\t\treturn false;\n+\n+\toptions_.back().keyValueParser = parser;\n+\treturn true;\n+}\n+\n OptionsParser::Options OptionsParser::parse(int argc, char **argv)\n {\n \tOptionsParser::Options options;\n@@ -301,6 +472,9 @@ void OptionsParser::usage()\n \t\t\t\tstd::cerr << help << std::endl;\n \t\t\t}\n \t\t}\n+\n+\t\tif (option.keyValueParser)\n+\t\t\toption.keyValueParser->usage(indent);\n \t}\n }\n \ndiff --git a/src/cam/options.h b/src/cam/options.h\nindex 8b611d374fd5..e1fd62ecd369 100644\n--- a/src/cam/options.h\n+++ b/src/cam/options.h\n@@ -11,6 +11,9 @@\n #include <list>\n #include <map>\n \n+class KeyValueParser;\n+class OptionValue;\n+\n enum OptionArgument {\n \tArgumentNone,\n \tArgumentRequired,\n@@ -21,6 +24,7 @@ enum OptionType {\n \tOptionNone,\n \tOptionInteger,\n \tOptionString,\n+\tOptionKeyValue,\n };\n \n struct Option {\n@@ -30,14 +34,13 @@ struct Option {\n \tOptionArgument argument;\n \tconst char *argumentName;\n \tconst char *help;\n+\tKeyValueParser *keyValueParser;\n \n \tbool hasShortOption() const { return isalnum(opt); }\n \tbool hasLongOption() const { return name != nullptr; }\n \tconst char *typeName() const;\n };\n \n-class OptionValue;\n-\n template <typename T>\n class OptionsBase\n {\n@@ -47,6 +50,7 @@ public:\n \tconst OptionValue &operator[](const T &opt) const;\n \n private:\n+\tfriend class KeyValueParser;\n \tfriend class OptionsParser;\n \n \tbool parseValue(const T &opt, const Option &option, const char *value);\n@@ -55,6 +59,23 @@ private:\n \tstd::map<T, OptionValue> values_;\n };\n \n+class KeyValueParser\n+{\n+public:\n+\tclass Options : public OptionsBase<std::string>\n+\t{\n+\t};\n+\n+\tbool addOption(const char *name, OptionType type, const char *help,\n+\t\t OptionArgument argument = ArgumentNone);\n+\n+\tOptions parse(const char *arguments);\n+\tvoid usage(int indent);\n+\n+private:\n+\tstd::map<std::string, Option> optionsMap_;\n+};\n+\n class OptionValue\n {\n public:\n@@ -62,16 +83,19 @@ public:\n \tOptionValue(int value);\n \tOptionValue(const char *value);\n \tOptionValue(const std::string &value);\n+\tOptionValue(const KeyValueParser::Options &value);\n \n \tOptionType type() const { return type_; }\n \n \toperator int() const;\n \toperator std::string() const;\n+\toperator KeyValueParser::Options() const;\n \n private:\n \tOptionType type_;\n \tint integer_;\n \tstd::string string_;\n+\tKeyValueParser::Options keyValues_;\n };\n \n class OptionsParser\n@@ -85,6 +109,8 @@ public:\n \t\t const char *name = nullptr,\n \t\t OptionArgument argument = ArgumentNone,\n \t\t const char *argumentName = nullptr);\n+\tbool addOption(int opt, KeyValueParser *parser, const char *help,\n+\t\t const char *name = nullptr);\n \n \tOptions parse(int argc, char *argv[]);\n \tvoid usage();\n", "prefixes": [ "libcamera-devel", "v2", "7/8" ] }