import
This commit is contained in:
3
qemu/qapi/Makefile.objs
Normal file
3
qemu/qapi/Makefile.objs
Normal file
@@ -0,0 +1,3 @@
|
||||
util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
|
||||
util-obj-y += qmp-output-visitor.o
|
||||
util-obj-y += string-input-visitor.o string-output-visitor.o
|
||||
30
qemu/qapi/common.json
Normal file
30
qemu/qapi/common.json
Normal file
@@ -0,0 +1,30 @@
|
||||
# -*- Mode: Python -*-
|
||||
#
|
||||
# QAPI common definitions
|
||||
|
||||
##
|
||||
# @ErrorClass
|
||||
#
|
||||
# QEMU error classes
|
||||
#
|
||||
# @GenericError: this is used for errors that don't require a specific error
|
||||
# class. This should be the default case for most errors
|
||||
#
|
||||
# @CommandNotFound: the requested command has not been found
|
||||
#
|
||||
# @DeviceEncrypted: the requested operation can't be fulfilled because the
|
||||
# selected device is encrypted
|
||||
#
|
||||
# @DeviceNotActive: a device has failed to be become active
|
||||
#
|
||||
# @DeviceNotFound: the requested device has not been found
|
||||
#
|
||||
# @KVMMissingCap: the requested operation can't be fulfilled because a
|
||||
# required KVM capability is missing
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'enum': 'ErrorClass',
|
||||
'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
|
||||
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
|
||||
|
||||
224
qemu/qapi/qapi-dealloc-visitor.c
Normal file
224
qemu/qapi/qapi-dealloc-visitor.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Dealloc Visitor
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Michael Roth <mdroth@linux.vnet.ibm.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 "qapi/dealloc-visitor.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
|
||||
typedef struct StackEntry
|
||||
{
|
||||
void *value;
|
||||
bool is_list_head;
|
||||
QTAILQ_ENTRY(StackEntry) node;
|
||||
} StackEntry;
|
||||
|
||||
struct QapiDeallocVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
QTAILQ_HEAD(, StackEntry) stack;
|
||||
bool is_list_head;
|
||||
};
|
||||
|
||||
static QapiDeallocVisitor *to_qov(Visitor *v)
|
||||
{
|
||||
return container_of(v, QapiDeallocVisitor, visitor);
|
||||
}
|
||||
|
||||
static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
|
||||
{
|
||||
StackEntry *e = g_malloc0(sizeof(*e));
|
||||
|
||||
e->value = value;
|
||||
|
||||
/* see if we're just pushing a list head tracker */
|
||||
if (value == NULL) {
|
||||
e->is_list_head = true;
|
||||
}
|
||||
QTAILQ_INSERT_HEAD(&qov->stack, e, node);
|
||||
}
|
||||
|
||||
static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
|
||||
{
|
||||
StackEntry *e = QTAILQ_FIRST(&qov->stack);
|
||||
QObject *value;
|
||||
QTAILQ_REMOVE(&qov->stack, e, node);
|
||||
value = e->value;
|
||||
g_free(e);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind,
|
||||
const char *name, size_t unused,
|
||||
Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
qapi_dealloc_push(qov, obj);
|
||||
}
|
||||
|
||||
static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
void **obj = qapi_dealloc_pop(qov);
|
||||
if (obj) {
|
||||
g_free(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void qapi_dealloc_start_implicit_struct(Visitor *v,
|
||||
void **obj,
|
||||
size_t size,
|
||||
Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
qapi_dealloc_push(qov, obj);
|
||||
}
|
||||
|
||||
static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
void **obj = qapi_dealloc_pop(qov);
|
||||
if (obj) {
|
||||
g_free(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
qapi_dealloc_push(qov, NULL);
|
||||
}
|
||||
|
||||
static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
|
||||
Error **errp)
|
||||
{
|
||||
GenericList *list = *listp;
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
StackEntry *e = QTAILQ_FIRST(&qov->stack);
|
||||
|
||||
if (e && e->is_list_head) {
|
||||
e->is_list_head = false;
|
||||
return list;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
list = list->next;
|
||||
g_free(*listp);
|
||||
return list;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void qapi_dealloc_end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
QapiDeallocVisitor *qov = to_qov(v);
|
||||
void *obj = qapi_dealloc_pop(qov);
|
||||
assert(obj == NULL); /* should've been list head tracker with no payload */
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
if (obj) {
|
||||
g_free(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||
const char *kind, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
/* If there's no data present, the dealloc visitor has nothing to free.
|
||||
* Thus, indicate to visitor code that the subsequent union fields can
|
||||
* be skipped. This is not an error condition, since the cleanup of the
|
||||
* rest of an object can continue unhindered, so leave errp unset in
|
||||
* these cases.
|
||||
*
|
||||
* NOTE: In cases where we're attempting to deallocate an object that
|
||||
* may have missing fields, the field indicating the union type may
|
||||
* be missing. In such a case, it's possible we don't have enough
|
||||
* information to differentiate data_present == false from a case where
|
||||
* data *is* present but happens to be a scalar with a value of 0.
|
||||
* This is okay, since in the case of the dealloc visitor there's no
|
||||
* work that needs to done in either situation.
|
||||
*
|
||||
* The current inability in QAPI code to more thoroughly verify a union
|
||||
* type in such cases will likely need to be addressed if we wish to
|
||||
* implement this interface for other types of visitors in the future,
|
||||
* however.
|
||||
*/
|
||||
static bool qapi_dealloc_start_union(Visitor *v, bool data_present,
|
||||
Error **errp)
|
||||
{
|
||||
return data_present;
|
||||
}
|
||||
|
||||
Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
|
||||
{
|
||||
return &v->visitor;
|
||||
}
|
||||
|
||||
void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
|
||||
{
|
||||
g_free(v);
|
||||
}
|
||||
|
||||
QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
|
||||
{
|
||||
QapiDeallocVisitor *v;
|
||||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->visitor.start_struct = qapi_dealloc_start_struct;
|
||||
v->visitor.end_struct = qapi_dealloc_end_struct;
|
||||
v->visitor.start_implicit_struct = qapi_dealloc_start_implicit_struct;
|
||||
v->visitor.end_implicit_struct = qapi_dealloc_end_implicit_struct;
|
||||
v->visitor.start_list = qapi_dealloc_start_list;
|
||||
v->visitor.next_list = qapi_dealloc_next_list;
|
||||
v->visitor.end_list = qapi_dealloc_end_list;
|
||||
v->visitor.type_enum = qapi_dealloc_type_enum;
|
||||
v->visitor.type_int = qapi_dealloc_type_int;
|
||||
v->visitor.type_bool = qapi_dealloc_type_bool;
|
||||
v->visitor.type_str = qapi_dealloc_type_str;
|
||||
v->visitor.type_number = qapi_dealloc_type_number;
|
||||
v->visitor.type_size = qapi_dealloc_type_size;
|
||||
v->visitor.start_union = qapi_dealloc_start_union;
|
||||
|
||||
QTAILQ_INIT(&v->stack);
|
||||
|
||||
return v;
|
||||
}
|
||||
313
qemu/qapi/qapi-visit-core.c
Normal file
313
qemu/qapi/qapi-visit-core.c
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Core Definitions for QAPI Visitor Classes
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.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-common.h"
|
||||
#include "qapi/qmp/qobject.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
|
||||
void visit_start_struct(Visitor *v, void **obj, const char *kind,
|
||||
const char *name, size_t size, Error **errp)
|
||||
{
|
||||
v->start_struct(v, obj, kind, name, size, errp);
|
||||
}
|
||||
|
||||
void visit_end_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
v->end_struct(v, errp);
|
||||
}
|
||||
|
||||
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
|
||||
Error **errp)
|
||||
{
|
||||
if (v->start_implicit_struct) {
|
||||
v->start_implicit_struct(v, obj, size, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_end_implicit_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
if (v->end_implicit_struct) {
|
||||
v->end_implicit_struct(v, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
v->start_list(v, name, errp);
|
||||
}
|
||||
|
||||
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
|
||||
{
|
||||
return v->next_list(v, list, errp);
|
||||
}
|
||||
|
||||
void visit_end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
v->end_list(v, errp);
|
||||
}
|
||||
|
||||
bool visit_start_union(Visitor *v, bool data_present, Error **errp)
|
||||
{
|
||||
if (v->start_union) {
|
||||
return v->start_union(v, data_present, errp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void visit_end_union(Visitor *v, bool data_present, Error **errp)
|
||||
{
|
||||
if (v->end_union) {
|
||||
v->end_union(v, data_present, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_optional(Visitor *v, bool *present, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
if (v->optional) {
|
||||
v->optional(v, present, name, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
if (v->get_next_type) {
|
||||
v->get_next_type(v, obj, qtypes, name, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||
const char *kind, const char *name, Error **errp)
|
||||
{
|
||||
v->type_enum(v, obj, strings, kind, name, errp);
|
||||
}
|
||||
|
||||
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
v->type_int(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_uint8) {
|
||||
v->type_uint8(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < 0 || value > UINT8_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"uint8_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_uint16) {
|
||||
v->type_uint16(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < 0 || value > UINT16_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"uint16_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_uint32) {
|
||||
v->type_uint32(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < 0 || value > UINT32_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"uint32_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_uint64) {
|
||||
v->type_uint64(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_int8) {
|
||||
v->type_int8(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < INT8_MIN || value > INT8_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"int8_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_int16) {
|
||||
v->type_int16(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < INT16_MIN || value > INT16_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"int16_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_int32) {
|
||||
v->type_int32(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
if (value < INT32_MIN || value > INT32_MAX) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"int32_t");
|
||||
return;
|
||||
}
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
if (v->type_int64) {
|
||||
v->type_int64(v, obj, name, errp);
|
||||
} else {
|
||||
v->type_int(v, obj, name, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (v->type_size) {
|
||||
v->type_size(v, obj, name, errp);
|
||||
} else if (v->type_uint64) {
|
||||
v->type_uint64(v, obj, name, errp);
|
||||
} else {
|
||||
value = *obj;
|
||||
v->type_int(v, &value, name, errp);
|
||||
*obj = value;
|
||||
}
|
||||
}
|
||||
|
||||
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
|
||||
{
|
||||
v->type_bool(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
|
||||
{
|
||||
v->type_str(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
|
||||
{
|
||||
v->type_number(v, obj, name, errp);
|
||||
}
|
||||
|
||||
void output_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||
const char *kind, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int i = 0;
|
||||
int value = *obj;
|
||||
char *enum_str;
|
||||
|
||||
assert(strings);
|
||||
while (strings[i++] != NULL);
|
||||
if (value < 0 || value >= i - 1) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
|
||||
return;
|
||||
}
|
||||
|
||||
enum_str = (char *)strings[value];
|
||||
visit_type_str(v, &enum_str, name, errp);
|
||||
}
|
||||
|
||||
void input_type_enum(Visitor *v, int *obj, const char *strings[],
|
||||
const char *kind, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int64_t value = 0;
|
||||
char *enum_str;
|
||||
|
||||
assert(strings);
|
||||
|
||||
visit_type_str(v, &enum_str, name, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
while (strings[value] != NULL) {
|
||||
if (strcmp(strings[value], enum_str) == 0) {
|
||||
break;
|
||||
}
|
||||
value++;
|
||||
}
|
||||
|
||||
if (strings[value] == NULL) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER, enum_str);
|
||||
g_free(enum_str);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(enum_str);
|
||||
*obj = value;
|
||||
}
|
||||
349
qemu/qapi/qmp-input-visitor.c
Normal file
349
qemu/qapi/qmp-input-visitor.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Input Visitor
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.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 "qapi/qmp-input-visitor.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
#define QIV_STACK_SIZE 1024
|
||||
|
||||
typedef struct StackObject
|
||||
{
|
||||
QObject *obj;
|
||||
const QListEntry *entry;
|
||||
GHashTable *h;
|
||||
} StackObject;
|
||||
|
||||
struct QmpInputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
StackObject stack[QIV_STACK_SIZE];
|
||||
int nb_stack;
|
||||
bool strict;
|
||||
};
|
||||
|
||||
static QmpInputVisitor *to_qiv(Visitor *v)
|
||||
{
|
||||
return container_of(v, QmpInputVisitor, visitor);
|
||||
}
|
||||
|
||||
static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
|
||||
const char *name,
|
||||
bool consume)
|
||||
{
|
||||
QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
|
||||
|
||||
if (qobj) {
|
||||
if (name && qobject_type(qobj) == QTYPE_QDICT) {
|
||||
if (qiv->stack[qiv->nb_stack - 1].h && consume) {
|
||||
g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
|
||||
}
|
||||
return qdict_get(qobject_to_qdict(qobj), name);
|
||||
} else if (qiv->stack[qiv->nb_stack - 1].entry) {
|
||||
return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
|
||||
}
|
||||
}
|
||||
|
||||
return qobj;
|
||||
}
|
||||
|
||||
static void qdict_add_key(const char *key, QObject *obj, void *opaque)
|
||||
{
|
||||
GHashTable *h = opaque;
|
||||
g_hash_table_insert(h, (gpointer) key, NULL);
|
||||
}
|
||||
|
||||
static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
|
||||
{
|
||||
GHashTable *h;
|
||||
|
||||
if (qiv->nb_stack >= QIV_STACK_SIZE) {
|
||||
error_setg(errp, "An internal buffer overran");
|
||||
return;
|
||||
}
|
||||
|
||||
qiv->stack[qiv->nb_stack].obj = obj;
|
||||
qiv->stack[qiv->nb_stack].entry = NULL;
|
||||
qiv->stack[qiv->nb_stack].h = NULL;
|
||||
|
||||
if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
|
||||
h = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
|
||||
qiv->stack[qiv->nb_stack].h = h;
|
||||
}
|
||||
|
||||
qiv->nb_stack++;
|
||||
}
|
||||
|
||||
/** Only for qmp_input_pop. */
|
||||
static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
|
||||
{
|
||||
*(const char **)user_pkey = (const char *)key;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
|
||||
{
|
||||
assert(qiv->nb_stack > 0);
|
||||
|
||||
if (qiv->strict) {
|
||||
GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
|
||||
if (top_ht) {
|
||||
if (g_hash_table_size(top_ht)) {
|
||||
const char *key;
|
||||
g_hash_table_find(top_ht, always_true, &key);
|
||||
error_set(errp, QERR_QMP_EXTRA_MEMBER, key);
|
||||
}
|
||||
g_hash_table_unref(top_ht);
|
||||
}
|
||||
}
|
||||
|
||||
qiv->nb_stack--;
|
||||
}
|
||||
|
||||
static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
|
||||
const char *name, size_t size, Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
Error *err = NULL;
|
||||
|
||||
if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"QDict");
|
||||
return;
|
||||
}
|
||||
|
||||
qmp_input_push(qiv, qobj, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
*obj = g_malloc0(size);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_input_end_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
|
||||
qmp_input_pop(qiv, errp);
|
||||
}
|
||||
|
||||
static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
|
||||
size_t size, Error **errp)
|
||||
{
|
||||
if (obj) {
|
||||
*obj = g_malloc0(size);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_input_end_implicit_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"list");
|
||||
return;
|
||||
}
|
||||
|
||||
qmp_input_push(qiv, qobj, errp);
|
||||
}
|
||||
|
||||
static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
GenericList *entry;
|
||||
StackObject *so = &qiv->stack[qiv->nb_stack - 1];
|
||||
bool first;
|
||||
|
||||
if (so->entry == NULL) {
|
||||
so->entry = qlist_first(qobject_to_qlist(so->obj));
|
||||
first = true;
|
||||
} else {
|
||||
so->entry = qlist_next(so->entry);
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (so->entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = g_malloc0(sizeof(*entry));
|
||||
if (first) {
|
||||
*list = entry;
|
||||
} else {
|
||||
(*list)->next = entry;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void qmp_input_end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
|
||||
qmp_input_pop(qiv, errp);
|
||||
}
|
||||
|
||||
static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects,
|
||||
const char *name, Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, false);
|
||||
|
||||
if (!qobj) {
|
||||
error_set(errp, QERR_MISSING_PARAMETER, name ? name : "null");
|
||||
return;
|
||||
}
|
||||
*kind = qobjects[qobject_type(qobj)];
|
||||
}
|
||||
|
||||
static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj || qobject_type(qobj) != QTYPE_QINT) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"integer");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = qint_get_int(qobject_to_qint(qobj));
|
||||
}
|
||||
|
||||
static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"boolean");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = qbool_get_int(qobject_to_qbool(qobj));
|
||||
}
|
||||
|
||||
static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"string");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = g_strdup(qstring_get_str(qobject_to_qstring(qobj)));
|
||||
}
|
||||
|
||||
static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj || (qobject_type(qobj) != QTYPE_QFLOAT &&
|
||||
qobject_type(qobj) != QTYPE_QINT)) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"number");
|
||||
return;
|
||||
}
|
||||
|
||||
if (qobject_type(qobj) == QTYPE_QINT) {
|
||||
*obj = qint_get_int(qobject_to_qint(qobj));
|
||||
} else {
|
||||
*obj = qfloat_get_double(qobject_to_qfloat(qobj));
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_input_optional(Visitor *v, bool *present, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpInputVisitor *qiv = to_qiv(v);
|
||||
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
||||
|
||||
if (!qobj) {
|
||||
*present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
*present = true;
|
||||
}
|
||||
|
||||
Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
|
||||
{
|
||||
return &v->visitor;
|
||||
}
|
||||
|
||||
void qmp_input_visitor_cleanup(QmpInputVisitor *v)
|
||||
{
|
||||
qobject_decref(v->stack[0].obj);
|
||||
g_free(v);
|
||||
}
|
||||
|
||||
QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
|
||||
{
|
||||
QmpInputVisitor *v;
|
||||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->visitor.start_struct = qmp_input_start_struct;
|
||||
v->visitor.end_struct = qmp_input_end_struct;
|
||||
v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
|
||||
v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;
|
||||
v->visitor.start_list = qmp_input_start_list;
|
||||
v->visitor.next_list = qmp_input_next_list;
|
||||
v->visitor.end_list = qmp_input_end_list;
|
||||
v->visitor.type_enum = input_type_enum;
|
||||
v->visitor.type_int = qmp_input_type_int;
|
||||
v->visitor.type_bool = qmp_input_type_bool;
|
||||
v->visitor.type_str = qmp_input_type_str;
|
||||
v->visitor.type_number = qmp_input_type_number;
|
||||
v->visitor.optional = qmp_input_optional;
|
||||
v->visitor.get_next_type = qmp_input_get_next_type;
|
||||
|
||||
qmp_input_push(v, obj, NULL);
|
||||
qobject_incref(obj);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
|
||||
{
|
||||
QmpInputVisitor *v;
|
||||
|
||||
v = qmp_input_visitor_new(obj);
|
||||
v->strict = true;
|
||||
|
||||
return v;
|
||||
}
|
||||
241
qemu/qapi/qmp-output-visitor.c
Normal file
241
qemu/qapi/qmp-output-visitor.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Core Definitions for QAPI/QMP Command Registry
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.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 "qapi/qmp-output-visitor.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/types.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
typedef struct QStackEntry
|
||||
{
|
||||
QObject *value;
|
||||
bool is_list_head;
|
||||
QTAILQ_ENTRY(QStackEntry) node;
|
||||
} QStackEntry;
|
||||
|
||||
typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
|
||||
|
||||
struct QmpOutputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
QStack stack;
|
||||
};
|
||||
|
||||
#define qmp_output_add(qov, name, value) \
|
||||
qmp_output_add_obj(qov, name, QOBJECT(value))
|
||||
#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value))
|
||||
|
||||
static QmpOutputVisitor *to_qov(Visitor *v)
|
||||
{
|
||||
return container_of(v, QmpOutputVisitor, visitor);
|
||||
}
|
||||
|
||||
static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
|
||||
{
|
||||
QStackEntry *e = g_malloc0(sizeof(*e));
|
||||
|
||||
e->value = value;
|
||||
if (qobject_type(e->value) == QTYPE_QLIST) {
|
||||
e->is_list_head = true;
|
||||
}
|
||||
QTAILQ_INSERT_HEAD(&qov->stack, e, node);
|
||||
}
|
||||
|
||||
static QObject *qmp_output_pop(QmpOutputVisitor *qov)
|
||||
{
|
||||
QStackEntry *e = QTAILQ_FIRST(&qov->stack);
|
||||
QObject *value;
|
||||
QTAILQ_REMOVE(&qov->stack, e, node);
|
||||
value = e->value;
|
||||
g_free(e);
|
||||
return value;
|
||||
}
|
||||
|
||||
static QObject *qmp_output_first(QmpOutputVisitor *qov)
|
||||
{
|
||||
QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
|
||||
|
||||
/* FIXME - find a better way to deal with NULL values */
|
||||
if (!e) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return e->value;
|
||||
}
|
||||
|
||||
static QObject *qmp_output_last(QmpOutputVisitor *qov)
|
||||
{
|
||||
QStackEntry *e = QTAILQ_FIRST(&qov->stack);
|
||||
return e->value;
|
||||
}
|
||||
|
||||
static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
|
||||
QObject *value)
|
||||
{
|
||||
QObject *cur;
|
||||
|
||||
if (QTAILQ_EMPTY(&qov->stack)) {
|
||||
qmp_output_push_obj(qov, value);
|
||||
return;
|
||||
}
|
||||
|
||||
cur = qmp_output_last(qov);
|
||||
|
||||
switch (qobject_type(cur)) {
|
||||
case QTYPE_QDICT:
|
||||
qdict_put_obj(qobject_to_qdict(cur), name, value);
|
||||
break;
|
||||
case QTYPE_QLIST:
|
||||
qlist_append_obj(qobject_to_qlist(cur), value);
|
||||
break;
|
||||
default:
|
||||
qobject_decref(qmp_output_pop(qov));
|
||||
qmp_output_push_obj(qov, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind,
|
||||
const char *name, size_t unused,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
QDict *dict = qdict_new();
|
||||
|
||||
qmp_output_add(qov, name, dict);
|
||||
qmp_output_push(qov, dict);
|
||||
}
|
||||
|
||||
static void qmp_output_end_struct(Visitor *v, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qmp_output_pop(qov);
|
||||
}
|
||||
|
||||
static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
QList *list = qlist_new();
|
||||
|
||||
qmp_output_add(qov, name, list);
|
||||
qmp_output_push(qov, list);
|
||||
}
|
||||
|
||||
static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
|
||||
Error **errp)
|
||||
{
|
||||
GenericList *list = *listp;
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
QStackEntry *e = QTAILQ_FIRST(&qov->stack);
|
||||
|
||||
assert(e);
|
||||
if (e->is_list_head) {
|
||||
e->is_list_head = false;
|
||||
return list;
|
||||
}
|
||||
|
||||
return list ? list->next : NULL;
|
||||
}
|
||||
|
||||
static void qmp_output_end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qmp_output_pop(qov);
|
||||
}
|
||||
|
||||
static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qmp_output_add(qov, name, qint_from_int(*obj));
|
||||
}
|
||||
|
||||
static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qmp_output_add(qov, name, qbool_from_int(*obj));
|
||||
}
|
||||
|
||||
static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
if (*obj) {
|
||||
qmp_output_add(qov, name, qstring_from_str(*obj));
|
||||
} else {
|
||||
qmp_output_add(qov, name, qstring_from_str(""));
|
||||
}
|
||||
}
|
||||
|
||||
static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
QmpOutputVisitor *qov = to_qov(v);
|
||||
qmp_output_add(qov, name, qfloat_from_double(*obj));
|
||||
}
|
||||
|
||||
QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
|
||||
{
|
||||
QObject *obj = qmp_output_first(qov);
|
||||
if (obj) {
|
||||
qobject_incref(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
|
||||
{
|
||||
return &v->visitor;
|
||||
}
|
||||
|
||||
void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
|
||||
{
|
||||
QStackEntry *e, *tmp;
|
||||
|
||||
/* The bottom QStackEntry, if any, owns the root QObject. See the
|
||||
* qmp_output_push_obj() invocations in qmp_output_add_obj(). */
|
||||
QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
|
||||
|
||||
QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
|
||||
QTAILQ_REMOVE(&v->stack, e, node);
|
||||
g_free(e);
|
||||
}
|
||||
|
||||
qobject_decref(root);
|
||||
g_free(v);
|
||||
}
|
||||
|
||||
QmpOutputVisitor *qmp_output_visitor_new(void)
|
||||
{
|
||||
QmpOutputVisitor *v;
|
||||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->visitor.start_struct = qmp_output_start_struct;
|
||||
v->visitor.end_struct = qmp_output_end_struct;
|
||||
v->visitor.start_list = qmp_output_start_list;
|
||||
v->visitor.next_list = qmp_output_next_list;
|
||||
v->visitor.end_list = qmp_output_end_list;
|
||||
v->visitor.type_enum = output_type_enum;
|
||||
v->visitor.type_int = qmp_output_type_int;
|
||||
v->visitor.type_bool = qmp_output_type_bool;
|
||||
v->visitor.type_str = qmp_output_type_str;
|
||||
v->visitor.type_number = qmp_output_type_number;
|
||||
|
||||
QTAILQ_INIT(&v->stack);
|
||||
|
||||
return v;
|
||||
}
|
||||
347
qemu/qapi/string-input-visitor.c
Normal file
347
qemu/qapi/string-input-visitor.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* String parsing visitor
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2012
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@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-common.h"
|
||||
#include "qapi/string-input-visitor.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/range.h"
|
||||
|
||||
|
||||
struct StringInputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
|
||||
bool head;
|
||||
|
||||
GList *ranges;
|
||||
GList *cur_range;
|
||||
int64_t cur;
|
||||
|
||||
const char *string;
|
||||
};
|
||||
|
||||
static void free_range(void *range, void *dummy)
|
||||
{
|
||||
g_free(range);
|
||||
}
|
||||
|
||||
static void parse_str(StringInputVisitor *siv, Error **errp)
|
||||
{
|
||||
char *str = (char *) siv->string;
|
||||
long long start, end;
|
||||
Range *cur;
|
||||
char *endptr;
|
||||
|
||||
if (siv->ranges) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
start = strtoll(str, &endptr, 0);
|
||||
if (errno == 0 && endptr > str) {
|
||||
if (*endptr == '\0') {
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
cur->begin = start;
|
||||
cur->end = start + 1;
|
||||
siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
|
||||
range_compare);
|
||||
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));
|
||||
cur->begin = start;
|
||||
cur->end = end + 1;
|
||||
siv->ranges =
|
||||
g_list_insert_sorted_merged(siv->ranges,
|
||||
cur,
|
||||
range_compare);
|
||||
cur = NULL;
|
||||
str = NULL;
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
cur->begin = start;
|
||||
cur->end = end + 1;
|
||||
siv->ranges =
|
||||
g_list_insert_sorted_merged(siv->ranges,
|
||||
cur,
|
||||
range_compare);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
cur->begin = start;
|
||||
cur->end = start + 1;
|
||||
siv->ranges = g_list_insert_sorted_merged(siv->ranges,
|
||||
cur,
|
||||
range_compare);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} while (str);
|
||||
|
||||
return;
|
||||
error:
|
||||
g_list_foreach(siv->ranges, free_range, NULL);
|
||||
g_list_free(siv->ranges);
|
||||
siv->ranges = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
|
||||
parse_str(siv, errp);
|
||||
|
||||
siv->cur_range = g_list_first(siv->ranges);
|
||||
if (siv->cur_range) {
|
||||
Range *r = siv->cur_range->data;
|
||||
if (r) {
|
||||
siv->cur = r->begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GenericList *
|
||||
next_list(Visitor *v, GenericList **list, Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
GenericList **link;
|
||||
Range *r;
|
||||
|
||||
if (!siv->ranges || !siv->cur_range) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (siv->cur < r->begin || siv->cur >= r->end) {
|
||||
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 = r->begin;
|
||||
}
|
||||
|
||||
if (siv->head) {
|
||||
link = list;
|
||||
siv->head = false;
|
||||
} else {
|
||||
link = &(*list)->next;
|
||||
}
|
||||
|
||||
*link = g_malloc0(sizeof **link);
|
||||
return *link;
|
||||
}
|
||||
|
||||
static void
|
||||
end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
siv->head = true;
|
||||
}
|
||||
|
||||
static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
|
||||
if (!siv->string) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"integer");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_str(siv, errp);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
siv->cur = r->begin;
|
||||
}
|
||||
|
||||
*obj = siv->cur;
|
||||
siv->cur++;
|
||||
return;
|
||||
|
||||
error:
|
||||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
|
||||
"an int64 value or range");
|
||||
}
|
||||
|
||||
static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
Error *err = NULL;
|
||||
uint64_t val;
|
||||
|
||||
if (siv->string) {
|
||||
parse_option_size(name, siv->string, &val, &err);
|
||||
} else {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"size");
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = val;
|
||||
}
|
||||
|
||||
static void parse_type_bool(Visitor *v, bool *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
|
||||
if (siv->string) {
|
||||
if (!strcasecmp(siv->string, "on") ||
|
||||
!strcasecmp(siv->string, "yes") ||
|
||||
!strcasecmp(siv->string, "true")) {
|
||||
*obj = true;
|
||||
return;
|
||||
}
|
||||
if (!strcasecmp(siv->string, "off") ||
|
||||
!strcasecmp(siv->string, "no") ||
|
||||
!strcasecmp(siv->string, "false")) {
|
||||
*obj = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"boolean");
|
||||
}
|
||||
|
||||
static void parse_type_str(Visitor *v, char **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
if (siv->string) {
|
||||
*obj = g_strdup(siv->string);
|
||||
} else {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"string");
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_type_number(Visitor *v, double *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
char *endp = (char *) siv->string;
|
||||
double val;
|
||||
|
||||
errno = 0;
|
||||
if (siv->string) {
|
||||
val = strtod(siv->string, &endp);
|
||||
}
|
||||
if (!siv->string || errno || endp == siv->string || *endp) {
|
||||
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"number");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = val;
|
||||
}
|
||||
|
||||
static void parse_optional(Visitor *v, bool *present, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
|
||||
|
||||
if (!siv->string) {
|
||||
*present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
*present = true;
|
||||
}
|
||||
|
||||
Visitor *string_input_get_visitor(StringInputVisitor *v)
|
||||
{
|
||||
return &v->visitor;
|
||||
}
|
||||
|
||||
void string_input_visitor_cleanup(StringInputVisitor *v)
|
||||
{
|
||||
g_list_foreach(v->ranges, free_range, NULL);
|
||||
g_list_free(v->ranges);
|
||||
g_free(v);
|
||||
}
|
||||
|
||||
StringInputVisitor *string_input_visitor_new(const char *str)
|
||||
{
|
||||
StringInputVisitor *v;
|
||||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->visitor.type_enum = input_type_enum;
|
||||
v->visitor.type_int = parse_type_int;
|
||||
v->visitor.type_size = parse_type_size;
|
||||
v->visitor.type_bool = parse_type_bool;
|
||||
v->visitor.type_str = parse_type_str;
|
||||
v->visitor.type_number = parse_type_number;
|
||||
v->visitor.start_list = start_list;
|
||||
v->visitor.next_list = next_list;
|
||||
v->visitor.end_list = end_list;
|
||||
v->visitor.optional = parse_optional;
|
||||
|
||||
v->string = str;
|
||||
v->head = true;
|
||||
return v;
|
||||
}
|
||||
354
qemu/qapi/string-output-visitor.c
Normal file
354
qemu/qapi/string-output-visitor.c
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* String printing Visitor
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2012
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@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-common.h"
|
||||
#include "qapi/string-output-visitor.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include <math.h>
|
||||
#include "qemu/range.h"
|
||||
|
||||
enum ListMode {
|
||||
LM_NONE, /* not traversing a list of repeated options */
|
||||
LM_STARTED, /* start_list() succeeded */
|
||||
|
||||
LM_IN_PROGRESS, /* next_list() has been called.
|
||||
*
|
||||
* Generating the next list link will consume the most
|
||||
* recently parsed QemuOpt instance of the repeated
|
||||
* option.
|
||||
*
|
||||
* Parsing a value into the list link will examine the
|
||||
* next QemuOpt instance of the repeated option, and
|
||||
* possibly enter LM_SIGNED_INTERVAL or
|
||||
* LM_UNSIGNED_INTERVAL.
|
||||
*/
|
||||
|
||||
LM_SIGNED_INTERVAL, /* next_list() has been called.
|
||||
*
|
||||
* Generating the next list link will consume the most
|
||||
* recently stored element from the signed interval,
|
||||
* parsed from the most recent QemuOpt instance of the
|
||||
* repeated option. This may consume QemuOpt itself
|
||||
* and return to LM_IN_PROGRESS.
|
||||
*
|
||||
* Parsing a value into the list link will store the
|
||||
* next element of the signed interval.
|
||||
*/
|
||||
|
||||
LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
|
||||
|
||||
LM_END
|
||||
};
|
||||
|
||||
typedef enum ListMode ListMode;
|
||||
|
||||
struct StringOutputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
bool human;
|
||||
GString *string;
|
||||
bool head;
|
||||
ListMode list_mode;
|
||||
union {
|
||||
int64_t s;
|
||||
uint64_t u;
|
||||
} range_start, range_end;
|
||||
GList *ranges;
|
||||
};
|
||||
|
||||
static void string_output_set(StringOutputVisitor *sov, char *string)
|
||||
{
|
||||
if (sov->string) {
|
||||
g_string_free(sov->string, true);
|
||||
}
|
||||
sov->string = g_string_new(string);
|
||||
g_free(string);
|
||||
}
|
||||
|
||||
static void string_output_append(StringOutputVisitor *sov, int64_t a)
|
||||
{
|
||||
Range *r = g_malloc0(sizeof(*r));
|
||||
r->begin = a;
|
||||
r->end = a + 1;
|
||||
sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
|
||||
}
|
||||
|
||||
static void string_output_append_range(StringOutputVisitor *sov,
|
||||
int64_t s, int64_t e)
|
||||
{
|
||||
Range *r = g_malloc0(sizeof(*r));
|
||||
r->begin = s;
|
||||
r->end = e + 1;
|
||||
sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
|
||||
}
|
||||
|
||||
static void format_string(StringOutputVisitor *sov, Range *r, bool next,
|
||||
bool human)
|
||||
{
|
||||
if (r->end - r->begin > 1) {
|
||||
if (human) {
|
||||
g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
|
||||
r->begin, r->end - 1);
|
||||
|
||||
} else {
|
||||
g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
|
||||
r->begin, r->end - 1);
|
||||
}
|
||||
} else {
|
||||
if (human) {
|
||||
g_string_append_printf(sov->string, "0x%" PRIx64, r->begin);
|
||||
} else {
|
||||
g_string_append_printf(sov->string, "%" PRId64, r->begin);
|
||||
}
|
||||
}
|
||||
if (next) {
|
||||
g_string_append(sov->string, ",");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
GList *l;
|
||||
|
||||
switch (sov->list_mode) {
|
||||
case LM_NONE:
|
||||
string_output_append(sov, *obj);
|
||||
break;
|
||||
|
||||
case LM_STARTED:
|
||||
sov->range_start.s = *obj;
|
||||
sov->range_end.s = *obj;
|
||||
sov->list_mode = LM_IN_PROGRESS;
|
||||
return;
|
||||
|
||||
case LM_IN_PROGRESS:
|
||||
if (sov->range_end.s + 1 == *obj) {
|
||||
sov->range_end.s++;
|
||||
} else {
|
||||
if (sov->range_start.s == sov->range_end.s) {
|
||||
string_output_append(sov, sov->range_end.s);
|
||||
} else {
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
}
|
||||
|
||||
sov->range_start.s = *obj;
|
||||
sov->range_end.s = *obj;
|
||||
}
|
||||
return;
|
||||
|
||||
case LM_END:
|
||||
if (sov->range_end.s + 1 == *obj) {
|
||||
sov->range_end.s++;
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
} else {
|
||||
if (sov->range_start.s == sov->range_end.s) {
|
||||
string_output_append(sov, sov->range_end.s);
|
||||
} else {
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
}
|
||||
string_output_append(sov, *obj);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
l = sov->ranges;
|
||||
while (l) {
|
||||
Range *r = l->data;
|
||||
format_string(sov, r, l->next != NULL, false);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (sov->human) {
|
||||
l = sov->ranges;
|
||||
g_string_append(sov->string, " (");
|
||||
while (l) {
|
||||
Range *r = l->data;
|
||||
format_string(sov, r, l->next != NULL, true);
|
||||
l = l->next;
|
||||
}
|
||||
g_string_append(sov->string, ")");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' };
|
||||
uint64_t div, val;
|
||||
char *out;
|
||||
int i;
|
||||
|
||||
if (!sov->human) {
|
||||
out = g_strdup_printf("%"PRIu64, *obj);
|
||||
string_output_set(sov, out);
|
||||
return;
|
||||
}
|
||||
|
||||
val = *obj;
|
||||
|
||||
/* The exponent (returned in i) minus one gives us
|
||||
* floor(log2(val * 1024 / 1000). The correction makes us
|
||||
* switch to the higher power when the integer part is >= 1000.
|
||||
*/
|
||||
frexp(val / (1000.0 / 1024.0), &i);
|
||||
i = (i - 1) / 10;
|
||||
assert(i < ARRAY_SIZE(suffixes));
|
||||
div = 1ULL << (i * 10);
|
||||
|
||||
out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val,
|
||||
(double)val/div, suffixes[i], i ? "iB" : "");
|
||||
string_output_set(sov, out);
|
||||
}
|
||||
|
||||
static void print_type_bool(Visitor *v, bool *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
string_output_set(sov, g_strdup(*obj ? "true" : "false"));
|
||||
}
|
||||
|
||||
static void print_type_str(Visitor *v, char **obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
char *out;
|
||||
|
||||
if (sov->human) {
|
||||
out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
|
||||
} else {
|
||||
out = g_strdup(*obj ? *obj : "");
|
||||
}
|
||||
string_output_set(sov, out);
|
||||
}
|
||||
|
||||
static void print_type_number(Visitor *v, double *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
string_output_set(sov, g_strdup_printf("%f", *obj));
|
||||
}
|
||||
|
||||
static void
|
||||
start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
|
||||
/* we can't traverse a list in a list */
|
||||
assert(sov->list_mode == LM_NONE);
|
||||
sov->list_mode = LM_STARTED;
|
||||
sov->head = true;
|
||||
}
|
||||
|
||||
static GenericList *
|
||||
next_list(Visitor *v, GenericList **list, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
GenericList *ret = NULL;
|
||||
if (*list) {
|
||||
if (sov->head) {
|
||||
ret = *list;
|
||||
} else {
|
||||
ret = (*list)->next;
|
||||
}
|
||||
|
||||
if (sov->head) {
|
||||
if (ret && ret->next == NULL) {
|
||||
sov->list_mode = LM_NONE;
|
||||
}
|
||||
sov->head = false;
|
||||
} else {
|
||||
if (ret && ret->next == NULL) {
|
||||
sov->list_mode = LM_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
|
||||
assert(sov->list_mode == LM_STARTED ||
|
||||
sov->list_mode == LM_END ||
|
||||
sov->list_mode == LM_NONE ||
|
||||
sov->list_mode == LM_IN_PROGRESS);
|
||||
sov->list_mode = LM_NONE;
|
||||
sov->head = true;
|
||||
|
||||
}
|
||||
|
||||
char *string_output_get_string(StringOutputVisitor *sov)
|
||||
{
|
||||
char *string = g_string_free(sov->string, false);
|
||||
sov->string = NULL;
|
||||
return string;
|
||||
}
|
||||
|
||||
Visitor *string_output_get_visitor(StringOutputVisitor *sov)
|
||||
{
|
||||
return &sov->visitor;
|
||||
}
|
||||
|
||||
static void free_range(void *range, void *dummy)
|
||||
{
|
||||
g_free(range);
|
||||
}
|
||||
|
||||
void string_output_visitor_cleanup(StringOutputVisitor *sov)
|
||||
{
|
||||
if (sov->string) {
|
||||
g_string_free(sov->string, true);
|
||||
}
|
||||
|
||||
g_list_foreach(sov->ranges, free_range, NULL);
|
||||
g_list_free(sov->ranges);
|
||||
g_free(sov);
|
||||
}
|
||||
|
||||
StringOutputVisitor *string_output_visitor_new(bool human)
|
||||
{
|
||||
StringOutputVisitor *v;
|
||||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->string = g_string_new(NULL);
|
||||
v->human = human;
|
||||
v->visitor.type_enum = output_type_enum;
|
||||
v->visitor.type_int = print_type_int;
|
||||
v->visitor.type_size = print_type_size;
|
||||
v->visitor.type_bool = print_type_bool;
|
||||
v->visitor.type_str = print_type_str;
|
||||
v->visitor.type_number = print_type_number;
|
||||
v->visitor.start_list = start_list;
|
||||
v->visitor.next_list = next_list;
|
||||
v->visitor.end_list = end_list;
|
||||
|
||||
return v;
|
||||
}
|
||||
Reference in New Issue
Block a user