Block layer patches:
- qemu-storage-daemon: Remove QemuOpts from --object parser - monitor: Fix order in monitor_cleanup() - Deprecate the sheepdog block driver -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAl+IYSoRHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9YH0g//aXB91wIDBVvyRYddD7rv9tjr6nzlqqkE gsHGNiSDtNrNvmPyDl5JMF+xkki6mF4AKOwZYgDXaz/Rg/93DbR73yt3ZqdHUwOq hGSHJhdxyWJzLoL3di2ORQDrj414YLJlbVNUJMZnB7AGr/T0lHFwLbJr2dlBKONn uwaqoQ5fLnGY3szpQgJIZuHP//5aX3Zx1KyRSnlF93blRgKDJSD7NpEdIJyhFLd7 qXcGPx75XsNPMCuxbAZFceMLUWsLh1//zuJU3dC4QS0lDjc+99oN7xiUT8DhlQAm nwSNaRoHdkVMtSFpJ/gmQzOFQbULXnAuLcdVXTWL31EGbINv64AjBrWByDJ0DsU5 Q+zTmpf8m6BnQyTghE7LkxJFBfVdqRVG6ebZiMjuoyvUXRhBVCYGHv2qddVTLL5O 5ixdArwgfXE0lRyF2Z2UUhZhAQO4YnHJxReRGwzSRCmM1kqzB5cSmNJylkbWYDh0 aku3skXTPUBBE/bRpsIwB+lEEm8OaZSQQrHLdaHtUHM7qNFWmYbOdZOCQOG845QX sLq1Ghhtki6e2IZLPjOFOdm+p2cStLbfGExunkjhke9osTNfLNVSV8JNT0xORPAm CQCLn6EiEwpMEHbIn8AsSWRl1hbqCubxRHeeCSKmM7EtHd6mr/osRjeo8jdlK/Qb z7ztkatr7iU= =gBMn -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches: - qemu-storage-daemon: Remove QemuOpts from --object parser - monitor: Fix order in monitor_cleanup() - Deprecate the sheepdog block driver # gpg: Signature made Thu 15 Oct 2020 15:48:10 BST # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: block: deprecate the sheepdog block driver block: drop moderated sheepdog mailing list from MAINTAINERS file monitor: Fix order in monitor_cleanup() qemu-storage-daemon: Remove QemuOpts from --object parser qom: Add user_creatable_print_help_from_qdict() qom: Factor out helpers from user_creatable_print_help() keyval: Parse help options keyval: Fix parsing of ',' in value of implied key test-keyval: Demonstrate misparse of ',' with implied key keyval: Fix and clarify grammar Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
7daf8f8d01
@ -2881,7 +2881,6 @@ F: block/rbd.c
|
||||
Sheepdog
|
||||
M: Liu Yuan <namei.unix@gmail.com>
|
||||
L: qemu-block@nongnu.org
|
||||
L: sheepdog@lists.wpkg.org
|
||||
S: Odd Fixes
|
||||
F: block/sheepdog.c
|
||||
|
||||
|
@ -242,6 +242,16 @@ typedef struct SheepdogInode {
|
||||
*/
|
||||
#define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
|
||||
|
||||
static void deprecation_warning(void)
|
||||
{
|
||||
static bool warned;
|
||||
|
||||
if (!warned) {
|
||||
warn_report("the sheepdog block driver is deprecated");
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 64 bit Fowler/Noll/Vo FNV-1a hash code
|
||||
*/
|
||||
@ -1548,6 +1558,8 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
char *buf = NULL;
|
||||
QemuOpts *opts;
|
||||
|
||||
deprecation_warning();
|
||||
|
||||
s->bs = bs;
|
||||
s->aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
@ -2007,6 +2019,8 @@ static int sd_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
|
||||
assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG);
|
||||
|
||||
deprecation_warning();
|
||||
|
||||
s = g_new0(BDRVSheepdogState, 1);
|
||||
|
||||
/* Steal SocketAddress from QAPI, set NULL to prevent double free */
|
||||
|
5
configure
vendored
5
configure
vendored
@ -433,7 +433,7 @@ vdi="yes"
|
||||
vvfat="yes"
|
||||
qed="yes"
|
||||
parallels="yes"
|
||||
sheepdog="yes"
|
||||
sheepdog="no"
|
||||
libxml2=""
|
||||
debug_mutex="no"
|
||||
libpmem=""
|
||||
@ -1830,7 +1830,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
vvfat vvfat image format support
|
||||
qed qed image format support
|
||||
parallels parallels image format support
|
||||
sheepdog sheepdog block driver support
|
||||
sheepdog sheepdog block driver support (deprecated)
|
||||
crypto-afalg Linux AF_ALG crypto backend driver
|
||||
capstone capstone disassembler support
|
||||
debug-mutex mutex debugging support
|
||||
@ -6729,6 +6729,7 @@ if test "$parallels" = "yes" ; then
|
||||
echo "CONFIG_PARALLELS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$sheepdog" = "yes" ; then
|
||||
add_to deprecated_features "sheepdog"
|
||||
echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$pty_h" = "yes" ; then
|
||||
|
@ -390,6 +390,15 @@ The above, converted to the current supported format::
|
||||
|
||||
json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"}
|
||||
|
||||
``sheepdog`` driver (since 5.2.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``sheepdog`` block device driver is deprecated. The corresponding upstream
|
||||
server project is no longer actively maintained. Users are recommended to switch
|
||||
to an alternative distributed block device driver such as RBD. The
|
||||
``qemu-img convert`` command can be used to liberate existing data by moving
|
||||
it out of sheepdog volumes into an alternative storage backend.
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
||||
|
@ -19,4 +19,15 @@ static inline bool is_help_option(const char *s)
|
||||
return !strcmp(s, "?") || !strcmp(s, "help");
|
||||
}
|
||||
|
||||
static inline int starts_with_help_option(const char *s)
|
||||
{
|
||||
if (*s == '?') {
|
||||
return 1;
|
||||
}
|
||||
if (g_str_has_prefix(s, "help")) {
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -149,6 +149,6 @@ void qemu_opts_free(QemuOptsList *list);
|
||||
QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
|
||||
|
||||
QDict *keyval_parse(const char *params, const char *implied_key,
|
||||
Error **errp);
|
||||
bool *help, Error **errp);
|
||||
|
||||
#endif
|
||||
|
@ -154,13 +154,28 @@ int user_creatable_add_opts_foreach(void *opaque,
|
||||
* @type: the QOM type to be added
|
||||
* @opts: options to create
|
||||
*
|
||||
* Prints help if requested in @opts.
|
||||
* Prints help if requested in @type or @opts. Note that if @type is neither
|
||||
* "help"/"?" nor a valid user creatable type, no help will be printed
|
||||
* regardless of @opts.
|
||||
*
|
||||
* Returns: true if @opts contained a help option and help was printed, false
|
||||
* if no help option was found.
|
||||
* Returns: true if a help option was found and help was printed, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool user_creatable_print_help(const char *type, QemuOpts *opts);
|
||||
|
||||
/**
|
||||
* user_creatable_print_help_from_qdict:
|
||||
* @args: options to create
|
||||
*
|
||||
* Prints help considering the other options given in @args (if "qom-type" is
|
||||
* given and valid, print properties for the type, otherwise print valid types)
|
||||
*
|
||||
* In contrast to user_creatable_print_help(), this function can't return that
|
||||
* no help was requested. It should only be called if we know that help is
|
||||
* requested and it will always print some help.
|
||||
*/
|
||||
void user_creatable_print_help_from_qdict(QDict *args);
|
||||
|
||||
/**
|
||||
* user_creatable_del:
|
||||
* @id: the unique ID for the object
|
||||
|
@ -632,23 +632,9 @@ void monitor_cleanup(void)
|
||||
iothread_stop(mon_iothread);
|
||||
}
|
||||
|
||||
/* Flush output buffers and destroy monitors */
|
||||
qemu_mutex_lock(&monitor_lock);
|
||||
monitor_destroyed = true;
|
||||
while (!QTAILQ_EMPTY(&mon_list)) {
|
||||
Monitor *mon = QTAILQ_FIRST(&mon_list);
|
||||
QTAILQ_REMOVE(&mon_list, mon, entry);
|
||||
/* Permit QAPI event emission from character frontend release */
|
||||
qemu_mutex_unlock(&monitor_lock);
|
||||
monitor_flush(mon);
|
||||
monitor_data_destroy(mon);
|
||||
qemu_mutex_lock(&monitor_lock);
|
||||
g_free(mon);
|
||||
}
|
||||
qemu_mutex_unlock(&monitor_lock);
|
||||
|
||||
/*
|
||||
* The dispatcher needs to stop before destroying the I/O thread.
|
||||
* The dispatcher needs to stop before destroying the monitor and
|
||||
* the I/O thread.
|
||||
*
|
||||
* We need to poll both qemu_aio_context and iohandler_ctx to make
|
||||
* sure that the dispatcher coroutine keeps making progress and
|
||||
@ -665,6 +651,21 @@ void monitor_cleanup(void)
|
||||
(aio_poll(iohandler_get_aio_context(), false),
|
||||
qatomic_mb_read(&qmp_dispatcher_co_busy)));
|
||||
|
||||
/* Flush output buffers and destroy monitors */
|
||||
qemu_mutex_lock(&monitor_lock);
|
||||
monitor_destroyed = true;
|
||||
while (!QTAILQ_EMPTY(&mon_list)) {
|
||||
Monitor *mon = QTAILQ_FIRST(&mon_list);
|
||||
QTAILQ_REMOVE(&mon_list, mon, entry);
|
||||
/* Permit QAPI event emission from character frontend release */
|
||||
qemu_mutex_unlock(&monitor_lock);
|
||||
monitor_flush(mon);
|
||||
monitor_data_destroy(mon);
|
||||
qemu_mutex_lock(&monitor_lock);
|
||||
g_free(mon);
|
||||
}
|
||||
qemu_mutex_unlock(&monitor_lock);
|
||||
|
||||
if (mon_iothread) {
|
||||
iothread_destroy(mon_iothread);
|
||||
mon_iothread = NULL;
|
||||
|
@ -757,7 +757,7 @@ Visitor *qobject_input_visitor_new_str(const char *str,
|
||||
assert(args);
|
||||
v = qobject_input_visitor_new(QOBJECT(args));
|
||||
} else {
|
||||
args = keyval_parse(str, implied_key, errp);
|
||||
args = keyval_parse(str, implied_key, NULL, errp);
|
||||
if (!args) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -214,11 +214,8 @@ char *object_property_help(const char *name, const char *type,
|
||||
return g_string_free(str, false);
|
||||
}
|
||||
|
||||
bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
||||
static void user_creatable_print_types(void)
|
||||
{
|
||||
ObjectClass *klass;
|
||||
|
||||
if (is_help_option(type)) {
|
||||
GSList *l, *list;
|
||||
|
||||
printf("List of user creatable objects:\n");
|
||||
@ -228,16 +225,22 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
||||
printf(" %s\n", object_class_get_name(oc));
|
||||
}
|
||||
g_slist_free(list);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
klass = object_class_by_name(type);
|
||||
if (klass && qemu_opt_has_help_opt(opts)) {
|
||||
static bool user_creatable_print_type_properites(const char *type)
|
||||
{
|
||||
ObjectClass *klass;
|
||||
ObjectPropertyIterator iter;
|
||||
ObjectProperty *prop;
|
||||
GPtrArray *array = g_ptr_array_new();
|
||||
GPtrArray *array;
|
||||
int i;
|
||||
|
||||
klass = object_class_by_name(type);
|
||||
if (!klass) {
|
||||
return false;
|
||||
}
|
||||
|
||||
array = g_ptr_array_new();
|
||||
object_class_property_iter_init(&iter, klass);
|
||||
while ((prop = object_property_iter_next(&iter))) {
|
||||
if (!prop->set) {
|
||||
@ -260,11 +263,31 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
||||
g_ptr_array_set_free_func(array, g_free);
|
||||
g_ptr_array_free(array, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
||||
{
|
||||
if (is_help_option(type)) {
|
||||
user_creatable_print_types();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (qemu_opt_has_help_opt(opts)) {
|
||||
return user_creatable_print_type_properites(type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void user_creatable_print_help_from_qdict(QDict *args)
|
||||
{
|
||||
const char *type = qdict_get_try_str(args, "qom-type");
|
||||
|
||||
if (!type || !user_creatable_print_type_properites(type)) {
|
||||
user_creatable_print_types();
|
||||
}
|
||||
}
|
||||
|
||||
bool user_creatable_del(const char *id, Error **errp)
|
||||
{
|
||||
Object *container;
|
||||
|
@ -264,21 +264,14 @@ static void process_options(int argc, char *argv[])
|
||||
}
|
||||
case OPTION_OBJECT:
|
||||
{
|
||||
QemuOpts *opts;
|
||||
const char *type;
|
||||
QDict *args;
|
||||
bool help;
|
||||
|
||||
/* FIXME The keyval parser rejects 'help' arguments, so we must
|
||||
* unconditionall try QemuOpts first. */
|
||||
opts = qemu_opts_parse(&qemu_object_opts,
|
||||
optarg, true, &error_fatal);
|
||||
type = qemu_opt_get(opts, "qom-type");
|
||||
if (type && user_creatable_print_help(type, opts)) {
|
||||
args = keyval_parse(optarg, "qom-type", &help, &error_fatal);
|
||||
if (help) {
|
||||
user_creatable_print_help_from_qdict(args);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
|
||||
args = keyval_parse(optarg, "qom-type", &error_fatal);
|
||||
user_creatable_add_dict(args, true, &error_fatal);
|
||||
qobject_unref(args);
|
||||
break;
|
||||
|
@ -27,27 +27,28 @@ static void test_keyval_parse(void)
|
||||
QDict *qdict, *sub_qdict;
|
||||
char long_key[129];
|
||||
char *params;
|
||||
bool help;
|
||||
|
||||
/* Nothing */
|
||||
qdict = keyval_parse("", NULL, &error_abort);
|
||||
qdict = keyval_parse("", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 0);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Empty key (qemu_opts_parse() accepts this) */
|
||||
qdict = keyval_parse("=val", NULL, &err);
|
||||
qdict = keyval_parse("=val", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Empty key fragment */
|
||||
qdict = keyval_parse(".", NULL, &err);
|
||||
qdict = keyval_parse(".", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
qdict = keyval_parse("key.", NULL, &err);
|
||||
qdict = keyval_parse("key.", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Invalid non-empty key (qemu_opts_parse() doesn't care) */
|
||||
qdict = keyval_parse("7up=val", NULL, &err);
|
||||
qdict = keyval_parse("7up=val", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
@ -56,25 +57,25 @@ static void test_keyval_parse(void)
|
||||
long_key[127] = 'z';
|
||||
long_key[128] = 0;
|
||||
params = g_strdup_printf("k.%s=v", long_key);
|
||||
qdict = keyval_parse(params + 2, NULL, &err);
|
||||
qdict = keyval_parse(params + 2, NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Overlong key fragment */
|
||||
qdict = keyval_parse(params, NULL, &err);
|
||||
qdict = keyval_parse(params, NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
g_free(params);
|
||||
|
||||
/* Long key (qemu_opts_parse() accepts and truncates silently) */
|
||||
params = g_strdup_printf("k.%s=v", long_key + 1);
|
||||
qdict = keyval_parse(params + 2, NULL, &error_abort);
|
||||
qdict = keyval_parse(params + 2, NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Long key fragment */
|
||||
qdict = keyval_parse(params, NULL, &error_abort);
|
||||
qdict = keyval_parse(params, NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
sub_qdict = qdict_get_qdict(qdict, "k");
|
||||
g_assert(sub_qdict);
|
||||
@ -84,25 +85,25 @@ static void test_keyval_parse(void)
|
||||
g_free(params);
|
||||
|
||||
/* Crap after valid key */
|
||||
qdict = keyval_parse("key[0]=val", NULL, &err);
|
||||
qdict = keyval_parse("key[0]=val", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Multiple keys, last one wins */
|
||||
qdict = keyval_parse("a=1,b=2,,x,a=3", NULL, &error_abort);
|
||||
qdict = keyval_parse("a=1,b=2,,x,a=3", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 2);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3");
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Even when it doesn't in qemu_opts_parse() */
|
||||
qdict = keyval_parse("id=foo,id=bar", NULL, &error_abort);
|
||||
qdict = keyval_parse("id=foo,id=bar", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Dotted keys */
|
||||
qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 2);
|
||||
sub_qdict = qdict_get_qdict(qdict, "a");
|
||||
g_assert(sub_qdict);
|
||||
@ -115,48 +116,48 @@ static void test_keyval_parse(void)
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Inconsistent dotted keys */
|
||||
qdict = keyval_parse("a.b=1,a=2", NULL, &err);
|
||||
qdict = keyval_parse("a.b=1,a=2", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
qdict = keyval_parse("a.b=1,a.b.c=2", NULL, &err);
|
||||
qdict = keyval_parse("a.b=1,a.b.c=2", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Trailing comma is ignored */
|
||||
qdict = keyval_parse("x=y,", NULL, &error_abort);
|
||||
qdict = keyval_parse("x=y,", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Except when it isn't */
|
||||
qdict = keyval_parse(",", NULL, &err);
|
||||
qdict = keyval_parse(",", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Value containing ,id= not misinterpreted as qemu_opts_parse() does */
|
||||
qdict = keyval_parse("x=,,id=bar", NULL, &error_abort);
|
||||
qdict = keyval_parse("x=,,id=bar", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */
|
||||
qdict = keyval_parse("id=666", NULL, &error_abort);
|
||||
qdict = keyval_parse("id=666", NULL, NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666");
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Implied value not supported (unlike qemu_opts_parse()) */
|
||||
qdict = keyval_parse("an,noaus,noaus=", NULL, &err);
|
||||
qdict = keyval_parse("an,noaus,noaus=", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Implied value, key "no" (qemu_opts_parse(): negated empty key) */
|
||||
qdict = keyval_parse("no", NULL, &err);
|
||||
qdict = keyval_parse("no", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Implied key */
|
||||
qdict = keyval_parse("an,aus=off,noaus=", "implied", &error_abort);
|
||||
qdict = keyval_parse("an,aus=off,noaus=", "implied", NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 3);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an");
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off");
|
||||
@ -164,7 +165,7 @@ static void test_keyval_parse(void)
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Implied dotted key */
|
||||
qdict = keyval_parse("val", "eins.zwei", &error_abort);
|
||||
qdict = keyval_parse("val", "eins.zwei", NULL, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
sub_qdict = qdict_get_qdict(qdict, "eins");
|
||||
g_assert(sub_qdict);
|
||||
@ -173,19 +174,81 @@ static void test_keyval_parse(void)
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Implied key with empty value (qemu_opts_parse() accepts this) */
|
||||
qdict = keyval_parse(",", "implied", &err);
|
||||
qdict = keyval_parse(",", "implied", NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Likewise (qemu_opts_parse(): implied key with comma value) */
|
||||
qdict = keyval_parse(",,,a=1", "implied", &err);
|
||||
qdict = keyval_parse(",,,a=1", "implied", NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Implied key's value can't have comma (qemu_opts_parse(): it can) */
|
||||
qdict = keyval_parse("val,,ue", "implied", NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Empty key is not an implied key */
|
||||
qdict = keyval_parse("=val", "implied", &err);
|
||||
qdict = keyval_parse("=val", "implied", NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* "help" by itself, without implied key */
|
||||
qdict = keyval_parse("help", NULL, &help, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 0);
|
||||
g_assert(help);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* "help" by itself, with implied key */
|
||||
qdict = keyval_parse("help", "implied", &help, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 0);
|
||||
g_assert(help);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* "help" when no help is available, without implied key */
|
||||
qdict = keyval_parse("help", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* "help" when no help is available, with implied key */
|
||||
qdict = keyval_parse("help", "implied", NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Key "help" */
|
||||
qdict = keyval_parse("help=on", NULL, &help, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "help"), ==, "on");
|
||||
g_assert(!help);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* "help" followed by crap, without implied key */
|
||||
qdict = keyval_parse("help.abc", NULL, &help, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* "help" followed by crap, with implied key */
|
||||
qdict = keyval_parse("help.abc", "implied", &help, &err);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 1);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help.abc");
|
||||
g_assert(!help);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* "help" with other stuff, without implied key */
|
||||
qdict = keyval_parse("number=42,help,foo=bar", NULL, &help, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 2);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "number"), ==, "42");
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar");
|
||||
g_assert(help);
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* "help" with other stuff, with implied key */
|
||||
qdict = keyval_parse("val,help,foo=bar", "implied", &help, &error_abort);
|
||||
g_assert_cmpuint(qdict_size(qdict), ==, 2);
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "val");
|
||||
g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar");
|
||||
g_assert(help);
|
||||
qobject_unref(qdict);
|
||||
}
|
||||
|
||||
static void check_list012(QList *qlist)
|
||||
@ -210,26 +273,26 @@ static void test_keyval_parse_list(void)
|
||||
QDict *qdict, *sub_qdict;
|
||||
|
||||
/* Root can't be a list */
|
||||
qdict = keyval_parse("0=1", NULL, &err);
|
||||
qdict = keyval_parse("0=1", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* List elements need not be in order */
|
||||
qdict = keyval_parse("list.0=null,list.2=zwei,list.1=eins",
|
||||
NULL, &error_abort);
|
||||
qdict = keyval_parse("list.0=null,list.2=zwei,list.1=eins", NULL, NULL,
|
||||
&error_abort);
|
||||
g_assert_cmpint(qdict_size(qdict), ==, 1);
|
||||
check_list012(qdict_get_qlist(qdict, "list"));
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Multiple indexes, last one wins */
|
||||
qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei",
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
g_assert_cmpint(qdict_size(qdict), ==, 1);
|
||||
check_list012(qdict_get_qlist(qdict, "list"));
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* List at deeper nesting */
|
||||
qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei",
|
||||
qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", NULL,
|
||||
NULL, &error_abort);
|
||||
g_assert_cmpint(qdict_size(qdict), ==, 1);
|
||||
sub_qdict = qdict_get_qdict(qdict, "a");
|
||||
@ -238,18 +301,19 @@ static void test_keyval_parse_list(void)
|
||||
qobject_unref(qdict);
|
||||
|
||||
/* Inconsistent dotted keys: both list and dictionary */
|
||||
qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, &err);
|
||||
qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
qdict = keyval_parse("a.0.c=1,a.b.c=2", NULL, &err);
|
||||
qdict = keyval_parse("a.0.c=1,a.b.c=2", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
|
||||
/* Missing list indexes */
|
||||
qdict = keyval_parse("list.1=lonely", NULL, &err);
|
||||
qdict = keyval_parse("list.1=lonely", NULL, NULL, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
qdict = keyval_parse("list.0=null,list.2=eins,list.02=zwei", NULL, &err);
|
||||
qdict = keyval_parse("list.0=null,list.2=eins,list.02=zwei", NULL, NULL,
|
||||
&err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!qdict);
|
||||
}
|
||||
@ -261,7 +325,7 @@ static void test_keyval_visit_bool(void)
|
||||
QDict *qdict;
|
||||
bool b;
|
||||
|
||||
qdict = keyval_parse("bool1=on,bool2=off", NULL, &error_abort);
|
||||
qdict = keyval_parse("bool1=on,bool2=off", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -273,7 +337,7 @@ static void test_keyval_visit_bool(void)
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
qdict = keyval_parse("bool1=offer", NULL, &error_abort);
|
||||
qdict = keyval_parse("bool1=offer", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -291,7 +355,7 @@ static void test_keyval_visit_number(void)
|
||||
uint64_t u;
|
||||
|
||||
/* Lower limit zero */
|
||||
qdict = keyval_parse("number1=0", NULL, &error_abort);
|
||||
qdict = keyval_parse("number1=0", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -302,7 +366,7 @@ static void test_keyval_visit_number(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Upper limit 2^64-1 */
|
||||
qdict = keyval_parse("number1=18446744073709551615,number2=-1",
|
||||
qdict = keyval_parse("number1=18446744073709551615,number2=-1", NULL,
|
||||
NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
@ -316,8 +380,8 @@ static void test_keyval_visit_number(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Above upper limit */
|
||||
qdict = keyval_parse("number1=18446744073709551616",
|
||||
NULL, &error_abort);
|
||||
qdict = keyval_parse("number1=18446744073709551616", NULL, NULL,
|
||||
&error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -327,8 +391,8 @@ static void test_keyval_visit_number(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Below lower limit */
|
||||
qdict = keyval_parse("number1=-18446744073709551616",
|
||||
NULL, &error_abort);
|
||||
qdict = keyval_parse("number1=-18446744073709551616", NULL, NULL,
|
||||
&error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -338,8 +402,7 @@ static void test_keyval_visit_number(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Hex and octal */
|
||||
qdict = keyval_parse("number1=0x2a,number2=052",
|
||||
NULL, &error_abort);
|
||||
qdict = keyval_parse("number1=0x2a,number2=052", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -352,8 +415,7 @@ static void test_keyval_visit_number(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Trailing crap */
|
||||
qdict = keyval_parse("number1=3.14,number2=08",
|
||||
NULL, &error_abort);
|
||||
qdict = keyval_parse("number1=3.14,number2=08", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -373,7 +435,7 @@ static void test_keyval_visit_size(void)
|
||||
uint64_t sz;
|
||||
|
||||
/* Lower limit zero */
|
||||
qdict = keyval_parse("sz1=0", NULL, &error_abort);
|
||||
qdict = keyval_parse("sz1=0", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -389,7 +451,7 @@ static void test_keyval_visit_size(void)
|
||||
qdict = keyval_parse("sz1=9007199254740991,"
|
||||
"sz2=9007199254740992,"
|
||||
"sz3=9007199254740993",
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -406,7 +468,7 @@ static void test_keyval_visit_size(void)
|
||||
/* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
|
||||
qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */
|
||||
"sz2=9223372036854775295", /* 7ffffffffffffdff */
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -421,7 +483,7 @@ static void test_keyval_visit_size(void)
|
||||
/* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
|
||||
qdict = keyval_parse("sz1=18446744073709549568," /* fffffffffffff800 */
|
||||
"sz2=18446744073709550591", /* fffffffffffffbff */
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -436,7 +498,7 @@ static void test_keyval_visit_size(void)
|
||||
/* Beyond limits */
|
||||
qdict = keyval_parse("sz1=-1,"
|
||||
"sz2=18446744073709550592", /* fffffffffffffc00 */
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -449,7 +511,7 @@ static void test_keyval_visit_size(void)
|
||||
|
||||
/* Suffixes */
|
||||
qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T",
|
||||
NULL, &error_abort);
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -468,7 +530,7 @@ static void test_keyval_visit_size(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Beyond limit with suffix */
|
||||
qdict = keyval_parse("sz1=16777216T", NULL, &error_abort);
|
||||
qdict = keyval_parse("sz1=16777216T", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -478,7 +540,7 @@ static void test_keyval_visit_size(void)
|
||||
visit_free(v);
|
||||
|
||||
/* Trailing crap */
|
||||
qdict = keyval_parse("sz1=0Z,sz2=16Gi", NULL, &error_abort);
|
||||
qdict = keyval_parse("sz1=0Z,sz2=16Gi", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -497,7 +559,7 @@ static void test_keyval_visit_dict(void)
|
||||
QDict *qdict;
|
||||
int64_t i;
|
||||
|
||||
qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -515,7 +577,7 @@ static void test_keyval_visit_dict(void)
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
qdict = keyval_parse("a.b=", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.b=", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -537,7 +599,7 @@ static void test_keyval_visit_list(void)
|
||||
QDict *qdict;
|
||||
char *s;
|
||||
|
||||
qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, NULL, &error_abort);
|
||||
/* TODO empty list */
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
@ -561,7 +623,7 @@ static void test_keyval_visit_list(void)
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
qdict = keyval_parse("a.0=,b.0.0=head", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.0=,b.0.0=head", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -590,7 +652,7 @@ static void test_keyval_visit_optional(void)
|
||||
bool present;
|
||||
int64_t i;
|
||||
|
||||
qdict = keyval_parse("a.b=1", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.b=1", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -626,7 +688,7 @@ static void test_keyval_visit_alternate(void)
|
||||
* the string variant if there is one, else an error.
|
||||
* TODO make it work for unambiguous cases like AltEnumBool below
|
||||
*/
|
||||
qdict = keyval_parse("a=1,b=2,c=on", NULL, &error_abort);
|
||||
qdict = keyval_parse("a=1,b=2,c=on", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
@ -650,7 +712,7 @@ static void test_keyval_visit_any(void)
|
||||
QList *qlist;
|
||||
QString *qstr;
|
||||
|
||||
qdict = keyval_parse("a.0=null,a.1=1", NULL, &error_abort);
|
||||
qdict = keyval_parse("a.0=null,a.1=1", NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
|
@ -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 = { / [^,]* / | ',,' }
|
||||
* 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
|
||||
@ -71,12 +75,16 @@
|
||||
* Awkward. Note that we carefully restrict alternate types to avoid
|
||||
* similar ambiguity.
|
||||
*
|
||||
* Additional syntax for use with an implied key:
|
||||
* Alternative syntax for use with an implied key:
|
||||
*
|
||||
* key-vals-ik = val-no-key [ ',' key-vals ]
|
||||
* val-no-key = / [^=,]* /
|
||||
* key-vals = [ key-val-1st { ',' key-val } [ ',' ] ]
|
||||
* key-val-1st = val-no-key | key-val
|
||||
* val-no-key = / [^=,]+ / - help
|
||||
*
|
||||
* where no-key is syntactic sugar for implied-key=val-no-key.
|
||||
* where val-no-key is syntactic sugar for implied-key=val-no-key.
|
||||
*
|
||||
* Note that you can't use the sugared form when the value contains
|
||||
* '=' or ','.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@ -85,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"
|
||||
|
||||
/*
|
||||
@ -158,18 +167,23 @@ 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, *s, *end;
|
||||
const char *key, *key_end, *val_end, *s, *end;
|
||||
size_t len;
|
||||
char key_in_cur[128];
|
||||
QDict *cur;
|
||||
@ -178,12 +192,24 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
|
||||
QString *val;
|
||||
|
||||
key = params;
|
||||
val_end = NULL;
|
||||
len = strcspn(params, "=,");
|
||||
if (implied_key && len && key[len] != '=') {
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -237,7 +263,11 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
|
||||
|
||||
if (key == implied_key) {
|
||||
assert(!*s);
|
||||
s = params;
|
||||
val = qstring_from_substr(params, 0, val_end - params);
|
||||
s = val_end;
|
||||
if (*s == ',') {
|
||||
s++;
|
||||
}
|
||||
} else {
|
||||
if (*s != '=') {
|
||||
error_setg(errp, "Expected '=' after parameter '%.*s'",
|
||||
@ -245,7 +275,6 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
|
||||
return NULL;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
val = qstring_new();
|
||||
for (;;) {
|
||||
@ -259,6 +288,7 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
|
||||
}
|
||||
qstring_append_chr(val, *s++);
|
||||
}
|
||||
}
|
||||
|
||||
if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) {
|
||||
return NULL;
|
||||
@ -388,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;
|
||||
@ -410,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);
|
||||
|
Loading…
Reference in New Issue
Block a user