* [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser @ 2020-09-30 12:45 Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf ` (3 more replies) 0 siblings, 4 replies; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 12:45 UTC (permalink / raw) To: qemu-block; +Cc: kwolf, qemu-devel, armbru, mreitz This replaces the QemuOpts-based help code for --object in the storage daemon with code based on the keyval parser. v2: - Fixed double comma by reusing the existing key and value parsers [Eric] - More tests to cover the additional cases Kevin Wolf (4): keyval: Parse help options qom: Factor out helpers from user_creatable_print_help() qom: Add user_creatable_print_help_from_qdict() qemu-storage-daemon: Remove QemuOpts from --object parser include/qemu/option.h | 2 +- include/qom/object_interfaces.h | 9 ++ qapi/qobject-input-visitor.c | 2 +- qom/object_interfaces.c | 99 ++++++++----- storage-daemon/qemu-storage-daemon.c | 15 +- tests/test-keyval.c | 205 +++++++++++++++++++-------- util/keyval.c | 38 ++++- 7 files changed, 252 insertions(+), 118 deletions(-) -- 2.25.4 ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/4] keyval: Parse help options 2020-09-30 12:45 [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf @ 2020-09-30 12:45 ` Kevin Wolf 2020-09-30 13:35 ` Eric Blake 2020-10-01 15:46 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() Kevin Wolf ` (2 subsequent siblings) 3 siblings, 2 replies; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 12:45 UTC (permalink / raw) To: qemu-block; +Cc: kwolf, qemu-devel, armbru, mreitz This adds a new parameter 'help' to keyval_parse() that enables parsing of help options. If NULL is passed, the function behaves the same as before. But if a bool pointer is given, it contains the information whether an option "help" without value was given (which would otherwise either result in an error or be interpreted as the value for an implied key). Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- include/qemu/option.h | 2 +- qapi/qobject-input-visitor.c | 2 +- storage-daemon/qemu-storage-daemon.c | 2 +- tests/test-keyval.c | 205 +++++++++++++++++++-------- util/keyval.c | 38 ++++- 5 files changed, 179 insertions(+), 70 deletions(-) diff --git a/include/qemu/option.h b/include/qemu/option.h index 05e8a15c73..ac69352e0e 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -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 diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index f918a05e5f..7b184b50a7 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -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; } diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index e6157ff518..bb9cb740f0 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -278,7 +278,7 @@ static void process_options(int argc, char *argv[]) } qemu_opts_del(opts); - args = keyval_parse(optarg, "qom-type", &error_fatal); + args = keyval_parse(optarg, "qom-type", NULL, &error_fatal); user_creatable_add_dict(args, true, &error_fatal); qobject_unref(args); break; diff --git a/tests/test-keyval.c b/tests/test-keyval.c index e331a84149..83b65f04f7 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -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,100 @@ 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); /* 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" is only a help option if it has no value */ + 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_false(help); + qobject_unref(qdict); + + /* Double comma after "help" in an implied key is not a help option */ + qdict = keyval_parse("help,,abc", "implied", &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_false(help); + qobject_unref(qdict); + + /* Without implied key and without value, it's an error */ + qdict = keyval_parse("help,,abc", NULL, &help, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" as the only option */ + qdict = keyval_parse("help", NULL, &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); + g_assert_true(help); + qobject_unref(qdict); + + /* "help" as the first part of the key */ + qdict = keyval_parse("help.abc", NULL, &help, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" as the last part of the key */ + qdict = keyval_parse("abc.help", NULL, &help, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" is not a value for the implied key if &help is given */ + qdict = keyval_parse("help", "implied", &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); + g_assert_true(help); + qobject_unref(qdict); + + /* "help" is a value for the implied key when passing NULL for help */ + qdict = keyval_parse("help", "implied", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help"); + qobject_unref(qdict); + + /* "help.abc" is a value for the 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_false(help); + qobject_unref(qdict); + + /* "abc.help" is a value for the implied key */ + qdict = keyval_parse("abc.help", "implied", &help, &err); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "abc.help"); + g_assert_false(help); + qobject_unref(qdict); + + /* "help" as the last part of the key */ + qdict = keyval_parse("abc.help", NULL, &help, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Other keys before and after help are still parsed normally */ + 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_true(help); + qobject_unref(qdict); + + /* ...even with an 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_true(help); + qobject_unref(qdict); } static void check_list012(QList *qlist) @@ -210,26 +292,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 +320,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 +344,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 +356,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 +374,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 +385,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 +399,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 +410,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 +421,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 +434,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 +454,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 +470,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 +487,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 +502,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 +517,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 +530,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 +549,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 +559,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 +578,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 +596,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 +618,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 +642,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 +671,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 +707,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 +731,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); diff --git a/util/keyval.c b/util/keyval.c index 13def4af54..c8ce3bb6c9 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -166,7 +166,7 @@ static QObject *keyval_parse_put(QDict *cur, * 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; @@ -238,13 +238,20 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, if (key == implied_key) { assert(!*s); s = params; + } else if (*s == '=') { + s++; } else { - if (*s != '=') { + if (help && !strncmp(key, "help", s - key)) { + *help = true; + if (*s) { + s++; + } + return s; + } else { error_setg(errp, "Expected '=' after parameter '%.*s'", (int)(s - key), key); return NULL; } - s++; } val = qstring_new(); @@ -260,6 +267,15 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, qstring_append_chr(val, *s++); } + if (help && key == implied_key) { + const char *str_val = qstring_get_str(val); + if (!strcmp(str_val, "help")) { + *help = true; + qobject_unref(val); + return s; + } + } + if (!keyval_parse_put(cur, key_in_cur, val, key, key_end, errp)) { return NULL; } @@ -388,21 +404,33 @@ 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 '='. + * + * If @help is given, an option "help" without a value isn't added to + * the resulting dictionary, but instead sets @help to true. If no + * help option is found, @help is false on return. All other options + * are parsed and returned normally so that context specific help can + * be printed. + * * 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 *help, Error **errp) { QDict *qdict = qdict_new(); QObject *listified; const char *s; + if (help) { + *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; -- 2.25.4 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf @ 2020-09-30 13:35 ` Eric Blake 2020-09-30 15:10 ` Kevin Wolf 2020-10-01 15:46 ` Markus Armbruster 1 sibling, 1 reply; 18+ messages in thread From: Eric Blake @ 2020-09-30 13:35 UTC (permalink / raw) To: Kevin Wolf, qemu-block; +Cc: qemu-devel, armbru, mreitz [-- Attachment #1.1: Type: text/plain, Size: 5957 bytes --] On 9/30/20 7:45 AM, Kevin Wolf wrote: > This adds a new parameter 'help' to keyval_parse() that enables parsing > of help options. If NULL is passed, the function behaves the same as > before. But if a bool pointer is given, it contains the information > whether an option "help" without value was given (which would otherwise > either result in an error or be interpreted as the value for an implied > key). > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > + > + /* "help" is only a help option if it has no value */ > + 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_false(help); > + qobject_unref(qdict); > + > + /* Double comma after "help" in an implied key is not a help option */ > + qdict = keyval_parse("help,,abc", "implied", &help, &error_abort); > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > + g_assert_false(help); > + qobject_unref(qdict); Worth checking qdict_get_try_str(qdict, "implied") for "help,abc"? > + > + /* Without implied key and without value, it's an error */ > + qdict = keyval_parse("help,,abc", NULL, &help, &err); > + error_free_or_abort(&err); > + g_assert(!qdict); > + > + /* "help" as the only option */ > + qdict = keyval_parse("help", NULL, &help, &error_abort); > + g_assert_cmpuint(qdict_size(qdict), ==, 0); > + g_assert_true(help); > + qobject_unref(qdict); > + > + /* "help" as the first part of the key */ > + qdict = keyval_parse("help.abc", NULL, &help, &err); > + error_free_or_abort(&err); > + g_assert(!qdict); Worth checking qdict_get_try_str(qdict, "help.abc") for "on"? (at least, that's my guess as what happened) > + > + /* "help" as the last part of the key */ > + qdict = keyval_parse("abc.help", NULL, &help, &err); > + error_free_or_abort(&err); > + g_assert(!qdict); [1] > + > + /* "help" is not a value for the implied key if &help is given */ > + qdict = keyval_parse("help", "implied", &help, &error_abort); > + g_assert_cmpuint(qdict_size(qdict), ==, 0); > + g_assert_true(help); > + qobject_unref(qdict); Worth checking that the qdict does not contain "implied"? Perhaps by checking qdict_size() == 0? > + > + /* "help" is a value for the implied key when passing NULL for help */ > + qdict = keyval_parse("help", "implied", NULL, &error_abort); > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help"); > + qobject_unref(qdict); > + > + /* "help.abc" is a value for the 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_false(help); > + qobject_unref(qdict); > + > + /* "abc.help" is a value for the implied key */ > + qdict = keyval_parse("abc.help", "implied", &help, &err); > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "abc.help"); > + g_assert_false(help); > + qobject_unref(qdict); > + > + /* "help" as the last part of the key */ > + qdict = keyval_parse("abc.help", NULL, &help, &err); > + error_free_or_abort(&err); > + g_assert(!qdict); duplicates [1] > + > + /* Other keys before and after help are still parsed normally */ > + 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_true(help); > + qobject_unref(qdict); > + > + /* ...even with an 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_true(help); > + qobject_unref(qdict); > } > Overall a nice set of additions. You could tweak it further, but I'm no longer seeing a hole like last time. > +++ b/util/keyval.c > @@ -166,7 +166,7 @@ static QObject *keyval_parse_put(QDict *cur, > * 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; > @@ -238,13 +238,20 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, > if (key == implied_key) { > assert(!*s); > s = params; > + } else if (*s == '=') { > + s++; > } else { > - if (*s != '=') { > + if (help && !strncmp(key, "help", s - key)) { Should this use is_help_option() to also accept "?", or are we okay demanding exactly "help"? > + *help = true; > + if (*s) { > + s++; > + } > + return s; > + } else { > error_setg(errp, "Expected '=' after parameter '%.*s'", > (int)(s - key), key); > return NULL; > } > - s++; > } Assuming you can touch up the testsuite before the pull request, and assuming we don't care about "?", Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-09-30 13:35 ` Eric Blake @ 2020-09-30 15:10 ` Kevin Wolf 2020-10-01 10:34 ` Markus Armbruster 0 siblings, 1 reply; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 15:10 UTC (permalink / raw) To: Eric Blake; +Cc: qemu-devel, armbru, qemu-block, mreitz [-- Attachment #1: Type: text/plain, Size: 6852 bytes --] Am 30.09.2020 um 15:35 hat Eric Blake geschrieben: > On 9/30/20 7:45 AM, Kevin Wolf wrote: > > This adds a new parameter 'help' to keyval_parse() that enables parsing > > of help options. If NULL is passed, the function behaves the same as > > before. But if a bool pointer is given, it contains the information > > whether an option "help" without value was given (which would otherwise > > either result in an error or be interpreted as the value for an implied > > key). > > > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > > --- > > > + > > + /* "help" is only a help option if it has no value */ > > + 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_false(help); > > + qobject_unref(qdict); > > + > > + /* Double comma after "help" in an implied key is not a help option */ > > + qdict = keyval_parse("help,,abc", "implied", &help, &error_abort); > > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > > + g_assert_false(help); > > + qobject_unref(qdict); > > Worth checking qdict_get_try_str(qdict, "implied") for "help,abc"? Yes, this makes sense. > > + > > + /* Without implied key and without value, it's an error */ > > + qdict = keyval_parse("help,,abc", NULL, &help, &err); > > + error_free_or_abort(&err); > > + g_assert(!qdict); > > + > > + /* "help" as the only option */ > > + qdict = keyval_parse("help", NULL, &help, &error_abort); > > + g_assert_cmpuint(qdict_size(qdict), ==, 0); > > + g_assert_true(help); > > + qobject_unref(qdict); > > + > > + /* "help" as the first part of the key */ > > + qdict = keyval_parse("help.abc", NULL, &help, &err); > > + error_free_or_abort(&err); > > + g_assert(!qdict); > > Worth checking qdict_get_try_str(qdict, "help.abc") for "on"? (at least, > that's my guess as what happened) The keyval parser doesn't support boolean options like this (I think this is intentional because it would be ambiguous with implied keys). This is an error case, and I don't think the QDict has defined content then. But if anything, we would want to check that it's empty. > > + > > + /* "help" as the last part of the key */ > > + qdict = keyval_parse("abc.help", NULL, &help, &err); > > + error_free_or_abort(&err); > > + g_assert(!qdict); > > [1] > > > + > > + /* "help" is not a value for the implied key if &help is given */ > > + qdict = keyval_parse("help", "implied", &help, &error_abort); > > + g_assert_cmpuint(qdict_size(qdict), ==, 0); > > + g_assert_true(help); > > + qobject_unref(qdict); > > Worth checking that the qdict does not contain "implied"? Perhaps by > checking qdict_size() == 0? Already there, the first line after keyval_parse(). :-) > > + > > + /* "help" is a value for the implied key when passing NULL for help */ > > + qdict = keyval_parse("help", "implied", NULL, &error_abort); > > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > > + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help"); > > + qobject_unref(qdict); > > + > > + /* "help.abc" is a value for the 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_false(help); > > + qobject_unref(qdict); > > + > > + /* "abc.help" is a value for the implied key */ > > + qdict = keyval_parse("abc.help", "implied", &help, &err); > > + g_assert_cmpuint(qdict_size(qdict), ==, 1); > > + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "abc.help"); > > + g_assert_false(help); > > + qobject_unref(qdict); > > + > > + /* "help" as the last part of the key */ > > + qdict = keyval_parse("abc.help", NULL, &help, &err); > > + error_free_or_abort(&err); > > + g_assert(!qdict); > > duplicates [1] So we can be extra sure! Somehow I suspect I wanted to test another case here, but I'm not sure which one. Maybe the same with an implied key? Or I can just remove it. > > + > > + /* Other keys before and after help are still parsed normally */ > > + 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_true(help); > > + qobject_unref(qdict); > > + > > + /* ...even with an 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_true(help); > > + qobject_unref(qdict); > > } > > > > Overall a nice set of additions. You could tweak it further, but I'm no > longer seeing a hole like last time. > > > +++ b/util/keyval.c > > @@ -166,7 +166,7 @@ static QObject *keyval_parse_put(QDict *cur, > > * 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; > > @@ -238,13 +238,20 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, > > if (key == implied_key) { > > assert(!*s); > > s = params; > > + } else if (*s == '=') { > > + s++; > > } else { > > - if (*s != '=') { > > + if (help && !strncmp(key, "help", s - key)) { > > Should this use is_help_option() to also accept "?", or are we okay > demanding exactly "help"? The comment for is_help_option() calls "?" deprecated, so I think we don't want to enable it in a new parser. > > + *help = true; > > + if (*s) { > > + s++; > > + } > > + return s; > > + } else { > > error_setg(errp, "Expected '=' after parameter '%.*s'", > > (int)(s - key), key); > > return NULL; > > } > > - s++; > > } > > Assuming you can touch up the testsuite before the pull request, and > assuming we don't care about "?", > > Reviewed-by: Eric Blake <eblake@redhat.com> Thanks! Kevin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-09-30 15:10 ` Kevin Wolf @ 2020-10-01 10:34 ` Markus Armbruster 2020-10-01 11:33 ` Kevin Wolf 0 siblings, 1 reply; 18+ messages in thread From: Markus Armbruster @ 2020-10-01 10:34 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Kevin Wolf <kwolf@redhat.com> writes: > Am 30.09.2020 um 15:35 hat Eric Blake geschrieben: >> On 9/30/20 7:45 AM, Kevin Wolf wrote: >> > This adds a new parameter 'help' to keyval_parse() that enables parsing >> > of help options. If NULL is passed, the function behaves the same as >> > before. But if a bool pointer is given, it contains the information >> > whether an option "help" without value was given (which would otherwise >> > either result in an error or be interpreted as the value for an implied >> > key). >> > >> > Signed-off-by: Kevin Wolf <kwolf@redhat.com> [...] >> > +++ b/util/keyval.c >> > @@ -166,7 +166,7 @@ static QObject *keyval_parse_put(QDict *cur, >> > * 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; >> > @@ -238,13 +238,20 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, >> > if (key == implied_key) { >> > assert(!*s); >> > s = params; >> > + } else if (*s == '=') { >> > + s++; >> > } else { >> > - if (*s != '=') { >> > + if (help && !strncmp(key, "help", s - key)) { >> >> Should this use is_help_option() to also accept "?", or are we okay >> demanding exactly "help"? > > The comment for is_help_option() calls "?" deprecated, so I think we > don't want to enable it in a new parser. Valid point. But do we really want comparisons for "help" inline everywhere we want to check for non-deprecated help requests? Would a common helper function be better? On the deprecation of "?": the comment is more than eight years old (commit c8057f951d6). We didn't have a deprecation process back then, but we did purge '?' from the documentation. Can we finally drop it? I'm going to ask that in a new thread. [...] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-10-01 10:34 ` Markus Armbruster @ 2020-10-01 11:33 ` Kevin Wolf 0 siblings, 0 replies; 18+ messages in thread From: Kevin Wolf @ 2020-10-01 11:33 UTC (permalink / raw) To: Markus Armbruster; +Cc: qemu-devel, qemu-block, mreitz Am 01.10.2020 um 12:34 hat Markus Armbruster geschrieben: > Kevin Wolf <kwolf@redhat.com> writes: > > > Am 30.09.2020 um 15:35 hat Eric Blake geschrieben: > >> On 9/30/20 7:45 AM, Kevin Wolf wrote: > >> > This adds a new parameter 'help' to keyval_parse() that enables parsing > >> > of help options. If NULL is passed, the function behaves the same as > >> > before. But if a bool pointer is given, it contains the information > >> > whether an option "help" without value was given (which would otherwise > >> > either result in an error or be interpreted as the value for an implied > >> > key). > >> > > >> > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > [...] > >> > +++ b/util/keyval.c > >> > @@ -166,7 +166,7 @@ static QObject *keyval_parse_put(QDict *cur, > >> > * 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; > >> > @@ -238,13 +238,20 @@ static const char *keyval_parse_one(QDict *qdict, const char *params, > >> > if (key == implied_key) { > >> > assert(!*s); > >> > s = params; > >> > + } else if (*s == '=') { > >> > + s++; > >> > } else { > >> > - if (*s != '=') { > >> > + if (help && !strncmp(key, "help", s - key)) { > >> > >> Should this use is_help_option() to also accept "?", or are we okay > >> demanding exactly "help"? > > > > The comment for is_help_option() calls "?" deprecated, so I think we > > don't want to enable it in a new parser. > > Valid point. > > But do we really want comparisons for "help" inline everywhere we want > to check for non-deprecated help requests? Would a common helper > function be better? How many more parsers are we going to add? I thought we intended the keyval parser to be the one canoncial place in the long term? If you prefer, I can make this a second static inline function in include/qemu/help_option.h (that would have to work both as strcmp and as strncmp), but I don't see a second user that would make it a "common" helper. > On the deprecation of "?": the comment is more than eight years old > (commit c8057f951d6). We didn't have a deprecation process back then, > but we did purge '?' from the documentation. Can we finally drop it? > I'm going to ask that in a new thread. I guess we can. Kevin ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf 2020-09-30 13:35 ` Eric Blake @ 2020-10-01 15:46 ` Markus Armbruster 2020-10-05 12:08 ` Markus Armbruster 1 sibling, 1 reply; 18+ messages in thread From: Markus Armbruster @ 2020-10-01 15:46 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Fried brain today, I have to go through this real slow. I apologize in advance for being denser and more error-prone than usual. Kevin Wolf <kwolf@redhat.com> writes: > This adds a new parameter 'help' to keyval_parse() that enables parsing > of help options. If NULL is passed, the function behaves the same as > before. But if a bool pointer is given, it contains the information > whether an option "help" without value was given (which would otherwise > either result in an error or be interpreted as the value for an implied > key). > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> You change the parser, but neglected to update the grammar in the big file comment. I made the mistake of attempting to review the parser change without fixing up the grammar first, and got lost in the weeds. In part because today is not my day, but also because doing parsers without a grammar never works out well for me. Grammar from the big file comment: * KEY=VALUE,... syntax: * * key-vals = [ key-val { ',' key-val } [ ',' ] ] * key-val = key '=' val * key = key-fragment { '.' key-fragment } * key-fragment = / [^=,.]* / * val = { / [^,]* / | ',,' } and * Additional syntax for use with an implied key: * * key-vals-ik = val-no-key [ ',' key-vals ] * val-no-key = / [^=,]* / * * where no-key is syntactic sugar for implied-key=val-no-key. According to the commit message, we want to recognize "help" in place of key-val and val-no-key, but only for callers that opt in. This is more complex than recognizing it in place of just key-vals. The commit message doesn't say why the extra complexity is wanted. Peeking ahead in the series, I can see it's for supporting -object TYPE,help in addition to -object help I see. Making help support opt-in complicates things. Is there a genuine use for not supporting help? Or is just to keep the users that don't support help yet (but should) working without change? Mind, I'm not asking you to make them work, I'm only asking whether you can think of a genuine case where help should not work. What are the existing users that don't support help? Let's see... many in tests/test-keyval.c (ignore), and qobject_input_visitor_new_str(). That one's used for qemu-system-FOO -audiodev, -display, -blockdev, and for qemu-storage-daemon --blockdev, --export, --monitor, --nbd-server. Attempting to get help for them fails like this: $ bld-x86/storage-daemon/qemu-storage-daemon --blockdev help qemu-storage-daemon: Invalid parameter 'help' $ bld-x86/storage-daemon/qemu-storage-daemon --blockdev file,help qemu-storage-daemon: Expected '=' after parameter 'help' I believe making them fail like qemu-storage-daemon: no help available would actually be an improvement. If we get rid of the case "help is not supported", the grammar update isn't too bad, something like * key-val = 'help' | key '=' val and * key-vals-ik = 'help' | val-no-key [ ',' key-vals ] Done for today, hope my brain unfries itself overnight. [...] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/4] keyval: Parse help options 2020-10-01 15:46 ` Markus Armbruster @ 2020-10-05 12:08 ` Markus Armbruster 0 siblings, 0 replies; 18+ messages in thread From: Markus Armbruster @ 2020-10-05 12:08 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Markus Armbruster <armbru@redhat.com> writes: [...] > Making help support opt-in complicates things. Is there a genuine use > for not supporting help? Or is just to keep the users that don't > support help yet (but should) working without change? Mind, I'm not > asking you to make them work, I'm only asking whether you can think of a > genuine case where help should not work. > > What are the existing users that don't support help? Let's see... many > in tests/test-keyval.c (ignore), and qobject_input_visitor_new_str(). > That one's used for qemu-system-FOO -audiodev, -display, -blockdev, and > for qemu-storage-daemon --blockdev, --export, --monitor, --nbd-server. > > Attempting to get help for them fails like this: > > $ bld-x86/storage-daemon/qemu-storage-daemon --blockdev help > qemu-storage-daemon: Invalid parameter 'help' > $ bld-x86/storage-daemon/qemu-storage-daemon --blockdev file,help > qemu-storage-daemon: Expected '=' after parameter 'help' > > I believe making them fail like > > qemu-storage-daemon: no help available > > would actually be an improvement. Potential issue: if an option's implied key may have the value "help", then option argument "help" can be either parsed as "give me help", or as "implied-key=help". None of the existing options have this issue: * audiodev: union Audiodev, implied key "driver" is enum AudiodevDriver, "help" is not among its values * display: union DisplayOptions, implied key "type" is enum DisplayType, "help" is not among its values * blockdev: union BlockdevOptions, implied key "driver is enum BlockdevDriver, "help" is not among its values * export: union BlockExport, implied key "type" is enum BlockExportType, "help" is not among its values * monitor: struct MonitorOptions, implied key "mode"is enum MonitorMode, "help" is not among its values * nbd-server: struct NbdServerOptions, no implied key. We're good. What's the risk of not staying good? The implied keys above are all QAPI enums. The only existing QAPI enum with a value 'help' is QKeyCode. The only existing C enum with a name that ends in _HELP is the one defining Mac keycodes in hw/input/adb-keys.h. For completeness, I double-checked non of the existing occurences of string "help" are used as values of option parameters. We'll almost certainly stay good. What if we manage to mess this up against all odds? In my opinion, consistency in getting help is more important than consistency within a badly designed option: option argument "help" should give you help even when that means you can't omit the implied key when its value is "help". [...] ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() 2020-09-30 12:45 [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf @ 2020-09-30 12:45 ` Kevin Wolf 2020-09-30 13:46 ` Eric Blake 2020-10-02 12:13 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 3 siblings, 2 replies; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 12:45 UTC (permalink / raw) To: qemu-block; +Cc: kwolf, qemu-devel, armbru, mreitz This creates separate helper functions for printing a list of user creatable object types and for printing a list of properties of a given type. This allows using these parts without having a QemuOpts. Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- qom/object_interfaces.c | 90 ++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index e8e1523960..3fd1da157e 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -214,54 +214,68 @@ 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) +{ + GSList *l, *list; + + printf("List of user creatable objects:\n"); + list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); + for (l = list; l != NULL; l = l->next) { + ObjectClass *oc = OBJECT_CLASS(l->data); + printf(" %s\n", object_class_get_name(oc)); + } + g_slist_free(list); +} + +static bool user_creatable_print_type_properites(const char *type) { ObjectClass *klass; + ObjectPropertyIterator iter; + ObjectProperty *prop; + GPtrArray *array; + int i; - if (is_help_option(type)) { - GSList *l, *list; + klass = object_class_by_name(type); + if (!klass) { + return false; + } - printf("List of user creatable objects:\n"); - list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); - for (l = list; l != NULL; l = l->next) { - ObjectClass *oc = OBJECT_CLASS(l->data); - printf(" %s\n", object_class_get_name(oc)); + array = g_ptr_array_new(); + object_class_property_iter_init(&iter, klass); + while ((prop = object_property_iter_next(&iter))) { + if (!prop->set) { + continue; } - g_slist_free(list); - return true; + + g_ptr_array_add(array, + object_property_help(prop->name, prop->type, + prop->defval, prop->description)); } + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + if (array->len > 0) { + printf("%s options:\n", type); + } else { + printf("There are no options for %s.\n", type); + } + for (i = 0; i < array->len; i++) { + printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); + return true; +} - klass = object_class_by_name(type); - if (klass && qemu_opt_has_help_opt(opts)) { - ObjectPropertyIterator iter; - ObjectProperty *prop; - GPtrArray *array = g_ptr_array_new(); - int i; - - object_class_property_iter_init(&iter, klass); - while ((prop = object_property_iter_next(&iter))) { - if (!prop->set) { - continue; - } - - g_ptr_array_add(array, - object_property_help(prop->name, prop->type, - prop->defval, prop->description)); - } - g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); - if (array->len > 0) { - printf("%s options:\n", type); - } else { - printf("There are no options for %s.\n", type); - } - for (i = 0; i < array->len; i++) { - printf("%s\n", (char *)array->pdata[i]); - } - g_ptr_array_set_free_func(array, g_free); - g_ptr_array_free(array, 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; } -- 2.25.4 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() 2020-09-30 12:45 ` [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() Kevin Wolf @ 2020-09-30 13:46 ` Eric Blake 2020-10-02 12:13 ` Markus Armbruster 1 sibling, 0 replies; 18+ messages in thread From: Eric Blake @ 2020-09-30 13:46 UTC (permalink / raw) To: Kevin Wolf, qemu-block; +Cc: qemu-devel, armbru, mreitz [-- Attachment #1.1: Type: text/plain, Size: 700 bytes --] On 9/30/20 7:45 AM, Kevin Wolf wrote: > This creates separate helper functions for printing a list of user > creatable object types and for printing a list of properties of a given > type. This allows using these parts without having a QemuOpts. > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > qom/object_interfaces.c | 90 ++++++++++++++++++++++++----------------- > 1 file changed, 52 insertions(+), 38 deletions(-) I don't see any changes from v1, so my R-b from there could have been applied. Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() 2020-09-30 12:45 ` [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() Kevin Wolf 2020-09-30 13:46 ` Eric Blake @ 2020-10-02 12:13 ` Markus Armbruster 1 sibling, 0 replies; 18+ messages in thread From: Markus Armbruster @ 2020-10-02 12:13 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Kevin Wolf <kwolf@redhat.com> writes: > This creates separate helper functions for printing a list of user > creatable object types and for printing a list of properties of a given > type. This allows using these parts without having a QemuOpts. Does the last sentence allude to a future patch? If yes, I suggest to phrase it as "This will allow ..." > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > qom/object_interfaces.c | 90 ++++++++++++++++++++++++----------------- > 1 file changed, 52 insertions(+), 38 deletions(-) > > diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c > index e8e1523960..3fd1da157e 100644 > --- a/qom/object_interfaces.c > +++ b/qom/object_interfaces.c > @@ -214,54 +214,68 @@ 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) > +{ > + GSList *l, *list; > + > + printf("List of user creatable objects:\n"); > + list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); > + for (l = list; l != NULL; l = l->next) { > + ObjectClass *oc = OBJECT_CLASS(l->data); > + printf(" %s\n", object_class_get_name(oc)); > + } > + g_slist_free(list); > +} > + > +static bool user_creatable_print_type_properites(const char *type) > { > ObjectClass *klass; > + ObjectPropertyIterator iter; > + ObjectProperty *prop; > + GPtrArray *array; > + int i; > > - if (is_help_option(type)) { > - GSList *l, *list; > + klass = object_class_by_name(type); > + if (!klass) { > + return false; > + } > > - printf("List of user creatable objects:\n"); > - list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); > - for (l = list; l != NULL; l = l->next) { > - ObjectClass *oc = OBJECT_CLASS(l->data); > - printf(" %s\n", object_class_get_name(oc)); > + array = g_ptr_array_new(); > + object_class_property_iter_init(&iter, klass); > + while ((prop = object_property_iter_next(&iter))) { > + if (!prop->set) { > + continue; > } > - g_slist_free(list); > - return true; > + > + g_ptr_array_add(array, > + object_property_help(prop->name, prop->type, > + prop->defval, prop->description)); > } > + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); > + if (array->len > 0) { > + printf("%s options:\n", type); > + } else { > + printf("There are no options for %s.\n", type); > + } > + for (i = 0; i < array->len; i++) { > + printf("%s\n", (char *)array->pdata[i]); > + } > + g_ptr_array_set_free_func(array, g_free); > + g_ptr_array_free(array, true); > + return true; > +} > > - klass = object_class_by_name(type); > - if (klass && qemu_opt_has_help_opt(opts)) { > - ObjectPropertyIterator iter; > - ObjectProperty *prop; > - GPtrArray *array = g_ptr_array_new(); > - int i; > - > - object_class_property_iter_init(&iter, klass); > - while ((prop = object_property_iter_next(&iter))) { > - if (!prop->set) { > - continue; > - } > - > - g_ptr_array_add(array, > - object_property_help(prop->name, prop->type, > - prop->defval, prop->description)); > - } > - g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); > - if (array->len > 0) { > - printf("%s options:\n", type); > - } else { > - printf("There are no options for %s.\n", type); > - } > - for (i = 0; i < array->len; i++) { > - printf("%s\n", (char *)array->pdata[i]); > - } > - g_ptr_array_set_free_func(array, g_free); > - g_ptr_array_free(array, 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; > } I'd make user_creatable_print_types() return true for summetry with user_creatable_print_type_properites(), but that's a matter of taste. Reviewed-by: Markus Armbruster <armbru@redhat.com> ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() 2020-09-30 12:45 [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() Kevin Wolf @ 2020-09-30 12:45 ` Kevin Wolf 2020-09-30 13:48 ` Eric Blake 2020-10-02 12:25 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 3 siblings, 2 replies; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 12:45 UTC (permalink / raw) To: qemu-block; +Cc: kwolf, qemu-devel, armbru, mreitz This adds a function that, given a QDict of non-help options, prints help for user creatable objects. Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- include/qom/object_interfaces.h | 9 +++++++++ qom/object_interfaces.c | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index f118fb516b..53b114b11a 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -161,6 +161,15 @@ int user_creatable_add_opts_foreach(void *opaque, */ 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) + */ +void user_creatable_print_help_from_qdict(QDict *args); + /** * user_creatable_del: * @id: the unique ID for the object diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 3fd1da157e..ed896fe764 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -279,6 +279,15 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts) 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; -- 2.25.4 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() 2020-09-30 12:45 ` [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() Kevin Wolf @ 2020-09-30 13:48 ` Eric Blake 2020-10-02 12:25 ` Markus Armbruster 1 sibling, 0 replies; 18+ messages in thread From: Eric Blake @ 2020-09-30 13:48 UTC (permalink / raw) To: Kevin Wolf, qemu-block; +Cc: qemu-devel, armbru, mreitz [-- Attachment #1.1: Type: text/plain, Size: 536 bytes --] On 9/30/20 7:45 AM, Kevin Wolf wrote: > This adds a function that, given a QDict of non-help options, prints > help for user creatable objects. > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > include/qom/object_interfaces.h | 9 +++++++++ > qom/object_interfaces.c | 9 +++++++++ > 2 files changed, 18 insertions(+) As with v1, Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() 2020-09-30 12:45 ` [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() Kevin Wolf 2020-09-30 13:48 ` Eric Blake @ 2020-10-02 12:25 ` Markus Armbruster 2020-10-02 12:36 ` Markus Armbruster 1 sibling, 1 reply; 18+ messages in thread From: Markus Armbruster @ 2020-10-02 12:25 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Kevin Wolf <kwolf@redhat.com> writes: > This adds a function that, given a QDict of non-help options, prints > help for user creatable objects. > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > include/qom/object_interfaces.h | 9 +++++++++ > qom/object_interfaces.c | 9 +++++++++ > 2 files changed, 18 insertions(+) > > diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h > index f118fb516b..53b114b11a 100644 > --- a/include/qom/object_interfaces.h > +++ b/include/qom/object_interfaces.h > @@ -161,6 +161,15 @@ int user_creatable_add_opts_foreach(void *opaque, > */ > 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) > + */ > +void user_creatable_print_help_from_qdict(QDict *args); > + > /** > * user_creatable_del: > * @id: the unique ID for the object > diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c > index 3fd1da157e..ed896fe764 100644 > --- a/qom/object_interfaces.c > +++ b/qom/object_interfaces.c > @@ -279,6 +279,15 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts) > 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(); > + } Existing user_creatable_print_help(): 1. "qom-type=help,..." and its sugared forms, in particular "help" List QOM types and succeed. 2. "qom-type=T,help,..." 2a. If T names a QOM type List T's properties and succeed. 2b. If T does not name a QOM type Fail. Callers typically interpret this as "no help requested", proceed, then choke on invalid qom-type=T. New user_creatable_print_help() treats case 2b like case 1. Intentional? > +} > + > bool user_creatable_del(const char *id, Error **errp) > { > Object *container; ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() 2020-10-02 12:25 ` Markus Armbruster @ 2020-10-02 12:36 ` Markus Armbruster 0 siblings, 0 replies; 18+ messages in thread From: Markus Armbruster @ 2020-10-02 12:36 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Markus Armbruster <armbru@redhat.com> writes: > Kevin Wolf <kwolf@redhat.com> writes: > >> This adds a function that, given a QDict of non-help options, prints >> help for user creatable objects. >> >> Signed-off-by: Kevin Wolf <kwolf@redhat.com> >> --- >> include/qom/object_interfaces.h | 9 +++++++++ >> qom/object_interfaces.c | 9 +++++++++ >> 2 files changed, 18 insertions(+) >> >> diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h >> index f118fb516b..53b114b11a 100644 >> --- a/include/qom/object_interfaces.h >> +++ b/include/qom/object_interfaces.h >> @@ -161,6 +161,15 @@ int user_creatable_add_opts_foreach(void *opaque, >> */ >> 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) >> + */ >> +void user_creatable_print_help_from_qdict(QDict *args); >> + >> /** >> * user_creatable_del: >> * @id: the unique ID for the object >> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c >> index 3fd1da157e..ed896fe764 100644 >> --- a/qom/object_interfaces.c >> +++ b/qom/object_interfaces.c >> @@ -279,6 +279,15 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts) >> 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(); >> + } > > Existing user_creatable_print_help(): > > 1. "qom-type=help,..." and its sugared forms, in particular "help" > > List QOM types and succeed. > > 2. "qom-type=T,help,..." > > 2a. If T names a QOM type > > List T's properties and succeed. > > 2b. If T does not name a QOM type > > Fail. Callers typically interpret this as "no help requested", > proceed, then choke on invalid qom-type=T. And of course 3. Else No help requested; fail. > New user_creatable_print_help() treats case 2b like case 1. > > Intentional? The next patch relies on this, so I figure the answer is yes. The difference to user_creatable_print_help() is subtle. Perhaps the contract should point it out explicitly. Up to you. By the way, the contract of user_creatable_print_help() is inaccurate: * Prints help if requested in @opts. * * Returns: true if @opts contained a help option and help was printed, false * if no help option was found. One, it prints help when either @type or @opts request it. Two, "if no help option was found" is misleading: case 2b. Not this patch's problem. > >> +} >> + >> bool user_creatable_del(const char *id, Error **errp) >> { >> Object *container; Reviewed-by: Markus Armbruster <armbru@redhat.com> ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser 2020-09-30 12:45 [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf ` (2 preceding siblings ...) 2020-09-30 12:45 ` [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() Kevin Wolf @ 2020-09-30 12:45 ` Kevin Wolf 2020-09-30 13:49 ` Eric Blake 2020-10-02 12:26 ` Markus Armbruster 3 siblings, 2 replies; 18+ messages in thread From: Kevin Wolf @ 2020-09-30 12:45 UTC (permalink / raw) To: qemu-block; +Cc: kwolf, qemu-devel, armbru, mreitz The command line parser for --object parses the input twice: Once into QemuOpts just for detecting help options, and then again into a QDict using the keyval parser for actually creating the object. Now that the keyval parser can also detect help options, we can simplify this and remove the QemuOpts part. Signed-off-by: Kevin Wolf <kwolf@redhat.com> --- storage-daemon/qemu-storage-daemon.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index bb9cb740f0..7cbdbf0b23 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -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", NULL, &error_fatal); user_creatable_add_dict(args, true, &error_fatal); qobject_unref(args); break; -- 2.25.4 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser 2020-09-30 12:45 ` [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf @ 2020-09-30 13:49 ` Eric Blake 2020-10-02 12:26 ` Markus Armbruster 1 sibling, 0 replies; 18+ messages in thread From: Eric Blake @ 2020-09-30 13:49 UTC (permalink / raw) To: Kevin Wolf, qemu-block; +Cc: qemu-devel, armbru, mreitz [-- Attachment #1.1: Type: text/plain, Size: 731 bytes --] On 9/30/20 7:45 AM, Kevin Wolf wrote: > The command line parser for --object parses the input twice: Once into > QemuOpts just for detecting help options, and then again into a QDict > using the keyval parser for actually creating the object. > > Now that the keyval parser can also detect help options, we can simplify > this and remove the QemuOpts part. > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > storage-daemon/qemu-storage-daemon.c | 15 ++++----------- > 1 file changed, 4 insertions(+), 11 deletions(-) As with v1, Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser 2020-09-30 12:45 ` [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 2020-09-30 13:49 ` Eric Blake @ 2020-10-02 12:26 ` Markus Armbruster 1 sibling, 0 replies; 18+ messages in thread From: Markus Armbruster @ 2020-10-02 12:26 UTC (permalink / raw) To: Kevin Wolf; +Cc: qemu-devel, qemu-block, mreitz Kevin Wolf <kwolf@redhat.com> writes: > The command line parser for --object parses the input twice: Once into > QemuOpts just for detecting help options, and then again into a QDict > using the keyval parser for actually creating the object. > > Now that the keyval parser can also detect help options, we can simplify > this and remove the QemuOpts part. > > Signed-off-by: Kevin Wolf <kwolf@redhat.com> > --- > storage-daemon/qemu-storage-daemon.c | 15 ++++----------- > 1 file changed, 4 insertions(+), 11 deletions(-) > > diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c > index bb9cb740f0..7cbdbf0b23 100644 > --- a/storage-daemon/qemu-storage-daemon.c > +++ b/storage-daemon/qemu-storage-daemon.c > @@ -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", NULL, &error_fatal); > user_creatable_add_dict(args, true, &error_fatal); > qobject_unref(args); > break; Reviewed-by: Markus Armbruster <armbru@redhat.com> ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2020-10-05 12:09 UTC | newest] Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-09-30 12:45 [PATCH v2 0/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 2020-09-30 12:45 ` [PATCH v2 1/4] keyval: Parse help options Kevin Wolf 2020-09-30 13:35 ` Eric Blake 2020-09-30 15:10 ` Kevin Wolf 2020-10-01 10:34 ` Markus Armbruster 2020-10-01 11:33 ` Kevin Wolf 2020-10-01 15:46 ` Markus Armbruster 2020-10-05 12:08 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 2/4] qom: Factor out helpers from user_creatable_print_help() Kevin Wolf 2020-09-30 13:46 ` Eric Blake 2020-10-02 12:13 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 3/4] qom: Add user_creatable_print_help_from_qdict() Kevin Wolf 2020-09-30 13:48 ` Eric Blake 2020-10-02 12:25 ` Markus Armbruster 2020-10-02 12:36 ` Markus Armbruster 2020-09-30 12:45 ` [PATCH v2 4/4] qemu-storage-daemon: Remove QemuOpts from --object parser Kevin Wolf 2020-09-30 13:49 ` Eric Blake 2020-10-02 12:26 ` Markus Armbruster
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.