1 | /* |
2 | * Input Visitor |
3 | * |
4 | * Copyright (C) 2012-2017 Red Hat, Inc. |
5 | * Copyright IBM, Corp. 2011 |
6 | * |
7 | * Authors: |
8 | * Anthony Liguori <aliguori@us.ibm.com> |
9 | * |
10 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |
11 | * See the COPYING.LIB file in the top-level directory. |
12 | * |
13 | */ |
14 | |
15 | #include "qemu/osdep.h" |
16 | #include <math.h> |
17 | #include "qapi/error.h" |
18 | #include "qapi/qobject-input-visitor.h" |
19 | #include "qapi/visitor-impl.h" |
20 | #include "qemu/queue.h" |
21 | #include "qapi/qmp/qjson.h" |
22 | #include "qapi/qmp/qbool.h" |
23 | #include "qapi/qmp/qdict.h" |
24 | #include "qapi/qmp/qerror.h" |
25 | #include "qapi/qmp/qlist.h" |
26 | #include "qapi/qmp/qnull.h" |
27 | #include "qapi/qmp/qnum.h" |
28 | #include "qapi/qmp/qstring.h" |
29 | #include "qemu/cutils.h" |
30 | #include "qemu/option.h" |
31 | |
32 | typedef struct StackObject { |
33 | const char *name; /* Name of @obj in its parent, if any */ |
34 | QObject *obj; /* QDict or QList being visited */ |
35 | void *qapi; /* sanity check that caller uses same pointer */ |
36 | |
37 | GHashTable *h; /* If @obj is QDict: unvisited keys */ |
38 | const QListEntry *entry; /* If @obj is QList: unvisited tail */ |
39 | unsigned index; /* If @obj is QList: list index of @entry */ |
40 | |
41 | QSLIST_ENTRY(StackObject) node; /* parent */ |
42 | } StackObject; |
43 | |
44 | struct QObjectInputVisitor { |
45 | Visitor visitor; |
46 | |
47 | /* Root of visit at visitor creation. */ |
48 | QObject *root; |
49 | bool keyval; /* Assume @root made with keyval_parse() */ |
50 | |
51 | /* Stack of objects being visited (all entries will be either |
52 | * QDict or QList). */ |
53 | QSLIST_HEAD(, StackObject) stack; |
54 | |
55 | GString *errname; /* Accumulator for full_name() */ |
56 | }; |
57 | |
58 | static QObjectInputVisitor *to_qiv(Visitor *v) |
59 | { |
60 | return container_of(v, QObjectInputVisitor, visitor); |
61 | } |
62 | |
63 | /* |
64 | * Find the full name of something @qiv is currently visiting. |
65 | * @qiv is visiting something named @name in the stack of containers |
66 | * @qiv->stack. |
67 | * If @n is zero, return its full name. |
68 | * If @n is positive, return the full name of the @n-th container |
69 | * counting from the top. The stack of containers must have at least |
70 | * @n elements. |
71 | * The returned string is valid until the next full_name_nth(@v) or |
72 | * destruction of @v. |
73 | */ |
74 | static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, |
75 | int n) |
76 | { |
77 | StackObject *so; |
78 | char buf[32]; |
79 | |
80 | if (qiv->errname) { |
81 | g_string_truncate(qiv->errname, 0); |
82 | } else { |
83 | qiv->errname = g_string_new("" ); |
84 | } |
85 | |
86 | QSLIST_FOREACH(so , &qiv->stack, node) { |
87 | if (n) { |
88 | n--; |
89 | } else if (qobject_type(so->obj) == QTYPE_QDICT) { |
90 | g_string_prepend(qiv->errname, name ?: "<anonymous>" ); |
91 | g_string_prepend_c(qiv->errname, '.'); |
92 | } else { |
93 | snprintf(buf, sizeof(buf), |
94 | qiv->keyval ? ".%u" : "[%u]" , |
95 | so->index); |
96 | g_string_prepend(qiv->errname, buf); |
97 | } |
98 | name = so->name; |
99 | } |
100 | assert(!n); |
101 | |
102 | if (name) { |
103 | g_string_prepend(qiv->errname, name); |
104 | } else if (qiv->errname->str[0] == '.') { |
105 | g_string_erase(qiv->errname, 0, 1); |
106 | } else if (!qiv->errname->str[0]) { |
107 | return "<anonymous>" ; |
108 | } |
109 | |
110 | return qiv->errname->str; |
111 | } |
112 | |
113 | static const char *full_name(QObjectInputVisitor *qiv, const char *name) |
114 | { |
115 | return full_name_nth(qiv, name, 0); |
116 | } |
117 | |
118 | static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, |
119 | const char *name, |
120 | bool consume) |
121 | { |
122 | StackObject *tos; |
123 | QObject *qobj; |
124 | QObject *ret; |
125 | |
126 | if (QSLIST_EMPTY(&qiv->stack)) { |
127 | /* Starting at root, name is ignored. */ |
128 | assert(qiv->root); |
129 | return qiv->root; |
130 | } |
131 | |
132 | /* We are in a container; find the next element. */ |
133 | tos = QSLIST_FIRST(&qiv->stack); |
134 | qobj = tos->obj; |
135 | assert(qobj); |
136 | |
137 | if (qobject_type(qobj) == QTYPE_QDICT) { |
138 | assert(name); |
139 | ret = qdict_get(qobject_to(QDict, qobj), name); |
140 | if (tos->h && consume && ret) { |
141 | bool removed = g_hash_table_remove(tos->h, name); |
142 | assert(removed); |
143 | } |
144 | } else { |
145 | assert(qobject_type(qobj) == QTYPE_QLIST); |
146 | assert(!name); |
147 | if (tos->entry) { |
148 | ret = qlist_entry_obj(tos->entry); |
149 | if (consume) { |
150 | tos->entry = qlist_next(tos->entry); |
151 | } |
152 | } else { |
153 | ret = NULL; |
154 | } |
155 | if (consume) { |
156 | tos->index++; |
157 | } |
158 | } |
159 | |
160 | return ret; |
161 | } |
162 | |
163 | static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, |
164 | const char *name, |
165 | bool consume, Error **errp) |
166 | { |
167 | QObject *obj = qobject_input_try_get_object(qiv, name, consume); |
168 | |
169 | if (!obj) { |
170 | error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); |
171 | } |
172 | return obj; |
173 | } |
174 | |
175 | static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, |
176 | const char *name, |
177 | Error **errp) |
178 | { |
179 | QObject *qobj; |
180 | QString *qstr; |
181 | |
182 | qobj = qobject_input_get_object(qiv, name, true, errp); |
183 | if (!qobj) { |
184 | return NULL; |
185 | } |
186 | |
187 | qstr = qobject_to(QString, qobj); |
188 | if (!qstr) { |
189 | switch (qobject_type(qobj)) { |
190 | case QTYPE_QDICT: |
191 | case QTYPE_QLIST: |
192 | error_setg(errp, "Parameters '%s.*' are unexpected" , |
193 | full_name(qiv, name)); |
194 | return NULL; |
195 | default: |
196 | /* Non-string scalar (should this be an assertion?) */ |
197 | error_setg(errp, "Internal error: parameter %s invalid" , |
198 | full_name(qiv, name)); |
199 | return NULL; |
200 | } |
201 | } |
202 | |
203 | return qstring_get_str(qstr); |
204 | } |
205 | |
206 | static void qdict_add_key(const char *key, QObject *obj, void *opaque) |
207 | { |
208 | GHashTable *h = opaque; |
209 | g_hash_table_insert(h, (gpointer) key, NULL); |
210 | } |
211 | |
212 | static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, |
213 | const char *name, |
214 | QObject *obj, void *qapi) |
215 | { |
216 | GHashTable *h; |
217 | StackObject *tos = g_new0(StackObject, 1); |
218 | |
219 | assert(obj); |
220 | tos->name = name; |
221 | tos->obj = obj; |
222 | tos->qapi = qapi; |
223 | |
224 | if (qobject_type(obj) == QTYPE_QDICT) { |
225 | h = g_hash_table_new(g_str_hash, g_str_equal); |
226 | qdict_iter(qobject_to(QDict, obj), qdict_add_key, h); |
227 | tos->h = h; |
228 | } else { |
229 | assert(qobject_type(obj) == QTYPE_QLIST); |
230 | tos->entry = qlist_first(qobject_to(QList, obj)); |
231 | tos->index = -1; |
232 | } |
233 | |
234 | QSLIST_INSERT_HEAD(&qiv->stack, tos, node); |
235 | return tos->entry; |
236 | } |
237 | |
238 | |
239 | static void qobject_input_check_struct(Visitor *v, Error **errp) |
240 | { |
241 | QObjectInputVisitor *qiv = to_qiv(v); |
242 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
243 | GHashTableIter iter; |
244 | const char *key; |
245 | |
246 | assert(tos && !tos->entry); |
247 | |
248 | g_hash_table_iter_init(&iter, tos->h); |
249 | if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { |
250 | error_setg(errp, "Parameter '%s' is unexpected" , |
251 | full_name(qiv, key)); |
252 | } |
253 | } |
254 | |
255 | static void qobject_input_stack_object_free(StackObject *tos) |
256 | { |
257 | if (tos->h) { |
258 | g_hash_table_unref(tos->h); |
259 | } |
260 | |
261 | g_free(tos); |
262 | } |
263 | |
264 | static void qobject_input_pop(Visitor *v, void **obj) |
265 | { |
266 | QObjectInputVisitor *qiv = to_qiv(v); |
267 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
268 | |
269 | assert(tos && tos->qapi == obj); |
270 | QSLIST_REMOVE_HEAD(&qiv->stack, node); |
271 | qobject_input_stack_object_free(tos); |
272 | } |
273 | |
274 | static void qobject_input_start_struct(Visitor *v, const char *name, void **obj, |
275 | size_t size, Error **errp) |
276 | { |
277 | QObjectInputVisitor *qiv = to_qiv(v); |
278 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
279 | |
280 | if (obj) { |
281 | *obj = NULL; |
282 | } |
283 | if (!qobj) { |
284 | return; |
285 | } |
286 | if (qobject_type(qobj) != QTYPE_QDICT) { |
287 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
288 | full_name(qiv, name), "object" ); |
289 | return; |
290 | } |
291 | |
292 | qobject_input_push(qiv, name, qobj, obj); |
293 | |
294 | if (obj) { |
295 | *obj = g_malloc0(size); |
296 | } |
297 | } |
298 | |
299 | static void qobject_input_end_struct(Visitor *v, void **obj) |
300 | { |
301 | QObjectInputVisitor *qiv = to_qiv(v); |
302 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
303 | |
304 | assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); |
305 | qobject_input_pop(v, obj); |
306 | } |
307 | |
308 | |
309 | static void qobject_input_start_list(Visitor *v, const char *name, |
310 | GenericList **list, size_t size, |
311 | Error **errp) |
312 | { |
313 | QObjectInputVisitor *qiv = to_qiv(v); |
314 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
315 | const QListEntry *entry; |
316 | |
317 | if (list) { |
318 | *list = NULL; |
319 | } |
320 | if (!qobj) { |
321 | return; |
322 | } |
323 | if (qobject_type(qobj) != QTYPE_QLIST) { |
324 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
325 | full_name(qiv, name), "array" ); |
326 | return; |
327 | } |
328 | |
329 | entry = qobject_input_push(qiv, name, qobj, list); |
330 | if (entry && list) { |
331 | *list = g_malloc0(size); |
332 | } |
333 | } |
334 | |
335 | static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, |
336 | size_t size) |
337 | { |
338 | QObjectInputVisitor *qiv = to_qiv(v); |
339 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
340 | |
341 | assert(tos && qobject_to(QList, tos->obj)); |
342 | |
343 | if (!tos->entry) { |
344 | return NULL; |
345 | } |
346 | tail->next = g_malloc0(size); |
347 | return tail->next; |
348 | } |
349 | |
350 | static void qobject_input_check_list(Visitor *v, Error **errp) |
351 | { |
352 | QObjectInputVisitor *qiv = to_qiv(v); |
353 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
354 | |
355 | assert(tos && qobject_to(QList, tos->obj)); |
356 | |
357 | if (tos->entry) { |
358 | error_setg(errp, "Only %u list elements expected in %s" , |
359 | tos->index + 1, full_name_nth(qiv, NULL, 1)); |
360 | } |
361 | } |
362 | |
363 | static void qobject_input_end_list(Visitor *v, void **obj) |
364 | { |
365 | QObjectInputVisitor *qiv = to_qiv(v); |
366 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
367 | |
368 | assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); |
369 | qobject_input_pop(v, obj); |
370 | } |
371 | |
372 | static void qobject_input_start_alternate(Visitor *v, const char *name, |
373 | GenericAlternate **obj, size_t size, |
374 | Error **errp) |
375 | { |
376 | QObjectInputVisitor *qiv = to_qiv(v); |
377 | QObject *qobj = qobject_input_get_object(qiv, name, false, errp); |
378 | |
379 | if (!qobj) { |
380 | *obj = NULL; |
381 | return; |
382 | } |
383 | *obj = g_malloc0(size); |
384 | (*obj)->type = qobject_type(qobj); |
385 | } |
386 | |
387 | static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, |
388 | Error **errp) |
389 | { |
390 | QObjectInputVisitor *qiv = to_qiv(v); |
391 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
392 | QNum *qnum; |
393 | |
394 | if (!qobj) { |
395 | return; |
396 | } |
397 | qnum = qobject_to(QNum, qobj); |
398 | if (!qnum || !qnum_get_try_int(qnum, obj)) { |
399 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
400 | full_name(qiv, name), "integer" ); |
401 | } |
402 | } |
403 | |
404 | static void qobject_input_type_int64_keyval(Visitor *v, const char *name, |
405 | int64_t *obj, Error **errp) |
406 | { |
407 | QObjectInputVisitor *qiv = to_qiv(v); |
408 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
409 | |
410 | if (!str) { |
411 | return; |
412 | } |
413 | |
414 | if (qemu_strtoi64(str, NULL, 0, obj) < 0) { |
415 | /* TODO report -ERANGE more nicely */ |
416 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
417 | full_name(qiv, name), "integer" ); |
418 | } |
419 | } |
420 | |
421 | static void qobject_input_type_uint64(Visitor *v, const char *name, |
422 | uint64_t *obj, Error **errp) |
423 | { |
424 | QObjectInputVisitor *qiv = to_qiv(v); |
425 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
426 | QNum *qnum; |
427 | int64_t val; |
428 | |
429 | if (!qobj) { |
430 | return; |
431 | } |
432 | qnum = qobject_to(QNum, qobj); |
433 | if (!qnum) { |
434 | goto err; |
435 | } |
436 | |
437 | if (qnum_get_try_uint(qnum, obj)) { |
438 | return; |
439 | } |
440 | |
441 | /* Need to accept negative values for backward compatibility */ |
442 | if (qnum_get_try_int(qnum, &val)) { |
443 | *obj = val; |
444 | return; |
445 | } |
446 | |
447 | err: |
448 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
449 | full_name(qiv, name), "uint64" ); |
450 | } |
451 | |
452 | static void qobject_input_type_uint64_keyval(Visitor *v, const char *name, |
453 | uint64_t *obj, Error **errp) |
454 | { |
455 | QObjectInputVisitor *qiv = to_qiv(v); |
456 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
457 | |
458 | if (!str) { |
459 | return; |
460 | } |
461 | |
462 | if (qemu_strtou64(str, NULL, 0, obj) < 0) { |
463 | /* TODO report -ERANGE more nicely */ |
464 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
465 | full_name(qiv, name), "integer" ); |
466 | } |
467 | } |
468 | |
469 | static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj, |
470 | Error **errp) |
471 | { |
472 | QObjectInputVisitor *qiv = to_qiv(v); |
473 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
474 | QBool *qbool; |
475 | |
476 | if (!qobj) { |
477 | return; |
478 | } |
479 | qbool = qobject_to(QBool, qobj); |
480 | if (!qbool) { |
481 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
482 | full_name(qiv, name), "boolean" ); |
483 | return; |
484 | } |
485 | |
486 | *obj = qbool_get_bool(qbool); |
487 | } |
488 | |
489 | static void qobject_input_type_bool_keyval(Visitor *v, const char *name, |
490 | bool *obj, Error **errp) |
491 | { |
492 | QObjectInputVisitor *qiv = to_qiv(v); |
493 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
494 | |
495 | if (!str) { |
496 | return; |
497 | } |
498 | |
499 | if (!strcmp(str, "on" )) { |
500 | *obj = true; |
501 | } else if (!strcmp(str, "off" )) { |
502 | *obj = false; |
503 | } else { |
504 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
505 | full_name(qiv, name), "'on' or 'off'" ); |
506 | } |
507 | } |
508 | |
509 | static void qobject_input_type_str(Visitor *v, const char *name, char **obj, |
510 | Error **errp) |
511 | { |
512 | QObjectInputVisitor *qiv = to_qiv(v); |
513 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
514 | QString *qstr; |
515 | |
516 | *obj = NULL; |
517 | if (!qobj) { |
518 | return; |
519 | } |
520 | qstr = qobject_to(QString, qobj); |
521 | if (!qstr) { |
522 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
523 | full_name(qiv, name), "string" ); |
524 | return; |
525 | } |
526 | |
527 | *obj = g_strdup(qstring_get_str(qstr)); |
528 | } |
529 | |
530 | static void qobject_input_type_str_keyval(Visitor *v, const char *name, |
531 | char **obj, Error **errp) |
532 | { |
533 | QObjectInputVisitor *qiv = to_qiv(v); |
534 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
535 | |
536 | *obj = g_strdup(str); |
537 | } |
538 | |
539 | static void qobject_input_type_number(Visitor *v, const char *name, double *obj, |
540 | Error **errp) |
541 | { |
542 | QObjectInputVisitor *qiv = to_qiv(v); |
543 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
544 | QNum *qnum; |
545 | |
546 | if (!qobj) { |
547 | return; |
548 | } |
549 | qnum = qobject_to(QNum, qobj); |
550 | if (!qnum) { |
551 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
552 | full_name(qiv, name), "number" ); |
553 | return; |
554 | } |
555 | |
556 | *obj = qnum_get_double(qnum); |
557 | } |
558 | |
559 | static void qobject_input_type_number_keyval(Visitor *v, const char *name, |
560 | double *obj, Error **errp) |
561 | { |
562 | QObjectInputVisitor *qiv = to_qiv(v); |
563 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
564 | double val; |
565 | |
566 | if (!str) { |
567 | return; |
568 | } |
569 | |
570 | if (qemu_strtod_finite(str, NULL, &val)) { |
571 | /* TODO report -ERANGE more nicely */ |
572 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
573 | full_name(qiv, name), "number" ); |
574 | return; |
575 | } |
576 | |
577 | *obj = val; |
578 | } |
579 | |
580 | static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, |
581 | Error **errp) |
582 | { |
583 | QObjectInputVisitor *qiv = to_qiv(v); |
584 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
585 | |
586 | *obj = NULL; |
587 | if (!qobj) { |
588 | return; |
589 | } |
590 | |
591 | *obj = qobject_ref(qobj); |
592 | } |
593 | |
594 | static void qobject_input_type_null(Visitor *v, const char *name, |
595 | QNull **obj, Error **errp) |
596 | { |
597 | QObjectInputVisitor *qiv = to_qiv(v); |
598 | QObject *qobj = qobject_input_get_object(qiv, name, true, errp); |
599 | |
600 | *obj = NULL; |
601 | if (!qobj) { |
602 | return; |
603 | } |
604 | |
605 | if (qobject_type(qobj) != QTYPE_QNULL) { |
606 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, |
607 | full_name(qiv, name), "null" ); |
608 | return; |
609 | } |
610 | *obj = qnull(); |
611 | } |
612 | |
613 | static void qobject_input_type_size_keyval(Visitor *v, const char *name, |
614 | uint64_t *obj, Error **errp) |
615 | { |
616 | QObjectInputVisitor *qiv = to_qiv(v); |
617 | const char *str = qobject_input_get_keyval(qiv, name, errp); |
618 | |
619 | if (!str) { |
620 | return; |
621 | } |
622 | |
623 | if (qemu_strtosz(str, NULL, obj) < 0) { |
624 | /* TODO report -ERANGE more nicely */ |
625 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, |
626 | full_name(qiv, name), "size" ); |
627 | } |
628 | } |
629 | |
630 | static void qobject_input_optional(Visitor *v, const char *name, bool *present) |
631 | { |
632 | QObjectInputVisitor *qiv = to_qiv(v); |
633 | QObject *qobj = qobject_input_try_get_object(qiv, name, false); |
634 | |
635 | if (!qobj) { |
636 | *present = false; |
637 | return; |
638 | } |
639 | |
640 | *present = true; |
641 | } |
642 | |
643 | static void qobject_input_free(Visitor *v) |
644 | { |
645 | QObjectInputVisitor *qiv = to_qiv(v); |
646 | |
647 | while (!QSLIST_EMPTY(&qiv->stack)) { |
648 | StackObject *tos = QSLIST_FIRST(&qiv->stack); |
649 | |
650 | QSLIST_REMOVE_HEAD(&qiv->stack, node); |
651 | qobject_input_stack_object_free(tos); |
652 | } |
653 | |
654 | qobject_unref(qiv->root); |
655 | if (qiv->errname) { |
656 | g_string_free(qiv->errname, TRUE); |
657 | } |
658 | g_free(qiv); |
659 | } |
660 | |
661 | static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) |
662 | { |
663 | QObjectInputVisitor *v = g_malloc0(sizeof(*v)); |
664 | |
665 | assert(obj); |
666 | |
667 | v->visitor.type = VISITOR_INPUT; |
668 | v->visitor.start_struct = qobject_input_start_struct; |
669 | v->visitor.check_struct = qobject_input_check_struct; |
670 | v->visitor.end_struct = qobject_input_end_struct; |
671 | v->visitor.start_list = qobject_input_start_list; |
672 | v->visitor.next_list = qobject_input_next_list; |
673 | v->visitor.check_list = qobject_input_check_list; |
674 | v->visitor.end_list = qobject_input_end_list; |
675 | v->visitor.start_alternate = qobject_input_start_alternate; |
676 | v->visitor.optional = qobject_input_optional; |
677 | v->visitor.free = qobject_input_free; |
678 | |
679 | v->root = qobject_ref(obj); |
680 | |
681 | return v; |
682 | } |
683 | |
684 | Visitor *qobject_input_visitor_new(QObject *obj) |
685 | { |
686 | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); |
687 | |
688 | v->visitor.type_int64 = qobject_input_type_int64; |
689 | v->visitor.type_uint64 = qobject_input_type_uint64; |
690 | v->visitor.type_bool = qobject_input_type_bool; |
691 | v->visitor.type_str = qobject_input_type_str; |
692 | v->visitor.type_number = qobject_input_type_number; |
693 | v->visitor.type_any = qobject_input_type_any; |
694 | v->visitor.type_null = qobject_input_type_null; |
695 | |
696 | return &v->visitor; |
697 | } |
698 | |
699 | Visitor *qobject_input_visitor_new_keyval(QObject *obj) |
700 | { |
701 | QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); |
702 | |
703 | v->visitor.type_int64 = qobject_input_type_int64_keyval; |
704 | v->visitor.type_uint64 = qobject_input_type_uint64_keyval; |
705 | v->visitor.type_bool = qobject_input_type_bool_keyval; |
706 | v->visitor.type_str = qobject_input_type_str_keyval; |
707 | v->visitor.type_number = qobject_input_type_number_keyval; |
708 | v->visitor.type_any = qobject_input_type_any; |
709 | v->visitor.type_null = qobject_input_type_null; |
710 | v->visitor.type_size = qobject_input_type_size_keyval; |
711 | v->keyval = true; |
712 | |
713 | return &v->visitor; |
714 | } |
715 | |
716 | Visitor *qobject_input_visitor_new_str(const char *str, |
717 | const char *implied_key, |
718 | Error **errp) |
719 | { |
720 | bool is_json = str[0] == '{'; |
721 | QObject *obj; |
722 | QDict *args; |
723 | Visitor *v; |
724 | |
725 | if (is_json) { |
726 | obj = qobject_from_json(str, errp); |
727 | if (!obj) { |
728 | return NULL; |
729 | } |
730 | args = qobject_to(QDict, obj); |
731 | assert(args); |
732 | v = qobject_input_visitor_new(QOBJECT(args)); |
733 | } else { |
734 | args = keyval_parse(str, implied_key, errp); |
735 | if (!args) { |
736 | return NULL; |
737 | } |
738 | v = qobject_input_visitor_new_keyval(QOBJECT(args)); |
739 | } |
740 | qobject_unref(args); |
741 | |
742 | return v; |
743 | } |
744 | |