| /* | |
| * Input Visitor | |
| * | |
| * Copyright (C) 2012-2017 Red Hat, Inc. | |
| * 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/osdep.h" | |
| #include <math.h> | |
| #include "qapi/compat-policy.h" | |
| #include "qapi/error.h" | |
| #include "qapi/qobject-input-visitor.h" | |
| #include "qapi/visitor-impl.h" | |
| #include "qemu/queue.h" | |
| #include "qobject/qjson.h" | |
| #include "qobject/qbool.h" | |
| #include "qobject/qdict.h" | |
| #include "qapi/qmp/qerror.h" | |
| #include "qobject/qlist.h" | |
| #include "qobject/qnull.h" | |
| #include "qobject/qnum.h" | |
| #include "qobject/qstring.h" | |
| #include "qemu/cutils.h" | |
| #include "qemu/keyval.h" | |
| typedef struct StackObject { | |
| const char *name; /* Name of @obj in its parent, if any */ | |
| QObject *obj; /* QDict or QList being visited */ | |
| void *qapi; /* sanity check that caller uses same pointer */ | |
| GHashTable *h; /* If @obj is QDict: unvisited keys */ | |
| const QListEntry *entry; /* If @obj is QList: unvisited tail */ | |
| unsigned index; /* If @obj is QList: list index of @entry */ | |
| QSLIST_ENTRY(StackObject) node; /* parent */ | |
| } StackObject; | |
| struct QObjectInputVisitor { | |
| Visitor visitor; | |
| /* Root of visit at visitor creation. */ | |
| QObject *root; | |
| bool keyval; /* Assume @root made with keyval_parse() */ | |
| /* Stack of objects being visited (all entries will be either | |
| * QDict or QList). */ | |
| QSLIST_HEAD(, StackObject) stack; | |
| GString *errname; /* Accumulator for full_name() */ | |
| }; | |
| static QObjectInputVisitor *to_qiv(Visitor *v) | |
| { | |
| return container_of(v, QObjectInputVisitor, visitor); | |
| } | |
| /* | |
| * Find the full name of something @qiv is currently visiting. | |
| * @qiv is visiting something named @name in the stack of containers | |
| * @qiv->stack. | |
| * If @n is zero, return its full name. | |
| * If @n is positive, return the full name of the @n-th container | |
| * counting from the top. The stack of containers must have at least | |
| * @n elements. | |
| * The returned string is valid until the next full_name_nth(@v) or | |
| * destruction of @v. | |
| */ | |
| static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, | |
| int n) | |
| { | |
| StackObject *so; | |
| char buf[32]; | |
| if (qiv->errname) { | |
| g_string_truncate(qiv->errname, 0); | |
| } else { | |
| qiv->errname = g_string_new(""); | |
| } | |
| QSLIST_FOREACH(so , &qiv->stack, node) { | |
| if (n) { | |
| n--; | |
| } else if (qobject_type(so->obj) == QTYPE_QDICT) { | |
| g_string_prepend(qiv->errname, name ?: "<anonymous>"); | |
| g_string_prepend_c(qiv->errname, '.'); | |
| } else { | |
| snprintf(buf, sizeof(buf), | |
| qiv->keyval ? ".%u" : "[%u]", | |
| so->index); | |
| g_string_prepend(qiv->errname, buf); | |
| } | |
| name = so->name; | |
| } | |
| assert(!n); | |
| if (name) { | |
| g_string_prepend(qiv->errname, name); | |
| } else if (qiv->errname->str[0] == '.') { | |
| g_string_erase(qiv->errname, 0, 1); | |
| } else if (!qiv->errname->str[0]) { | |
| return "<anonymous>"; | |
| } | |
| return qiv->errname->str; | |
| } | |
| static const char *full_name(QObjectInputVisitor *qiv, const char *name) | |
| { | |
| return full_name_nth(qiv, name, 0); | |
| } | |
| static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, | |
| const char *name, | |
| bool consume) | |
| { | |
| StackObject *tos; | |
| QObject *qobj; | |
| QObject *ret; | |
| if (QSLIST_EMPTY(&qiv->stack)) { | |
| /* Starting at root, name is ignored. */ | |
| assert(qiv->root); | |
| return qiv->root; | |
| } | |
| /* We are in a container; find the next element. */ | |
| tos = QSLIST_FIRST(&qiv->stack); | |
| qobj = tos->obj; | |
| assert(qobj); | |
| if (qobject_type(qobj) == QTYPE_QDICT) { | |
| assert(name); | |
| ret = qdict_get(qobject_to(QDict, qobj), name); | |
| if (tos->h && consume && ret) { | |
| bool removed = g_hash_table_remove(tos->h, name); | |
| assert(removed); | |
| } | |
| } else { | |
| assert(qobject_type(qobj) == QTYPE_QLIST); | |
| assert(!name); | |
| if (tos->entry) { | |
| ret = qlist_entry_obj(tos->entry); | |
| if (consume) { | |
| tos->entry = qlist_next(tos->entry); | |
| } | |
| } else { | |
| ret = NULL; | |
| } | |
| if (consume) { | |
| tos->index++; | |
| } | |
| } | |
| return ret; | |
| } | |
| static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, | |
| const char *name, | |
| bool consume, Error **errp) | |
| { | |
| QObject *obj = qobject_input_try_get_object(qiv, name, consume); | |
| if (!obj) { | |
| error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); | |
| } | |
| return obj; | |
| } | |
| static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, | |
| const char *name, | |
| Error **errp) | |
| { | |
| QObject *qobj; | |
| QString *qstr; | |
| qobj = qobject_input_get_object(qiv, name, true, errp); | |
| if (!qobj) { | |
| return NULL; | |
| } | |
| qstr = qobject_to(QString, qobj); | |
| if (!qstr) { | |
| switch (qobject_type(qobj)) { | |
| case QTYPE_QDICT: | |
| case QTYPE_QLIST: | |
| error_setg(errp, "Parameters '%s.*' are unexpected", | |
| full_name(qiv, name)); | |
| return NULL; | |
| default: | |
| /* Non-string scalar (should this be an assertion?) */ | |
| error_setg(errp, "Internal error: parameter %s invalid", | |
| full_name(qiv, name)); | |
| return NULL; | |
| } | |
| } | |
| return qstring_get_str(qstr); | |
| } | |
| static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, | |
| const char *name, | |
| QObject *obj, void *qapi) | |
| { | |
| GHashTable *h; | |
| StackObject *tos = g_new0(StackObject, 1); | |
| QDict *qdict = qobject_to(QDict, obj); | |
| QList *qlist = qobject_to(QList, obj); | |
| const QDictEntry *entry; | |
| assert(obj); | |
| tos->name = name; | |
| tos->obj = obj; | |
| tos->qapi = qapi; | |
| if (qdict) { | |
| h = g_hash_table_new(g_str_hash, g_str_equal); | |
| for (entry = qdict_first(qdict); | |
| entry; | |
| entry = qdict_next(qdict, entry)) { | |
| g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL); | |
| } | |
| tos->h = h; | |
| } else { | |
| assert(qlist); | |
| tos->entry = qlist_first(qlist); | |
| tos->index = -1; | |
| } | |
| QSLIST_INSERT_HEAD(&qiv->stack, tos, node); | |
| return tos->entry; | |
| } | |
| static bool qobject_input_check_struct(Visitor *v, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| GHashTableIter iter; | |
| const char *key; | |
| assert(tos && !tos->entry); | |
| g_hash_table_iter_init(&iter, tos->h); | |
| if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { | |
| error_setg(errp, "Parameter '%s' is unexpected", | |
| full_name(qiv, key)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void qobject_input_stack_object_free(StackObject *tos) | |
| { | |
| if (tos->h) { | |
| g_hash_table_unref(tos->h); | |
| } | |
| g_free(tos); | |
| } | |
| static void qobject_input_pop(Visitor *v, void **obj) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| assert(tos && tos->qapi == obj); | |
| QSLIST_REMOVE_HEAD(&qiv->stack, node); | |
| qobject_input_stack_object_free(tos); | |
| } | |
| static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, | |
| size_t size, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| if (obj) { | |
| *obj = NULL; | |
| } | |
| if (!qobj) { | |
| return false; | |
| } | |
| if (qobject_type(qobj) != QTYPE_QDICT) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: object", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| qobject_input_push(qiv, name, qobj, obj); | |
| if (obj) { | |
| *obj = g_malloc0(size); | |
| } | |
| return true; | |
| } | |
| static void qobject_input_end_struct(Visitor *v, void **obj) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); | |
| qobject_input_pop(v, obj); | |
| } | |
| static bool qobject_input_start_list(Visitor *v, const char *name, | |
| GenericList **list, size_t size, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| const QListEntry *entry; | |
| if (list) { | |
| *list = NULL; | |
| } | |
| if (!qobj) { | |
| return false; | |
| } | |
| if (qobject_type(qobj) != QTYPE_QLIST) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: array", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| entry = qobject_input_push(qiv, name, qobj, list); | |
| if (entry && list) { | |
| *list = g_malloc0(size); | |
| } | |
| return true; | |
| } | |
| static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, | |
| size_t size) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| assert(tos && qobject_to(QList, tos->obj)); | |
| if (!tos->entry) { | |
| return NULL; | |
| } | |
| tail->next = g_malloc0(size); | |
| return tail->next; | |
| } | |
| static bool qobject_input_check_list(Visitor *v, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| assert(tos && qobject_to(QList, tos->obj)); | |
| if (tos->entry) { | |
| error_setg(errp, "Only %u list elements expected in %s", | |
| tos->index + 1, full_name_nth(qiv, NULL, 1)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void qobject_input_end_list(Visitor *v, void **obj) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); | |
| qobject_input_pop(v, obj); | |
| } | |
| static bool qobject_input_start_alternate(Visitor *v, const char *name, | |
| GenericAlternate **obj, size_t size, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, false, errp); | |
| if (!qobj) { | |
| *obj = NULL; | |
| return false; | |
| } | |
| *obj = g_malloc0(size); | |
| (*obj)->type = qobject_type(qobj); | |
| return true; | |
| } | |
| static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| QNum *qnum; | |
| if (!qobj) { | |
| return false; | |
| } | |
| qnum = qobject_to(QNum, qobj); | |
| if (!qnum || !qnum_get_try_int(qnum, obj)) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: integer", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, | |
| int64_t *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| if (!str) { | |
| return false; | |
| } | |
| if (qemu_strtoi64(str, NULL, 0, obj) < 0) { | |
| /* TODO report -ERANGE more nicely */ | |
| error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
| full_name(qiv, name), "integer"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool qobject_input_type_uint64(Visitor *v, const char *name, | |
| uint64_t *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| QNum *qnum; | |
| int64_t val; | |
| if (!qobj) { | |
| return false; | |
| } | |
| qnum = qobject_to(QNum, qobj); | |
| if (!qnum) { | |
| goto err; | |
| } | |
| if (qnum_get_try_uint(qnum, obj)) { | |
| return true; | |
| } | |
| /* Need to accept negative values for backward compatibility */ | |
| if (qnum_get_try_int(qnum, &val)) { | |
| *obj = val; | |
| return true; | |
| } | |
| err: | |
| error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
| full_name(qiv, name), "uint64"); | |
| return false; | |
| } | |
| static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, | |
| uint64_t *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| if (!str) { | |
| return false; | |
| } | |
| if (qemu_strtou64(str, NULL, 0, obj) < 0) { | |
| /* TODO report -ERANGE more nicely */ | |
| error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
| full_name(qiv, name), "integer"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| QBool *qbool; | |
| if (!qobj) { | |
| return false; | |
| } | |
| qbool = qobject_to(QBool, qobj); | |
| if (!qbool) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: boolean", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| *obj = qbool_get_bool(qbool); | |
| return true; | |
| } | |
| static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, | |
| bool *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| if (!str) { | |
| return false; | |
| } | |
| if (!qapi_bool_parse(name, str, obj, NULL)) { | |
| error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
| full_name(qiv, name), "'on' or 'off'"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| QString *qstr; | |
| *obj = NULL; | |
| if (!qobj) { | |
| return false; | |
| } | |
| qstr = qobject_to(QString, qobj); | |
| if (!qstr) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: string", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| *obj = g_strdup(qstring_get_str(qstr)); | |
| return true; | |
| } | |
| static bool qobject_input_type_str_keyval(Visitor *v, const char *name, | |
| char **obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| *obj = g_strdup(str); | |
| return !!str; | |
| } | |
| static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| QNum *qnum; | |
| if (!qobj) { | |
| return false; | |
| } | |
| qnum = qobject_to(QNum, qobj); | |
| if (!qnum) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: number", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| *obj = qnum_get_double(qnum); | |
| return true; | |
| } | |
| static bool qobject_input_type_number_keyval(Visitor *v, const char *name, | |
| double *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| double val; | |
| if (!str) { | |
| return false; | |
| } | |
| if (qemu_strtod_finite(str, NULL, &val)) { | |
| /* TODO report -ERANGE more nicely */ | |
| error_setg(errp, "Invalid parameter type for '%s', expected: number", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| *obj = val; | |
| return true; | |
| } | |
| static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj, | |
| Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| *obj = NULL; | |
| if (!qobj) { | |
| return false; | |
| } | |
| *obj = qobject_ref(qobj); | |
| return true; | |
| } | |
| static bool qobject_input_type_null(Visitor *v, const char *name, | |
| QNull **obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_get_object(qiv, name, true, errp); | |
| *obj = NULL; | |
| if (!qobj) { | |
| return false; | |
| } | |
| if (qobject_type(qobj) != QTYPE_QNULL) { | |
| error_setg(errp, "Invalid parameter type for '%s', expected: null", | |
| full_name(qiv, name)); | |
| return false; | |
| } | |
| *obj = qnull(); | |
| return true; | |
| } | |
| static bool qobject_input_type_size_keyval(Visitor *v, const char *name, | |
| uint64_t *obj, Error **errp) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| const char *str = qobject_input_get_keyval(qiv, name, errp); | |
| if (!str) { | |
| return false; | |
| } | |
| if (qemu_strtosz(str, NULL, obj) < 0) { | |
| /* TODO report -ERANGE more nicely */ | |
| error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
| full_name(qiv, name), "size"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void qobject_input_optional(Visitor *v, const char *name, bool *present) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| QObject *qobj = qobject_input_try_get_object(qiv, name, false); | |
| if (!qobj) { | |
| *present = false; | |
| return; | |
| } | |
| *present = true; | |
| } | |
| static bool qobject_input_policy_reject(Visitor *v, const char *name, | |
| uint64_t features, | |
| Error **errp) | |
| { | |
| return !compat_policy_input_ok(features, &v->compat_policy, | |
| ERROR_CLASS_GENERIC_ERROR, | |
| "parameter", name, errp); | |
| } | |
| static void qobject_input_free(Visitor *v) | |
| { | |
| QObjectInputVisitor *qiv = to_qiv(v); | |
| while (!QSLIST_EMPTY(&qiv->stack)) { | |
| StackObject *tos = QSLIST_FIRST(&qiv->stack); | |
| QSLIST_REMOVE_HEAD(&qiv->stack, node); | |
| qobject_input_stack_object_free(tos); | |
| } | |
| qobject_unref(qiv->root); | |
| if (qiv->errname) { | |
| g_string_free(qiv->errname, TRUE); | |
| } | |
| g_free(qiv); | |
| } | |
| static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) | |
| { | |
| QObjectInputVisitor *v = g_malloc0(sizeof(*v)); | |
| assert(obj); | |
| v->visitor.type = VISITOR_INPUT; | |
| v->visitor.start_struct = qobject_input_start_struct; | |
| v->visitor.check_struct = qobject_input_check_struct; | |
| v->visitor.end_struct = qobject_input_end_struct; | |
| v->visitor.start_list = qobject_input_start_list; | |
| v->visitor.next_list = qobject_input_next_list; | |
| v->visitor.check_list = qobject_input_check_list; | |
| v->visitor.end_list = qobject_input_end_list; | |
| v->visitor.start_alternate = qobject_input_start_alternate; | |
| v->visitor.optional = qobject_input_optional; | |
| v->visitor.policy_reject = qobject_input_policy_reject; | |
| v->visitor.free = qobject_input_free; | |
| v->root = qobject_ref(obj); | |
| return v; | |
| } | |
| Visitor *qobject_input_visitor_new(QObject *obj) | |
| { | |
| QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); | |
| v->visitor.type_int64 = qobject_input_type_int64; | |
| v->visitor.type_uint64 = qobject_input_type_uint64; | |
| v->visitor.type_bool = qobject_input_type_bool; | |
| v->visitor.type_str = qobject_input_type_str; | |
| v->visitor.type_number = qobject_input_type_number; | |
| v->visitor.type_any = qobject_input_type_any; | |
| v->visitor.type_null = qobject_input_type_null; | |
| return &v->visitor; | |
| } | |
| Visitor *qobject_input_visitor_new_keyval(QObject *obj) | |
| { | |
| QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); | |
| v->visitor.type_int64 = qobject_input_type_int64_keyval; | |
| v->visitor.type_uint64 = qobject_input_type_uint64_keyval; | |
| v->visitor.type_bool = qobject_input_type_bool_keyval; | |
| v->visitor.type_str = qobject_input_type_str_keyval; | |
| v->visitor.type_number = qobject_input_type_number_keyval; | |
| v->visitor.type_any = qobject_input_type_any; | |
| v->visitor.type_null = qobject_input_type_null; | |
| v->visitor.type_size = qobject_input_type_size_keyval; | |
| v->keyval = true; | |
| return &v->visitor; | |
| } | |
| Visitor *qobject_input_visitor_new_str(const char *str, | |
| const char *implied_key, | |
| Error **errp) | |
| { | |
| bool is_json = str[0] == '{'; | |
| QObject *obj; | |
| QDict *args; | |
| Visitor *v; | |
| if (is_json) { | |
| obj = qobject_from_json(str, errp); | |
| if (!obj) { | |
| return NULL; | |
| } | |
| args = qobject_to(QDict, obj); | |
| assert(args); | |
| v = qobject_input_visitor_new(QOBJECT(args)); | |
| } else { | |
| args = keyval_parse(str, implied_key, NULL, errp); | |
| if (!args) { | |
| return NULL; | |
| } | |
| v = qobject_input_visitor_new_keyval(QOBJECT(args)); | |
| } | |
| qobject_unref(args); | |
| return v; | |
| } |