/*
* Options Visitor
*
- * Copyright Red Hat, Inc. 2012, 2013
+ * Copyright Red Hat, Inc. 2012-2016
*
*
*
*/
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
#include "qapi/qmp/qerror.h"
#include "qapi/opts-visitor.h"
#include "qemu/queue.h"
enum ListMode
{
LM_NONE, /* not traversing a list of repeated options */
- LM_STARTED, /* opts_start_list() succeeded */
- LM_IN_PROGRESS, /* opts_next_list() has been called.
+ LM_IN_PROGRESS, /* opts_next_list() ready to be called.
*
* Generating the next list link will consume the most
* recently parsed QemuOpt instance of the repeated
};
+static OptsVisitor *to_ov(Visitor *v)
+{
+ return container_of(v, OptsVisitor, visitor);
+}
+
+
static void
destroy_list(gpointer list)
{
static void
-opts_start_struct(Visitor *v, void **obj, const char *kind,
- const char *name, size_t size, Error **errp)
+opts_start_struct(Visitor *v, const char *name, void **obj,
+ size_t size, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
if (obj) {
- *obj = g_malloc0(size > 0 ? size : 1);
+ *obj = g_malloc0(size);
}
if (ov->depth++ > 0) {
return;
if (ov->opts_root->id != NULL) {
ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
- ov->fake_id_opt->name = "id";
- ov->fake_id_opt->str = ov->opts_root->id;
+ ov->fake_id_opt->name = g_strdup("id");
+ ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
}
}
-static gboolean
-ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
-{
- return TRUE;
-}
-
-
static void
-opts_end_struct(Visitor *v, Error **errp)
+opts_check_struct(Visitor *v, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
+ GHashTableIter iter;
GQueue *any;
- if (--ov->depth > 0) {
+ if (ov->depth > 1) {
return;
}
/* we should have processed all (distinct) QemuOpt instances */
- any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
- if (any) {
+ g_hash_table_iter_init(&iter, ov->unprocessed_opts);
+ if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
const QemuOpt *first;
first = g_queue_peek_head(any);
- error_set(errp, QERR_INVALID_PARAMETER, first->name);
+ error_setg(errp, QERR_INVALID_PARAMETER, first->name);
+ }
+}
+
+
+static void
+opts_end_struct(Visitor *v, void **obj)
+{
+ OptsVisitor *ov = to_ov(v);
+
+ if (--ov->depth > 0) {
+ return;
}
+
g_hash_table_destroy(ov->unprocessed_opts);
ov->unprocessed_opts = NULL;
- g_free(ov->fake_id_opt);
+ if (ov->fake_id_opt) {
+ g_free(ov->fake_id_opt->name);
+ g_free(ov->fake_id_opt->str);
+ g_free(ov->fake_id_opt);
+ }
ov->fake_id_opt = NULL;
}
list = g_hash_table_lookup(ov->unprocessed_opts, name);
if (!list) {
- error_set(errp, QERR_MISSING_PARAMETER, name);
+ error_setg(errp, QERR_MISSING_PARAMETER, name);
}
return list;
}
static void
-opts_start_list(Visitor *v, const char *name, Error **errp)
+opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+ Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
/* we can't traverse a list in a list */
assert(ov->list_mode == LM_NONE);
+ /* we don't support visits without a list */
+ assert(list);
ov->repeated_opts = lookup_distinct(ov, name, errp);
- if (ov->repeated_opts != NULL) {
- ov->list_mode = LM_STARTED;
+ if (ov->repeated_opts) {
+ ov->list_mode = LM_IN_PROGRESS;
+ *list = g_malloc0(size);
+ } else {
+ *list = NULL;
}
}
static GenericList *
-opts_next_list(Visitor *v, GenericList **list, Error **errp)
+opts_next_list(Visitor *v, GenericList *tail, size_t size)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
- GenericList **link;
+ OptsVisitor *ov = to_ov(v);
switch (ov->list_mode) {
- case LM_STARTED:
- ov->list_mode = LM_IN_PROGRESS;
- link = list;
- break;
-
case LM_SIGNED_INTERVAL:
case LM_UNSIGNED_INTERVAL:
- link = &(*list)->next;
-
if (ov->list_mode == LM_SIGNED_INTERVAL) {
if (ov->range_next.s < ov->range_limit.s) {
++ov->range_next.s;
g_hash_table_remove(ov->unprocessed_opts, opt->name);
return NULL;
}
- link = &(*list)->next;
break;
}
abort();
}
- *link = g_malloc0(sizeof **link);
- return *link;
+ tail->next = g_malloc0(size);
+ return tail->next;
}
static void
-opts_end_list(Visitor *v, Error **errp)
+opts_check_list(Visitor *v, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ /*
+ * Unvisited list elements will be reported later when checking
+ * whether unvisited struct members remain.
+ */
+}
+
- assert(ov->list_mode == LM_STARTED ||
- ov->list_mode == LM_IN_PROGRESS ||
+static void
+opts_end_list(Visitor *v, void **obj)
+{
+ OptsVisitor *ov = to_ov(v);
+
+ assert(ov->list_mode == LM_IN_PROGRESS ||
ov->list_mode == LM_SIGNED_INTERVAL ||
ov->list_mode == LM_UNSIGNED_INTERVAL);
ov->repeated_opts = NULL;
static void
-opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
opt = lookup_scalar(ov, name, errp);
if (!opt) {
+ *obj = NULL;
return;
}
*obj = g_strdup(opt->str ? opt->str : "");
+ /* Note that we consume a string even if this is called as part of
+ * an enum visit that later fails because the string is not a
+ * valid enum value; this is harmless because tracking what gets
+ * consumed only matters to visit_end_struct() as the final error
+ * check if there were no other failures during the visit. */
processed(ov, name);
}
/* mimics qemu-option.c::parse_option_bool() */
static void
-opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
opt = lookup_scalar(ov, name, errp);
strcmp(opt->str, "n") == 0) {
*obj = false;
} else {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
- "on|yes|y|off|no|n");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "on|yes|y|off|no|n");
return;
}
} else {
static void
-opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
const char *str;
long long val;
}
}
}
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
- (ov->list_mode == LM_NONE) ? "an int64 value" :
- "an int64 value or range");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ (ov->list_mode == LM_NONE) ? "an int64 value" :
+ "an int64 value or range");
}
static void
-opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
const char *str;
unsigned long long val;
}
}
}
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
- (ov->list_mode == LM_NONE) ? "a uint64 value" :
- "a uint64 value or range");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ (ov->list_mode == LM_NONE) ? "a uint64 value" :
+ "a uint64 value or range");
}
static void
-opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
- int64_t val;
- char *endptr;
+ int err;
opt = lookup_scalar(ov, name, errp);
if (!opt) {
return;
}
- val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
- STRTOSZ_DEFSUFFIX_B);
- if (val < 0 || *endptr) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
- "a size value representible as a non-negative int64");
+ err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj);
+ if (err < 0) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "a size value");
return;
}
- *obj = val;
processed(ov, name);
}
static void
-opts_start_optional(Visitor *v, bool *present, const char *name,
- Error **errp)
+opts_optional(Visitor *v, const char *name, bool *present)
{
- OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+ OptsVisitor *ov = to_ov(v);
/* we only support a single mandatory scalar field in a list node */
assert(ov->list_mode == LM_NONE);
}
-OptsVisitor *
+static void
+opts_free(Visitor *v)
+{
+ OptsVisitor *ov = to_ov(v);
+
+ if (ov->unprocessed_opts != NULL) {
+ g_hash_table_destroy(ov->unprocessed_opts);
+ }
+ g_free(ov->fake_id_opt);
+ g_free(ov);
+}
+
+
+Visitor *
opts_visitor_new(const QemuOpts *opts)
{
OptsVisitor *ov;
+ assert(opts);
ov = g_malloc0(sizeof *ov);
+ ov->visitor.type = VISITOR_INPUT;
+
ov->visitor.start_struct = &opts_start_struct;
+ ov->visitor.check_struct = &opts_check_struct;
ov->visitor.end_struct = &opts_end_struct;
ov->visitor.start_list = &opts_start_list;
ov->visitor.next_list = &opts_next_list;
+ ov->visitor.check_list = &opts_check_list;
ov->visitor.end_list = &opts_end_list;
- /* input_type_enum() covers both "normal" enums and union discriminators.
- * The union discriminator field is always generated as "type"; it should
- * match the "type" QemuOpt child of any QemuOpts.
- *
- * input_type_enum() will remove the looked-up key from the
- * "unprocessed_opts" hash even if the lookup fails, because the removal is
- * done earlier in opts_type_str(). This should be harmless.
- */
- ov->visitor.type_enum = &input_type_enum;
-
- ov->visitor.type_int = &opts_type_int;
+ ov->visitor.type_int64 = &opts_type_int64;
ov->visitor.type_uint64 = &opts_type_uint64;
ov->visitor.type_size = &opts_type_size;
ov->visitor.type_bool = &opts_type_bool;
/* type_number() is not filled in, but this is not the first visitor to
* skip some mandatory methods... */
- ov->visitor.start_optional = &opts_start_optional;
+ ov->visitor.optional = &opts_optional;
+ ov->visitor.free = opts_free;
ov->opts_root = opts;
- return ov;
-}
-
-
-void
-opts_visitor_cleanup(OptsVisitor *ov)
-{
- if (ov->unprocessed_opts != NULL) {
- g_hash_table_destroy(ov->unprocessed_opts);
- }
- g_free(ov->fake_id_opt);
- g_free(ov);
-}
-
-
-Visitor *
-opts_get_visitor(OptsVisitor *ov)
-{
return &ov->visitor;
}