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