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