QAPI patches for 2018-12-13
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJcE0VvAAoJEDhwtADrkYZTLCkP/RvRR9iTcoM98kcFNjqBZRQa rUbSNBavxwzutPiT40WcNhg7hc0Uaptve8oMkGcfyyTh9UyhdOe8WNPxTos96vYt GtUhNhknGlvP4A7Zjs6KIIhl084MtPkpuPERkXZL4lgNrIw8BrFoj5hkZ3UIvItf 14oA1o6Zf9UxN1Yt12lZnG9N8t4ld5IKhkXh/FQ6OJNHz9GrhPq4A7vd4ipBRBjt PjvXVOYCEkiHRfJ3Qv5Thk2C1xzLRFusA5ff1rju324KGPoM8oZ+xGSUVqD0hhMe Kpzv4a6HV7SuM1fqJoZrF87VOhAO9bpxzIHUp83FhpKGDH4xqppDWYno/+9imPDA DAHUaOeaKpX6O4ttB96jRwTEOAbq3TzPqtYiyRaXhbtCc0dKi0HxHmIpwS4KNkHK Y3VuoTavarMfuLl2gDO+9PJhHxol8g0oYiaxXddW0svgnSM3xBTz/hGE2duStHTb DSWDVB/oVIOyR8eWSglUnc+OOJrxSkiaJelSU730Uc6kIk7hiY8PFQiwqebsI6uq IOABDG1/W0FkSRNl5QwXnGlD0eUzl1ySm2zvsgvJrC8ooAhzjjWdkcwtEdEYxlUj KqH+8ZFP+mOckrW9boqYPVqOL4GzNMnK23vEoidurhyShsmiCTyk+jckiJrl/IMy OlwA850MKVJ3W3+knR0I =bymN -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2018-12-13-v2' into staging QAPI patches for 2018-12-13 # gpg: Signature made Fri 14 Dec 2018 05:53:51 GMT # gpg: using RSA key 3870B400EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2018-12-13-v2: (32 commits) qapi: add conditions to REPLICATION type/commands on the schema qapi: add more conditions to SPICE qapi: add condition to variants documentation qapi: add 'If:' condition to struct members documentation qapi: add 'If:' condition to enum values documentation qapi: Add #if conditions to generated code members qapi: add 'if' to alternate members qapi: add 'if' to union members qapi: Add 'if' to implicit struct members qapi: add a dictionary form for TYPE qapi-events: add 'if' condition to implicit event enum qapi: add 'if' to enum members qapi: add a dictionary form with 'name' key for enum members qapi: improve reporting of unknown or missing keys qapi: factor out checking for keys tests: print enum type members more like object type members qapi: change enum visitor and gen_enum* to take QAPISchemaMember qapi: Do not define enumeration value explicitly qapi: break long lines at 'data' member qapi: rename QAPISchemaEnumType.values to .members ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
81781be3c9
@ -26,7 +26,7 @@ how the schemas, scripts, and resulting code are used.
|
||||
== QMP/Guest agent schema ==
|
||||
|
||||
A QAPI schema file is designed to be loosely based on JSON
|
||||
(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style
|
||||
(http://www.ietf.org/rfc/rfc8259.txt) with changes for quoting style
|
||||
and the use of comments; a QAPI schema file is then parsed by a python
|
||||
code generation program. A valid QAPI schema consists of a series of
|
||||
top-level expressions, with no commas between them. Where
|
||||
@ -752,6 +752,25 @@ gets its generated code guarded like this:
|
||||
#endif /* defined(HAVE_BAR) */
|
||||
#endif /* defined(CONFIG_FOO) */
|
||||
|
||||
Where a member can be defined with a single string value for its type,
|
||||
it is also possible to supply a dictionary instead with both 'type'
|
||||
and 'if' keys.
|
||||
|
||||
Example: a conditional 'bar' member
|
||||
|
||||
{ 'struct': 'IfStruct', 'data':
|
||||
{ 'foo': 'int',
|
||||
'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } }
|
||||
|
||||
An enum value can be replaced by a dictionary with a 'name' and a 'if'
|
||||
key.
|
||||
|
||||
Example: a conditional 'bar' enum member.
|
||||
|
||||
{ 'enum': 'IfEnum', 'data':
|
||||
[ 'foo',
|
||||
{ 'name' : 'bar', 'if': 'defined(IFCOND)' } ] }
|
||||
|
||||
Please note that you are responsible to ensure that the C code will
|
||||
compile with an arbitrary combination of conditions, since the
|
||||
generators are unable to check it at this point.
|
||||
|
@ -32,7 +32,7 @@ following format:
|
||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined
|
||||
by the JSON standard:
|
||||
|
||||
http://www.ietf.org/rfc/rfc7159.txt
|
||||
http://www.ietf.org/rfc/rfc8259.txt
|
||||
|
||||
The server expects its input to be encoded in UTF-8, and sends its
|
||||
output encoded in ASCII.
|
||||
|
@ -19,8 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor;
|
||||
|
||||
/*
|
||||
* The string input visitor does not implement support for visiting
|
||||
* QAPI structs, alternates, null, or arbitrary QTypes. It also
|
||||
* requires a non-null list argument to visit_start_list().
|
||||
* QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists
|
||||
* of integers (except type "size") are supported.
|
||||
*/
|
||||
Visitor *string_input_visitor_new(const char *str);
|
||||
|
||||
|
@ -146,14 +146,16 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base,
|
||||
int64_t *result);
|
||||
int qemu_strtou64(const char *nptr, const char **endptr, int base,
|
||||
uint64_t *result);
|
||||
int qemu_strtod(const char *nptr, const char **endptr, double *result);
|
||||
int qemu_strtod_finite(const char *nptr, const char **endptr, double *result);
|
||||
|
||||
int parse_uint(const char *s, unsigned long long *value, char **endptr,
|
||||
int base);
|
||||
int parse_uint_full(const char *s, unsigned long long *value, int base);
|
||||
|
||||
int qemu_strtosz(const char *nptr, char **end, uint64_t *result);
|
||||
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result);
|
||||
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result);
|
||||
int qemu_strtosz(const char *nptr, const char **end, uint64_t *result);
|
||||
int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result);
|
||||
int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result);
|
||||
|
||||
/* used to print char* safely */
|
||||
#define STR_OR_NULL(str) ((str) ? (str) : "null")
|
||||
|
@ -24,7 +24,9 @@
|
||||
#include "trace.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/failover.h"
|
||||
#ifdef CONFIG_REPLICATION
|
||||
#include "replication.h"
|
||||
#endif
|
||||
#include "net/colo-compare.h"
|
||||
#include "net/colo.h"
|
||||
#include "block/block.h"
|
||||
@ -201,11 +203,11 @@ void colo_do_failover(MigrationState *s)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REPLICATION
|
||||
void qmp_xen_set_replication(bool enable, bool primary,
|
||||
bool has_failover, bool failover,
|
||||
Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_REPLICATION
|
||||
ReplicationMode mode = primary ?
|
||||
REPLICATION_MODE_PRIMARY :
|
||||
REPLICATION_MODE_SECONDARY;
|
||||
@ -224,14 +226,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
|
||||
}
|
||||
replication_stop_all(failover, failover ? NULL : errp);
|
||||
}
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_REPLICATION
|
||||
Error *err = NULL;
|
||||
ReplicationStatus *s = g_new0(ReplicationStatus, 1);
|
||||
|
||||
@ -246,19 +244,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
|
||||
|
||||
error_free(err);
|
||||
return s;
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
void qmp_xen_colo_do_checkpoint(Error **errp)
|
||||
{
|
||||
#ifdef CONFIG_REPLICATION
|
||||
replication_do_checkpoint_all(errp);
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
COLOStatus *qmp_query_colo_status(Error **errp)
|
||||
{
|
||||
|
@ -1147,11 +1147,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
|
||||
*/
|
||||
static void qmp_unregister_commands_hack(void)
|
||||
{
|
||||
#ifndef CONFIG_REPLICATION
|
||||
qmp_unregister_command(&qmp_commands, "xen-set-replication");
|
||||
qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
|
||||
qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
|
||||
#endif
|
||||
#ifndef TARGET_I386
|
||||
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
|
||||
qmp_unregister_command(&qmp_commands, "query-sev");
|
||||
@ -3232,7 +3227,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
|
||||
{
|
||||
int ret;
|
||||
uint64_t val;
|
||||
char *end;
|
||||
const char *end;
|
||||
|
||||
while (qemu_isspace(*p)) {
|
||||
p++;
|
||||
|
@ -1143,8 +1143,10 @@
|
||||
# This command is now obsolete and will always return an error since 2.10
|
||||
#
|
||||
##
|
||||
{ 'command': 'block_passwd', 'data': {'*device': 'str',
|
||||
'*node-name': 'str', 'password': 'str'} }
|
||||
{ 'command': 'block_passwd',
|
||||
'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'password': 'str' } }
|
||||
|
||||
##
|
||||
# @block_resize:
|
||||
@ -1171,9 +1173,10 @@
|
||||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'block_resize', 'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'size': 'int' }}
|
||||
{ 'command': 'block_resize',
|
||||
'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'size': 'int' } }
|
||||
|
||||
##
|
||||
# @NewImageMode:
|
||||
@ -2620,7 +2623,9 @@
|
||||
'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
|
||||
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
|
||||
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
|
||||
'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
|
||||
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog',
|
||||
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
|
||||
|
||||
##
|
||||
@ -3377,7 +3382,8 @@
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
|
||||
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsReplication:
|
||||
@ -3395,7 +3401,8 @@
|
||||
{ 'struct': 'BlockdevOptionsReplication',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { 'mode': 'ReplicationMode',
|
||||
'*top-id': 'str' } }
|
||||
'*top-id': 'str' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @NFSTransport:
|
||||
@ -3711,7 +3718,8 @@
|
||||
'quorum': 'BlockdevOptionsQuorum',
|
||||
'raw': 'BlockdevOptionsRaw',
|
||||
'rbd': 'BlockdevOptionsRbd',
|
||||
'replication':'BlockdevOptionsReplication',
|
||||
'replication': { 'type': 'BlockdevOptionsReplication',
|
||||
'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog': 'BlockdevOptionsSheepdog',
|
||||
'ssh': 'BlockdevOptionsSsh',
|
||||
'throttle': 'BlockdevOptionsThrottle',
|
||||
|
150
qapi/char.json
150
qapi/char.json
@ -25,9 +25,10 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
|
||||
'filename': 'str',
|
||||
'frontend-open': 'bool'} }
|
||||
{ 'struct': 'ChardevInfo',
|
||||
'data': { 'label': 'str',
|
||||
'filename': 'str',
|
||||
'frontend-open': 'bool' } }
|
||||
|
||||
##
|
||||
# @query-chardev:
|
||||
@ -152,7 +153,8 @@
|
||||
#
|
||||
##
|
||||
{ 'command': 'ringbuf-write',
|
||||
'data': {'device': 'str', 'data': 'str',
|
||||
'data': { 'device': 'str',
|
||||
'data': 'str',
|
||||
'*format': 'DataFormat'} }
|
||||
|
||||
##
|
||||
@ -202,8 +204,9 @@
|
||||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str',
|
||||
'*logappend': 'bool' } }
|
||||
{ 'struct': 'ChardevCommon',
|
||||
'data': { '*logfile': 'str',
|
||||
'*logappend': 'bool' } }
|
||||
|
||||
##
|
||||
# @ChardevFile:
|
||||
@ -217,9 +220,10 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
|
||||
'out' : 'str',
|
||||
'*append': 'bool' },
|
||||
{ 'struct': 'ChardevFile',
|
||||
'data': { '*in': 'str',
|
||||
'out': 'str',
|
||||
'*append': 'bool' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -232,7 +236,8 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' },
|
||||
{ 'struct': 'ChardevHostdev',
|
||||
'data': { 'device': 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -260,15 +265,16 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy',
|
||||
'*tls-creds' : 'str',
|
||||
'*server' : 'bool',
|
||||
'*wait' : 'bool',
|
||||
'*nodelay' : 'bool',
|
||||
'*telnet' : 'bool',
|
||||
'*tn3270' : 'bool',
|
||||
'*websocket' : 'bool',
|
||||
'*reconnect' : 'int' },
|
||||
{ 'struct': 'ChardevSocket',
|
||||
'data': { 'addr': 'SocketAddressLegacy',
|
||||
'*tls-creds': 'str',
|
||||
'*server': 'bool',
|
||||
'*wait': 'bool',
|
||||
'*nodelay': 'bool',
|
||||
'*telnet': 'bool',
|
||||
'*tn3270': 'bool',
|
||||
'*websocket': 'bool',
|
||||
'*reconnect': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -281,8 +287,9 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddressLegacy',
|
||||
'*local' : 'SocketAddressLegacy' },
|
||||
{ 'struct': 'ChardevUdp',
|
||||
'data': { 'remote': 'SocketAddressLegacy',
|
||||
'*local': 'SocketAddressLegacy' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -294,7 +301,8 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' },
|
||||
{ 'struct': 'ChardevMux',
|
||||
'data': { 'chardev': 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -308,7 +316,8 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' },
|
||||
{ 'struct': 'ChardevStdio',
|
||||
'data': { '*signal': 'bool' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
|
||||
@ -321,9 +330,10 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
# TODO: 'if': 'defined(CONFIG_SPICE)'
|
||||
{ 'struct': 'ChardevSpiceChannel',
|
||||
'data': { 'type': 'str' },
|
||||
'base': 'ChardevCommon',
|
||||
'if': 'defined(CONFIG_SPICE)' }
|
||||
|
||||
##
|
||||
# @ChardevSpicePort:
|
||||
@ -334,9 +344,10 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
# TODO: 'if': 'defined(CONFIG_SPICE)'
|
||||
{ 'struct': 'ChardevSpicePort',
|
||||
'data': { 'fqdn': 'str' },
|
||||
'base': 'ChardevCommon',
|
||||
'if': 'defined(CONFIG_SPICE)' }
|
||||
|
||||
##
|
||||
# @ChardevVC:
|
||||
@ -350,10 +361,11 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevVC', 'data': { '*width' : 'int',
|
||||
'*height' : 'int',
|
||||
'*cols' : 'int',
|
||||
'*rows' : 'int' },
|
||||
{ 'struct': 'ChardevVC',
|
||||
'data': { '*width': 'int',
|
||||
'*height': 'int',
|
||||
'*cols': 'int',
|
||||
'*rows': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -365,7 +377,8 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' },
|
||||
{ 'struct': 'ChardevRingbuf',
|
||||
'data': { '*size': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
@ -375,29 +388,30 @@
|
||||
#
|
||||
# Since: 1.4 (testdev since 2.2, wctablet since 2.9)
|
||||
##
|
||||
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
|
||||
'serial' : 'ChardevHostdev',
|
||||
'parallel': 'ChardevHostdev',
|
||||
'pipe' : 'ChardevHostdev',
|
||||
'socket' : 'ChardevSocket',
|
||||
'udp' : 'ChardevUdp',
|
||||
'pty' : 'ChardevCommon',
|
||||
'null' : 'ChardevCommon',
|
||||
'mux' : 'ChardevMux',
|
||||
'msmouse': 'ChardevCommon',
|
||||
'wctablet' : 'ChardevCommon',
|
||||
'braille': 'ChardevCommon',
|
||||
'testdev': 'ChardevCommon',
|
||||
'stdio' : 'ChardevStdio',
|
||||
'console': 'ChardevCommon',
|
||||
'spicevmc': 'ChardevSpiceChannel',
|
||||
# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
|
||||
'spiceport': 'ChardevSpicePort',
|
||||
# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
|
||||
'vc' : 'ChardevVC',
|
||||
'ringbuf': 'ChardevRingbuf',
|
||||
# next one is just for compatibility
|
||||
'memory' : 'ChardevRingbuf' } }
|
||||
{ 'union': 'ChardevBackend',
|
||||
'data': { 'file': 'ChardevFile',
|
||||
'serial': 'ChardevHostdev',
|
||||
'parallel': 'ChardevHostdev',
|
||||
'pipe': 'ChardevHostdev',
|
||||
'socket': 'ChardevSocket',
|
||||
'udp': 'ChardevUdp',
|
||||
'pty': 'ChardevCommon',
|
||||
'null': 'ChardevCommon',
|
||||
'mux': 'ChardevMux',
|
||||
'msmouse': 'ChardevCommon',
|
||||
'wctablet': 'ChardevCommon',
|
||||
'braille': 'ChardevCommon',
|
||||
'testdev': 'ChardevCommon',
|
||||
'stdio': 'ChardevStdio',
|
||||
'console': 'ChardevCommon',
|
||||
'spicevmc': { 'type': 'ChardevSpiceChannel',
|
||||
'if': 'defined(CONFIG_SPICE)' },
|
||||
'spiceport': { 'type': 'ChardevSpicePort',
|
||||
'if': 'defined(CONFIG_SPICE)' },
|
||||
'vc': 'ChardevVC',
|
||||
'ringbuf': 'ChardevRingbuf',
|
||||
# next one is just for compatibility
|
||||
'memory': 'ChardevRingbuf' } }
|
||||
|
||||
##
|
||||
# @ChardevReturn:
|
||||
@ -409,7 +423,8 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
|
||||
{ 'struct' : 'ChardevReturn',
|
||||
'data': { '*pty': 'str' } }
|
||||
|
||||
##
|
||||
# @chardev-add:
|
||||
@ -442,8 +457,9 @@
|
||||
# <- { "return": { "pty" : "/dev/pty/42" } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-add', 'data': {'id' : 'str',
|
||||
'backend' : 'ChardevBackend' },
|
||||
{ 'command': 'chardev-add',
|
||||
'data': { 'id': 'str',
|
||||
'backend': 'ChardevBackend' },
|
||||
'returns': 'ChardevReturn' }
|
||||
|
||||
##
|
||||
@ -482,8 +498,9 @@
|
||||
# <- {"return": {}}
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-change', 'data': {'id' : 'str',
|
||||
'backend' : 'ChardevBackend' },
|
||||
{ 'command': 'chardev-change',
|
||||
'data': { 'id': 'str',
|
||||
'backend': 'ChardevBackend' },
|
||||
'returns': 'ChardevReturn' }
|
||||
|
||||
##
|
||||
@ -503,7 +520,8 @@
|
||||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-remove', 'data': {'id': 'str'} }
|
||||
{ 'command': 'chardev-remove',
|
||||
'data': { 'id': 'str' } }
|
||||
|
||||
##
|
||||
# @chardev-send-break:
|
||||
@ -522,7 +540,8 @@
|
||||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-send-break', 'data': {'id': 'str'} }
|
||||
{ 'command': 'chardev-send-break',
|
||||
'data': { 'id': 'str' } }
|
||||
|
||||
##
|
||||
# @VSERPORT_CHANGE:
|
||||
@ -543,4 +562,5 @@
|
||||
#
|
||||
##
|
||||
{ 'event': 'VSERPORT_CHANGE',
|
||||
'data': { 'id': 'str', 'open': 'bool' } }
|
||||
'data': { 'id': 'str',
|
||||
'open': 'bool' } }
|
||||
|
@ -1257,7 +1257,8 @@
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'xen-set-replication',
|
||||
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
|
||||
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @ReplicationStatus:
|
||||
@ -1272,7 +1273,8 @@
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'struct': 'ReplicationStatus',
|
||||
'data': { 'error': 'bool', '*desc': 'str' } }
|
||||
'data': { 'error': 'bool', '*desc': 'str' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @query-xen-replication-status:
|
||||
@ -1289,7 +1291,8 @@
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'query-xen-replication-status',
|
||||
'returns': 'ReplicationStatus' }
|
||||
'returns': 'ReplicationStatus',
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @xen-colo-do-checkpoint:
|
||||
@ -1305,7 +1308,8 @@
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'xen-colo-do-checkpoint' }
|
||||
{ 'command': 'xen-colo-do-checkpoint',
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @COLOStatus:
|
||||
@ -1356,7 +1360,8 @@
|
||||
#
|
||||
# Since: 3.0
|
||||
##
|
||||
{ 'command': 'migrate-recover', 'data': { 'uri': 'str' },
|
||||
{ 'command': 'migrate-recover',
|
||||
'data': { 'uri': 'str' },
|
||||
'allow-oob': true }
|
||||
|
||||
##
|
||||
|
@ -2385,7 +2385,9 @@
|
||||
# <- { "return": { "fdset-id": 1, "fd": 3 } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
|
||||
{ 'command': 'add-fd',
|
||||
'data': { '*fdset-id': 'int',
|
||||
'*opaque': 'str' },
|
||||
'returns': 'AddfdInfo' }
|
||||
|
||||
##
|
||||
@ -2657,7 +2659,8 @@
|
||||
# }
|
||||
#
|
||||
##
|
||||
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
|
||||
{'command': 'query-command-line-options',
|
||||
'data': { '*option': 'str' },
|
||||
'returns': ['CommandLineOptionInfo'],
|
||||
'allow-preconfig': true }
|
||||
|
||||
|
@ -657,7 +657,8 @@
|
||||
# }
|
||||
#
|
||||
##
|
||||
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
|
||||
{ 'command': 'query-rx-filter',
|
||||
'data': { '*name': 'str' },
|
||||
'returns': ['RxFilterInfo'] }
|
||||
|
||||
##
|
||||
|
@ -562,19 +562,20 @@ static void qobject_input_type_number_keyval(Visitor *v, const char *name,
|
||||
{
|
||||
QObjectInputVisitor *qiv = to_qiv(v);
|
||||
const char *str = qobject_input_get_keyval(qiv, name, errp);
|
||||
char *endp;
|
||||
double val;
|
||||
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
*obj = strtod(str, &endp);
|
||||
if (errno || endp == str || *endp || !isfinite(*obj)) {
|
||||
if (qemu_strtod_finite(str, NULL, &val)) {
|
||||
/* TODO report -ERANGE more nicely */
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||
full_name(qiv, name), "number");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = val;
|
||||
}
|
||||
|
||||
static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
|
||||
|
@ -4,10 +4,10 @@
|
||||
* Copyright Red Hat, Inc. 2012-2016
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
* David Hildenbrand <david@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@ -18,20 +18,42 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qnull.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
typedef enum ListMode {
|
||||
/* no list parsing active / no list expected */
|
||||
LM_NONE,
|
||||
/* we have an unparsed string remaining */
|
||||
LM_UNPARSED,
|
||||
/* we have an unfinished int64 range */
|
||||
LM_INT64_RANGE,
|
||||
/* we have an unfinished uint64 range */
|
||||
LM_UINT64_RANGE,
|
||||
/* we have parsed the string completely and no range is remaining */
|
||||
LM_END,
|
||||
} ListMode;
|
||||
|
||||
/* protect against DOS attacks, limit the amount of elements per range */
|
||||
#define RANGE_MAX_ELEMENTS 65536
|
||||
|
||||
typedef union RangeElement {
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
} RangeElement;
|
||||
|
||||
struct StringInputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
|
||||
GList *ranges;
|
||||
GList *cur_range;
|
||||
int64_t cur;
|
||||
/* List parsing state */
|
||||
ListMode lm;
|
||||
RangeElement rangeNext;
|
||||
RangeElement rangeEnd;
|
||||
const char *unparsed_string;
|
||||
void *list;
|
||||
|
||||
/* The original string to parse */
|
||||
const char *string;
|
||||
void *list; /* Only needed for sanity checking the caller */
|
||||
};
|
||||
|
||||
static StringInputVisitor *to_siv(Visitor *v)
|
||||
@ -39,136 +61,42 @@ static StringInputVisitor *to_siv(Visitor *v)
|
||||
return container_of(v, StringInputVisitor, visitor);
|
||||
}
|
||||
|
||||
static void free_range(void *range, void *dummy)
|
||||
{
|
||||
g_free(range);
|
||||
}
|
||||
|
||||
static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
|
||||
{
|
||||
char *str = (char *) siv->string;
|
||||
long long start, end;
|
||||
Range *cur;
|
||||
char *endptr;
|
||||
|
||||
if (siv->ranges) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
start = strtoll(str, &endptr, 0);
|
||||
if (errno == 0 && endptr > str) {
|
||||
if (*endptr == '\0') {
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, start);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
str = NULL;
|
||||
} else if (*endptr == '-') {
|
||||
str = endptr + 1;
|
||||
errno = 0;
|
||||
end = strtoll(str, &endptr, 0);
|
||||
if (errno == 0 && endptr > str && start <= end &&
|
||||
(start > INT64_MAX - 65536 ||
|
||||
end < start + 65536)) {
|
||||
if (*endptr == '\0') {
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, end);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
str = NULL;
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, end);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, start);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} while (str);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
g_list_foreach(siv->ranges, free_range, NULL);
|
||||
g_list_free(siv->ranges);
|
||||
siv->ranges = NULL;
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"an int64 value or range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
start_list(Visitor *v, const char *name, GenericList **list, size_t size,
|
||||
Error **errp)
|
||||
static void start_list(Visitor *v, const char *name, GenericList **list,
|
||||
size_t size, Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
/* We don't support visits without a list */
|
||||
assert(list);
|
||||
assert(siv->lm == LM_NONE);
|
||||
siv->list = list;
|
||||
siv->unparsed_string = siv->string;
|
||||
|
||||
if (parse_str(siv, name, errp) < 0) {
|
||||
*list = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
siv->cur_range = g_list_first(siv->ranges);
|
||||
if (siv->cur_range) {
|
||||
Range *r = siv->cur_range->data;
|
||||
if (r) {
|
||||
siv->cur = range_lob(r);
|
||||
if (!siv->string[0]) {
|
||||
if (list) {
|
||||
*list = NULL;
|
||||
}
|
||||
*list = g_malloc0(size);
|
||||
siv->lm = LM_END;
|
||||
} else {
|
||||
*list = NULL;
|
||||
if (list) {
|
||||
*list = g_malloc0(size);
|
||||
}
|
||||
siv->lm = LM_UNPARSED;
|
||||
}
|
||||
}
|
||||
|
||||
static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
Range *r;
|
||||
|
||||
if (!siv->ranges || !siv->cur_range) {
|
||||
switch (siv->lm) {
|
||||
case LM_END:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!range_contains(r, siv->cur)) {
|
||||
siv->cur_range = g_list_next(siv->cur_range);
|
||||
if (!siv->cur_range) {
|
||||
return NULL;
|
||||
}
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
siv->cur = range_lob(r);
|
||||
case LM_INT64_RANGE:
|
||||
case LM_UINT64_RANGE:
|
||||
case LM_UNPARSED:
|
||||
/* we have an unparsed string or something left in a range */
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
tail->next = g_malloc0(size);
|
||||
@ -178,88 +106,208 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
|
||||
static void check_list(Visitor *v, Error **errp)
|
||||
{
|
||||
const StringInputVisitor *siv = to_siv(v);
|
||||
Range *r;
|
||||
GList *cur_range;
|
||||
|
||||
if (!siv->ranges || !siv->cur_range) {
|
||||
switch (siv->lm) {
|
||||
case LM_INT64_RANGE:
|
||||
case LM_UINT64_RANGE:
|
||||
case LM_UNPARSED:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
case LM_END:
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!range_contains(r, siv->cur)) {
|
||||
cur_range = g_list_next(siv->cur_range);
|
||||
if (!cur_range) {
|
||||
return;
|
||||
}
|
||||
r = cur_range->data;
|
||||
if (!r) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error_setg(errp, "Range contains too many values");
|
||||
}
|
||||
|
||||
static void end_list(Visitor *v, void **obj)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm != LM_NONE);
|
||||
assert(siv->list == obj);
|
||||
siv->list = NULL;
|
||||
siv->unparsed_string = NULL;
|
||||
siv->lm = LM_NONE;
|
||||
}
|
||||
|
||||
static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
|
||||
{
|
||||
const char *endptr;
|
||||
int64_t start, end;
|
||||
|
||||
/* parse a simple int64 or range */
|
||||
if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
end = start;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
case '-':
|
||||
/* parse the end of the range */
|
||||
if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* we have a proper range (with maybe only one element) */
|
||||
siv->lm = LM_INT64_RANGE;
|
||||
siv->rangeNext.i64 = start;
|
||||
siv->rangeEnd.i64 = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
int64_t val;
|
||||
|
||||
if (parse_str(siv, name, errp) < 0) {
|
||||
switch (siv->lm) {
|
||||
case LM_NONE:
|
||||
/* just parse a simple int64, bail out if not completely consumed */
|
||||
if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
name ? name : "null", "int64");
|
||||
return;
|
||||
}
|
||||
*obj = val;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!siv->ranges) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!siv->cur_range) {
|
||||
Range *r;
|
||||
|
||||
siv->cur_range = g_list_first(siv->ranges);
|
||||
if (!siv->cur_range) {
|
||||
goto error;
|
||||
case LM_UNPARSED:
|
||||
if (try_parse_int64_list_entry(siv, obj)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"list of int64 values or ranges");
|
||||
return;
|
||||
}
|
||||
assert(siv->lm == LM_INT64_RANGE);
|
||||
/* fall through */
|
||||
case LM_INT64_RANGE:
|
||||
/* return the next element in the range */
|
||||
assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
|
||||
*obj = siv->rangeNext.i64++;
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
goto error;
|
||||
if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
|
||||
/* end of range, check if there is more to parse */
|
||||
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
|
||||
}
|
||||
return;
|
||||
case LM_END:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
siv->cur = range_lob(r);
|
||||
static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
|
||||
{
|
||||
const char *endptr;
|
||||
uint64_t start, end;
|
||||
|
||||
/* parse a simple uint64 or range */
|
||||
if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
end = start;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
case '-':
|
||||
/* parse the end of the range */
|
||||
if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*obj = siv->cur;
|
||||
siv->cur++;
|
||||
return;
|
||||
|
||||
error:
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"an int64 value or range");
|
||||
/* we have a proper range (with maybe only one element) */
|
||||
siv->lm = LM_UINT64_RANGE;
|
||||
siv->rangeNext.u64 = start;
|
||||
siv->rangeEnd.u64 = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
/* FIXME: parse_type_int64 mishandles values over INT64_MAX */
|
||||
int64_t i;
|
||||
Error *err = NULL;
|
||||
parse_type_int64(v, name, &i, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
} else {
|
||||
*obj = i;
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
uint64_t val;
|
||||
|
||||
switch (siv->lm) {
|
||||
case LM_NONE:
|
||||
/* just parse a simple uint64, bail out if not completely consumed */
|
||||
if (qemu_strtou64(siv->string, NULL, 0, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"uint64");
|
||||
return;
|
||||
}
|
||||
*obj = val;
|
||||
return;
|
||||
case LM_UNPARSED:
|
||||
if (try_parse_uint64_list_entry(siv, obj)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"list of uint64 values or ranges");
|
||||
return;
|
||||
}
|
||||
assert(siv->lm == LM_UINT64_RANGE);
|
||||
/* fall through */
|
||||
case LM_UINT64_RANGE:
|
||||
/* return the next element in the range */
|
||||
assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
|
||||
*obj = siv->rangeNext.u64++;
|
||||
|
||||
if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
|
||||
/* end of range, check if there is more to parse */
|
||||
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
|
||||
}
|
||||
return;
|
||||
case LM_END:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +318,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
|
||||
Error *err = NULL;
|
||||
uint64_t val;
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
parse_option_size(name, siv->string, &val, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
@ -284,6 +333,7 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj,
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
if (!strcasecmp(siv->string, "on") ||
|
||||
!strcasecmp(siv->string, "yes") ||
|
||||
!strcasecmp(siv->string, "true")) {
|
||||
@ -306,6 +356,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
*obj = g_strdup(siv->string);
|
||||
}
|
||||
|
||||
@ -313,12 +364,10 @@ static void parse_type_number(Visitor *v, const char *name, double *obj,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
char *endp = (char *) siv->string;
|
||||
double val;
|
||||
|
||||
errno = 0;
|
||||
val = strtod(siv->string, &endp);
|
||||
if (errno || endp == siv->string || *endp) {
|
||||
assert(siv->lm == LM_NONE);
|
||||
if (qemu_strtod_finite(siv->string, NULL, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"number");
|
||||
return;
|
||||
@ -332,9 +381,10 @@ static void parse_type_null(Visitor *v, const char *name, QNull **obj,
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
*obj = NULL;
|
||||
|
||||
if (!siv->string || siv->string[0]) {
|
||||
if (siv->string[0]) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"null");
|
||||
return;
|
||||
@ -347,8 +397,6 @@ static void string_input_free(Visitor *v)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
g_list_foreach(siv->ranges, free_range, NULL);
|
||||
g_list_free(siv->ranges);
|
||||
g_free(siv);
|
||||
}
|
||||
|
||||
@ -374,5 +422,6 @@ Visitor *string_input_visitor_new(const char *str)
|
||||
v->visitor.free = string_input_free;
|
||||
|
||||
v->string = str;
|
||||
v->lm = LM_NONE;
|
||||
return &v->visitor;
|
||||
}
|
||||
|
@ -76,8 +76,9 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
|
||||
'*cancel-path' : 'str'} }
|
||||
{ 'struct': 'TPMPassthroughOptions',
|
||||
'data': { '*path': 'str',
|
||||
'*cancel-path': 'str' } }
|
||||
|
||||
##
|
||||
# @TPMEmulatorOptions:
|
||||
|
@ -598,7 +598,8 @@
|
||||
# Notes: An empty password in this command will set the password to the empty
|
||||
# string. Existing clients are unaffected by executing this command.
|
||||
##
|
||||
{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
|
||||
{ 'command': 'change-vnc-password',
|
||||
'data': { 'password': 'str' },
|
||||
'if': 'defined(CONFIG_VNC)' }
|
||||
|
||||
##
|
||||
|
@ -288,6 +288,11 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qdict_haskey(dict, qstring_get_str(key))) {
|
||||
parse_error(ctxt, token, "duplicate key");
|
||||
goto out;
|
||||
}
|
||||
|
||||
qdict_put_obj(dict, qstring_get_str(key), value);
|
||||
|
||||
qobject_unref(key);
|
||||
|
@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr):
|
||||
if not base_members:
|
||||
return None
|
||||
|
||||
discriminator_type = base_members.get(discriminator)
|
||||
if not discriminator_type:
|
||||
discriminator_value = base_members.get(discriminator)
|
||||
if not discriminator_value:
|
||||
return None
|
||||
|
||||
return enum_types.get(discriminator_type)
|
||||
return enum_types.get(discriminator_value['type'])
|
||||
|
||||
|
||||
# Names must be letters, numbers, -, and _. They must start with letter,
|
||||
@ -704,8 +704,10 @@ def check_type(info, source, value, allow_array=False,
|
||||
% (source, key))
|
||||
# Todo: allow dictionaries to represent default values of
|
||||
# an optional argument.
|
||||
check_type(info, "Member '%s' of %s" % (key, source), arg,
|
||||
allow_array=True,
|
||||
check_known_keys(info, "member '%s' of %s" % (key, source),
|
||||
arg, ['type'], ['if'])
|
||||
check_type(info, "Member '%s' of %s" % (key, source),
|
||||
arg['type'], allow_array=True,
|
||||
allow_metas=['built-in', 'union', 'alternate', 'struct',
|
||||
'enum'])
|
||||
|
||||
@ -740,6 +742,10 @@ def check_event(expr, info):
|
||||
allow_metas=meta)
|
||||
|
||||
|
||||
def enum_get_names(expr):
|
||||
return [e['name'] for e in expr['data']]
|
||||
|
||||
|
||||
def check_union(expr, info):
|
||||
name = expr['union']
|
||||
base = expr.get('base')
|
||||
@ -772,13 +778,17 @@ def check_union(expr, info):
|
||||
# member of the base struct.
|
||||
check_name(info, "Discriminator of flat union '%s'" % name,
|
||||
discriminator)
|
||||
discriminator_type = base_members.get(discriminator)
|
||||
if not discriminator_type:
|
||||
discriminator_value = base_members.get(discriminator)
|
||||
if not discriminator_value:
|
||||
raise QAPISemError(info,
|
||||
"Discriminator '%s' is not a member of base "
|
||||
"struct '%s'"
|
||||
% (discriminator, base))
|
||||
enum_define = enum_types.get(discriminator_type)
|
||||
if discriminator_value.get('if'):
|
||||
raise QAPISemError(info, 'The discriminator %s.%s for union %s '
|
||||
'must not be conditional' %
|
||||
(base, discriminator, name))
|
||||
enum_define = enum_types.get(discriminator_value['type'])
|
||||
allow_metas = ['struct']
|
||||
# Do not allow string discriminator
|
||||
if not enum_define:
|
||||
@ -792,14 +802,17 @@ def check_union(expr, info):
|
||||
for (key, value) in members.items():
|
||||
check_name(info, "Member of union '%s'" % name, key)
|
||||
|
||||
check_known_keys(info, "member '%s' of union '%s'" % (key, name),
|
||||
value, ['type'], ['if'])
|
||||
# Each value must name a known type
|
||||
check_type(info, "Member '%s' of union '%s'" % (key, name),
|
||||
value, allow_array=not base, allow_metas=allow_metas)
|
||||
value['type'],
|
||||
allow_array=not base, allow_metas=allow_metas)
|
||||
|
||||
# If the discriminator names an enum type, then all members
|
||||
# of 'data' must also be members of the enum type.
|
||||
if enum_define:
|
||||
if key not in enum_define['data']:
|
||||
if key not in enum_get_names(enum_define):
|
||||
raise QAPISemError(info,
|
||||
"Discriminator value '%s' is not found in "
|
||||
"enum '%s'"
|
||||
@ -818,20 +831,23 @@ def check_alternate(expr, info):
|
||||
"in 'data'" % name)
|
||||
for (key, value) in members.items():
|
||||
check_name(info, "Member of alternate '%s'" % name, key)
|
||||
check_known_keys(info,
|
||||
"member '%s' of alternate '%s'" % (key, name),
|
||||
value, ['type'], ['if'])
|
||||
typ = value['type']
|
||||
|
||||
# Ensure alternates have no type conflicts.
|
||||
check_type(info, "Member '%s' of alternate '%s'" % (key, name),
|
||||
value,
|
||||
check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
|
||||
allow_metas=['built-in', 'union', 'struct', 'enum'])
|
||||
qtype = find_alternate_member_qtype(value)
|
||||
qtype = find_alternate_member_qtype(typ)
|
||||
if not qtype:
|
||||
raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
|
||||
"type '%s'" % (name, key, value))
|
||||
"type '%s'" % (name, key, typ))
|
||||
conflicting = set([qtype])
|
||||
if qtype == 'QTYPE_QSTRING':
|
||||
enum_expr = enum_types.get(value)
|
||||
enum_expr = enum_types.get(typ)
|
||||
if enum_expr:
|
||||
for v in enum_expr['data']:
|
||||
for v in enum_get_names(enum_expr):
|
||||
if v in ['on', 'off']:
|
||||
conflicting.add('QTYPE_QBOOL')
|
||||
if re.match(r'[-+0-9.]', v): # lazy, could be tightened
|
||||
@ -849,7 +865,7 @@ def check_alternate(expr, info):
|
||||
|
||||
def check_enum(expr, info):
|
||||
name = expr['enum']
|
||||
members = expr.get('data')
|
||||
members = expr['data']
|
||||
prefix = expr.get('prefix')
|
||||
|
||||
if not isinstance(members, list):
|
||||
@ -858,8 +874,12 @@ def check_enum(expr, info):
|
||||
if prefix is not None and not isinstance(prefix, str):
|
||||
raise QAPISemError(info,
|
||||
"Enum '%s' requires a string for 'prefix'" % name)
|
||||
|
||||
for member in members:
|
||||
check_name(info, "Member of enum '%s'" % name, member,
|
||||
source = "dictionary member of enum '%s'" % name
|
||||
check_known_keys(info, source, member, ['name'], ['if'])
|
||||
check_if(member, info)
|
||||
check_name(info, "Member of enum '%s'" % name, member['name'],
|
||||
enum_member=True)
|
||||
|
||||
|
||||
@ -873,6 +893,24 @@ def check_struct(expr, info):
|
||||
allow_metas=['struct'])
|
||||
|
||||
|
||||
def check_known_keys(info, source, keys, required, optional):
|
||||
|
||||
def pprint(elems):
|
||||
return ', '.join("'" + e + "'" for e in sorted(elems))
|
||||
|
||||
missing = set(required) - set(keys)
|
||||
if missing:
|
||||
raise QAPISemError(info, "Key%s %s %s missing from %s"
|
||||
% ('s' if len(missing) > 1 else '', pprint(missing),
|
||||
'are' if len(missing) > 1 else 'is', source))
|
||||
allowed = set(required + optional)
|
||||
unknown = set(keys) - allowed
|
||||
if unknown:
|
||||
raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
|
||||
% ('s' if len(unknown) > 1 else '', pprint(unknown),
|
||||
source, pprint(allowed)))
|
||||
|
||||
|
||||
def check_keys(expr_elem, meta, required, optional=[]):
|
||||
expr = expr_elem['expr']
|
||||
info = expr_elem['info']
|
||||
@ -880,10 +918,9 @@ def check_keys(expr_elem, meta, required, optional=[]):
|
||||
if not isinstance(name, str):
|
||||
raise QAPISemError(info, "'%s' key must have a string value" % meta)
|
||||
required = required + [meta]
|
||||
source = "%s '%s'" % (meta, name)
|
||||
check_known_keys(info, source, expr.keys(), required, optional)
|
||||
for (key, value) in expr.items():
|
||||
if key not in required and key not in optional:
|
||||
raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
|
||||
% (key, meta, name))
|
||||
if key in ['gen', 'success-response'] and value is not False:
|
||||
raise QAPISemError(info,
|
||||
"'%s' of %s '%s' should only use false value"
|
||||
@ -895,10 +932,20 @@ def check_keys(expr_elem, meta, required, optional=[]):
|
||||
% (key, meta, name))
|
||||
if key == 'if':
|
||||
check_if(expr, info)
|
||||
for key in required:
|
||||
if key not in expr:
|
||||
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
|
||||
% (key, meta, name))
|
||||
|
||||
|
||||
def normalize_enum(expr):
|
||||
if isinstance(expr['data'], list):
|
||||
expr['data'] = [m if isinstance(m, dict) else {'name': m}
|
||||
for m in expr['data']]
|
||||
|
||||
|
||||
def normalize_members(members):
|
||||
if isinstance(members, OrderedDict):
|
||||
for key, arg in members.items():
|
||||
if isinstance(arg, dict):
|
||||
continue
|
||||
members[key] = {'type': arg}
|
||||
|
||||
|
||||
def check_exprs(exprs):
|
||||
@ -924,27 +971,34 @@ def check_exprs(exprs):
|
||||
if 'enum' in expr:
|
||||
meta = 'enum'
|
||||
check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
|
||||
normalize_enum(expr)
|
||||
enum_types[expr[meta]] = expr
|
||||
elif 'union' in expr:
|
||||
meta = 'union'
|
||||
check_keys(expr_elem, 'union', ['data'],
|
||||
['base', 'discriminator', 'if'])
|
||||
normalize_members(expr.get('base'))
|
||||
normalize_members(expr['data'])
|
||||
union_types[expr[meta]] = expr
|
||||
elif 'alternate' in expr:
|
||||
meta = 'alternate'
|
||||
check_keys(expr_elem, 'alternate', ['data'], ['if'])
|
||||
normalize_members(expr['data'])
|
||||
elif 'struct' in expr:
|
||||
meta = 'struct'
|
||||
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
|
||||
normalize_members(expr['data'])
|
||||
struct_types[expr[meta]] = expr
|
||||
elif 'command' in expr:
|
||||
meta = 'command'
|
||||
check_keys(expr_elem, 'command', [],
|
||||
['data', 'returns', 'gen', 'success-response',
|
||||
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
|
||||
normalize_members(expr.get('data'))
|
||||
elif 'event' in expr:
|
||||
meta = 'event'
|
||||
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
|
||||
normalize_members(expr.get('data'))
|
||||
else:
|
||||
raise QAPISemError(expr_elem['info'],
|
||||
"Expression is missing metatype")
|
||||
@ -1063,7 +1117,7 @@ class QAPISchemaVisitor(object):
|
||||
def visit_builtin_type(self, name, info, json_type):
|
||||
pass
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
pass
|
||||
|
||||
def visit_array_type(self, name, info, ifcond, element_type):
|
||||
@ -1161,22 +1215,22 @@ class QAPISchemaBuiltinType(QAPISchemaType):
|
||||
|
||||
|
||||
class QAPISchemaEnumType(QAPISchemaType):
|
||||
def __init__(self, name, info, doc, ifcond, values, prefix):
|
||||
def __init__(self, name, info, doc, ifcond, members, prefix):
|
||||
QAPISchemaType.__init__(self, name, info, doc, ifcond)
|
||||
for v in values:
|
||||
assert isinstance(v, QAPISchemaMember)
|
||||
v.set_owner(name)
|
||||
for m in members:
|
||||
assert isinstance(m, QAPISchemaMember)
|
||||
m.set_owner(name)
|
||||
assert prefix is None or isinstance(prefix, str)
|
||||
self.values = values
|
||||
self.members = members
|
||||
self.prefix = prefix
|
||||
|
||||
def check(self, schema):
|
||||
QAPISchemaType.check(self, schema)
|
||||
seen = {}
|
||||
for v in self.values:
|
||||
v.check_clash(self.info, seen)
|
||||
for m in self.members:
|
||||
m.check_clash(self.info, seen)
|
||||
if self.doc:
|
||||
self.doc.connect_member(v)
|
||||
self.doc.connect_member(m)
|
||||
|
||||
def is_implicit(self):
|
||||
# See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
|
||||
@ -1186,14 +1240,14 @@ class QAPISchemaEnumType(QAPISchemaType):
|
||||
return c_name(self.name)
|
||||
|
||||
def member_names(self):
|
||||
return [v.name for v in self.values]
|
||||
return [m.name for m in self.members]
|
||||
|
||||
def json_type(self):
|
||||
return 'string'
|
||||
|
||||
def visit(self, visitor):
|
||||
visitor.visit_enum_type(self.name, self.info, self.ifcond,
|
||||
self.member_names(), self.prefix)
|
||||
self.members, self.prefix)
|
||||
|
||||
|
||||
class QAPISchemaArrayType(QAPISchemaType):
|
||||
@ -1318,9 +1372,10 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||
class QAPISchemaMember(object):
|
||||
role = 'member'
|
||||
|
||||
def __init__(self, name):
|
||||
def __init__(self, name, ifcond=None):
|
||||
assert isinstance(name, str)
|
||||
self.name = name
|
||||
self.ifcond = listify_cond(ifcond)
|
||||
self.owner = None
|
||||
|
||||
def set_owner(self, name):
|
||||
@ -1361,8 +1416,8 @@ class QAPISchemaMember(object):
|
||||
|
||||
|
||||
class QAPISchemaObjectTypeMember(QAPISchemaMember):
|
||||
def __init__(self, name, typ, optional):
|
||||
QAPISchemaMember.__init__(self, name)
|
||||
def __init__(self, name, typ, optional, ifcond=None):
|
||||
QAPISchemaMember.__init__(self, name, ifcond)
|
||||
assert isinstance(typ, str)
|
||||
assert isinstance(optional, bool)
|
||||
self._type_name = typ
|
||||
@ -1403,9 +1458,9 @@ class QAPISchemaObjectTypeVariants(object):
|
||||
if self._tag_name: # flat union
|
||||
# branches that are not explicitly covered get an empty type
|
||||
cases = set([v.name for v in self.variants])
|
||||
for val in self.tag_member.type.values:
|
||||
if val.name not in cases:
|
||||
v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
|
||||
for m in self.tag_member.type.members:
|
||||
if m.name not in cases:
|
||||
v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
|
||||
v.set_owner(self.tag_member.owner)
|
||||
self.variants.append(v)
|
||||
for v in self.variants:
|
||||
@ -1428,8 +1483,8 @@ class QAPISchemaObjectTypeVariants(object):
|
||||
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
|
||||
role = 'branch'
|
||||
|
||||
def __init__(self, name, typ):
|
||||
QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
|
||||
def __init__(self, name, typ, ifcond=None):
|
||||
QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
|
||||
|
||||
|
||||
class QAPISchemaAlternateType(QAPISchemaType):
|
||||
@ -1620,14 +1675,16 @@ class QAPISchema(object):
|
||||
self.the_empty_object_type = QAPISchemaObjectType(
|
||||
'q_empty', None, None, None, None, [], None)
|
||||
self._def_entity(self.the_empty_object_type)
|
||||
qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
|
||||
'qstring', 'qdict', 'qlist',
|
||||
'qbool'])
|
||||
|
||||
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
|
||||
'qbool']
|
||||
qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
|
||||
|
||||
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
|
||||
qtype_values, 'QTYPE'))
|
||||
|
||||
def _make_enum_members(self, values):
|
||||
return [QAPISchemaMember(v) for v in values]
|
||||
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
|
||||
|
||||
def _make_implicit_enum_type(self, name, info, ifcond, values):
|
||||
# See also QAPISchemaObjectTypeMember._pretty_owner()
|
||||
@ -1674,7 +1731,7 @@ class QAPISchema(object):
|
||||
name, info, doc, ifcond,
|
||||
self._make_enum_members(data), prefix))
|
||||
|
||||
def _make_member(self, name, typ, info):
|
||||
def _make_member(self, name, typ, ifcond, info):
|
||||
optional = False
|
||||
if name.startswith('*'):
|
||||
name = name[1:]
|
||||
@ -1682,10 +1739,10 @@ class QAPISchema(object):
|
||||
if isinstance(typ, list):
|
||||
assert len(typ) == 1
|
||||
typ = self._make_array_type(typ[0], info)
|
||||
return QAPISchemaObjectTypeMember(name, typ, optional)
|
||||
return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
|
||||
|
||||
def _make_members(self, data, info):
|
||||
return [self._make_member(key, value, info)
|
||||
return [self._make_member(key, value['type'], value.get('if'), info)
|
||||
for (key, value) in data.items()]
|
||||
|
||||
def _def_struct_type(self, expr, info, doc):
|
||||
@ -1697,17 +1754,17 @@ class QAPISchema(object):
|
||||
self._make_members(data, info),
|
||||
None))
|
||||
|
||||
def _make_variant(self, case, typ):
|
||||
return QAPISchemaObjectTypeVariant(case, typ)
|
||||
def _make_variant(self, case, typ, ifcond):
|
||||
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
|
||||
|
||||
def _make_simple_variant(self, case, typ, info):
|
||||
def _make_simple_variant(self, case, typ, ifcond, info):
|
||||
if isinstance(typ, list):
|
||||
assert len(typ) == 1
|
||||
typ = self._make_array_type(typ[0], info)
|
||||
typ = self._make_implicit_object_type(
|
||||
typ, info, None, self.lookup_type(typ),
|
||||
'wrapper', [self._make_member('data', typ, info)])
|
||||
return QAPISchemaObjectTypeVariant(case, typ)
|
||||
'wrapper', [self._make_member('data', typ, None, info)])
|
||||
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
|
||||
|
||||
def _def_union_type(self, expr, info, doc):
|
||||
name = expr['union']
|
||||
@ -1721,14 +1778,15 @@ class QAPISchema(object):
|
||||
name, info, doc, ifcond,
|
||||
'base', self._make_members(base, info))
|
||||
if tag_name:
|
||||
variants = [self._make_variant(key, value)
|
||||
variants = [self._make_variant(key, value['type'], value.get('if'))
|
||||
for (key, value) in data.items()]
|
||||
members = []
|
||||
else:
|
||||
variants = [self._make_simple_variant(key, value, info)
|
||||
variants = [self._make_simple_variant(key, value['type'],
|
||||
value.get('if'), info)
|
||||
for (key, value) in data.items()]
|
||||
typ = self._make_implicit_enum_type(name, info, ifcond,
|
||||
[v.name for v in variants])
|
||||
enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
|
||||
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
|
||||
tag_member = QAPISchemaObjectTypeMember('type', typ, False)
|
||||
members = [tag_member]
|
||||
self._def_entity(
|
||||
@ -1741,7 +1799,7 @@ class QAPISchema(object):
|
||||
name = expr['alternate']
|
||||
data = expr['data']
|
||||
ifcond = expr.get('if')
|
||||
variants = [self._make_variant(key, value)
|
||||
variants = [self._make_variant(key, value['type'], value.get('if'))
|
||||
for (key, value) in data.items()]
|
||||
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
|
||||
self._def_entity(
|
||||
@ -2012,19 +2070,21 @@ def _wrap_ifcond(ifcond, before, after):
|
||||
return out
|
||||
|
||||
|
||||
def gen_enum_lookup(name, values, prefix=None):
|
||||
def gen_enum_lookup(name, members, prefix=None):
|
||||
ret = mcgen('''
|
||||
|
||||
const QEnumLookup %(c_name)s_lookup = {
|
||||
.array = (const char *const[]) {
|
||||
''',
|
||||
c_name=c_name(name))
|
||||
for value in values:
|
||||
index = c_enum_const(name, value, prefix)
|
||||
for m in members:
|
||||
ret += gen_if(m.ifcond)
|
||||
index = c_enum_const(name, m.name, prefix)
|
||||
ret += mcgen('''
|
||||
[%(index)s] = "%(value)s",
|
||||
[%(index)s] = "%(name)s",
|
||||
''',
|
||||
index=index, value=value)
|
||||
index=index, name=m.name)
|
||||
ret += gen_endif(m.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
},
|
||||
@ -2035,9 +2095,9 @@ const QEnumLookup %(c_name)s_lookup = {
|
||||
return ret
|
||||
|
||||
|
||||
def gen_enum(name, values, prefix=None):
|
||||
def gen_enum(name, members, prefix=None):
|
||||
# append automatically generated _MAX value
|
||||
enum_values = values + ['_MAX']
|
||||
enum_members = members + [QAPISchemaMember('_MAX')]
|
||||
|
||||
ret = mcgen('''
|
||||
|
||||
@ -2045,14 +2105,13 @@ typedef enum %(c_name)s {
|
||||
''',
|
||||
c_name=c_name(name))
|
||||
|
||||
i = 0
|
||||
for value in enum_values:
|
||||
for m in enum_members:
|
||||
ret += gen_if(m.ifcond)
|
||||
ret += mcgen('''
|
||||
%(c_enum)s = %(i)d,
|
||||
%(c_enum)s,
|
||||
''',
|
||||
c_enum=c_enum_const(name, value, prefix),
|
||||
i=i)
|
||||
i += 1
|
||||
c_enum=c_enum_const(name, m.name, prefix))
|
||||
ret += gen_endif(m.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
} %(c_name)s;
|
||||
|
@ -126,19 +126,27 @@ def texi_body(doc):
|
||||
return texi_format(doc.body.text)
|
||||
|
||||
|
||||
def texi_enum_value(value):
|
||||
def texi_if(ifcond, prefix='\n', suffix='\n'):
|
||||
"""Format the #if condition"""
|
||||
if not ifcond:
|
||||
return ''
|
||||
return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
|
||||
|
||||
|
||||
def texi_enum_value(value, desc, suffix):
|
||||
"""Format a table of members item for an enumeration value"""
|
||||
return '@item @code{%s}\n' % value.name
|
||||
return '@item @code{%s}\n%s%s' % (
|
||||
value.name, desc, texi_if(value.ifcond, prefix='@*'))
|
||||
|
||||
|
||||
def texi_member(member, suffix=''):
|
||||
def texi_member(member, desc, suffix):
|
||||
"""Format a table of members item for an object type member"""
|
||||
typ = member.type.doc_type()
|
||||
membertype = ': ' + typ if typ else ''
|
||||
return '@item @code{%s%s}%s%s\n' % (
|
||||
return '@item @code{%s%s}%s%s\n%s%s' % (
|
||||
member.name, membertype,
|
||||
' (optional)' if member.optional else '',
|
||||
suffix)
|
||||
suffix, desc, texi_if(member.ifcond, prefix='@*'))
|
||||
|
||||
|
||||
def texi_members(doc, what, base, variants, member_func):
|
||||
@ -155,17 +163,17 @@ def texi_members(doc, what, base, variants, member_func):
|
||||
desc = 'One of ' + members_text + '\n'
|
||||
else:
|
||||
desc = 'Not documented\n'
|
||||
items += member_func(section.member) + desc
|
||||
items += member_func(section.member, desc, suffix='')
|
||||
if base:
|
||||
items += '@item The members of @code{%s}\n' % base.doc_type()
|
||||
if variants:
|
||||
for v in variants.variants:
|
||||
when = ' when @code{%s} is @t{"%s"}' % (
|
||||
variants.tag_member.name, v.name)
|
||||
when = ' when @code{%s} is @t{"%s"}%s' % (
|
||||
variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")"))
|
||||
if v.type.is_implicit():
|
||||
assert not v.type.base and not v.type.variants
|
||||
for m in v.type.local_members:
|
||||
items += member_func(m, when)
|
||||
items += member_func(m, desc='', suffix=when)
|
||||
else:
|
||||
items += '@item The members of @code{%s}%s\n' % (
|
||||
v.type.doc_type(), when)
|
||||
@ -185,8 +193,7 @@ def texi_sections(doc, ifcond):
|
||||
body += texi_example(section.text)
|
||||
else:
|
||||
body += texi_format(section.text)
|
||||
if ifcond:
|
||||
body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
|
||||
body += texi_if(ifcond, suffix='')
|
||||
return body
|
||||
|
||||
|
||||
@ -206,7 +213,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
|
||||
def write(self, output_dir):
|
||||
self._gen.write(output_dir, self._prefix + 'qapi-doc.texi')
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
doc = self.cur_doc
|
||||
self._gen.add(TYPE_FMT(type='Enum',
|
||||
name=doc.symbol,
|
||||
|
@ -143,8 +143,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
||||
QAPISchemaModularCVisitor.__init__(
|
||||
self, prefix, 'qapi-events',
|
||||
' * Schema-defined QAPI/QMP events', __doc__)
|
||||
self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
|
||||
self._event_names = []
|
||||
self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
|
||||
self._event_enum_members = []
|
||||
|
||||
def _begin_module(self, name):
|
||||
types = self._module_basename('qapi-types', name)
|
||||
@ -170,15 +170,16 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
|
||||
|
||||
def visit_end(self):
|
||||
(genc, genh) = self._module[self._main_module]
|
||||
genh.add(gen_enum(self._enum_name, self._event_names))
|
||||
genc.add(gen_enum_lookup(self._enum_name, self._event_names))
|
||||
genh.add(gen_enum(self._event_enum_name, self._event_enum_members))
|
||||
genc.add(gen_enum_lookup(self._event_enum_name,
|
||||
self._event_enum_members))
|
||||
|
||||
def visit_event(self, name, info, ifcond, arg_type, boxed):
|
||||
with ifcontext(ifcond, self._genh, self._genc):
|
||||
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
|
||||
self._genc.add(gen_event_send(name, arg_type, boxed,
|
||||
self._enum_name))
|
||||
self._event_names.append(name)
|
||||
self._event_enum_name))
|
||||
self._event_enum_members.append(QAPISchemaMember(name, ifcond))
|
||||
|
||||
|
||||
def gen_events(schema, output_dir, prefix):
|
||||
|
@ -162,6 +162,8 @@ const QLitObject %(c_name)s = %(c_string)s;
|
||||
ret = {'name': member.name, 'type': self._use_type(member.type)}
|
||||
if member.optional:
|
||||
ret['default'] = None
|
||||
if member.ifcond:
|
||||
ret = (ret, {'if': member.ifcond})
|
||||
return ret
|
||||
|
||||
def _gen_variants(self, tag_name, variants):
|
||||
@ -169,13 +171,17 @@ const QLitObject %(c_name)s = %(c_string)s;
|
||||
'variants': [self._gen_variant(v) for v in variants]}
|
||||
|
||||
def _gen_variant(self, variant):
|
||||
return {'case': variant.name, 'type': self._use_type(variant.type)}
|
||||
return ({'case': variant.name, 'type': self._use_type(variant.type)},
|
||||
{'if': variant.ifcond})
|
||||
|
||||
def visit_builtin_type(self, name, info, json_type):
|
||||
self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
self._gen_qlit(name, 'enum', {'values': values}, ifcond)
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
self._gen_qlit(name, 'enum',
|
||||
{'values':
|
||||
[(m.name, {'if': m.ifcond}) for m in members]},
|
||||
ifcond)
|
||||
|
||||
def visit_array_type(self, name, info, ifcond, element_type):
|
||||
element = self._use_type(element_type)
|
||||
@ -191,8 +197,9 @@ const QLitObject %(c_name)s = %(c_string)s;
|
||||
|
||||
def visit_alternate_type(self, name, info, ifcond, variants):
|
||||
self._gen_qlit(name, 'alternate',
|
||||
{'members': [{'type': self._use_type(m.type)}
|
||||
for m in variants.variants]}, ifcond)
|
||||
{'members': [
|
||||
({'type': self._use_type(m.type)}, {'if': m.ifcond})
|
||||
for m in variants.variants]}, ifcond)
|
||||
|
||||
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
|
||||
success_response, boxed, allow_oob, allow_preconfig):
|
||||
|
@ -43,6 +43,7 @@ struct %(c_name)s {
|
||||
def gen_struct_members(members):
|
||||
ret = ''
|
||||
for memb in members:
|
||||
ret += gen_if(memb.ifcond)
|
||||
if memb.optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(c_name)s;
|
||||
@ -52,6 +53,7 @@ def gen_struct_members(members):
|
||||
%(c_type)s %(c_name)s;
|
||||
''',
|
||||
c_type=memb.type.c_type(), c_name=c_name(memb.name))
|
||||
ret += gen_endif(memb.ifcond)
|
||||
return ret
|
||||
|
||||
|
||||
@ -131,11 +133,13 @@ def gen_variants(variants):
|
||||
for var in variants.variants:
|
||||
if var.type.name == 'q_empty':
|
||||
continue
|
||||
ret += gen_if(var.ifcond)
|
||||
ret += mcgen('''
|
||||
%(c_type)s %(c_name)s;
|
||||
''',
|
||||
c_type=var.type.c_unboxed_type(),
|
||||
c_name=c_name(var.name))
|
||||
ret += gen_endif(var.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
} u;
|
||||
@ -212,10 +216,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
|
||||
self._genh.add(gen_type_cleanup_decl(name))
|
||||
self._genc.add(gen_type_cleanup(name))
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
with ifcontext(ifcond, self._genh, self._genc):
|
||||
self._genh.preamble_add(gen_enum(name, values, prefix))
|
||||
self._genc.add(gen_enum_lookup(name, values, prefix))
|
||||
self._genh.preamble_add(gen_enum(name, members, prefix))
|
||||
self._genc.add(gen_enum_lookup(name, members, prefix))
|
||||
|
||||
def visit_array_type(self, name, info, ifcond, element_type):
|
||||
with ifcontext(ifcond, self._genh, self._genc):
|
||||
|
@ -54,6 +54,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||
c_type=base.c_name())
|
||||
|
||||
for memb in members:
|
||||
ret += gen_if(memb.ifcond)
|
||||
if memb.optional:
|
||||
ret += mcgen('''
|
||||
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
|
||||
@ -73,6 +74,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||
ret += mcgen('''
|
||||
}
|
||||
''')
|
||||
ret += gen_endif(memb.ifcond)
|
||||
|
||||
if variants:
|
||||
ret += mcgen('''
|
||||
@ -84,6 +86,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||
case_str = c_enum_const(variants.tag_member.type.name,
|
||||
var.name,
|
||||
variants.tag_member.type.prefix)
|
||||
ret += gen_if(var.ifcond)
|
||||
if var.type.name == 'q_empty':
|
||||
# valid variant and nothing to do
|
||||
ret += mcgen('''
|
||||
@ -100,6 +103,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||
case=case_str,
|
||||
c_type=var.type.c_name(), c_name=c_name(var.name))
|
||||
|
||||
ret += gen_endif(var.ifcond)
|
||||
ret += mcgen('''
|
||||
default:
|
||||
abort();
|
||||
@ -190,6 +194,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
|
||||
c_name=c_name(name))
|
||||
|
||||
for var in variants.variants:
|
||||
ret += gen_if(var.ifcond)
|
||||
ret += mcgen('''
|
||||
case %(case)s:
|
||||
''',
|
||||
@ -217,6 +222,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
|
||||
ret += mcgen('''
|
||||
break;
|
||||
''')
|
||||
ret += gen_endif(var.ifcond)
|
||||
|
||||
ret += mcgen('''
|
||||
case QTYPE_NONE:
|
||||
@ -310,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
|
||||
''',
|
||||
types=types))
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
with ifcontext(ifcond, self._genh, self._genc):
|
||||
self._genh.add(gen_visit_decl(name, scalar=True))
|
||||
self._genc.add(gen_visit_enum(name))
|
||||
|
@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
|
||||
qapi-schema += alternate-conflict-bool-string.json
|
||||
qapi-schema += alternate-conflict-num-string.json
|
||||
qapi-schema += alternate-empty.json
|
||||
qapi-schema += alternate-invalid-dict.json
|
||||
qapi-schema += alternate-nested.json
|
||||
qapi-schema += alternate-unknown.json
|
||||
qapi-schema += args-alternate.json
|
||||
@ -379,10 +380,12 @@ qapi-schema += double-data.json
|
||||
qapi-schema += double-type.json
|
||||
qapi-schema += duplicate-key.json
|
||||
qapi-schema += empty.json
|
||||
qapi-schema += enum-bad-member.json
|
||||
qapi-schema += enum-bad-name.json
|
||||
qapi-schema += enum-bad-prefix.json
|
||||
qapi-schema += enum-clash-member.json
|
||||
qapi-schema += enum-dict-member.json
|
||||
qapi-schema += enum-dict-member-unknown.json
|
||||
qapi-schema += enum-if-invalid.json
|
||||
qapi-schema += enum-int-member.json
|
||||
qapi-schema += enum-member-case.json
|
||||
qapi-schema += enum-missing-data.json
|
||||
@ -392,6 +395,7 @@ qapi-schema += escape-too-big.json
|
||||
qapi-schema += escape-too-short.json
|
||||
qapi-schema += event-boxed-empty.json
|
||||
qapi-schema += event-case.json
|
||||
qapi-schema += event-member-invalid-dict.json
|
||||
qapi-schema += event-nest-struct.json
|
||||
qapi-schema += flat-union-array-branch.json
|
||||
qapi-schema += flat-union-bad-base.json
|
||||
@ -401,9 +405,11 @@ qapi-schema += flat-union-base-union.json
|
||||
qapi-schema += flat-union-clash-member.json
|
||||
qapi-schema += flat-union-empty.json
|
||||
qapi-schema += flat-union-inline.json
|
||||
qapi-schema += flat-union-inline-invalid-dict.json
|
||||
qapi-schema += flat-union-int-branch.json
|
||||
qapi-schema += flat-union-invalid-branch-key.json
|
||||
qapi-schema += flat-union-invalid-discriminator.json
|
||||
qapi-schema += flat-union-invalid-if-discriminator.json
|
||||
qapi-schema += flat-union-no-base.json
|
||||
qapi-schema += flat-union-optional-discriminator.json
|
||||
qapi-schema += flat-union-string-discriminator.json
|
||||
@ -428,6 +434,7 @@ qapi-schema += missing-comma-list.json
|
||||
qapi-schema += missing-comma-object.json
|
||||
qapi-schema += missing-type.json
|
||||
qapi-schema += nested-struct-data.json
|
||||
qapi-schema += nested-struct-data-invalid-dict.json
|
||||
qapi-schema += non-objects.json
|
||||
qapi-schema += oob-test.json
|
||||
qapi-schema += allow-preconfig-test.json
|
||||
@ -458,6 +465,7 @@ qapi-schema += returns-whitelist.json
|
||||
qapi-schema += struct-base-clash-deep.json
|
||||
qapi-schema += struct-base-clash.json
|
||||
qapi-schema += struct-data-invalid.json
|
||||
qapi-schema += struct-member-invalid-dict.json
|
||||
qapi-schema += struct-member-invalid.json
|
||||
qapi-schema += trailing-comma-list.json
|
||||
qapi-schema += trailing-comma-object.json
|
||||
@ -469,6 +477,7 @@ qapi-schema += unicode-str.json
|
||||
qapi-schema += union-base-empty.json
|
||||
qapi-schema += union-base-no-discriminator.json
|
||||
qapi-schema += union-branch-case.json
|
||||
qapi-schema += union-branch-invalid-dict.json
|
||||
qapi-schema += union-clash-branches.json
|
||||
qapi-schema += union-empty.json
|
||||
qapi-schema += union-invalid-base.json
|
||||
|
@ -1 +1,2 @@
|
||||
tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
|
||||
Valid keys are 'alternate', 'data', 'if'.
|
||||
|
1
tests/qapi-schema/alternate-invalid-dict.err
Normal file
1
tests/qapi-schema/alternate-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
|
4
tests/qapi-schema/alternate-invalid-dict.json
Normal file
4
tests/qapi-schema/alternate-invalid-dict.json
Normal file
@ -0,0 +1,4 @@
|
||||
# exploded member form must have a 'type'
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'str',
|
||||
'two': { 'if': 'foo' } } }
|
@ -1,5 +1,15 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module comments.json
|
||||
enum Status ['good', 'bad', 'ugly']
|
||||
enum Status
|
||||
member good
|
||||
member bad
|
||||
member ugly
|
||||
|
@ -1,8 +1,17 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module doc-bad-section.json
|
||||
enum Enum ['one', 'two']
|
||||
enum Enum
|
||||
member one
|
||||
member two
|
||||
doc symbol=Enum
|
||||
body=
|
||||
== Produces *invalid* texinfo
|
||||
|
@ -55,7 +55,9 @@
|
||||
#
|
||||
# @two is undocumented
|
||||
##
|
||||
{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
|
||||
{ 'enum': 'Enum', 'data':
|
||||
[ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ],
|
||||
'if': 'defined(IFCOND)' }
|
||||
|
||||
##
|
||||
# @Base:
|
||||
@ -70,7 +72,8 @@
|
||||
#
|
||||
# Another paragraph (but no @var: line)
|
||||
##
|
||||
{ 'struct': 'Variant1', 'data': { 'var1': 'str' } }
|
||||
{ 'struct': 'Variant1',
|
||||
'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } }
|
||||
|
||||
##
|
||||
# @Variant2:
|
||||
@ -83,13 +86,13 @@
|
||||
{ 'union': 'Object',
|
||||
'base': 'Base',
|
||||
'discriminator': 'base1',
|
||||
'data': { 'one': 'Variant1', 'two': 'Variant2' } }
|
||||
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
|
||||
|
||||
##
|
||||
# @SugaredUnion:
|
||||
##
|
||||
{ 'union': 'SugaredUnion',
|
||||
'data': { 'one': 'Variant1', 'two': 'Variant2' } }
|
||||
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
|
||||
|
||||
##
|
||||
# == Another subsection
|
||||
|
@ -1,29 +1,45 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module doc-good.json
|
||||
enum Enum ['one', 'two']
|
||||
enum Enum
|
||||
member one
|
||||
if ['defined(IFONE)']
|
||||
member two
|
||||
if ['defined(IFCOND)']
|
||||
object Base
|
||||
member base1: Enum optional=False
|
||||
object Variant1
|
||||
member var1: str optional=False
|
||||
if ['defined(IFSTR)']
|
||||
object Variant2
|
||||
object Object
|
||||
base Base
|
||||
tag base1
|
||||
case one: Variant1
|
||||
case two: Variant2
|
||||
if ['IFTWO']
|
||||
object q_obj_Variant1-wrapper
|
||||
member data: Variant1 optional=False
|
||||
object q_obj_Variant2-wrapper
|
||||
member data: Variant2 optional=False
|
||||
enum SugaredUnionKind ['one', 'two']
|
||||
enum SugaredUnionKind
|
||||
member one
|
||||
member two
|
||||
if ['IFTWO']
|
||||
object SugaredUnion
|
||||
member type: SugaredUnionKind optional=False
|
||||
tag type
|
||||
case one: q_obj_Variant1-wrapper
|
||||
case two: q_obj_Variant2-wrapper
|
||||
if ['IFTWO']
|
||||
object q_obj_cmd-arg
|
||||
member arg1: int optional=False
|
||||
member arg2: str optional=True
|
||||
|
@ -84,12 +84,12 @@ Examples:
|
||||
@table @asis
|
||||
@item @code{one}
|
||||
The @emph{one} @{and only@}
|
||||
@*@b{If:} @code{defined(IFONE)}
|
||||
@item @code{two}
|
||||
Not documented
|
||||
@end table
|
||||
@code{two} is undocumented
|
||||
|
||||
|
||||
@b{If:} @code{defined(IFCOND)}
|
||||
@end deftp
|
||||
|
||||
@ -119,6 +119,7 @@ Another paragraph (but no @code{var}: line)
|
||||
@table @asis
|
||||
@item @code{var1: string}
|
||||
Not documented
|
||||
@*@b{If:} @code{defined(IFSTR)}
|
||||
@end table
|
||||
|
||||
@end deftp
|
||||
@ -141,7 +142,7 @@ Not documented
|
||||
@table @asis
|
||||
@item The members of @code{Base}
|
||||
@item The members of @code{Variant1} when @code{base1} is @t{"one"}
|
||||
@item The members of @code{Variant2} when @code{base1} is @t{"two"}
|
||||
@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO})
|
||||
@end table
|
||||
|
||||
@end deftp
|
||||
@ -157,7 +158,7 @@ Not documented
|
||||
@item @code{type}
|
||||
One of @t{"one"}, @t{"two"}
|
||||
@item @code{data: Variant1} when @code{type} is @t{"one"}
|
||||
@item @code{data: Variant2} when @code{type} is @t{"two"}
|
||||
@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO})
|
||||
@end table
|
||||
|
||||
@end deftp
|
||||
|
@ -1 +1,2 @@
|
||||
tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
|
||||
Valid keys are 'base', 'data', 'if', 'struct'.
|
||||
|
@ -1,3 +1,10 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
|
1
tests/qapi-schema/enum-bad-member.err
Normal file
1
tests/qapi-schema/enum-bad-member.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name
|
1
tests/qapi-schema/enum-bad-member.exit
Normal file
1
tests/qapi-schema/enum-bad-member.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/enum-bad-member.json
Normal file
2
tests/qapi-schema/enum-bad-member.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject any enum member that is not a string
|
||||
{ 'enum': 'MyEnum', 'data': [ [ ] ] }
|
0
tests/qapi-schema/enum-bad-member.out
Normal file
0
tests/qapi-schema/enum-bad-member.out
Normal file
2
tests/qapi-schema/enum-dict-member-unknown.err
Normal file
2
tests/qapi-schema/enum-dict-member-unknown.err
Normal file
@ -0,0 +1,2 @@
|
||||
tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
|
||||
Valid keys are 'if', 'name'.
|
1
tests/qapi-schema/enum-dict-member-unknown.exit
Normal file
1
tests/qapi-schema/enum-dict-member-unknown.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/enum-dict-member-unknown.json
Normal file
2
tests/qapi-schema/enum-dict-member-unknown.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject any enum member that is not a string or a dict with 'name'
|
||||
{ 'enum': 'MyEnum', 'data': [ { 'name': 'foo', 'bad-key': 'str' } ] }
|
0
tests/qapi-schema/enum-dict-member-unknown.out
Normal file
0
tests/qapi-schema/enum-dict-member-unknown.out
Normal file
@ -1 +0,0 @@
|
||||
tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
|
@ -1,2 +0,0 @@
|
||||
# we reject any enum member that is not a string
|
||||
{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
|
1
tests/qapi-schema/enum-if-invalid.err
Normal file
1
tests/qapi-schema/enum-if-invalid.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings
|
1
tests/qapi-schema/enum-if-invalid.exit
Normal file
1
tests/qapi-schema/enum-if-invalid.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/enum-if-invalid.json
Normal file
3
tests/qapi-schema/enum-if-invalid.json
Normal file
@ -0,0 +1,3 @@
|
||||
# check invalid 'if' type
|
||||
{ 'enum': 'TestIfEnum', 'data':
|
||||
[ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }
|
0
tests/qapi-schema/enum-if-invalid.out
Normal file
0
tests/qapi-schema/enum-if-invalid.out
Normal file
@ -1,6 +1,13 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module event-case.json
|
||||
event oops None
|
||||
boxed=False
|
||||
|
1
tests/qapi-schema/event-member-invalid-dict.err
Normal file
1
tests/qapi-schema/event-member-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
|
1
tests/qapi-schema/event-member-invalid-dict.exit
Normal file
1
tests/qapi-schema/event-member-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/event-member-invalid-dict.json
Normal file
2
tests/qapi-schema/event-member-invalid-dict.json
Normal file
@ -0,0 +1,2 @@
|
||||
{ 'event': 'EVENT_A',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
0
tests/qapi-schema/event-member-invalid-dict.out
Normal file
0
tests/qapi-schema/event-member-invalid-dict.out
Normal file
@ -1,2 +1,2 @@
|
||||
{ 'event': 'EVENT_A',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
||||
'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }
|
||||
|
1
tests/qapi-schema/flat-union-inline-invalid-dict.err
Normal file
1
tests/qapi-schema/flat-union-inline-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
|
1
tests/qapi-schema/flat-union-inline-invalid-dict.exit
Normal file
1
tests/qapi-schema/flat-union-inline-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
11
tests/qapi-schema/flat-union-inline-invalid-dict.json
Normal file
11
tests/qapi-schema/flat-union-inline-invalid-dict.json
Normal file
@ -0,0 +1,11 @@
|
||||
# we require branches to be a struct name
|
||||
# TODO: should we allow anonymous inline branch types?
|
||||
{ 'enum': 'TestEnum',
|
||||
'data': [ 'value1', 'value2' ] }
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
|
||||
{ 'union': 'TestUnion',
|
||||
'base': 'Base',
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1': { 'string': 'str' },
|
||||
'value2': { 'integer': 'int' } } }
|
@ -7,5 +7,5 @@
|
||||
{ 'union': 'TestUnion',
|
||||
'base': 'Base',
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1': { 'string': 'str' },
|
||||
'data': { 'value1': { 'type': {} },
|
||||
'value2': { 'integer': 'int' } } }
|
||||
|
@ -0,0 +1 @@
|
||||
tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional
|
@ -0,0 +1 @@
|
||||
1
|
17
tests/qapi-schema/flat-union-invalid-if-discriminator.json
Normal file
17
tests/qapi-schema/flat-union-invalid-if-discriminator.json
Normal file
@ -0,0 +1,17 @@
|
||||
{ 'enum': 'TestEnum',
|
||||
'data': [ 'value1', 'value2' ] }
|
||||
|
||||
{ 'struct': 'TestBase',
|
||||
'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
|
||||
|
||||
{ 'struct': 'TestTypeA',
|
||||
'data': { 'string': 'str' } }
|
||||
|
||||
{ 'struct': 'TestTypeB',
|
||||
'data': { 'integer': 'int' } }
|
||||
|
||||
{ 'union': 'TestUnion',
|
||||
'base': 'TestBase',
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1': 'TestTypeA',
|
||||
'value2': 'TestTypeB' } }
|
@ -1,6 +1,13 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module ident-with-escape.json
|
||||
object q_obj_fooA-arg
|
||||
member bar1: str optional=False
|
||||
|
@ -1,9 +1,19 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module include-relpath.json
|
||||
include include/relpath.json
|
||||
module include/relpath.json
|
||||
include include-relpath-sub.json
|
||||
module include-relpath-sub.json
|
||||
enum Status ['good', 'bad', 'ugly']
|
||||
enum Status
|
||||
member good
|
||||
member bad
|
||||
member ugly
|
||||
|
@ -1,10 +1,20 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module include-repetition.json
|
||||
include comments.json
|
||||
module comments.json
|
||||
enum Status ['good', 'bad', 'ugly']
|
||||
enum Status
|
||||
member good
|
||||
member bad
|
||||
member ugly
|
||||
module include-repetition.json
|
||||
include include-repetition-sub.json
|
||||
module include-repetition-sub.json
|
||||
|
@ -1,7 +1,17 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module include-simple.json
|
||||
include include-simple-sub.json
|
||||
module include-simple-sub.json
|
||||
enum Status ['good', 'bad', 'ugly']
|
||||
enum Status
|
||||
member good
|
||||
member bad
|
||||
member ugly
|
||||
|
@ -1,6 +1,13 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module indented-expr.json
|
||||
command eins None -> None
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
|
1
tests/qapi-schema/nested-struct-data-invalid-dict.err
Normal file
1
tests/qapi-schema/nested-struct-data-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
|
1
tests/qapi-schema/nested-struct-data-invalid-dict.exit
Normal file
1
tests/qapi-schema/nested-struct-data-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/nested-struct-data-invalid-dict.json
Normal file
3
tests/qapi-schema/nested-struct-data-invalid-dict.json
Normal file
@ -0,0 +1,3 @@
|
||||
# inline subtypes collide with our desired future use of defaults
|
||||
{ 'command': 'foo',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
@ -1,3 +1,3 @@
|
||||
# inline subtypes collide with our desired future use of defaults
|
||||
{ 'command': 'foo',
|
||||
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
|
||||
'data': { 'a' : { 'type': {} }, 'b' : 'str' } }
|
||||
|
@ -11,7 +11,7 @@
|
||||
'guest-sync' ] } }
|
||||
|
||||
{ 'struct': 'TestStruct',
|
||||
'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
|
||||
'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
|
||||
|
||||
# for testing enums
|
||||
{ 'struct': 'NestedEnumsOne',
|
||||
@ -77,7 +77,7 @@
|
||||
{ 'union': 'UserDefFlatUnion',
|
||||
'base': 'UserDefUnionBase', # intentional forward reference
|
||||
'discriminator': 'enum1',
|
||||
'data': { 'value1' : 'UserDefA',
|
||||
'data': { 'value1' : {'type': 'UserDefA'},
|
||||
'value2' : 'UserDefB',
|
||||
'value3' : 'UserDefB'
|
||||
# 'value4' defaults to empty
|
||||
@ -98,7 +98,7 @@
|
||||
{ 'struct': 'WrapAlternate',
|
||||
'data': { 'alt': 'UserDefAlternate' } }
|
||||
{ 'alternate': 'UserDefAlternate',
|
||||
'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
|
||||
'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
|
||||
'n': 'null' } }
|
||||
|
||||
{ 'struct': 'UserDefC',
|
||||
@ -134,7 +134,7 @@
|
||||
{ 'command': 'user_def_cmd', 'data': {} }
|
||||
{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
|
||||
{ 'command': 'user_def_cmd2',
|
||||
'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
|
||||
'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
|
||||
'returns': 'UserDefTwo' }
|
||||
|
||||
{ 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
|
||||
@ -166,7 +166,7 @@
|
||||
|
||||
# testing event
|
||||
{ 'struct': 'EventStructOne',
|
||||
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
|
||||
'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
|
||||
|
||||
{ 'event': 'EVENT_A' }
|
||||
{ 'event': 'EVENT_B',
|
||||
@ -201,23 +201,40 @@
|
||||
|
||||
# test 'if' condition handling
|
||||
|
||||
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
|
||||
{ 'struct': 'TestIfStruct', 'data':
|
||||
{ 'foo': 'int',
|
||||
'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} },
|
||||
'if': 'defined(TEST_IF_STRUCT)' }
|
||||
|
||||
{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
|
||||
{ 'enum': 'TestIfEnum', 'data':
|
||||
[ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
|
||||
'if': 'defined(TEST_IF_ENUM)' }
|
||||
|
||||
{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
|
||||
{ 'union': 'TestIfUnion', 'data':
|
||||
{ 'foo': 'TestStruct',
|
||||
'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
|
||||
'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
|
||||
|
||||
{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
|
||||
{ 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
|
||||
'if': 'defined(TEST_IF_UNION)' }
|
||||
|
||||
{ 'alternate': 'TestIfAlternate', 'data':
|
||||
{ 'foo': 'int',
|
||||
'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
|
||||
'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
|
||||
|
||||
{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
|
||||
{ 'command': 'TestIfAlternateCmd', 'data': { 'alt_cmd_arg': 'TestIfAlternate' },
|
||||
'if': 'defined(TEST_IF_ALT)' }
|
||||
|
||||
{ 'command': 'TestIfCmd', 'data':
|
||||
{ 'foo': 'TestIfStruct',
|
||||
'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } },
|
||||
'returns': 'UserDefThree',
|
||||
'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
|
||||
|
||||
{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
|
||||
|
||||
{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
|
||||
{ 'event': 'TestIfEvent', 'data':
|
||||
{ 'foo': 'TestIfStruct',
|
||||
'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_EVT_BAR)' } },
|
||||
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
|
||||
|
@ -1,6 +1,13 @@
|
||||
object q_empty
|
||||
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
|
||||
enum QType
|
||||
prefix QTYPE
|
||||
member none
|
||||
member qnull
|
||||
member qnum
|
||||
member qstring
|
||||
member qdict
|
||||
member qlist
|
||||
member qbool
|
||||
module qapi-schema-test.json
|
||||
object TestStruct
|
||||
member integer: int optional=False
|
||||
@ -11,19 +18,25 @@ object NestedEnumsOne
|
||||
member enum2: EnumOne optional=True
|
||||
member enum3: EnumOne optional=False
|
||||
member enum4: EnumOne optional=True
|
||||
enum MyEnum []
|
||||
enum MyEnum
|
||||
object Empty1
|
||||
object Empty2
|
||||
base Empty1
|
||||
command user_def_cmd0 Empty2 -> Empty2
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
enum QEnumTwo ['value1', 'value2']
|
||||
enum QEnumTwo
|
||||
prefix QENUM_TWO
|
||||
member value1
|
||||
member value2
|
||||
object UserDefOne
|
||||
base UserDefZero
|
||||
member string: str optional=False
|
||||
member enum1: EnumOne optional=True
|
||||
enum EnumOne ['value1', 'value2', 'value3', 'value4']
|
||||
enum EnumOne
|
||||
member value1
|
||||
member value2
|
||||
member value3
|
||||
member value4
|
||||
object UserDefZero
|
||||
member integer: int optional=False
|
||||
object UserDefTwoDictDict
|
||||
@ -127,7 +140,21 @@ object q_obj_sizeList-wrapper
|
||||
member data: sizeList optional=False
|
||||
object q_obj_anyList-wrapper
|
||||
member data: anyList optional=False
|
||||
enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
|
||||
enum UserDefNativeListUnionKind
|
||||
member integer
|
||||
member s8
|
||||
member s16
|
||||
member s32
|
||||
member s64
|
||||
member u8
|
||||
member u16
|
||||
member u32
|
||||
member u64
|
||||
member number
|
||||
member boolean
|
||||
member string
|
||||
member sizes
|
||||
member any
|
||||
object UserDefNativeListUnion
|
||||
member type: UserDefNativeListUnionKind optional=False
|
||||
tag type
|
||||
@ -204,7 +231,8 @@ event EVENT_E UserDefZero
|
||||
boxed=True
|
||||
event EVENT_F UserDefAlternate
|
||||
boxed=True
|
||||
enum __org.qemu_x-Enum ['__org.qemu_x-value']
|
||||
enum __org.qemu_x-Enum
|
||||
member __org.qemu_x-value
|
||||
object __org.qemu_x-Base
|
||||
member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
|
||||
object __org.qemu_x-Struct
|
||||
@ -213,7 +241,8 @@ object __org.qemu_x-Struct
|
||||
member wchar-t: int optional=True
|
||||
object q_obj_str-wrapper
|
||||
member data: str optional=False
|
||||
enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
|
||||
enum __org.qemu_x-Union1Kind
|
||||
member __org.qemu_x-branch
|
||||
object __org.qemu_x-Union1
|
||||
member type: __org.qemu_x-Union1Kind optional=False
|
||||
tag type
|
||||
@ -239,25 +268,50 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
object TestIfStruct
|
||||
member foo: int optional=False
|
||||
member bar: int optional=False
|
||||
if ['defined(TEST_IF_STRUCT_BAR)']
|
||||
if ['defined(TEST_IF_STRUCT)']
|
||||
enum TestIfEnum ['foo', 'bar']
|
||||
enum TestIfEnum
|
||||
member foo
|
||||
member bar
|
||||
if ['defined(TEST_IF_ENUM_BAR)']
|
||||
if ['defined(TEST_IF_ENUM)']
|
||||
object q_obj_TestStruct-wrapper
|
||||
member data: TestStruct optional=False
|
||||
enum TestIfUnionKind ['foo']
|
||||
enum TestIfUnionKind
|
||||
member foo
|
||||
member union_bar
|
||||
if ['defined(TEST_IF_UNION_BAR)']
|
||||
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
|
||||
object TestIfUnion
|
||||
member type: TestIfUnionKind optional=False
|
||||
tag type
|
||||
case foo: q_obj_TestStruct-wrapper
|
||||
case union_bar: q_obj_str-wrapper
|
||||
if ['defined(TEST_IF_UNION_BAR)']
|
||||
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
|
||||
object q_obj_TestIfUnionCmd-arg
|
||||
member union_cmd_arg: TestIfUnion optional=False
|
||||
if ['defined(TEST_IF_UNION)']
|
||||
command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
if ['defined(TEST_IF_UNION)']
|
||||
alternate TestIfAlternate
|
||||
tag type
|
||||
case foo: int
|
||||
case bar: TestStruct
|
||||
if ['defined(TEST_IF_ALT_BAR)']
|
||||
if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
|
||||
object q_obj_TestIfAlternateCmd-arg
|
||||
member alt_cmd_arg: TestIfAlternate optional=False
|
||||
if ['defined(TEST_IF_ALT)']
|
||||
command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
if ['defined(TEST_IF_ALT)']
|
||||
object q_obj_TestIfCmd-arg
|
||||
member foo: TestIfStruct optional=False
|
||||
member bar: TestIfEnum optional=False
|
||||
if ['defined(TEST_IF_CMD_BAR)']
|
||||
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
|
||||
command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
@ -266,6 +320,8 @@ command TestCmdReturnDefThree None -> UserDefThree
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
object q_obj_TestIfEvent-arg
|
||||
member foo: TestIfStruct optional=False
|
||||
member bar: TestIfEnum optional=False
|
||||
if ['defined(TEST_IF_EVT_BAR)']
|
||||
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
|
||||
event TestIfEvent q_obj_TestIfEvent-arg
|
||||
boxed=False
|
||||
|
1
tests/qapi-schema/struct-member-invalid-dict.err
Normal file
1
tests/qapi-schema/struct-member-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
|
1
tests/qapi-schema/struct-member-invalid-dict.exit
Normal file
1
tests/qapi-schema/struct-member-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/struct-member-invalid-dict.json
Normal file
3
tests/qapi-schema/struct-member-invalid-dict.json
Normal file
@ -0,0 +1,3 @@
|
||||
# Long form of member must have a value member 'type'
|
||||
{ 'struct': 'foo',
|
||||
'data': { '*a': { 'case': 'foo' } } }
|
0
tests/qapi-schema/struct-member-invalid-dict.out
Normal file
0
tests/qapi-schema/struct-member-invalid-dict.out
Normal file
@ -23,10 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
|
||||
def visit_include(self, name, info):
|
||||
print('include %s' % name)
|
||||
|
||||
def visit_enum_type(self, name, info, ifcond, values, prefix):
|
||||
print('enum %s %s' % (name, values))
|
||||
def visit_enum_type(self, name, info, ifcond, members, prefix):
|
||||
print('enum %s' % name)
|
||||
if prefix:
|
||||
print(' prefix %s' % prefix)
|
||||
for m in members:
|
||||
print(' member %s' % m.name)
|
||||
self._print_if(m.ifcond, indent=8)
|
||||
self._print_if(ifcond)
|
||||
|
||||
def visit_object_type(self, name, info, ifcond, base, members, variants):
|
||||
@ -36,6 +39,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
|
||||
for m in members:
|
||||
print(' member %s: %s optional=%s'
|
||||
% (m.name, m.type.name, m.optional))
|
||||
self._print_if(m.ifcond, 8)
|
||||
self._print_variants(variants)
|
||||
self._print_if(ifcond)
|
||||
|
||||
@ -64,6 +68,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
|
||||
print(' tag %s' % variants.tag_member.name)
|
||||
for v in variants.variants:
|
||||
print(' case %s: %s' % (v.name, v.type.name))
|
||||
QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
|
||||
|
||||
@staticmethod
|
||||
def _print_if(ifcond, indent=4):
|
||||
|
1
tests/qapi-schema/union-branch-invalid-dict.err
Normal file
1
tests/qapi-schema/union-branch-invalid-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
|
1
tests/qapi-schema/union-branch-invalid-dict.exit
Normal file
1
tests/qapi-schema/union-branch-invalid-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
tests/qapi-schema/union-branch-invalid-dict.json
Normal file
4
tests/qapi-schema/union-branch-invalid-dict.json
Normal file
@ -0,0 +1,4 @@
|
||||
# Long form of member must have a value member 'type'
|
||||
{ 'union': 'UnionInvalidBranch',
|
||||
'data': { 'integer': { 'if': 'foo'},
|
||||
's8': 'int8' } }
|
0
tests/qapi-schema/union-branch-invalid-dict.out
Normal file
0
tests/qapi-schema/union-branch-invalid-dict.out
Normal file
@ -1 +1,2 @@
|
||||
tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
|
||||
tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar'
|
||||
Valid keys are 'base', 'data', 'if', 'struct'.
|
||||
|
@ -1,2 +1,2 @@
|
||||
# we reject an expression with unknown top-level keys
|
||||
{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }
|
||||
{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { }, 'phony': { } }
|
||||
|
@ -69,7 +69,6 @@ echo
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
"{'execute': 'drive-mirror',
|
||||
'arguments': {'device': 'testdisk',
|
||||
'mode': 'absolute-paths',
|
||||
'format': '$IMGFMT',
|
||||
'target': '$DEST_IMG',
|
||||
'sync': 'full',
|
||||
|
@ -1950,7 +1950,7 @@ static void test_qemu_strtou64_full_max(void)
|
||||
static void test_qemu_strtosz_simple(void)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
@ -2017,7 +2017,7 @@ static void test_qemu_strtosz_units(void)
|
||||
const char *p = "1P";
|
||||
const char *e = "1E";
|
||||
int err;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
/* default is M */
|
||||
@ -2066,7 +2066,7 @@ static void test_qemu_strtosz_float(void)
|
||||
{
|
||||
const char *str = "12.345M";
|
||||
int err;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
@ -2078,7 +2078,7 @@ static void test_qemu_strtosz_float(void)
|
||||
static void test_qemu_strtosz_invalid(void)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
@ -2096,12 +2096,22 @@ static void test_qemu_strtosz_invalid(void)
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
str = "inf";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
str = "NaN";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtosz_trailing(void)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
@ -2126,7 +2136,7 @@ static void test_qemu_strtosz_trailing(void)
|
||||
static void test_qemu_strtosz_erange(void)
|
||||
{
|
||||
const char *str;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
@ -2160,7 +2170,7 @@ static void test_qemu_strtosz_metric(void)
|
||||
{
|
||||
const char *str = "12345k";
|
||||
int err;
|
||||
char *endptr = NULL;
|
||||
const char *endptr;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
err = qemu_strtosz_metric(str, &endptr, &res);
|
||||
|
@ -92,16 +92,6 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n)
|
||||
uint64List *tail;
|
||||
int i;
|
||||
|
||||
/* BUG: unsigned numbers above INT64_MAX don't work */
|
||||
for (i = 0; i < n; i++) {
|
||||
if (expected[i] > INT64_MAX) {
|
||||
Error *err = NULL;
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
visit_type_uint64List(v, NULL, &res, &error_abort);
|
||||
tail = res;
|
||||
for (i = 0; i < n; i++) {
|
||||
@ -117,14 +107,14 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n)
|
||||
static void test_visitor_in_intList(TestInputVisitorData *data,
|
||||
const void *unused)
|
||||
{
|
||||
/* Note: the visitor *sorts* ranges *unsigned* */
|
||||
int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 };
|
||||
int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7,
|
||||
8, 9, 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
int64_t expect2[] = { 32767, -32768, -32767 };
|
||||
int64_t expect3[] = { INT64_MAX, INT64_MIN };
|
||||
uint64_t expect4[] = { UINT64_MAX };
|
||||
int64_t expect3[] = { INT64_MIN, INT64_MAX };
|
||||
int64_t expect4[] = { 1 };
|
||||
int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX };
|
||||
Error *err = NULL;
|
||||
int64List *res = NULL;
|
||||
int64List *tail;
|
||||
Visitor *v;
|
||||
int64_t val;
|
||||
|
||||
@ -140,8 +130,45 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
|
||||
"-9223372036854775808,9223372036854775807");
|
||||
check_ilist(v, expect3, ARRAY_SIZE(expect3));
|
||||
|
||||
v = visitor_input_test_init(data, "18446744073709551615");
|
||||
check_ulist(v, expect4, ARRAY_SIZE(expect4));
|
||||
v = visitor_input_test_init(data, "1-1");
|
||||
check_ilist(v, expect4, ARRAY_SIZE(expect4));
|
||||
|
||||
v = visitor_input_test_init(data,
|
||||
"9223372036854775805-9223372036854775807");
|
||||
check_ilist(v, expect5, ARRAY_SIZE(expect5));
|
||||
|
||||
/* Value too large */
|
||||
|
||||
v = visitor_input_test_init(data, "9223372036854775808");
|
||||
visit_type_int64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Value too small */
|
||||
|
||||
v = visitor_input_test_init(data, "-9223372036854775809");
|
||||
visit_type_int64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Range not ascending */
|
||||
|
||||
v = visitor_input_test_init(data, "3-1");
|
||||
visit_type_int64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
v = visitor_input_test_init(data, "9223372036854775807-0");
|
||||
visit_type_int64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Range too big (65536 is the limit against DOS attacks) */
|
||||
|
||||
v = visitor_input_test_init(data, "0-65536");
|
||||
visit_type_int64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Empty list */
|
||||
|
||||
@ -161,39 +188,140 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
|
||||
|
||||
v = visitor_input_test_init(data, "0,2-3");
|
||||
|
||||
/* Would be simpler if the visitor genuinely supported virtual walks */
|
||||
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
|
||||
&error_abort);
|
||||
tail = res;
|
||||
visit_type_int64(v, NULL, &tail->value, &error_abort);
|
||||
g_assert_cmpint(tail->value, ==, 0);
|
||||
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
|
||||
g_assert(tail);
|
||||
visit_type_int64(v, NULL, &tail->value, &error_abort);
|
||||
g_assert_cmpint(tail->value, ==, 2);
|
||||
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
|
||||
g_assert(tail);
|
||||
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_int64(v, NULL, &val, &error_abort);
|
||||
g_assert_cmpint(val, ==, 0);
|
||||
visit_type_int64(v, NULL, &val, &error_abort);
|
||||
g_assert_cmpint(val, ==, 2);
|
||||
|
||||
visit_check_list(v, &err);
|
||||
error_free_or_abort(&err);
|
||||
visit_end_list(v, (void **)&res);
|
||||
|
||||
qapi_free_int64List(res);
|
||||
visit_end_list(v, NULL);
|
||||
|
||||
/* Visit beyond end of list */
|
||||
|
||||
v = visitor_input_test_init(data, "0");
|
||||
|
||||
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
|
||||
&error_abort);
|
||||
tail = res;
|
||||
visit_type_int64(v, NULL, &tail->value, &err);
|
||||
g_assert_cmpint(tail->value, ==, 0);
|
||||
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_int64(v, NULL, &val, &err);
|
||||
g_assert_cmpint(val, ==, 1); /* BUG */
|
||||
visit_check_list(v, &error_abort);
|
||||
visit_end_list(v, (void **)&res);
|
||||
g_assert_cmpint(val, ==, 0);
|
||||
visit_type_int64(v, NULL, &val, &err);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
qapi_free_int64List(res);
|
||||
visit_check_list(v, &error_abort);
|
||||
visit_end_list(v, NULL);
|
||||
}
|
||||
|
||||
static void test_visitor_in_uintList(TestInputVisitorData *data,
|
||||
const void *unused)
|
||||
{
|
||||
uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7,
|
||||
8, 9, 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
uint64_t expect2[] = { 32767, -32768, -32767 };
|
||||
uint64_t expect3[] = { INT64_MIN, INT64_MAX };
|
||||
uint64_t expect4[] = { 1 };
|
||||
uint64_t expect5[] = { UINT64_MAX };
|
||||
uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX };
|
||||
Error *err = NULL;
|
||||
uint64List *res = NULL;
|
||||
Visitor *v;
|
||||
uint64_t val;
|
||||
|
||||
/* Valid lists */
|
||||
|
||||
v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");
|
||||
check_ulist(v, expect1, ARRAY_SIZE(expect1));
|
||||
|
||||
v = visitor_input_test_init(data, "32767,-32768--32767");
|
||||
check_ulist(v, expect2, ARRAY_SIZE(expect2));
|
||||
|
||||
v = visitor_input_test_init(data,
|
||||
"-9223372036854775808,9223372036854775807");
|
||||
check_ulist(v, expect3, ARRAY_SIZE(expect3));
|
||||
|
||||
v = visitor_input_test_init(data, "1-1");
|
||||
check_ulist(v, expect4, ARRAY_SIZE(expect4));
|
||||
|
||||
v = visitor_input_test_init(data, "18446744073709551615");
|
||||
check_ulist(v, expect5, ARRAY_SIZE(expect5));
|
||||
|
||||
v = visitor_input_test_init(data,
|
||||
"18446744073709551613-18446744073709551615");
|
||||
check_ulist(v, expect6, ARRAY_SIZE(expect6));
|
||||
|
||||
/* Value too large */
|
||||
|
||||
v = visitor_input_test_init(data, "18446744073709551616");
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Value too small */
|
||||
|
||||
v = visitor_input_test_init(data, "-18446744073709551616");
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Range not ascending */
|
||||
|
||||
v = visitor_input_test_init(data, "3-1");
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
v = visitor_input_test_init(data, "18446744073709551615-0");
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Range too big (65536 is the limit against DOS attacks) */
|
||||
|
||||
v = visitor_input_test_init(data, "0-65536");
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Empty list */
|
||||
|
||||
v = visitor_input_test_init(data, "");
|
||||
visit_type_uint64List(v, NULL, &res, &error_abort);
|
||||
g_assert(!res);
|
||||
|
||||
/* Not a list */
|
||||
|
||||
v = visitor_input_test_init(data, "not an uint list");
|
||||
|
||||
visit_type_uint64List(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!res);
|
||||
|
||||
/* Unvisited list tail */
|
||||
|
||||
v = visitor_input_test_init(data, "0,2-3");
|
||||
|
||||
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_uint64(v, NULL, &val, &error_abort);
|
||||
g_assert_cmpuint(val, ==, 0);
|
||||
visit_type_uint64(v, NULL, &val, &error_abort);
|
||||
g_assert_cmpuint(val, ==, 2);
|
||||
|
||||
visit_check_list(v, &err);
|
||||
error_free_or_abort(&err);
|
||||
visit_end_list(v, NULL);
|
||||
|
||||
/* Visit beyond end of list */
|
||||
|
||||
v = visitor_input_test_init(data, "0");
|
||||
|
||||
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_uint64(v, NULL, &val, &err);
|
||||
g_assert_cmpuint(val, ==, 0);
|
||||
visit_type_uint64(v, NULL, &val, &err);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
visit_check_list(v, &error_abort);
|
||||
visit_end_list(v, NULL);
|
||||
}
|
||||
|
||||
static void test_visitor_in_bool(TestInputVisitorData *data,
|
||||
@ -252,6 +380,19 @@ static void test_visitor_in_number(TestInputVisitorData *data,
|
||||
visit_type_number(v, NULL, &res, &err);
|
||||
g_assert(!err);
|
||||
g_assert_cmpfloat(res, ==, value);
|
||||
|
||||
/* NaN and infinity has to be rejected */
|
||||
|
||||
v = visitor_input_test_init(data, "NaN");
|
||||
|
||||
visit_type_number(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
v = visitor_input_test_init(data, "inf");
|
||||
|
||||
visit_type_number(v, NULL, &res, &err);
|
||||
error_free_or_abort(&err);
|
||||
|
||||
}
|
||||
|
||||
static void test_visitor_in_string(TestInputVisitorData *data,
|
||||
@ -356,6 +497,8 @@ int main(int argc, char **argv)
|
||||
&in_visitor_data, test_visitor_in_int);
|
||||
input_visitor_test_add("/string-visitor/input/intList",
|
||||
&in_visitor_data, test_visitor_in_intList);
|
||||
input_visitor_test_add("/string-visitor/input/uintList",
|
||||
&in_visitor_data, test_visitor_in_uintList);
|
||||
input_visitor_test_add("/string-visitor/input/bool",
|
||||
&in_visitor_data, test_visitor_in_bool);
|
||||
input_visitor_test_add("/string-visitor/input/number",
|
||||
|
@ -203,23 +203,21 @@ static int64_t suffix_mul(char suffix, int64_t unit)
|
||||
/*
|
||||
* Convert string to bytes, allowing either B/b for bytes, K/k for KB,
|
||||
* M/m for MB, G/g for GB or T/t for TB. End pointer will be returned
|
||||
* in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on
|
||||
* in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on
|
||||
* other error.
|
||||
*/
|
||||
static int do_strtosz(const char *nptr, char **end,
|
||||
static int do_strtosz(const char *nptr, const char **end,
|
||||
const char default_suffix, int64_t unit,
|
||||
uint64_t *result)
|
||||
{
|
||||
int retval;
|
||||
char *endptr;
|
||||
const char *endptr;
|
||||
unsigned char c;
|
||||
int mul_required = 0;
|
||||
double val, mul, integral, fraction;
|
||||
|
||||
errno = 0;
|
||||
val = strtod(nptr, &endptr);
|
||||
if (isnan(val) || endptr == nptr || errno != 0) {
|
||||
retval = -EINVAL;
|
||||
retval = qemu_strtod_finite(nptr, &endptr, &val);
|
||||
if (retval) {
|
||||
goto out;
|
||||
}
|
||||
fraction = modf(val, &integral);
|
||||
@ -259,17 +257,17 @@ out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int qemu_strtosz(const char *nptr, char **end, uint64_t *result)
|
||||
int qemu_strtosz(const char *nptr, const char **end, uint64_t *result)
|
||||
{
|
||||
return do_strtosz(nptr, end, 'B', 1024, result);
|
||||
}
|
||||
|
||||
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result)
|
||||
int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result)
|
||||
{
|
||||
return do_strtosz(nptr, end, 'M', 1024, result);
|
||||
}
|
||||
|
||||
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result)
|
||||
int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result)
|
||||
{
|
||||
return do_strtosz(nptr, end, 'B', 1000, result);
|
||||
}
|
||||
@ -551,6 +549,71 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base,
|
||||
return check_strtox_error(nptr, ep, endptr, errno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string @nptr to a double.
|
||||
*
|
||||
* This is a wrapper around strtod() that is harder to misuse.
|
||||
* Semantics of @nptr and @endptr match strtod() with differences
|
||||
* noted below.
|
||||
*
|
||||
* @nptr may be null, and no conversion is performed then.
|
||||
*
|
||||
* If no conversion is performed, store @nptr in *@endptr and return
|
||||
* -EINVAL.
|
||||
*
|
||||
* If @endptr is null, and the string isn't fully converted, return
|
||||
* -EINVAL. This is the case when the pointer that would be stored in
|
||||
* a non-null @endptr points to a character other than '\0'.
|
||||
*
|
||||
* If the conversion overflows, store +/-HUGE_VAL in @result, depending
|
||||
* on the sign, and return -ERANGE.
|
||||
*
|
||||
* If the conversion underflows, store +/-0.0 in @result, depending on the
|
||||
* sign, and return -ERANGE.
|
||||
*
|
||||
* Else store the converted value in @result, and return zero.
|
||||
*/
|
||||
int qemu_strtod(const char *nptr, const char **endptr, double *result)
|
||||
{
|
||||
char *ep;
|
||||
|
||||
if (!nptr) {
|
||||
if (endptr) {
|
||||
*endptr = nptr;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
*result = strtod(nptr, &ep);
|
||||
return check_strtox_error(nptr, ep, endptr, errno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string @nptr to a finite double.
|
||||
*
|
||||
* Works like qemu_strtod(), except that "NaN" and "inf" are rejected
|
||||
* with -EINVAL and no conversion is performed.
|
||||
*/
|
||||
int qemu_strtod_finite(const char *nptr, const char **endptr, double *result)
|
||||
{
|
||||
double tmp;
|
||||
int ret;
|
||||
|
||||
ret = qemu_strtod(nptr, endptr, &tmp);
|
||||
if (!ret && !isfinite(tmp)) {
|
||||
if (endptr) {
|
||||
*endptr = nptr;
|
||||
}
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret != -EINVAL) {
|
||||
*result = tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the first occurrence of 'c' in 's', and returns a pointer
|
||||
* to the trailing null byte if none was found.
|
||||
|
Loading…
Reference in New Issue
Block a user