diff options
author | Kevin Wolf <kwolf@redhat.com> | 2020-10-11 09:35:02 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2020-10-15 16:06:27 +0200 |
commit | 8bf12c4f752b439eb37e3de8a063af32e986c730 (patch) | |
tree | b7cb13dcfd038f122643016139eeef1da40f6f74 /util/keyval.c | |
parent | 7051ae6cf1ec1072d2cdaa978660b22245a1efad (diff) |
keyval: Parse help options
This adds a special meaning for 'help' and '?' as options to the keyval
parser. Instead of being an error (because of a missing value) or a
value for an implied key, they now request help, which is a new boolean
output of the parser in addition to the QDict.
A new parameter 'p_help' is added to keyval_parse() that contains on
return whether help was requested. If NULL is passed, requesting help
results in an error and all other cases work like before.
Turning previous error cases into help is a compatible extension. The
behaviour potentially changes for implied keys: They could previously
get 'help' as their value, which is now interpreted as requesting help.
This is not a problem in practice because 'help' and '?' are not a valid
values for the implied key of any option parsed with keyval_parse():
* audiodev: union Audiodev, implied key "driver" is enum AudiodevDriver,
"help" and "?" are not among its values
* display: union DisplayOptions, implied key "type" is enum
DisplayType, "help" and "?" are not among its values
* blockdev: union BlockdevOptions, implied key "driver is enum
BlockdevDriver, "help" and "?" are not among its values
* export: union BlockExport, implied key "type" is enum BlockExportType,
"help" and "?" are not among its values
* monitor: struct MonitorOptions, implied key "mode" is enum MonitorMode,
"help" and "?" are not among its values
* nbd-server: struct NbdServerOptions, no implied key.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20201011073505.1185335-5-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'util/keyval.c')
-rw-r--r-- | util/keyval.c | 63 |
1 files changed, 51 insertions, 12 deletions
diff --git a/util/keyval.c b/util/keyval.c index 8f33a36a7c..7f625ad33c 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -14,10 +14,11 @@ * KEY=VALUE,... syntax: * * key-vals = [ key-val { ',' key-val } [ ',' ] ] - * key-val = key '=' val + * key-val = key '=' val | help * key = key-fragment { '.' key-fragment } * key-fragment = / [^=,.]+ / * val = { / [^,]+ / | ',,' } + * help = 'help' | '?' * * Semantics defined by reduction to JSON: * @@ -54,6 +55,9 @@ * * The length of any key-fragment must be between 1 and 127. * + * If any key-val is help, the object is to be treated as a help + * request. + * * Design flaw: there is no way to denote an empty array or non-root * object. While interpreting "key absent" as empty seems natural * (removing a key-val from the input string removes the member when @@ -75,7 +79,7 @@ * * key-vals = [ key-val-1st { ',' key-val } [ ',' ] ] * key-val-1st = val-no-key | key-val - * val-no-key = / [^=,]+ / + * val-no-key = / [^=,]+ / - help * * where val-no-key is syntactic sugar for implied-key=val-no-key. * @@ -89,6 +93,7 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" #include "qemu/cutils.h" +#include "qemu/help_option.h" #include "qemu/option.h" /* @@ -162,15 +167,20 @@ static QObject *keyval_parse_put(QDict *cur, } /* - * Parse one KEY=VALUE from @params, store result in @qdict. + * Parse one parameter from @params. + * + * If we're looking at KEY=VALUE, store result in @qdict. * The first fragment of KEY applies to @qdict. Subsequent fragments * apply to nested QDicts, which are created on demand. @implied_key * is as in keyval_parse(). - * On success, return a pointer to the next KEY=VALUE, or else to '\0'. + * + * If we're looking at "help" or "?", set *help to true. + * + * On success, return a pointer to the next parameter, or else to '\0'. * On failure, return NULL. */ static const char *keyval_parse_one(QDict *qdict, const char *params, - const char *implied_key, + const char *implied_key, bool *help, Error **errp) { const char *key, *key_end, *val_end, *s, *end; @@ -184,11 +194,21 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, key = params; val_end = NULL; len = strcspn(params, "=,"); - if (implied_key && len && key[len] != '=') { - /* Desugar implied key */ - key = implied_key; - val_end = params + len; - len = strlen(implied_key); + if (len && key[len] != '=') { + if (starts_with_help_option(key) == len) { + *help = true; + s = key + len; + if (*s == ',') { + s++; + } + return s; + } + if (implied_key) { + /* Desugar implied key */ + key = implied_key; + val_end = params + len; + len = strlen(implied_key); + } } key_end = key + len; @@ -398,21 +418,32 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp) /* * Parse @params in QEMU's traditional KEY=VALUE,... syntax. + * * If @implied_key, the first KEY= can be omitted. @implied_key is * implied then, and VALUE can't be empty or contain ',' or '='. + * + * A parameter "help" or "?" without a value isn't added to the + * resulting dictionary, but instead is interpreted as help request. + * All other options are parsed and returned normally so that context + * specific help can be printed. + * + * If @p_help is not NULL, store whether help is requested there. + * If @p_help is NULL and help is requested, fail. + * * On success, return a dictionary of the parsed keys and values. * On failure, store an error through @errp and return NULL. */ QDict *keyval_parse(const char *params, const char *implied_key, - Error **errp) + bool *p_help, Error **errp) { QDict *qdict = qdict_new(); QObject *listified; const char *s; + bool help = false; s = params; while (*s) { - s = keyval_parse_one(qdict, s, implied_key, errp); + s = keyval_parse_one(qdict, s, implied_key, &help, errp); if (!s) { qobject_unref(qdict); return NULL; @@ -420,6 +451,14 @@ QDict *keyval_parse(const char *params, const char *implied_key, implied_key = NULL; } + if (p_help) { + *p_help = help; + } else if (help) { + error_setg(errp, "Help is not available for this option"); + qobject_unref(qdict); + return NULL; + } + listified = keyval_listify(qdict, NULL, errp); if (!listified) { qobject_unref(qdict); |