xref: /openbmc/qemu/qapi/opts-visitor.c (revision 012d4c96)
1eb7ee2cbSLaszlo Ersek /*
2eb7ee2cbSLaszlo Ersek  * Options Visitor
3eb7ee2cbSLaszlo Ersek  *
408f9541dSEric Blake  * Copyright Red Hat, Inc. 2012-2016
5eb7ee2cbSLaszlo Ersek  *
6eb7ee2cbSLaszlo Ersek  * Author: Laszlo Ersek <lersek@redhat.com>
7eb7ee2cbSLaszlo Ersek  *
8eb7ee2cbSLaszlo Ersek  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9eb7ee2cbSLaszlo Ersek  * See the COPYING.LIB file in the top-level directory.
10eb7ee2cbSLaszlo Ersek  *
11eb7ee2cbSLaszlo Ersek  */
12eb7ee2cbSLaszlo Ersek 
13cbf21151SPeter Maydell #include "qemu/osdep.h"
14da34e65cSMarkus Armbruster #include "qapi/error.h"
15f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
167b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
177b1b5d19SPaolo Bonzini #include "qapi/opts-visitor.h"
181de7afc9SPaolo Bonzini #include "qemu/queue.h"
191de7afc9SPaolo Bonzini #include "qemu/option_int.h"
207b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h"
21eb7ee2cbSLaszlo Ersek 
22eb7ee2cbSLaszlo Ersek 
23d9570434SLaszlo Ersek enum ListMode
24d9570434SLaszlo Ersek {
25d9570434SLaszlo Ersek     LM_NONE,             /* not traversing a list of repeated options */
26d8754f40SLaszlo Ersek 
27863f195fSAndrey Shinkevich     LM_IN_PROGRESS,      /*
28863f195fSAndrey Shinkevich                           * opts_next_list() ready to be called.
29d8754f40SLaszlo Ersek                           *
30d8754f40SLaszlo Ersek                           * Generating the next list link will consume the most
31d8754f40SLaszlo Ersek                           * recently parsed QemuOpt instance of the repeated
32d8754f40SLaszlo Ersek                           * option.
33d8754f40SLaszlo Ersek                           *
34d8754f40SLaszlo Ersek                           * Parsing a value into the list link will examine the
35d8754f40SLaszlo Ersek                           * next QemuOpt instance of the repeated option, and
36d8754f40SLaszlo Ersek                           * possibly enter LM_SIGNED_INTERVAL or
37d8754f40SLaszlo Ersek                           * LM_UNSIGNED_INTERVAL.
38d8754f40SLaszlo Ersek                           */
39d8754f40SLaszlo Ersek 
40863f195fSAndrey Shinkevich     LM_SIGNED_INTERVAL,  /*
41863f195fSAndrey Shinkevich                           * opts_next_list() has been called.
42d8754f40SLaszlo Ersek                           *
43d8754f40SLaszlo Ersek                           * Generating the next list link will consume the most
44d8754f40SLaszlo Ersek                           * recently stored element from the signed interval,
45d8754f40SLaszlo Ersek                           * parsed from the most recent QemuOpt instance of the
46d8754f40SLaszlo Ersek                           * repeated option. This may consume QemuOpt itself
47d8754f40SLaszlo Ersek                           * and return to LM_IN_PROGRESS.
48d8754f40SLaszlo Ersek                           *
49d8754f40SLaszlo Ersek                           * Parsing a value into the list link will store the
50d8754f40SLaszlo Ersek                           * next element of the signed interval.
51d8754f40SLaszlo Ersek                           */
52d8754f40SLaszlo Ersek 
53863f195fSAndrey Shinkevich     LM_UNSIGNED_INTERVAL, /* Same as above, only for an unsigned interval. */
54863f195fSAndrey Shinkevich 
55863f195fSAndrey Shinkevich     LM_TRAVERSED          /*
56863f195fSAndrey Shinkevich                            * opts_next_list() has been called.
57863f195fSAndrey Shinkevich                            *
58863f195fSAndrey Shinkevich                            * No more QemuOpt instance in the list.
59863f195fSAndrey Shinkevich                            * The traversal has been completed.
60863f195fSAndrey Shinkevich                            */
61d9570434SLaszlo Ersek };
62d9570434SLaszlo Ersek 
63d9570434SLaszlo Ersek typedef enum ListMode ListMode;
64d9570434SLaszlo Ersek 
65eb7ee2cbSLaszlo Ersek struct OptsVisitor
66eb7ee2cbSLaszlo Ersek {
67eb7ee2cbSLaszlo Ersek     Visitor visitor;
68eb7ee2cbSLaszlo Ersek 
69eb7ee2cbSLaszlo Ersek     /* Ownership remains with opts_visitor_new()'s caller. */
70eb7ee2cbSLaszlo Ersek     const QemuOpts *opts_root;
71eb7ee2cbSLaszlo Ersek 
72eb7ee2cbSLaszlo Ersek     unsigned depth;
73eb7ee2cbSLaszlo Ersek 
74eb7ee2cbSLaszlo Ersek     /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
75eb7ee2cbSLaszlo Ersek      * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
76eb7ee2cbSLaszlo Ersek      * name. */
77eb7ee2cbSLaszlo Ersek     GHashTable *unprocessed_opts;
78eb7ee2cbSLaszlo Ersek 
79eb7ee2cbSLaszlo Ersek     /* The list currently being traversed with opts_start_list() /
80eb7ee2cbSLaszlo Ersek      * opts_next_list(). The list must have a struct element type in the
81eb7ee2cbSLaszlo Ersek      * schema, with a single mandatory scalar member. */
82d9570434SLaszlo Ersek     ListMode list_mode;
83eb7ee2cbSLaszlo Ersek     GQueue *repeated_opts;
84eb7ee2cbSLaszlo Ersek 
85d8754f40SLaszlo Ersek     /* When parsing a list of repeating options as integers, values of the form
86d8754f40SLaszlo Ersek      * "a-b", representing a closed interval, are allowed. Elements in the
87d8754f40SLaszlo Ersek      * range are generated individually.
88d8754f40SLaszlo Ersek      */
89d8754f40SLaszlo Ersek     union {
90d8754f40SLaszlo Ersek         int64_t s;
91d8754f40SLaszlo Ersek         uint64_t u;
92d8754f40SLaszlo Ersek     } range_next, range_limit;
93d8754f40SLaszlo Ersek 
94eb7ee2cbSLaszlo Ersek     /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
95eb7ee2cbSLaszlo Ersek      * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
96eb7ee2cbSLaszlo Ersek      * not survive or escape the OptsVisitor object.
97eb7ee2cbSLaszlo Ersek      */
98eb7ee2cbSLaszlo Ersek     QemuOpt *fake_id_opt;
99eb7ee2cbSLaszlo Ersek };
100eb7ee2cbSLaszlo Ersek 
101eb7ee2cbSLaszlo Ersek 
102d7bea75dSEric Blake static OptsVisitor *to_ov(Visitor *v)
103d7bea75dSEric Blake {
104d7bea75dSEric Blake     return container_of(v, OptsVisitor, visitor);
105d7bea75dSEric Blake }
106d7bea75dSEric Blake 
107d7bea75dSEric Blake 
108eb7ee2cbSLaszlo Ersek static void
109eb7ee2cbSLaszlo Ersek destroy_list(gpointer list)
110eb7ee2cbSLaszlo Ersek {
111eb7ee2cbSLaszlo Ersek   g_queue_free(list);
112eb7ee2cbSLaszlo Ersek }
113eb7ee2cbSLaszlo Ersek 
114eb7ee2cbSLaszlo Ersek 
115eb7ee2cbSLaszlo Ersek static void
116eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
117eb7ee2cbSLaszlo Ersek {
118eb7ee2cbSLaszlo Ersek     GQueue *list;
119eb7ee2cbSLaszlo Ersek 
120eb7ee2cbSLaszlo Ersek     list = g_hash_table_lookup(unprocessed_opts, opt->name);
121eb7ee2cbSLaszlo Ersek     if (list == NULL) {
122eb7ee2cbSLaszlo Ersek         list = g_queue_new();
123eb7ee2cbSLaszlo Ersek 
124eb7ee2cbSLaszlo Ersek         /* GHashTable will never try to free the keys -- we supply NULL as
125eb7ee2cbSLaszlo Ersek          * "key_destroy_func" in opts_start_struct(). Thus cast away key
126eb7ee2cbSLaszlo Ersek          * const-ness in order to suppress gcc's warning.
127eb7ee2cbSLaszlo Ersek          */
128eb7ee2cbSLaszlo Ersek         g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
129eb7ee2cbSLaszlo Ersek     }
130eb7ee2cbSLaszlo Ersek 
131eb7ee2cbSLaszlo Ersek     /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
132eb7ee2cbSLaszlo Ersek     g_queue_push_tail(list, (gpointer)opt);
133eb7ee2cbSLaszlo Ersek }
134eb7ee2cbSLaszlo Ersek 
135eb7ee2cbSLaszlo Ersek 
136*012d4c96SMarkus Armbruster static bool
137337283dfSEric Blake opts_start_struct(Visitor *v, const char *name, void **obj,
1380b2a0d6bSEric Blake                   size_t size, Error **errp)
139eb7ee2cbSLaszlo Ersek {
140d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
141eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
142eb7ee2cbSLaszlo Ersek 
143b7745397SMarkus Armbruster     if (obj) {
144e58d695eSEric Blake         *obj = g_malloc0(size);
145b7745397SMarkus Armbruster     }
146eb7ee2cbSLaszlo Ersek     if (ov->depth++ > 0) {
147*012d4c96SMarkus Armbruster         return true;
148eb7ee2cbSLaszlo Ersek     }
149eb7ee2cbSLaszlo Ersek 
150eb7ee2cbSLaszlo Ersek     ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
151eb7ee2cbSLaszlo Ersek                                                  NULL, &destroy_list);
152eb7ee2cbSLaszlo Ersek     QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
153eb7ee2cbSLaszlo Ersek         /* ensured by qemu-option.c::opts_do_parse() */
154eb7ee2cbSLaszlo Ersek         assert(strcmp(opt->name, "id") != 0);
155eb7ee2cbSLaszlo Ersek 
156eb7ee2cbSLaszlo Ersek         opts_visitor_insert(ov->unprocessed_opts, opt);
157eb7ee2cbSLaszlo Ersek     }
158eb7ee2cbSLaszlo Ersek 
159eb7ee2cbSLaszlo Ersek     if (ov->opts_root->id != NULL) {
160eb7ee2cbSLaszlo Ersek         ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
161eb7ee2cbSLaszlo Ersek 
162dc8622f2SChunyan Liu         ov->fake_id_opt->name = g_strdup("id");
163dc8622f2SChunyan Liu         ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
164eb7ee2cbSLaszlo Ersek         opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
165eb7ee2cbSLaszlo Ersek     }
166*012d4c96SMarkus Armbruster     return true;
167eb7ee2cbSLaszlo Ersek }
168eb7ee2cbSLaszlo Ersek 
169eb7ee2cbSLaszlo Ersek 
170*012d4c96SMarkus Armbruster static bool
17115c2f669SEric Blake opts_check_struct(Visitor *v, Error **errp)
172eb7ee2cbSLaszlo Ersek {
173d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
174f96493b1SEric Blake     GHashTableIter iter;
175eb7ee2cbSLaszlo Ersek     GQueue *any;
176eb7ee2cbSLaszlo Ersek 
17721f88d02SEric Blake     if (ov->depth > 1) {
178*012d4c96SMarkus Armbruster         return true;
179eb7ee2cbSLaszlo Ersek     }
180eb7ee2cbSLaszlo Ersek 
181eb7ee2cbSLaszlo Ersek     /* we should have processed all (distinct) QemuOpt instances */
182f96493b1SEric Blake     g_hash_table_iter_init(&iter, ov->unprocessed_opts);
183f96493b1SEric Blake     if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
184eb7ee2cbSLaszlo Ersek         const QemuOpt *first;
185eb7ee2cbSLaszlo Ersek 
186eb7ee2cbSLaszlo Ersek         first = g_queue_peek_head(any);
187c6bd8c70SMarkus Armbruster         error_setg(errp, QERR_INVALID_PARAMETER, first->name);
188*012d4c96SMarkus Armbruster         return false;
189eb7ee2cbSLaszlo Ersek     }
190*012d4c96SMarkus Armbruster     return true;
19115c2f669SEric Blake }
19215c2f669SEric Blake 
19315c2f669SEric Blake 
19415c2f669SEric Blake static void
1951158bb2aSEric Blake opts_end_struct(Visitor *v, void **obj)
19615c2f669SEric Blake {
19715c2f669SEric Blake     OptsVisitor *ov = to_ov(v);
19815c2f669SEric Blake 
19915c2f669SEric Blake     if (--ov->depth > 0) {
20015c2f669SEric Blake         return;
20115c2f669SEric Blake     }
20215c2f669SEric Blake 
203eb7ee2cbSLaszlo Ersek     g_hash_table_destroy(ov->unprocessed_opts);
204eb7ee2cbSLaszlo Ersek     ov->unprocessed_opts = NULL;
205dc8622f2SChunyan Liu     if (ov->fake_id_opt) {
206dc8622f2SChunyan Liu         g_free(ov->fake_id_opt->name);
207dc8622f2SChunyan Liu         g_free(ov->fake_id_opt->str);
208eb7ee2cbSLaszlo Ersek         g_free(ov->fake_id_opt);
209dc8622f2SChunyan Liu     }
210eb7ee2cbSLaszlo Ersek     ov->fake_id_opt = NULL;
211eb7ee2cbSLaszlo Ersek }
212eb7ee2cbSLaszlo Ersek 
213eb7ee2cbSLaszlo Ersek 
214eb7ee2cbSLaszlo Ersek static GQueue *
215eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
216eb7ee2cbSLaszlo Ersek {
217eb7ee2cbSLaszlo Ersek     GQueue *list;
218eb7ee2cbSLaszlo Ersek 
219eb7ee2cbSLaszlo Ersek     list = g_hash_table_lookup(ov->unprocessed_opts, name);
220eb7ee2cbSLaszlo Ersek     if (!list) {
221c6bd8c70SMarkus Armbruster         error_setg(errp, QERR_MISSING_PARAMETER, name);
222eb7ee2cbSLaszlo Ersek     }
223eb7ee2cbSLaszlo Ersek     return list;
224eb7ee2cbSLaszlo Ersek }
225eb7ee2cbSLaszlo Ersek 
226eb7ee2cbSLaszlo Ersek 
227*012d4c96SMarkus Armbruster static bool
228d9f62ddeSEric Blake opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size,
229d9f62ddeSEric Blake                 Error **errp)
230eb7ee2cbSLaszlo Ersek {
231d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
232eb7ee2cbSLaszlo Ersek 
233eb7ee2cbSLaszlo Ersek     /* we can't traverse a list in a list */
234d9570434SLaszlo Ersek     assert(ov->list_mode == LM_NONE);
235d9f62ddeSEric Blake     /* we don't support visits without a list */
236d9f62ddeSEric Blake     assert(list);
237eb7ee2cbSLaszlo Ersek     ov->repeated_opts = lookup_distinct(ov, name, errp);
238*012d4c96SMarkus Armbruster     if (!ov->repeated_opts) {
239*012d4c96SMarkus Armbruster         *list = NULL;
240*012d4c96SMarkus Armbruster         return false;
241*012d4c96SMarkus Armbruster     }
242d9f62ddeSEric Blake     ov->list_mode = LM_IN_PROGRESS;
243d9f62ddeSEric Blake     *list = g_malloc0(size);
244*012d4c96SMarkus Armbruster     return true;
245eb7ee2cbSLaszlo Ersek }
246eb7ee2cbSLaszlo Ersek 
247eb7ee2cbSLaszlo Ersek 
248eb7ee2cbSLaszlo Ersek static GenericList *
249d9f62ddeSEric Blake opts_next_list(Visitor *v, GenericList *tail, size_t size)
250eb7ee2cbSLaszlo Ersek {
251d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
252eb7ee2cbSLaszlo Ersek 
253d9570434SLaszlo Ersek     switch (ov->list_mode) {
254863f195fSAndrey Shinkevich     case LM_TRAVERSED:
255863f195fSAndrey Shinkevich         return NULL;
256d8754f40SLaszlo Ersek     case LM_SIGNED_INTERVAL:
257d8754f40SLaszlo Ersek     case LM_UNSIGNED_INTERVAL:
258d8754f40SLaszlo Ersek         if (ov->list_mode == LM_SIGNED_INTERVAL) {
259d8754f40SLaszlo Ersek             if (ov->range_next.s < ov->range_limit.s) {
260d8754f40SLaszlo Ersek                 ++ov->range_next.s;
261d8754f40SLaszlo Ersek                 break;
262d8754f40SLaszlo Ersek             }
263d8754f40SLaszlo Ersek         } else if (ov->range_next.u < ov->range_limit.u) {
264d8754f40SLaszlo Ersek             ++ov->range_next.u;
265d8754f40SLaszlo Ersek             break;
266d8754f40SLaszlo Ersek         }
267d8754f40SLaszlo Ersek         ov->list_mode = LM_IN_PROGRESS;
268d8754f40SLaszlo Ersek         /* range has been completed, fall through in order to pop option */
269d8754f40SLaszlo Ersek 
270d9570434SLaszlo Ersek     case LM_IN_PROGRESS: {
271eb7ee2cbSLaszlo Ersek         const QemuOpt *opt;
272eb7ee2cbSLaszlo Ersek 
273eb7ee2cbSLaszlo Ersek         opt = g_queue_pop_head(ov->repeated_opts);
274eb7ee2cbSLaszlo Ersek         if (g_queue_is_empty(ov->repeated_opts)) {
275eb7ee2cbSLaszlo Ersek             g_hash_table_remove(ov->unprocessed_opts, opt->name);
276863f195fSAndrey Shinkevich             ov->repeated_opts = NULL;
277863f195fSAndrey Shinkevich             ov->list_mode = LM_TRAVERSED;
278eb7ee2cbSLaszlo Ersek             return NULL;
279eb7ee2cbSLaszlo Ersek         }
280d9570434SLaszlo Ersek         break;
281d9570434SLaszlo Ersek     }
282d9570434SLaszlo Ersek 
283d9570434SLaszlo Ersek     default:
284d9570434SLaszlo Ersek         abort();
285eb7ee2cbSLaszlo Ersek     }
286eb7ee2cbSLaszlo Ersek 
287d9f62ddeSEric Blake     tail->next = g_malloc0(size);
288d9f62ddeSEric Blake     return tail->next;
289eb7ee2cbSLaszlo Ersek }
290eb7ee2cbSLaszlo Ersek 
291eb7ee2cbSLaszlo Ersek 
292*012d4c96SMarkus Armbruster static bool
293a4a1c70dSMarkus Armbruster opts_check_list(Visitor *v, Error **errp)
294a4a1c70dSMarkus Armbruster {
295a4a1c70dSMarkus Armbruster     /*
29621f88d02SEric Blake      * Unvisited list elements will be reported later when checking
29721f88d02SEric Blake      * whether unvisited struct members remain.
298a4a1c70dSMarkus Armbruster      */
299*012d4c96SMarkus Armbruster     return true;
300a4a1c70dSMarkus Armbruster }
301a4a1c70dSMarkus Armbruster 
302a4a1c70dSMarkus Armbruster 
303a4a1c70dSMarkus Armbruster static void
3041158bb2aSEric Blake opts_end_list(Visitor *v, void **obj)
305eb7ee2cbSLaszlo Ersek {
306d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
307eb7ee2cbSLaszlo Ersek 
308d9f62ddeSEric Blake     assert(ov->list_mode == LM_IN_PROGRESS ||
309d8754f40SLaszlo Ersek            ov->list_mode == LM_SIGNED_INTERVAL ||
310863f195fSAndrey Shinkevich            ov->list_mode == LM_UNSIGNED_INTERVAL ||
311863f195fSAndrey Shinkevich            ov->list_mode == LM_TRAVERSED);
312eb7ee2cbSLaszlo Ersek     ov->repeated_opts = NULL;
313d9570434SLaszlo Ersek     ov->list_mode = LM_NONE;
314eb7ee2cbSLaszlo Ersek }
315eb7ee2cbSLaszlo Ersek 
316eb7ee2cbSLaszlo Ersek 
317eb7ee2cbSLaszlo Ersek static const QemuOpt *
318eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
319eb7ee2cbSLaszlo Ersek {
320d9570434SLaszlo Ersek     if (ov->list_mode == LM_NONE) {
321eb7ee2cbSLaszlo Ersek         GQueue *list;
322eb7ee2cbSLaszlo Ersek 
323eb7ee2cbSLaszlo Ersek         /* the last occurrence of any QemuOpt takes effect when queried by name
324eb7ee2cbSLaszlo Ersek          */
325eb7ee2cbSLaszlo Ersek         list = lookup_distinct(ov, name, errp);
326eb7ee2cbSLaszlo Ersek         return list ? g_queue_peek_tail(list) : NULL;
327eb7ee2cbSLaszlo Ersek     }
328863f195fSAndrey Shinkevich     if (ov->list_mode == LM_TRAVERSED) {
329863f195fSAndrey Shinkevich         error_setg(errp, "Fewer list elements than expected");
330863f195fSAndrey Shinkevich         return NULL;
331863f195fSAndrey Shinkevich     }
332d9570434SLaszlo Ersek     assert(ov->list_mode == LM_IN_PROGRESS);
333eb7ee2cbSLaszlo Ersek     return g_queue_peek_head(ov->repeated_opts);
334eb7ee2cbSLaszlo Ersek }
335eb7ee2cbSLaszlo Ersek 
336eb7ee2cbSLaszlo Ersek 
337eb7ee2cbSLaszlo Ersek static void
338eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name)
339eb7ee2cbSLaszlo Ersek {
340d9570434SLaszlo Ersek     if (ov->list_mode == LM_NONE) {
341eb7ee2cbSLaszlo Ersek         g_hash_table_remove(ov->unprocessed_opts, name);
342d9570434SLaszlo Ersek         return;
343eb7ee2cbSLaszlo Ersek     }
344d9570434SLaszlo Ersek     assert(ov->list_mode == LM_IN_PROGRESS);
345d9570434SLaszlo Ersek     /* do nothing */
346eb7ee2cbSLaszlo Ersek }
347eb7ee2cbSLaszlo Ersek 
348eb7ee2cbSLaszlo Ersek 
349*012d4c96SMarkus Armbruster static bool
3500b2a0d6bSEric Blake opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
351eb7ee2cbSLaszlo Ersek {
352d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
353eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
354eb7ee2cbSLaszlo Ersek 
355eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
356eb7ee2cbSLaszlo Ersek     if (!opt) {
357e58d695eSEric Blake         *obj = NULL;
358*012d4c96SMarkus Armbruster         return false;
359eb7ee2cbSLaszlo Ersek     }
360eb7ee2cbSLaszlo Ersek     *obj = g_strdup(opt->str ? opt->str : "");
361983f52d4SEric Blake     /* Note that we consume a string even if this is called as part of
362983f52d4SEric Blake      * an enum visit that later fails because the string is not a
363983f52d4SEric Blake      * valid enum value; this is harmless because tracking what gets
364983f52d4SEric Blake      * consumed only matters to visit_end_struct() as the final error
365983f52d4SEric Blake      * check if there were no other failures during the visit.  */
366eb7ee2cbSLaszlo Ersek     processed(ov, name);
367*012d4c96SMarkus Armbruster     return true;
368eb7ee2cbSLaszlo Ersek }
369eb7ee2cbSLaszlo Ersek 
370eb7ee2cbSLaszlo Ersek 
371eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */
372*012d4c96SMarkus Armbruster static bool
3730b2a0d6bSEric Blake opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
374eb7ee2cbSLaszlo Ersek {
375d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
376eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
377eb7ee2cbSLaszlo Ersek 
378eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
379eb7ee2cbSLaszlo Ersek     if (!opt) {
380*012d4c96SMarkus Armbruster         return false;
381eb7ee2cbSLaszlo Ersek     }
382eb7ee2cbSLaszlo Ersek 
383eb7ee2cbSLaszlo Ersek     if (opt->str) {
384eb7ee2cbSLaszlo Ersek         if (strcmp(opt->str, "on") == 0 ||
385eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "yes") == 0 ||
386eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "y") == 0) {
387eb7ee2cbSLaszlo Ersek             *obj = true;
388eb7ee2cbSLaszlo Ersek         } else if (strcmp(opt->str, "off") == 0 ||
389eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "no") == 0 ||
390eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "n") == 0) {
391eb7ee2cbSLaszlo Ersek             *obj = false;
392eb7ee2cbSLaszlo Ersek         } else {
393c6bd8c70SMarkus Armbruster             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
394eb7ee2cbSLaszlo Ersek                        "on|yes|y|off|no|n");
395*012d4c96SMarkus Armbruster             return false;
396eb7ee2cbSLaszlo Ersek         }
397eb7ee2cbSLaszlo Ersek     } else {
398eb7ee2cbSLaszlo Ersek         *obj = true;
399eb7ee2cbSLaszlo Ersek     }
400eb7ee2cbSLaszlo Ersek 
401eb7ee2cbSLaszlo Ersek     processed(ov, name);
402*012d4c96SMarkus Armbruster     return true;
403eb7ee2cbSLaszlo Ersek }
404eb7ee2cbSLaszlo Ersek 
405eb7ee2cbSLaszlo Ersek 
406*012d4c96SMarkus Armbruster static bool
4070b2a0d6bSEric Blake opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp)
408eb7ee2cbSLaszlo Ersek {
409d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
410eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
411eb7ee2cbSLaszlo Ersek     const char *str;
412eb7ee2cbSLaszlo Ersek     long long val;
413eb7ee2cbSLaszlo Ersek     char *endptr;
414eb7ee2cbSLaszlo Ersek 
415d8754f40SLaszlo Ersek     if (ov->list_mode == LM_SIGNED_INTERVAL) {
416d8754f40SLaszlo Ersek         *obj = ov->range_next.s;
417*012d4c96SMarkus Armbruster         return true;
418d8754f40SLaszlo Ersek     }
419d8754f40SLaszlo Ersek 
420eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
421eb7ee2cbSLaszlo Ersek     if (!opt) {
422*012d4c96SMarkus Armbruster         return false;
423eb7ee2cbSLaszlo Ersek     }
424eb7ee2cbSLaszlo Ersek     str = opt->str ? opt->str : "";
425eb7ee2cbSLaszlo Ersek 
4261e1c555aSLaszlo Ersek     /* we've gotten past lookup_scalar() */
4271e1c555aSLaszlo Ersek     assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS);
4281e1c555aSLaszlo Ersek 
429eb7ee2cbSLaszlo Ersek     errno = 0;
430eb7ee2cbSLaszlo Ersek     val = strtoll(str, &endptr, 0);
4311e1c555aSLaszlo Ersek     if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
4321e1c555aSLaszlo Ersek         if (*endptr == '\0') {
433eb7ee2cbSLaszlo Ersek             *obj = val;
434eb7ee2cbSLaszlo Ersek             processed(ov, name);
435*012d4c96SMarkus Armbruster             return true;
436eb7ee2cbSLaszlo Ersek         }
4371e1c555aSLaszlo Ersek         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
4381e1c555aSLaszlo Ersek             long long val2;
4391e1c555aSLaszlo Ersek 
4401e1c555aSLaszlo Ersek             str = endptr + 1;
4411e1c555aSLaszlo Ersek             val2 = strtoll(str, &endptr, 0);
4421e1c555aSLaszlo Ersek             if (errno == 0 && endptr > str && *endptr == '\0' &&
44315a849beSLaszlo Ersek                 INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 &&
44415a849beSLaszlo Ersek                 (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX ||
44515a849beSLaszlo Ersek                  val2 < val + OPTS_VISITOR_RANGE_MAX)) {
4461e1c555aSLaszlo Ersek                 ov->range_next.s = val;
4471e1c555aSLaszlo Ersek                 ov->range_limit.s = val2;
4481e1c555aSLaszlo Ersek                 ov->list_mode = LM_SIGNED_INTERVAL;
4491e1c555aSLaszlo Ersek 
4501e1c555aSLaszlo Ersek                 /* as if entering on the top */
4511e1c555aSLaszlo Ersek                 *obj = ov->range_next.s;
452*012d4c96SMarkus Armbruster                 return true;
4531e1c555aSLaszlo Ersek             }
4541e1c555aSLaszlo Ersek         }
4551e1c555aSLaszlo Ersek     }
456c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
4571e1c555aSLaszlo Ersek                (ov->list_mode == LM_NONE) ? "an int64 value" :
4581e1c555aSLaszlo Ersek                                             "an int64 value or range");
459*012d4c96SMarkus Armbruster     return false;
460eb7ee2cbSLaszlo Ersek }
461eb7ee2cbSLaszlo Ersek 
462eb7ee2cbSLaszlo Ersek 
463*012d4c96SMarkus Armbruster static bool
4640b2a0d6bSEric Blake opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp)
465eb7ee2cbSLaszlo Ersek {
466d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
467eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
468eb7ee2cbSLaszlo Ersek     const char *str;
46962d090e2SLaszlo Ersek     unsigned long long val;
470581a8a80SLaszlo Ersek     char *endptr;
471eb7ee2cbSLaszlo Ersek 
472d8754f40SLaszlo Ersek     if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
473d8754f40SLaszlo Ersek         *obj = ov->range_next.u;
474*012d4c96SMarkus Armbruster         return true;
475d8754f40SLaszlo Ersek     }
476d8754f40SLaszlo Ersek 
477eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
478eb7ee2cbSLaszlo Ersek     if (!opt) {
479*012d4c96SMarkus Armbruster         return false;
480eb7ee2cbSLaszlo Ersek     }
481eb7ee2cbSLaszlo Ersek     str = opt->str;
482eb7ee2cbSLaszlo Ersek 
483581a8a80SLaszlo Ersek     /* we've gotten past lookup_scalar() */
484581a8a80SLaszlo Ersek     assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS);
485581a8a80SLaszlo Ersek 
486581a8a80SLaszlo Ersek     if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
487581a8a80SLaszlo Ersek         if (*endptr == '\0') {
488eb7ee2cbSLaszlo Ersek             *obj = val;
489eb7ee2cbSLaszlo Ersek             processed(ov, name);
490*012d4c96SMarkus Armbruster             return true;
491eb7ee2cbSLaszlo Ersek         }
492581a8a80SLaszlo Ersek         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
493581a8a80SLaszlo Ersek             unsigned long long val2;
494581a8a80SLaszlo Ersek 
495581a8a80SLaszlo Ersek             str = endptr + 1;
496581a8a80SLaszlo Ersek             if (parse_uint_full(str, &val2, 0) == 0 &&
49715a849beSLaszlo Ersek                 val2 <= UINT64_MAX && val <= val2 &&
49815a849beSLaszlo Ersek                 val2 - val < OPTS_VISITOR_RANGE_MAX) {
499581a8a80SLaszlo Ersek                 ov->range_next.u = val;
500581a8a80SLaszlo Ersek                 ov->range_limit.u = val2;
501581a8a80SLaszlo Ersek                 ov->list_mode = LM_UNSIGNED_INTERVAL;
502581a8a80SLaszlo Ersek 
503581a8a80SLaszlo Ersek                 /* as if entering on the top */
504581a8a80SLaszlo Ersek                 *obj = ov->range_next.u;
505*012d4c96SMarkus Armbruster                 return true;
506581a8a80SLaszlo Ersek             }
507581a8a80SLaszlo Ersek         }
508581a8a80SLaszlo Ersek     }
509c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
510581a8a80SLaszlo Ersek                (ov->list_mode == LM_NONE) ? "a uint64 value" :
511581a8a80SLaszlo Ersek                                             "a uint64 value or range");
512*012d4c96SMarkus Armbruster     return false;
513eb7ee2cbSLaszlo Ersek }
514eb7ee2cbSLaszlo Ersek 
515eb7ee2cbSLaszlo Ersek 
516*012d4c96SMarkus Armbruster static bool
5170b2a0d6bSEric Blake opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp)
518eb7ee2cbSLaszlo Ersek {
519d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
520eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
521f17fd4fdSMarkus Armbruster     int err;
522eb7ee2cbSLaszlo Ersek 
523eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
524eb7ee2cbSLaszlo Ersek     if (!opt) {
525*012d4c96SMarkus Armbruster         return false;
526eb7ee2cbSLaszlo Ersek     }
527eb7ee2cbSLaszlo Ersek 
528f46bfdbfSMarkus Armbruster     err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj);
529f17fd4fdSMarkus Armbruster     if (err < 0) {
530c6bd8c70SMarkus Armbruster         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
531f46bfdbfSMarkus Armbruster                    "a size value");
532*012d4c96SMarkus Armbruster         return false;
533cb45de67SAmos Kong     }
534cb45de67SAmos Kong 
535cb45de67SAmos Kong     processed(ov, name);
536*012d4c96SMarkus Armbruster     return true;
537eb7ee2cbSLaszlo Ersek }
538eb7ee2cbSLaszlo Ersek 
539eb7ee2cbSLaszlo Ersek 
540eb7ee2cbSLaszlo Ersek static void
5410b2a0d6bSEric Blake opts_optional(Visitor *v, const char *name, bool *present)
542eb7ee2cbSLaszlo Ersek {
543d7bea75dSEric Blake     OptsVisitor *ov = to_ov(v);
544eb7ee2cbSLaszlo Ersek 
545eb7ee2cbSLaszlo Ersek     /* we only support a single mandatory scalar field in a list node */
546d9570434SLaszlo Ersek     assert(ov->list_mode == LM_NONE);
547eb7ee2cbSLaszlo Ersek     *present = (lookup_distinct(ov, name, NULL) != NULL);
548eb7ee2cbSLaszlo Ersek }
549eb7ee2cbSLaszlo Ersek 
550eb7ee2cbSLaszlo Ersek 
5512c0ef9f4SEric Blake static void
5522c0ef9f4SEric Blake opts_free(Visitor *v)
5532c0ef9f4SEric Blake {
5542c0ef9f4SEric Blake     OptsVisitor *ov = to_ov(v);
5552c0ef9f4SEric Blake 
55609204eacSEric Blake     if (ov->unprocessed_opts != NULL) {
55709204eacSEric Blake         g_hash_table_destroy(ov->unprocessed_opts);
55809204eacSEric Blake     }
55909204eacSEric Blake     g_free(ov->fake_id_opt);
56009204eacSEric Blake     g_free(ov);
5612c0ef9f4SEric Blake }
5622c0ef9f4SEric Blake 
5632c0ef9f4SEric Blake 
56409204eacSEric Blake Visitor *
565eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts)
566eb7ee2cbSLaszlo Ersek {
567eb7ee2cbSLaszlo Ersek     OptsVisitor *ov;
568eb7ee2cbSLaszlo Ersek 
569f332e830SMarkus Armbruster     assert(opts);
570eb7ee2cbSLaszlo Ersek     ov = g_malloc0(sizeof *ov);
571eb7ee2cbSLaszlo Ersek 
572983f52d4SEric Blake     ov->visitor.type = VISITOR_INPUT;
573983f52d4SEric Blake 
574eb7ee2cbSLaszlo Ersek     ov->visitor.start_struct = &opts_start_struct;
57515c2f669SEric Blake     ov->visitor.check_struct = &opts_check_struct;
576eb7ee2cbSLaszlo Ersek     ov->visitor.end_struct   = &opts_end_struct;
577eb7ee2cbSLaszlo Ersek 
578eb7ee2cbSLaszlo Ersek     ov->visitor.start_list = &opts_start_list;
579eb7ee2cbSLaszlo Ersek     ov->visitor.next_list  = &opts_next_list;
580a4a1c70dSMarkus Armbruster     ov->visitor.check_list = &opts_check_list;
581eb7ee2cbSLaszlo Ersek     ov->visitor.end_list   = &opts_end_list;
582eb7ee2cbSLaszlo Ersek 
5834c40314aSEric Blake     ov->visitor.type_int64  = &opts_type_int64;
584eb7ee2cbSLaszlo Ersek     ov->visitor.type_uint64 = &opts_type_uint64;
585eb7ee2cbSLaszlo Ersek     ov->visitor.type_size   = &opts_type_size;
586eb7ee2cbSLaszlo Ersek     ov->visitor.type_bool   = &opts_type_bool;
587eb7ee2cbSLaszlo Ersek     ov->visitor.type_str    = &opts_type_str;
588eb7ee2cbSLaszlo Ersek 
589eb7ee2cbSLaszlo Ersek     /* type_number() is not filled in, but this is not the first visitor to
590eb7ee2cbSLaszlo Ersek      * skip some mandatory methods... */
591eb7ee2cbSLaszlo Ersek 
592e2cd0f4fSMarkus Armbruster     ov->visitor.optional = &opts_optional;
5932c0ef9f4SEric Blake     ov->visitor.free = opts_free;
594eb7ee2cbSLaszlo Ersek 
595eb7ee2cbSLaszlo Ersek     ov->opts_root = opts;
596eb7ee2cbSLaszlo Ersek 
597eb7ee2cbSLaszlo Ersek     return &ov->visitor;
598eb7ee2cbSLaszlo Ersek }
599