xref: /openbmc/qemu/qapi/opts-visitor.c (revision 1de7afc9)
1eb7ee2cbSLaszlo Ersek /*
2eb7ee2cbSLaszlo Ersek  * Options Visitor
3eb7ee2cbSLaszlo Ersek  *
4eb7ee2cbSLaszlo Ersek  * Copyright Red Hat, Inc. 2012
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 
1379ee7df8SPaolo Bonzini #include "qemu-common.h"
147b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
157b1b5d19SPaolo Bonzini #include "qapi/opts-visitor.h"
16*1de7afc9SPaolo Bonzini #include "qemu/queue.h"
17*1de7afc9SPaolo Bonzini #include "qemu/option_int.h"
187b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h"
19eb7ee2cbSLaszlo Ersek 
20eb7ee2cbSLaszlo Ersek 
21eb7ee2cbSLaszlo Ersek struct OptsVisitor
22eb7ee2cbSLaszlo Ersek {
23eb7ee2cbSLaszlo Ersek     Visitor visitor;
24eb7ee2cbSLaszlo Ersek 
25eb7ee2cbSLaszlo Ersek     /* Ownership remains with opts_visitor_new()'s caller. */
26eb7ee2cbSLaszlo Ersek     const QemuOpts *opts_root;
27eb7ee2cbSLaszlo Ersek 
28eb7ee2cbSLaszlo Ersek     unsigned depth;
29eb7ee2cbSLaszlo Ersek 
30eb7ee2cbSLaszlo Ersek     /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
31eb7ee2cbSLaszlo Ersek      * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
32eb7ee2cbSLaszlo Ersek      * name. */
33eb7ee2cbSLaszlo Ersek     GHashTable *unprocessed_opts;
34eb7ee2cbSLaszlo Ersek 
35eb7ee2cbSLaszlo Ersek     /* The list currently being traversed with opts_start_list() /
36eb7ee2cbSLaszlo Ersek      * opts_next_list(). The list must have a struct element type in the
37eb7ee2cbSLaszlo Ersek      * schema, with a single mandatory scalar member. */
38eb7ee2cbSLaszlo Ersek     GQueue *repeated_opts;
39eb7ee2cbSLaszlo Ersek     bool repeated_opts_first;
40eb7ee2cbSLaszlo Ersek 
41eb7ee2cbSLaszlo Ersek     /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
42eb7ee2cbSLaszlo Ersek      * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
43eb7ee2cbSLaszlo Ersek      * not survive or escape the OptsVisitor object.
44eb7ee2cbSLaszlo Ersek      */
45eb7ee2cbSLaszlo Ersek     QemuOpt *fake_id_opt;
46eb7ee2cbSLaszlo Ersek };
47eb7ee2cbSLaszlo Ersek 
48eb7ee2cbSLaszlo Ersek 
49eb7ee2cbSLaszlo Ersek static void
50eb7ee2cbSLaszlo Ersek destroy_list(gpointer list)
51eb7ee2cbSLaszlo Ersek {
52eb7ee2cbSLaszlo Ersek   g_queue_free(list);
53eb7ee2cbSLaszlo Ersek }
54eb7ee2cbSLaszlo Ersek 
55eb7ee2cbSLaszlo Ersek 
56eb7ee2cbSLaszlo Ersek static void
57eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
58eb7ee2cbSLaszlo Ersek {
59eb7ee2cbSLaszlo Ersek     GQueue *list;
60eb7ee2cbSLaszlo Ersek 
61eb7ee2cbSLaszlo Ersek     list = g_hash_table_lookup(unprocessed_opts, opt->name);
62eb7ee2cbSLaszlo Ersek     if (list == NULL) {
63eb7ee2cbSLaszlo Ersek         list = g_queue_new();
64eb7ee2cbSLaszlo Ersek 
65eb7ee2cbSLaszlo Ersek         /* GHashTable will never try to free the keys -- we supply NULL as
66eb7ee2cbSLaszlo Ersek          * "key_destroy_func" in opts_start_struct(). Thus cast away key
67eb7ee2cbSLaszlo Ersek          * const-ness in order to suppress gcc's warning.
68eb7ee2cbSLaszlo Ersek          */
69eb7ee2cbSLaszlo Ersek         g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
70eb7ee2cbSLaszlo Ersek     }
71eb7ee2cbSLaszlo Ersek 
72eb7ee2cbSLaszlo Ersek     /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
73eb7ee2cbSLaszlo Ersek     g_queue_push_tail(list, (gpointer)opt);
74eb7ee2cbSLaszlo Ersek }
75eb7ee2cbSLaszlo Ersek 
76eb7ee2cbSLaszlo Ersek 
77eb7ee2cbSLaszlo Ersek static void
78eb7ee2cbSLaszlo Ersek opts_start_struct(Visitor *v, void **obj, const char *kind,
79eb7ee2cbSLaszlo Ersek                   const char *name, size_t size, Error **errp)
80eb7ee2cbSLaszlo Ersek {
81eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
82eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
83eb7ee2cbSLaszlo Ersek 
84eb7ee2cbSLaszlo Ersek     *obj = g_malloc0(size > 0 ? size : 1);
85eb7ee2cbSLaszlo Ersek     if (ov->depth++ > 0) {
86eb7ee2cbSLaszlo Ersek         return;
87eb7ee2cbSLaszlo Ersek     }
88eb7ee2cbSLaszlo Ersek 
89eb7ee2cbSLaszlo Ersek     ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
90eb7ee2cbSLaszlo Ersek                                                  NULL, &destroy_list);
91eb7ee2cbSLaszlo Ersek     QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
92eb7ee2cbSLaszlo Ersek         /* ensured by qemu-option.c::opts_do_parse() */
93eb7ee2cbSLaszlo Ersek         assert(strcmp(opt->name, "id") != 0);
94eb7ee2cbSLaszlo Ersek 
95eb7ee2cbSLaszlo Ersek         opts_visitor_insert(ov->unprocessed_opts, opt);
96eb7ee2cbSLaszlo Ersek     }
97eb7ee2cbSLaszlo Ersek 
98eb7ee2cbSLaszlo Ersek     if (ov->opts_root->id != NULL) {
99eb7ee2cbSLaszlo Ersek         ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
100eb7ee2cbSLaszlo Ersek 
101eb7ee2cbSLaszlo Ersek         ov->fake_id_opt->name = "id";
102eb7ee2cbSLaszlo Ersek         ov->fake_id_opt->str = ov->opts_root->id;
103eb7ee2cbSLaszlo Ersek         opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
104eb7ee2cbSLaszlo Ersek     }
105eb7ee2cbSLaszlo Ersek }
106eb7ee2cbSLaszlo Ersek 
107eb7ee2cbSLaszlo Ersek 
108eb7ee2cbSLaszlo Ersek static gboolean
109eb7ee2cbSLaszlo Ersek ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
110eb7ee2cbSLaszlo Ersek {
111eb7ee2cbSLaszlo Ersek     return TRUE;
112eb7ee2cbSLaszlo Ersek }
113eb7ee2cbSLaszlo Ersek 
114eb7ee2cbSLaszlo Ersek 
115eb7ee2cbSLaszlo Ersek static void
116eb7ee2cbSLaszlo Ersek opts_end_struct(Visitor *v, Error **errp)
117eb7ee2cbSLaszlo Ersek {
118eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
119eb7ee2cbSLaszlo Ersek     GQueue *any;
120eb7ee2cbSLaszlo Ersek 
121eb7ee2cbSLaszlo Ersek     if (--ov->depth > 0) {
122eb7ee2cbSLaszlo Ersek         return;
123eb7ee2cbSLaszlo Ersek     }
124eb7ee2cbSLaszlo Ersek 
125eb7ee2cbSLaszlo Ersek     /* we should have processed all (distinct) QemuOpt instances */
126eb7ee2cbSLaszlo Ersek     any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
127eb7ee2cbSLaszlo Ersek     if (any) {
128eb7ee2cbSLaszlo Ersek         const QemuOpt *first;
129eb7ee2cbSLaszlo Ersek 
130eb7ee2cbSLaszlo Ersek         first = g_queue_peek_head(any);
131eb7ee2cbSLaszlo Ersek         error_set(errp, QERR_INVALID_PARAMETER, first->name);
132eb7ee2cbSLaszlo Ersek     }
133eb7ee2cbSLaszlo Ersek     g_hash_table_destroy(ov->unprocessed_opts);
134eb7ee2cbSLaszlo Ersek     ov->unprocessed_opts = NULL;
135eb7ee2cbSLaszlo Ersek     g_free(ov->fake_id_opt);
136eb7ee2cbSLaszlo Ersek     ov->fake_id_opt = NULL;
137eb7ee2cbSLaszlo Ersek }
138eb7ee2cbSLaszlo Ersek 
139eb7ee2cbSLaszlo Ersek 
140eb7ee2cbSLaszlo Ersek static GQueue *
141eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
142eb7ee2cbSLaszlo Ersek {
143eb7ee2cbSLaszlo Ersek     GQueue *list;
144eb7ee2cbSLaszlo Ersek 
145eb7ee2cbSLaszlo Ersek     list = g_hash_table_lookup(ov->unprocessed_opts, name);
146eb7ee2cbSLaszlo Ersek     if (!list) {
147eb7ee2cbSLaszlo Ersek         error_set(errp, QERR_MISSING_PARAMETER, name);
148eb7ee2cbSLaszlo Ersek     }
149eb7ee2cbSLaszlo Ersek     return list;
150eb7ee2cbSLaszlo Ersek }
151eb7ee2cbSLaszlo Ersek 
152eb7ee2cbSLaszlo Ersek 
153eb7ee2cbSLaszlo Ersek static void
154eb7ee2cbSLaszlo Ersek opts_start_list(Visitor *v, const char *name, Error **errp)
155eb7ee2cbSLaszlo Ersek {
156eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
157eb7ee2cbSLaszlo Ersek 
158eb7ee2cbSLaszlo Ersek     /* we can't traverse a list in a list */
159eb7ee2cbSLaszlo Ersek     assert(ov->repeated_opts == NULL);
160eb7ee2cbSLaszlo Ersek     ov->repeated_opts = lookup_distinct(ov, name, errp);
161eb7ee2cbSLaszlo Ersek     ov->repeated_opts_first = (ov->repeated_opts != NULL);
162eb7ee2cbSLaszlo Ersek }
163eb7ee2cbSLaszlo Ersek 
164eb7ee2cbSLaszlo Ersek 
165eb7ee2cbSLaszlo Ersek static GenericList *
166eb7ee2cbSLaszlo Ersek opts_next_list(Visitor *v, GenericList **list, Error **errp)
167eb7ee2cbSLaszlo Ersek {
168eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
169eb7ee2cbSLaszlo Ersek     GenericList **link;
170eb7ee2cbSLaszlo Ersek 
171eb7ee2cbSLaszlo Ersek     if (ov->repeated_opts_first) {
172eb7ee2cbSLaszlo Ersek         ov->repeated_opts_first = false;
173eb7ee2cbSLaszlo Ersek         link = list;
174eb7ee2cbSLaszlo Ersek     } else {
175eb7ee2cbSLaszlo Ersek         const QemuOpt *opt;
176eb7ee2cbSLaszlo Ersek 
177eb7ee2cbSLaszlo Ersek         opt = g_queue_pop_head(ov->repeated_opts);
178eb7ee2cbSLaszlo Ersek         if (g_queue_is_empty(ov->repeated_opts)) {
179eb7ee2cbSLaszlo Ersek             g_hash_table_remove(ov->unprocessed_opts, opt->name);
180eb7ee2cbSLaszlo Ersek             return NULL;
181eb7ee2cbSLaszlo Ersek         }
182eb7ee2cbSLaszlo Ersek         link = &(*list)->next;
183eb7ee2cbSLaszlo Ersek     }
184eb7ee2cbSLaszlo Ersek 
185eb7ee2cbSLaszlo Ersek     *link = g_malloc0(sizeof **link);
186eb7ee2cbSLaszlo Ersek     return *link;
187eb7ee2cbSLaszlo Ersek }
188eb7ee2cbSLaszlo Ersek 
189eb7ee2cbSLaszlo Ersek 
190eb7ee2cbSLaszlo Ersek static void
191eb7ee2cbSLaszlo Ersek opts_end_list(Visitor *v, Error **errp)
192eb7ee2cbSLaszlo Ersek {
193eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
194eb7ee2cbSLaszlo Ersek 
195eb7ee2cbSLaszlo Ersek     ov->repeated_opts = NULL;
196eb7ee2cbSLaszlo Ersek }
197eb7ee2cbSLaszlo Ersek 
198eb7ee2cbSLaszlo Ersek 
199eb7ee2cbSLaszlo Ersek static const QemuOpt *
200eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
201eb7ee2cbSLaszlo Ersek {
202eb7ee2cbSLaszlo Ersek     if (ov->repeated_opts == NULL) {
203eb7ee2cbSLaszlo Ersek         GQueue *list;
204eb7ee2cbSLaszlo Ersek 
205eb7ee2cbSLaszlo Ersek         /* the last occurrence of any QemuOpt takes effect when queried by name
206eb7ee2cbSLaszlo Ersek          */
207eb7ee2cbSLaszlo Ersek         list = lookup_distinct(ov, name, errp);
208eb7ee2cbSLaszlo Ersek         return list ? g_queue_peek_tail(list) : NULL;
209eb7ee2cbSLaszlo Ersek     }
210eb7ee2cbSLaszlo Ersek     return g_queue_peek_head(ov->repeated_opts);
211eb7ee2cbSLaszlo Ersek }
212eb7ee2cbSLaszlo Ersek 
213eb7ee2cbSLaszlo Ersek 
214eb7ee2cbSLaszlo Ersek static void
215eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name)
216eb7ee2cbSLaszlo Ersek {
217eb7ee2cbSLaszlo Ersek     if (ov->repeated_opts == NULL) {
218eb7ee2cbSLaszlo Ersek         g_hash_table_remove(ov->unprocessed_opts, name);
219eb7ee2cbSLaszlo Ersek     }
220eb7ee2cbSLaszlo Ersek }
221eb7ee2cbSLaszlo Ersek 
222eb7ee2cbSLaszlo Ersek 
223eb7ee2cbSLaszlo Ersek static void
224eb7ee2cbSLaszlo Ersek opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
225eb7ee2cbSLaszlo Ersek {
226eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
227eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
228eb7ee2cbSLaszlo Ersek 
229eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
230eb7ee2cbSLaszlo Ersek     if (!opt) {
231eb7ee2cbSLaszlo Ersek         return;
232eb7ee2cbSLaszlo Ersek     }
233eb7ee2cbSLaszlo Ersek     *obj = g_strdup(opt->str ? opt->str : "");
234eb7ee2cbSLaszlo Ersek     processed(ov, name);
235eb7ee2cbSLaszlo Ersek }
236eb7ee2cbSLaszlo Ersek 
237eb7ee2cbSLaszlo Ersek 
238eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */
239eb7ee2cbSLaszlo Ersek static void
240eb7ee2cbSLaszlo Ersek opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
241eb7ee2cbSLaszlo Ersek {
242eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
243eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
244eb7ee2cbSLaszlo Ersek 
245eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
246eb7ee2cbSLaszlo Ersek     if (!opt) {
247eb7ee2cbSLaszlo Ersek         return;
248eb7ee2cbSLaszlo Ersek     }
249eb7ee2cbSLaszlo Ersek 
250eb7ee2cbSLaszlo Ersek     if (opt->str) {
251eb7ee2cbSLaszlo Ersek         if (strcmp(opt->str, "on") == 0 ||
252eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "yes") == 0 ||
253eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "y") == 0) {
254eb7ee2cbSLaszlo Ersek             *obj = true;
255eb7ee2cbSLaszlo Ersek         } else if (strcmp(opt->str, "off") == 0 ||
256eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "no") == 0 ||
257eb7ee2cbSLaszlo Ersek             strcmp(opt->str, "n") == 0) {
258eb7ee2cbSLaszlo Ersek             *obj = false;
259eb7ee2cbSLaszlo Ersek         } else {
260eb7ee2cbSLaszlo Ersek             error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
261eb7ee2cbSLaszlo Ersek                 "on|yes|y|off|no|n");
262eb7ee2cbSLaszlo Ersek             return;
263eb7ee2cbSLaszlo Ersek         }
264eb7ee2cbSLaszlo Ersek     } else {
265eb7ee2cbSLaszlo Ersek         *obj = true;
266eb7ee2cbSLaszlo Ersek     }
267eb7ee2cbSLaszlo Ersek 
268eb7ee2cbSLaszlo Ersek     processed(ov, name);
269eb7ee2cbSLaszlo Ersek }
270eb7ee2cbSLaszlo Ersek 
271eb7ee2cbSLaszlo Ersek 
272eb7ee2cbSLaszlo Ersek static void
273eb7ee2cbSLaszlo Ersek opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
274eb7ee2cbSLaszlo Ersek {
275eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
276eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
277eb7ee2cbSLaszlo Ersek     const char *str;
278eb7ee2cbSLaszlo Ersek     long long val;
279eb7ee2cbSLaszlo Ersek     char *endptr;
280eb7ee2cbSLaszlo Ersek 
281eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
282eb7ee2cbSLaszlo Ersek     if (!opt) {
283eb7ee2cbSLaszlo Ersek         return;
284eb7ee2cbSLaszlo Ersek     }
285eb7ee2cbSLaszlo Ersek     str = opt->str ? opt->str : "";
286eb7ee2cbSLaszlo Ersek 
287eb7ee2cbSLaszlo Ersek     errno = 0;
288eb7ee2cbSLaszlo Ersek     val = strtoll(str, &endptr, 0);
289eb7ee2cbSLaszlo Ersek     if (*str != '\0' && *endptr == '\0' && errno == 0 && INT64_MIN <= val &&
290eb7ee2cbSLaszlo Ersek         val <= INT64_MAX) {
291eb7ee2cbSLaszlo Ersek         *obj = val;
292eb7ee2cbSLaszlo Ersek         processed(ov, name);
293eb7ee2cbSLaszlo Ersek         return;
294eb7ee2cbSLaszlo Ersek     }
295eb7ee2cbSLaszlo Ersek     error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, "an int64 value");
296eb7ee2cbSLaszlo Ersek }
297eb7ee2cbSLaszlo Ersek 
298eb7ee2cbSLaszlo Ersek 
299eb7ee2cbSLaszlo Ersek static void
300eb7ee2cbSLaszlo Ersek opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
301eb7ee2cbSLaszlo Ersek {
302eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
303eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
304eb7ee2cbSLaszlo Ersek     const char *str;
305eb7ee2cbSLaszlo Ersek 
306eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
307eb7ee2cbSLaszlo Ersek     if (!opt) {
308eb7ee2cbSLaszlo Ersek         return;
309eb7ee2cbSLaszlo Ersek     }
310eb7ee2cbSLaszlo Ersek 
311eb7ee2cbSLaszlo Ersek     str = opt->str;
312eb7ee2cbSLaszlo Ersek     if (str != NULL) {
313eb7ee2cbSLaszlo Ersek         while (isspace((unsigned char)*str)) {
314eb7ee2cbSLaszlo Ersek             ++str;
315eb7ee2cbSLaszlo Ersek         }
316eb7ee2cbSLaszlo Ersek 
317eb7ee2cbSLaszlo Ersek         if (*str != '-' && *str != '\0') {
318eb7ee2cbSLaszlo Ersek             unsigned long long val;
319eb7ee2cbSLaszlo Ersek             char *endptr;
320eb7ee2cbSLaszlo Ersek 
321eb7ee2cbSLaszlo Ersek             /* non-empty, non-negative subject sequence */
322eb7ee2cbSLaszlo Ersek             errno = 0;
323eb7ee2cbSLaszlo Ersek             val = strtoull(str, &endptr, 0);
324eb7ee2cbSLaszlo Ersek             if (*endptr == '\0' && errno == 0 && val <= UINT64_MAX) {
325eb7ee2cbSLaszlo Ersek                 *obj = val;
326eb7ee2cbSLaszlo Ersek                 processed(ov, name);
327eb7ee2cbSLaszlo Ersek                 return;
328eb7ee2cbSLaszlo Ersek             }
329eb7ee2cbSLaszlo Ersek         }
330eb7ee2cbSLaszlo Ersek     }
331eb7ee2cbSLaszlo Ersek     error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
332eb7ee2cbSLaszlo Ersek               "an uint64 value");
333eb7ee2cbSLaszlo Ersek }
334eb7ee2cbSLaszlo Ersek 
335eb7ee2cbSLaszlo Ersek 
336eb7ee2cbSLaszlo Ersek static void
337eb7ee2cbSLaszlo Ersek opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
338eb7ee2cbSLaszlo Ersek {
339eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
340eb7ee2cbSLaszlo Ersek     const QemuOpt *opt;
341eb7ee2cbSLaszlo Ersek     int64_t val;
342eb7ee2cbSLaszlo Ersek     char *endptr;
343eb7ee2cbSLaszlo Ersek 
344eb7ee2cbSLaszlo Ersek     opt = lookup_scalar(ov, name, errp);
345eb7ee2cbSLaszlo Ersek     if (!opt) {
346eb7ee2cbSLaszlo Ersek         return;
347eb7ee2cbSLaszlo Ersek     }
348eb7ee2cbSLaszlo Ersek 
349eb7ee2cbSLaszlo Ersek     val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
350eb7ee2cbSLaszlo Ersek                          STRTOSZ_DEFSUFFIX_B);
351eb7ee2cbSLaszlo Ersek     if (val != -1 && *endptr == '\0') {
352eb7ee2cbSLaszlo Ersek         *obj = val;
353eb7ee2cbSLaszlo Ersek         processed(ov, name);
354eb7ee2cbSLaszlo Ersek         return;
355eb7ee2cbSLaszlo Ersek     }
356eb7ee2cbSLaszlo Ersek     error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
357eb7ee2cbSLaszlo Ersek               "a size value representible as a non-negative int64");
358eb7ee2cbSLaszlo Ersek }
359eb7ee2cbSLaszlo Ersek 
360eb7ee2cbSLaszlo Ersek 
361eb7ee2cbSLaszlo Ersek static void
362eb7ee2cbSLaszlo Ersek opts_start_optional(Visitor *v, bool *present, const char *name,
363eb7ee2cbSLaszlo Ersek                        Error **errp)
364eb7ee2cbSLaszlo Ersek {
365eb7ee2cbSLaszlo Ersek     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
366eb7ee2cbSLaszlo Ersek 
367eb7ee2cbSLaszlo Ersek     /* we only support a single mandatory scalar field in a list node */
368eb7ee2cbSLaszlo Ersek     assert(ov->repeated_opts == NULL);
369eb7ee2cbSLaszlo Ersek     *present = (lookup_distinct(ov, name, NULL) != NULL);
370eb7ee2cbSLaszlo Ersek }
371eb7ee2cbSLaszlo Ersek 
372eb7ee2cbSLaszlo Ersek 
373eb7ee2cbSLaszlo Ersek OptsVisitor *
374eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts)
375eb7ee2cbSLaszlo Ersek {
376eb7ee2cbSLaszlo Ersek     OptsVisitor *ov;
377eb7ee2cbSLaszlo Ersek 
378eb7ee2cbSLaszlo Ersek     ov = g_malloc0(sizeof *ov);
379eb7ee2cbSLaszlo Ersek 
380eb7ee2cbSLaszlo Ersek     ov->visitor.start_struct = &opts_start_struct;
381eb7ee2cbSLaszlo Ersek     ov->visitor.end_struct   = &opts_end_struct;
382eb7ee2cbSLaszlo Ersek 
383eb7ee2cbSLaszlo Ersek     ov->visitor.start_list = &opts_start_list;
384eb7ee2cbSLaszlo Ersek     ov->visitor.next_list  = &opts_next_list;
385eb7ee2cbSLaszlo Ersek     ov->visitor.end_list   = &opts_end_list;
386eb7ee2cbSLaszlo Ersek 
387eb7ee2cbSLaszlo Ersek     /* input_type_enum() covers both "normal" enums and union discriminators.
388eb7ee2cbSLaszlo Ersek      * The union discriminator field is always generated as "type"; it should
389eb7ee2cbSLaszlo Ersek      * match the "type" QemuOpt child of any QemuOpts.
390eb7ee2cbSLaszlo Ersek      *
391eb7ee2cbSLaszlo Ersek      * input_type_enum() will remove the looked-up key from the
392eb7ee2cbSLaszlo Ersek      * "unprocessed_opts" hash even if the lookup fails, because the removal is
393eb7ee2cbSLaszlo Ersek      * done earlier in opts_type_str(). This should be harmless.
394eb7ee2cbSLaszlo Ersek      */
395eb7ee2cbSLaszlo Ersek     ov->visitor.type_enum = &input_type_enum;
396eb7ee2cbSLaszlo Ersek 
397eb7ee2cbSLaszlo Ersek     ov->visitor.type_int    = &opts_type_int;
398eb7ee2cbSLaszlo Ersek     ov->visitor.type_uint64 = &opts_type_uint64;
399eb7ee2cbSLaszlo Ersek     ov->visitor.type_size   = &opts_type_size;
400eb7ee2cbSLaszlo Ersek     ov->visitor.type_bool   = &opts_type_bool;
401eb7ee2cbSLaszlo Ersek     ov->visitor.type_str    = &opts_type_str;
402eb7ee2cbSLaszlo Ersek 
403eb7ee2cbSLaszlo Ersek     /* type_number() is not filled in, but this is not the first visitor to
404eb7ee2cbSLaszlo Ersek      * skip some mandatory methods... */
405eb7ee2cbSLaszlo Ersek 
406eb7ee2cbSLaszlo Ersek     ov->visitor.start_optional = &opts_start_optional;
407eb7ee2cbSLaszlo Ersek 
408eb7ee2cbSLaszlo Ersek     ov->opts_root = opts;
409eb7ee2cbSLaszlo Ersek 
410eb7ee2cbSLaszlo Ersek     return ov;
411eb7ee2cbSLaszlo Ersek }
412eb7ee2cbSLaszlo Ersek 
413eb7ee2cbSLaszlo Ersek 
414eb7ee2cbSLaszlo Ersek void
415eb7ee2cbSLaszlo Ersek opts_visitor_cleanup(OptsVisitor *ov)
416eb7ee2cbSLaszlo Ersek {
417eb7ee2cbSLaszlo Ersek     if (ov->unprocessed_opts != NULL) {
418eb7ee2cbSLaszlo Ersek         g_hash_table_destroy(ov->unprocessed_opts);
419eb7ee2cbSLaszlo Ersek     }
420eb7ee2cbSLaszlo Ersek     g_free(ov->fake_id_opt);
421e36c8766SStefan Weil     g_free(ov);
422eb7ee2cbSLaszlo Ersek }
423eb7ee2cbSLaszlo Ersek 
424eb7ee2cbSLaszlo Ersek 
425eb7ee2cbSLaszlo Ersek Visitor *
426eb7ee2cbSLaszlo Ersek opts_get_visitor(OptsVisitor *ov)
427eb7ee2cbSLaszlo Ersek {
428eb7ee2cbSLaszlo Ersek     return &ov->visitor;
429eb7ee2cbSLaszlo Ersek }
430